Hello World | Couchbase Docs (original) (raw)
Couchbase documents are organized into buckets, scopes, and collections.CRUD operations — Create, Read, Update, Delete — can be performed upon documents in a collection.
JSON
We’ll create a snippet of JSON to work with, using the client’s own JSON library, but you can read about the Scala SDK’s support for other JSON libraries on theJSON Libraries page.
val json = JsonObject("status" -> "awesome")
Insert (Create) and Upsert
insert
and upsert
will both create a new document. The difference between the two is that if a document with that key already exists, the insert
operation will fail, while the upsert
operation will succeed, replacing the content.
We need to provide a unique ID as the key, and we’ll use a UUID here:
Creating a new document
val docId = UUID.randomUUID().toString
collection.upsert(docId, json) match {
case Success(result) =>
case Failure(exception) => println("Error: " + exception)
}
Get (Read)
The get
method reads a document from a collection.
As mentioned above, the Scala SDK will not throw exceptions. Instead, methods that can error — such as the upsert
above — will return a Scala Try
result, which can either be a Success
containing the result, or a Failure
containing a Throwable exception. The easiest way to handle a single operation is with pattern matching, as shown above.
Now let’s get the data back (this example will look a little messy due the nested handling of multiple Try
results, but we’ll see how to clean it up shortly):
// Get a document
collection.get(docId) match {
case Success(result) =>
// Convert the content to a JsonObjectSafe
result.contentAs[JsonObjectSafe] match {
case Success(json) =>
// Pull out the JSON's status field, if it exists
json.str("status") match {
case Success(hello) => println(s"Couchbase is $hello")
case _ => println("Field 'status' did not exist")
}
case Failure(err) => println("Error decoding result: " + err)
}
case Failure(err) => println("Error getting document: " + err)
}
Here we’re fetching the value for the key docId
, converting that value to a JsonObjectSafe
(a simple wrapper around JsonObject
that returns Try
results — seeJsonObjectSafe for details), and then accessing the value of the status key as a String.
Better Error Handling
All three of these operations could fail, so there’s quite a lot of error handling code here to do something quite simple. One way to improve on this is by using flatMap
, like this:
val result: Try[String] = collection
.get(docId)
.flatMap(_.contentAs[JsonObjectSafe])
.flatMap(_.str("status"))
result match {
case Success(status) => println(s"Couchbase is $status")
case Failure(err) => println("Error: " + err)
}
Alternatively, you can use a for-comprehension, like so:
val result: Try[String] = for {
result <- collection.get(docId)
json <- result.contentAs[JsonObjectSafe]
status <- json.str("status")
} yield status
result match {
case Success(status) => println(s"Couchbase is $status")
case Failure(err) => println("Error: " + err)
}
Either of these methods will stop on the first failed operation. So the final returned Try
contains either a) Success
and the result of the final operation, indicating that everything was successful, or b) Failure
with the error returned by the first failing operation.
Replace (Update) and Overloads
You’ll notice that most operations in the Scala SDK have two overloads. One will take an Options builder, which provides all possible options that operation takes. For instance:
The replace method updates the value of an existing document
collection.replace(
docId,
json,
ReplaceOptions()
.expiry(10.seconds)
.durability(Durability.Majority)
) match {
case Success(status) =>
case Failure(err) => println("Error: " + err)
}
These options blocks are implemented as Scala case classes: they are immutable data objects that return a copy of themselves on each change.
The other overload is provided purely for convenience. It takes named arguments instead of an Options object, and provides only the most commonly used options:
collection.replace(docId, json, durability = Durability.Majority) match {
case Success(status) =>
case Failure(err) => println("Error: " + err)
}
| | When you replace a document, it’s usually good practice to use optimistic locking. Otherwise, changes might get lost if two people change the same document at the same time. | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
Remove (Delete)
The remove method deletes a document from a collection:
collection.remove("document-key") match {
case Success(result) => println("Document removed successfully")
case Failure(err: DocumentNotFoundException) =>
println("The document does not exist")
case Failure(err) => println("Error: " + err)
}