MapReduce Views Using the Java SDK with Couchbase Server

You can use MapReduce views to create queryable indexes in Couchbase Server.
Although still maintained and supported for legacy use, Views date from the earliest days of Couchbase Server development, and as such are rarely the best choice over, say, our Query service for your application, see our guide to choosing the right service.

The normal CRUD methods allow you to look up a document by its ID. A MapReduce (view query) allows you to lookup one or more documents based on various criteria. MapReduce views are comprised of a map function that is executed once per document (this is done incrementally, so this is not run each time you query the view) and an optional reduce function that performs aggregation on the results of the map function. The map and reduce functions are stored on the server and written in JavaScript.

MapReduce queries can be further customized during query time to allow only a subset (or range) of the data to be returned.

See the Incremental MapReduce Views and Querying Data with Views sections of the general documentation to learn more about views and their architecture.

Querying Views

View operations are accessible on the Bucket API. Once you have a reference to the bucket you need to at least supply the name of the design document and the name of the view:

Cluster cluster = Cluster.connect("127.0.0.1", "username", "password");
Bucket bucket = cluster.bucket("bucket-name");

ViewResult viewResult = bucket.viewQuery("design-doc-name", "view-name");
for (ViewRow row : viewResult.rows()) {
  System.out.println("Found row: " + row);
}

It is important to remember that by default, the SDK will look for a view deployed to the production namespace. If you want to query one which is still in the development namespace you can do it like this:

ViewResult viewResult = bucket.viewQuery(
  "ddoc",
  "view",
  viewOptions().namespace(DesignDocumentNamespace.DEVELOPMENT)
);

In the same way you can supply all kinds of different options, including the ViewScanConsistency which was previously called Stale.

ViewResult viewResult = bucket.viewQuery(
  "ddoc",
  "view",
  viewOptions()
    .scanConsistency(ViewScanConsistency.REQUEST_PLUS)
    .limit(5)
    .inclusiveEnd(true)
);

If you have a reduce function in your view and you want to call it, make sure to call .reduce(true) as well.

Working with View Rows

A ViewResult can emit 0 to N view rows. The available data inside each ViewRow depends both on the map function written and if a reduce function is used.

The structure of a ViewRow looks like this:

public class ViewRow {
  public Optional<String> id() { /* ... */ }

  public <T> Optional<T> keyAs(Class<T> target) { /* ... */ }

  public <T> Optional<T> keyAs(TypeRef<T> target) { /* ... */ }

  public <T> Optional<T> valueAs(Class<T> target) { /* ... */ }

  public <T> Optional<T> valueAs(TypeRef<T> target) { /* ... */ }
}

Both the key and the value can be converted into any target type, depending on how you wrote your view function. Note that the document ID is always a String, but it is wrapped in an Optional because it is not available if a reduce function is used.

Accessing View Metadata

In addition to the rows the view result contains additional ViewMetaData.

ViewResult viewResult = bucket.viewQuery("ddoc", "view", viewOptions().debug(true));

ViewMetaData viewMeta = viewResult.metaData();
System.out.println("Got total rows: " + viewMeta.totalRows());
viewMeta.debug().ifPresent(debug -> System.out.println("Got debug info as well: " + debug));

The number of rows is always present, the debug information only if you enable it on the ViewOptions as indicated through being wrapped into an Optional<JsonObject>.