All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sandinh.couchbase.access.CaoTrait.scala Maven / Gradle / Ivy

The newest version!
package com.sandinh.couchbase.access

import com.couchbase.client.scala.kv.OptionsConvert._
import com.couchbase.client.core.error.{
  CasMismatchException,
  DocumentExistsException,
  DocumentNotFoundException
}
import com.couchbase.client.scala.durability.Durability
import com.couchbase.client.scala.durability.Durability.Disabled
import com.couchbase.client.scala.kv.{
  GetOptions,
  GetResult,
  InsertOptions,
  MutationResult,
  RemoveOptions,
  ReplaceOptions,
  UpsertOptions
}
import play.api.libs.json.{Format, JsValue}

import scala.concurrent.{ExecutionContext, Future}
import scala.concurrent.duration.Duration

/** Common interface for [[JsCao]] and [[JsCao1]]
  *
  * @tparam A String for document ID type, as in [[JsCao]]
  *           Or some type that will be used to create the document id, as in JsCao1.key(A)
  */
private[access] trait CaoTrait[T, A] {
  protected implicit val fmt: Format[T]

  protected def expiry: Duration = null

  @deprecated("", "10.0.0")
  final type DocumentCAS = (T, Long)

  /** @param id document id or the param of JsCao1.key(a: A) */
  def getResult(
    id: A,
    options: GetOptions = GetOptions()
  ): Future[GetResult]

  /** @param id document id or the param of JsCao1.key(a: A) */
  final def get(
    id: A,
    options: GetOptions = GetOptions()
  )(implicit ec: ExecutionContext): Future[T] =
    getResult(id, options).map(_.contentAs[JsValue].get.as[T])

  /** @param id document id or the param of JsCao1.key(a: A) */
  final def getWithCAS(
    id: A,
    options: GetOptions = GetOptions()
  )(implicit ec: ExecutionContext): Future[(T, Long)] =
    getResult(id, options).map { r =>
      r.contentAs[JsValue].get.as[T] -> r.cas
    }

  /** @param id document id or the param of JsCao1.key(a: A) */
  final def getOrElse(
    id: A,
    options: GetOptions = GetOptions()
  )(default: => T)(implicit ec: ExecutionContext): Future[T] =
    get(id, options).recover { case _: DocumentNotFoundException =>
      default
    }

  /** @param id document id or the param of JsCao1.key(a: A) */
  final def getOrElseWithCAS(
    id: A,
    options: GetOptions = GetOptions()
  )(default: => T)(implicit ec: ExecutionContext): Future[(T, Long)] =
    getWithCAS(id, options).recover { case _: DocumentNotFoundException =>
      (default, -1)
    }

  /** @param id document id or the param of JsCao1.key(a: A) */
  final def getOrUpdate(
    id: A,
    options: GetOptions = GetOptions()
  )(default: => T)(implicit ec: ExecutionContext): Future[T] =
    get(id, options).recoverWith { case _: DocumentNotFoundException =>
      setT(id, default)
    }

  /** @param ids Seq of document id or the param of JsCao1.key(a: A) */
  final def getBulk(ids: Seq[A])(
    implicit ec: ExecutionContext
  ): Future[Seq[T]] =
    Future.traverse(ids)(get(_, GetOptions()))

  /** @param ids Seq of document id or the param of JsCao1.key(a: A) */
  final def getBulkWithCAS(ids: Seq[A])(
    implicit ec: ExecutionContext
  ): Future[Seq[(T, Long)]] =
    Future.traverse(ids)(getWithCAS(_, GetOptions()))

  /** @param ids Seq of document id or the param of JsCao1.key(a: A) */
  final def setBulk(ids: Seq[A], contents: Seq[T])(
    implicit ec: ExecutionContext
  ): Future[Seq[MutationResult]] =
    Future.traverse(ids zip contents) { case (a, t) => upsert(a, t) }

  @deprecated("Use upsert or replace. This `set` method use `upsert`", "10.0.0")
  final def set(
    id: A,
    content: T,
    options: UpsertOptions = UpsertOptions()
  ): Future[MutationResult] = upsert(id, content, options)

  /** Inserts a full document into this collection, if it does not exist already.
    * @param id document id or the param of JsCao1.key(a: A)
    * @param content the object of your own type `T` ex T=`case class User(...)`
    *          to be insert into cb server
    * @see [[upsert]], [[replace]]
    */
  def insert(
    id: A,
    content: T,
    options: InsertOptions = InsertOptions()
  ): Future[MutationResult]

  /** Upserts the contents of a full document in this collection.
    * @param id document id or the param of JsCao1.key(a: A)
    * @param content the object of your own type `T` ex T=`case class User(...)`
    *          to be upsert into cb server
    * @see [[insert]], [[replace]]
    */
  def upsert(
    id: A,
    content: T,
    options: UpsertOptions = UpsertOptions()
  ): Future[MutationResult]

  /** Replaces the contents of a full document, if it already exists.
    * @param id document id or the param of JsCao1.key(a: A)
    * @param content the object of your own type `T` ex T=`case class User(...)`
    *          to be replace into cb server
    * @see [[insert]], [[upsert]]
    */
  def replace(
    id: A,
    content: T,
    options: ReplaceOptions
  ): Future[MutationResult]

  /** Replaces the contents of a full document, if it already exists.
    * @param id document id or the param of JsCao1.key(a: A)
    * @param content the object of your own type `T` ex T=`case class User(...)`
    *          to be replace into cb server
    * @see [[insert]], [[upsert]]
    */
  final def replace(
    id: A,
    content: T,
    cas: Long = 0,
    durability: Durability = Disabled,
    timeout: Duration = Duration.MinusInf
  ): Future[MutationResult] = replace(
    id,
    content,
    ReplaceOptions().cas(cas).durability(durability).timeout(timeout)
  )

  @deprecated("Use replace", "10.0.0")
  final def update(id: A, content: T, cas: Long = 0): Future[MutationResult] =
    replace(id, content, cas)

  @deprecated("Use replace", "10.0.0")
  final def updateWithCAS(
    id: A,
    content: T,
    cas: Long = 0
  ): Future[MutationResult] =
    replace(id, content, cas)

  /** convenient method. = set(..).map(_ => t) */
  final def setT(
    id: A,
    content: T,
    options: UpsertOptions = UpsertOptions()
  )(implicit ec: ExecutionContext): Future[T] =
    upsert(id, content, options).map(_ => content)

  /** Removes a document from this collection, if it exists.
    * @param id document id or the param of JsCao1.key(a: A)
    */
  def remove(id: A, options: RemoveOptions): Future[MutationResult]

  /** @param id document id or the param of JsCao1.key(a: A)
    * @note This method will retry on CasMismatchException
    */
  final def change(
    id: A,
    getOptions: GetOptions = GetOptions(),
    setOptions: InsertOptions = InsertOptions()
  )(
    f: Option[T] => T
  )(implicit ec: ExecutionContext): Future[MutationResult] =
    getWithCAS(id, getOptions)
      .map(Option(_))
      .recover { case _: DocumentNotFoundException => None }
      .flatMap {
        case None =>
          insert(id, f(None), setOptions)
            .recoverWith { case _: DocumentExistsException =>
              change(id, getOptions, setOptions)(f)
            }
        case Some((t, cas)) =>
          replace(id, f(Some(t)), setOptions.toReplaceOptions(cas))
            .recoverWith { case _: CasMismatchException =>
              change(id, getOptions, setOptions)(f)
            }
      }

  /** @param id document id or the param of JsCao1.key(a: A) */
  final def flatChange(
    id: A,
    getOptions: GetOptions = GetOptions(),
    setOptions: InsertOptions = InsertOptions()
  )(
    f: Option[T] => Future[T]
  )(implicit ec: ExecutionContext): Future[MutationResult] =
    getWithCAS(id, getOptions)
      .map(Option(_))
      .recover { case _: DocumentNotFoundException => None }
      .flatMap {
        case None =>
          f(None).flatMap {
            insert(id, _, setOptions).recoverWith {
              case _: DocumentExistsException =>
                flatChange(id, getOptions, setOptions)(f)
            }
          }
        case Some((t, cas)) =>
          f(Some(t))
            .flatMap(replace(id, _, setOptions.toReplaceOptions(cas)))
            .recoverWith { case _: CasMismatchException =>
              flatChange(id, getOptions, setOptions)(f)
            }
      }

  /** @param ids Seq of document id or the param of JsCao1.key(a: A) */
  final def changeBulk(ids: Seq[A])(
    f: Option[T] => T
  )(implicit ec: ExecutionContext): Future[Seq[MutationResult]] =
    Future.traverse(ids)(change(_)(f))

  /** @param ids Seq of document id or the param of JsCao1.key(a: A) */
  final def flatChangeBulk(ids: Seq[A])(
    f: Option[T] => Future[T]
  )(implicit ec: ExecutionContext): Future[Seq[MutationResult]] =
    Future.traverse(ids)(flatChange(_)(f))
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy