com.sandinh.couchbase.CBBucket.scala Maven / Gradle / Ivy
The newest version!
package com.sandinh.couchbase
import com.couchbase.client.scala.codec.{
JsonDeserializer,
JsonSerializer,
Transcoder
}
import com.couchbase.client.scala.durability.Durability
import com.couchbase.client.scala.durability.Durability.Disabled
import com.couchbase.client.scala.kv.{
AppendOptions,
CounterResult,
GetAllReplicasOptions,
GetAndLockOptions,
GetAndTouchOptions,
GetAnyReplicaOptions,
GetOptions,
GetReplicaResult,
GetResult,
IncrementOptions,
InsertOptions,
MutationResult,
PrependOptions,
RemoveOptions,
ReplaceOptions,
TouchOptions,
UnlockOptions,
UpsertOptions
}
import com.couchbase.client.scala.manager.bucket.AsyncBucketManager
import com.couchbase.client.scala.manager.view.AsyncViewIndexManager
import com.couchbase.client.scala.query.{
QueryOptions,
QueryParameters,
QueryResult
}
import com.couchbase.client.core.error.DocumentNotFoundException
import com.couchbase.client.scala.view.{ViewOptions, ViewResult}
import com.couchbase.client.scala.{AsyncBucket, AsyncCluster, AsyncCollection}
import play.api.libs.json.{JsValue, Reads}
import java.time.Instant
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import scala.concurrent.duration._
import scala.concurrent.duration.Duration.MinusInf
import scala.reflect.ClassTag
import scala.reflect.runtime.universe.WeakTypeTag
/** @define CounterDoc though it is common to use Couchbase to store exclusively JSON, Couchbase is actually
* agnostic to what is stored. It is possible to use a document as a 'counter' - e.g. it
* stores an integer. This is useful for use-cases such as implementing
* AUTO_INCREMENT-style functionality, where each new document can be given a unique
* monotonically increasing id.
* @define OnlyBinary This method should not be used with JSON documents. This operates
* at the byte level and is unsuitable for dealing with JSON documents. Use this method only
* when explicitly dealing with binary or UTF-8 documents. It may invalidate an existing JSON
* document.
* @define OnlyCounter this method should not be used with JSON documents. Use this method only
* when explicitly dealing with counter documents. It may invalidate an existing JSON
* document.
* @define Id the unique identifier of the document
* @define CAS Couchbase documents all have a CAS (Compare-And-Set) field, a simple integer that allows
* optimistic concurrency - e.g. it can detect if another agent has modified a document
* in-between this agent getting and modifying the document. See
* [[https://docs.couchbase.com/scala-sdk/1.0/howtos/json.html these JSON docs]] for a full
* description. The default is 0, which disables CAS checking.
* @define Timeout when the operation will timeout. This will default to `timeoutConfig().kvTimeout()` in the
* provided [[com.couchbase.client.scala.env.ClusterEnvironment]].
* @define ErrorHandling any `scala.util.control.NonFatal` error returned will derive ultimately from
* `com.couchbase.client.core.error.CouchbaseException`. See
* [[https://docs.couchbase.com/scala-sdk/1.0/howtos/error-handling.html the error handling docs]]
* for more detail.
* @define SupportedTypes this can be of any type for which an implicit
* `com.couchbase.client.scala.codec.Conversions.JsonSerializer` can be found: a list
* of types that are supported 'out of the box' is available at
* [[https://docs.couchbase.com/scala-sdk/1.0/howtos/json.html these JSON docs]]
* @define Durability writes in Couchbase are written to a single node, and from there the Couchbase Server will
* take care of sending that mutation to any configured replicas. This parameter provides
* some control over ensuring the success of the mutation's replication. See
* [[com.couchbase.client.scala.durability.Durability]]
* for a detailed discussion.
* @define Options configure options that affect this operation
* @define OrNotFound or fail with a `CouchbaseException`.
* This could be [[com.couchbase.client.core.error.DocumentNotFoundException]],
* indicating the document could not be found.
* @define ExpiryNote On mutations if this is left at the default (0), then any expiry
* will be removed and the document will never expire. If the application wants to
* preserve expiration then they should use the `withExpiration` parameter on any gets,
* and provide the returned expiration parameter to any mutations.
*
* @define ExpiryTimeNote If both `expiry` and `expiryTime` are provided then `expiryTime` is used -
*
* see [[com.couchbase.client.scala.util.ExpiryUtil.expiryActual]]
* @define Transcoder control over how JSON is converted and stored on the Couchbase Server.
* If not specified it will default to to `transcoder()` in the
* [[com.couchbase.client.scala.env.ClusterEnvironment]]
*/
final class CBBucket(val underlying: AsyncBucket, val cluster: AsyncCluster) {
@inline def name: String = underlying.name
lazy val defaultCol: AsyncCollection = underlying.defaultCollection
@deprecated("Use underlying", "10.0.0")
def asJava: AsyncBucket = underlying
def getJsT[T: Reads](
id: String,
options: GetOptions = GetOptions()
)(implicit ec: ExecutionContext): Future[T] =
get(id, options).map(_.contentAs[JsValue].get.as[T])
/** Fetches a full document from this collection.
*
* This overload provides only the most commonly used options. If you need to configure something more
* esoteric, use the overload that takes an [[com.couchbase.client.scala.kv.GetOptions]] instead, which supports all available options.
*
* @param id $Id
* @param timeout $Timeout
* @param transcoder $Transcoder
* @param withExpiry Couchbase documents optionally can have an expiration field set, e.g. when they will
* automatically expire. For efficiency reasons, by default the value of this expiration
* field is not fetched upon getting a document. If expiry is being used, then set this
* field to true to ensure the expiration is fetched. This will not only make it available
* in the returned result, but also ensure that the expiry is available to use when mutating
* the document, to avoid accidentally resetting the expiry to the default of 0.
* @param project Projection is an advanced feature allowing one or more fields to be fetched from a JSON
* document, and the results combined into a `JsonObject` result.
*
* It combines the efficiency of a Sub-Document fetch, in that only specific fields need to be retrieved, with
* the ease-of-handling of a regular fetch, in that the results can be handled as one JSON.
* @return Future success with a `GetResult`, $OrNotFound
*
* $ErrorHandling
*/
def get(
id: String,
timeout: Duration = MinusInf, // MinusInf will be converted to kvReadTimeout
transcoder: Transcoder = null,
withExpiry: Boolean = false,
project: Seq[String] = Nil
): Future[GetResult] = defaultCol.get(
id,
GetOptions(
withExpiry,
project,
timeout,
transcoder = Option(transcoder)
)
)
/** See doc of the other overload method */
def get(
id: String,
options: GetOptions
): Future[GetResult] = defaultCol.get(id, options)
/** usage: {{{
* import com.sandinh.couchbase.Implicits._
*
* bucket.getT[String](id)
* bucket.getT[JsValue](id)
* }}}
*/
def getT[T](id: String)(
implicit ec: ExecutionContext,
ser: JsonDeserializer[T],
tt: WeakTypeTag[T],
tag: ClassTag[T]
): Future[T] =
get(id).map(_.contentAs[T].get)
/** Retrieves any available version of the document.
*
* The application should default to using `.get()` instead. This method is intended for high-availability
* situations where, say, a `.get()` operation has failed, and the
* application wants to return any - even possibly stale - data as soon as possible.
*
* Under the hood this sends a request to all configured replicas for the document, including the active, and
* whichever returns first is returned.
*
* @param id $Id
* @param timeout $Timeout
* @param transcoder $Transcoder
* @return Future success with a `GetReplicaResult`, $OrNotFound
*
* $ErrorHandling
*/
def getAnyReplica(
id: String,
timeout: Duration = MinusInf, // MinusInf will be converted to kvReadTimeout
transcoder: Transcoder = null,
): Future[GetReplicaResult] = defaultCol.getAnyReplica(
id,
GetAnyReplicaOptions(
timeout,
transcoder = Option(transcoder)
)
)
/** See doc of the other overload method */
def getAnyReplica(
id: String,
options: GetAnyReplicaOptions
): Future[GetReplicaResult] = defaultCol.getAnyReplica(id, options)
/** Retrieves all available versions of the document.
*
* The application should default to using `.get()` instead. This method is intended for advanced scenarios,
* including where a particular write has ambiguously failed (e.g. it may or may not have succeeded), and the
* application wants to attempt manual verification and resolution.
*
* @param id $Id
* @param timeout $Timeout
* @param transcoder $Transcoder
* @return Future success with a `GetReplicaResult`, $OrNotFound
*
* $ErrorHandling
*/
def getAllReplicas(
id: String,
timeout: Duration = MinusInf, // MinusInf will be converted to kvReadTimeout
transcoder: Transcoder = null,
): Seq[Future[GetReplicaResult]] = defaultCol.getAllReplicas(
id,
GetAllReplicasOptions(
timeout,
transcoder = Option(transcoder)
)
)
/** See doc of the other overload method */
def getAllReplicas(
id: String,
options: GetAllReplicasOptions
): Seq[Future[GetReplicaResult]] = defaultCol.getAllReplicas(id, options)
/** Fetches a full document from this collection, and simultaneously lock the document from writes.
*
* The CAS value returned in the [[com.couchbase.client.scala.kv.GetResult]] is the document's 'key':
* during the locked period, the document may only be modified by providing this CAS.
* @param id $Id
* @param lockTime how long to lock the document for
* @param timeout $Timeout
* @param transcoder $Transcoder
* @return Future success with a `GetResult`, $OrNotFound
*
* $ErrorHandling
*/
def getAndLock(
id: String,
lockTime: Duration,
timeout: Duration = MinusInf, // MinusInf will be converted to kvReadTimeout
transcoder: Transcoder = null,
): Future[GetResult] = defaultCol.getAndLock(
id,
lockTime,
GetAndLockOptions(
timeout,
transcoder = Option(transcoder)
)
)
/** See doc of the other overload method */
def getAndLock(
id: String,
lockTime: Duration,
options: GetAndLockOptions
): Future[GetResult] = defaultCol.getAndLock(id, lockTime, options)
/** Fetches a full document from this collection, and simultaneously update the expiry value of the document.
* @param id $Id
* @param expiry $Expiry
* @param timeout $Timeout
* @param transcoder $Transcoder
* @return Future success with a `GetResult`, $OrNotFound
*
* $ErrorHandling
*/
def getAndTouch(
id: String,
expiry: Duration,
timeout: Duration = MinusInf, // MinusInf will be converted to kvReadTimeout
transcoder: Transcoder = null,
): Future[GetResult] = defaultCol.getAndTouch(
id,
expiry,
GetAndTouchOptions(
timeout,
transcoder = Option(transcoder)
)
)
/** See doc of the other overload method */
def getAndTouch(
id: String,
expiry: Duration,
options: GetAndTouchOptions
): Future[GetResult] = defaultCol.getAndTouch(id, expiry, options)
/** Inserts a full document into this collection, if it does not exist already.
* @param id $Id
* @param content $SupportedTypes
* @param durability $Durability
* @param timeout $Timeout
* @param transcoder $Transcoder
* @param expiry should be used for any expiration times 30 days.
* If over that, use `expiryTime: Instant` instead.
* @param expiryTime should be used for any expiration times >= 30 days.
* If below that, use `expiry: Duration` instead.
* @return Future success with a `MutationResult`, or fail with a `CouchbaseException`.
* This could be [[com.couchbase.client.core.error.DocumentExistsException]],
* indicating the document already exists.
*
* $ErrorHandling
* @note $ExpiryNote
* @note $ExpiryTimeNote
* @see [[upsert]], [[replace]]
*/
def insert[T: JsonSerializer](
id: String,
content: T,
durability: Durability = Disabled,
timeout: Duration = MinusInf,
transcoder: Transcoder = null,
expiry: Duration = null,
expiryTime: Instant = null,
): Future[MutationResult] = defaultCol.insert(
id,
content,
InsertOptions(
durability,
timeout,
transcoder = Option(transcoder),
expiry = expiry,
expiryTime = Option(expiryTime),
)
)
/** See doc of the other overload method */
def insert[T: JsonSerializer](
id: String,
content: T,
options: InsertOptions
): Future[MutationResult] = defaultCol.insert(id, content, options)
/** Upserts the contents of a full document in this collection.
*
* Upsert here means to insert the document if it does not exist, or replace the content if it does.
*
* @param id $Id
* @param content $SupportedTypes
* @param durability $Durability
* @param timeout $Timeout
* @param transcoder $Transcoder
* @param expiry should be used for any expiration times 30 days.
* If over that, use `expiryTime: Instant` instead.
* @param expiryTime should be used for any expiration times >= 30 days.
* If below that, use `expiry: Duration` instead.
* @param preserveExpiry Whether an existing document's expiry should be preserved.
* Requires Couchbase Server 7.0 or later.
* @return Future success with a `MutationResult`, or fail with a `CouchbaseException`.
*
* $ErrorHandling
* @note $ExpiryNote
* @note $ExpiryTimeNote
* @see [[insert]], [[replace]]
*/
def upsert[T: JsonSerializer](
id: String,
content: T,
durability: Durability = Disabled,
timeout: Duration = MinusInf,
transcoder: Transcoder = null,
expiry: Duration = null,
expiryTime: Instant = null,
preserveExpiry: Boolean = false,
): Future[MutationResult] = defaultCol.upsert(
id,
content,
UpsertOptions(
durability,
timeout,
transcoder = Option(transcoder),
expiry = expiry,
expiryTime = Option(expiryTime),
preserveExpiry = preserveExpiry
)
)
/** See doc of the other overload method */
def upsert[T: JsonSerializer](
id: String,
content: T,
options: UpsertOptions
): Future[MutationResult] = defaultCol.upsert(id, content, options)
/** Replaces the contents of a full document in this collection, if it already exists.
* @param id $Id
* @param content $SupportedTypes
* @param cas $CAS
* @param durability $Durability
* @param timeout $Timeout
* @param transcoder $Transcoder
* @param expiry should be used for any expiration times 30 days.
* If over that, use `expiryTime: Instant` instead.
* @param expiryTime should be used for any expiration times >= 30 days.
* If below that, use `expiry: Duration` instead.
* @param preserveExpiry Whether an existing document's expiry should be preserved.
* Requires Couchbase Server 7.0 or later.
* @return Future success with a `MutationResult`, $OrNotFound
*
* $ErrorHandling
* @note $ExpiryNote
* @note $ExpiryTimeNote
* @see [[insert]], [[upsert]]
*/
def replace[T: JsonSerializer](
id: String,
content: T,
cas: Long = 0,
durability: Durability = Disabled,
timeout: Duration = MinusInf,
transcoder: Transcoder = null,
expiry: Duration = null,
expiryTime: Instant = null,
preserveExpiry: Boolean = false,
): Future[MutationResult] = defaultCol.replace(
id,
content,
ReplaceOptions(
cas,
durability,
timeout,
transcoder = Option(transcoder),
expiry = expiry,
expiryTime = Option(expiryTime),
preserveExpiry = preserveExpiry
)
)
/** See doc of the other overload method */
def replace[T: JsonSerializer](
id: String,
content: T,
options: ReplaceOptions
): Future[MutationResult] = defaultCol.replace(id, content, options)
/** Removes a document from this collection, if it exists.
* @param id $Id
* @param cas $CAS
* @param durability $Durability
* @param timeout $Timeout
* @return Future success with a `MutationResult`, $OrNotFound
*
* $ErrorHandling
*/
def remove(
id: String,
cas: Long = 0,
durability: Durability = Disabled,
timeout: Duration = MinusInf
): Future[MutationResult] = defaultCol.remove(
id,
RemoveOptions(
cas,
durability,
timeout
)
)
/** See doc of the other overload method */
def remove(
id: String,
options: RemoveOptions
): Future[MutationResult] = defaultCol.remove(id, options)
/** Performs a view query against the cluster.
* @param designDoc the view design document to use
* @param viewName the view to use
* @param options any view query options - see [[com.couchbase.client.scala.view.ViewOptions]] for documentation
*/
def viewQuery(
designDoc: String,
viewName: String,
options: ViewOptions = ViewOptions()
): Future[ViewResult] = underlying.viewQuery(designDoc, viewName, options)
/** See doc of the other overload method */
def query(statement: String, options: QueryOptions): Future[QueryResult] =
cluster.query(statement, options)
/** Performs a N1QL query against the cluster.
* @param statement the N1QL statement to execute
* @param parameters provides named or positional parameters for queries parameterised that way.
* @param timeout sets a maximum timeout for processing.
* @param adhoc if true (the default), adhoc mode is enabled: queries are just run. If false, adhoc mode is disabled
* and transparent prepared statement mode is enabled: queries are first prepared so they can be executed
* more efficiently in the future.
*/
def query(
statement: String,
parameters: QueryParameters = QueryParameters.None,
timeout: Duration =
cluster.env.core.timeoutConfig.queryTimeout().toNanos.nanos,
adhoc: Boolean = true
): Future[QueryResult] =
cluster.query(statement, parameters, timeout, adhoc)
/** Unlock a locked document.
* @param id $Id
* @param cas must match the CAS value return from a previous `.getAndLock()` to successfully
* unlock the document
* @param timeout $Timeout
* @return Future success with a `Unit`, $OrNotFound
*
* $ErrorHandling
*/
def unlock(
id: String,
cas: Long,
timeout: Duration = MinusInf
): Future[Unit] = defaultCol.unlock(id, cas, UnlockOptions(timeout))
/** See doc of the other overload method */
def unlock(
id: String,
cas: Long,
options: UnlockOptions
): Future[Unit] = defaultCol.unlock(id, cas, options)
/** Updates the expiry of the document with the given id.
* @param id $Id
* @param timeout $Timeout
*
* @return Future success with a `MutationResult`, $OrNotFound
*
* $ErrorHandling
*/
def touch(
id: String,
expiry: Duration,
timeout: Duration = MinusInf
): Future[MutationResult] = defaultCol.touch(
id,
expiry,
TouchOptions(
timeout
)
)
/** See doc of the other overload method */
def touch(
id: String,
expiry: Duration,
options: TouchOptions
): Future[MutationResult] = defaultCol.touch(id, expiry, options)
/** Increment a Couchbase 'counter' document. $CounterDoc
*
* $OnlyCounter
*
* @param id $Id
* @param delta the amount to increment by
* @param initial if not-None, the amount to initialise the document too, if it does not exist. If this is
* not set, and the document does not exist, the result Future will failed with DocumentNotFoundException
* @param durability $Durability
* @param timeout $Timeout
* @return Future success with a `CounterResult`, $OrNotFound
*
* $ErrorHandling
*
* @note $ExpiryNote
* @note $ExpiryTimeNote
*/
def counter(
id: String,
delta: Long = 0L,
initial: Option[Long] = None,
durability: Durability = Disabled,
timeout: Duration = MinusInf, // MinusInf will be converted to kvReadTimeout
expiry: Duration = null,
expiryTime: Instant = null,
): Future[CounterResult] = defaultCol.binary.increment(
id,
delta,
IncrementOptions(
initial,
durability,
timeout,
expiry = expiry,
expiryTime = Option(expiryTime)
)
)
/** convenient method. {{{ = counter(id).map(_.content).recoverNotExist(default) }}} */
def getCounter(id: String, default: Long = 0L)(
implicit ec: ExecutionContext
): Future[Long] = counter(id)
.map(_.content)
.recover { case _: DocumentNotFoundException => default }
/** See doc of the other overload method */
def counter(
id: String,
delta: Long,
options: IncrementOptions
): Future[CounterResult] = defaultCol.binary.increment(id, delta, options)
/** See doc of the other overload method */
def counter(
id: String,
delta: Long,
initial: Long
): Future[CounterResult] =
defaultCol.binary.increment(id, delta, IncrementOptions(Some(initial)))
/** See doc of the other overload method */
def counter(
id: String,
delta: Long,
initial: Long,
expiry: Duration
): Future[CounterResult] =
defaultCol.binary.increment(
id,
delta,
IncrementOptions(Some(initial), expiry = expiry)
)
/** Add bytes to the end of a Couchbase binary document.
*
* $OnlyBinary
*
* @param id $Id
* @param content the bytes to append
* @param cas $CAS
* @param durability $Durability
* @param timeout $Timeout
*
* @return Future success with a `MutationResult`, $OrNotFound
*
* $ErrorHandling
*/
def append(
id: String,
content: Array[Byte],
cas: Long = 0,
durability: Durability = Disabled,
timeout: Duration = MinusInf
): Future[MutationResult] = defaultCol.binary.append(
id,
content,
AppendOptions(
cas,
durability,
timeout
)
)
/** See doc of the other overload method */
def append(
id: String,
content: Array[Byte],
options: AppendOptions
): Future[MutationResult] = defaultCol.binary.append(id, content, options)
/** Add bytes to the beginning of a Couchbase binary document.
*
* $OnlyBinary
*
* @param id $Id
* @param content the bytes to prepend
* @param cas $CAS
* @param durability $Durability
* @param timeout $Timeout
*
* @return Future success with a `MutationResult`, $OrNotFound
*
* $ErrorHandling
*/
def prepend(
id: String,
content: Array[Byte],
cas: Long = 0,
durability: Durability = Disabled,
timeout: Duration = MinusInf
): Future[MutationResult] = defaultCol.binary.prepend(
id,
content,
PrependOptions(
cas,
durability,
timeout
)
)
/** See doc of the other overload method */
def prepend(
id: String,
content: Array[Byte],
options: PrependOptions
): Future[MutationResult] = defaultCol.binary.prepend(id, content, options)
def viewIndexes: AsyncViewIndexManager = underlying.viewIndexes
/** The AsyncBucketManager provides access to creating and getting buckets. */
def bucketManager: AsyncBucketManager = cluster.buckets
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy