Retrieving documents

Describes how to load documents using the various get() methods.

Regular reads

Reading a document is done by using the get() method. This can be done by either passing in the id of the Document or a Document itself (where the id is taken from).

Observable<JsonDocument> loadedFromId = bucket.get("id");
Observable<JsonDocument> loadedFromDoc = bucket.get(JsonDocument.create("id"));

Both methods have the same effect. The latter method is helpful if you are already dealing with Document instances in your code and you don't want to extract the ID out of them on your own.

When only the ID is passed in, there is no way to figure out which Document type should be used, so JsonDocument is selected as a sensible default. If you want to override this, you can pass in a specific Document type like this:

Observable<LegacyDocument> loaded = bucket.get("legacyId", LegacyDocument.class);

If the document is not found, an empty Observable is returned. This aligns with how Observable objects are supposed to work by contract, but also makes it easier to deal with the implementation later. If no document is returned, the subsequent operations are just not executed, which avoids having null checks all over the place (if say, a Document would be returned but with the content set to null).

If you are dealing with blocking code, use singleOrDefault() to avoid a NoSuchElementException to be thrown. You can either use a Document populated with default settings or just null, which can be checked later:

JsonDocument found = bucket.get("notexisting").toBlocking().singleOrDefault(null);
if (found == null) {
    // doc not found
} else {
    // doc found
}

Reading from replica

A regular read always reads the document from its master node. If this node is down or not available, the document cannot be loaded. Reading from replica allows you to load the document from one or more replica nodes instead.

Note: When replica reads are used, always use them under the assumption that the data returned is stale. There is no way to guarantee that the data is up-to-date on the replica node unless the proper durability requirements have been set and succeeded on write operations. Only use replica reads if you understand the implications.

You can either read the data from one specific replica or all of the available replicas:

// Read from all available replicas and the master node and return all responding
bucket.getFromReplica("id", ReplicaMode.ALL);

// Read only from the first replica
bucket.getFromReplica("id", ReplicaMode.FIRST);

// Read only from the second replica
bucket.getFromReplica("id", ReplicaMode.SECOND);

// Read only from the third replica
bucket.getFromReplica("id", ReplicaMode.THIRD);

Note that if ReplicaMode.ALL is used, requests are sent to the master node and all configured replicas. The main goal is to get responses back as fast as possible, but because more requests are sent, more responses can arrive. You can use this to either compare all of the responding documents and draw conclusions from that, or just pick the first one arriving:

bucket
    .getFromReplica("id", ReplicaMode.ALL)
    .first()
    .subscribe();

In addition, you can add operations to filter based on some of your assumptions. Imagine you have a version field in your document and you want to only use the replica information if it this specific version:

bucket
    .getFromReplica("id", ReplicaMode.ALL)
    .filter(document -> document.content().getInt("version") > 5)
    .first()
    .subscribe();

Reading and locking

Reading and locking works very similar to a regular read, but in addition the Document is write locked (not read locked) on the server side for the given amount of time.

// Get and lock for 10 seconds
Observable<JsonDocument> doc = bucket.getAndLock("id", 10);
Note: You can only write lock a document for a maximum of 30 seconds. If an invalid lock time (less than 0 or greater than 30 seconds) is provided, 15 seconds is used as the default.

The Document is unlocked under the following conditions:

  • The unlock() command is used.
  • The Document is replaced with the correct CAS value.
  • 30 seconds are over and the server unlocks it for you.

Reading and touching

Reading and touching works very similar to a regular read, but it also refreshes the expiration time of the document to the specified value.

// Get and set the new expiration time to 4 seconds
Observable<JsonDocument> doc = bucket.getAndTouch("id", 4);

You can also use the touch() command if you do not want to read the document and just refresh its expiration time.

If you specify an expiration time greater than 30 days in seconds (60 seconds * 60 minutes * 24 hours * 30 days = 2,592,000 seconds) it is considered an absolute time stamp instead of a relative one.