MapReduce Views Using the PHP SDK with Couchbase Server

You can use MapReduce views to create queryable secondary indexes in Couchbase Server.

The normal CRUD methods allow you to look up a document by its ID. A MapReduce (view query) allows you to look up 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.

The following example is the definition of a by_name view in a "landmarks" design document in the "travel-sample" sample dataset. This view checks whether a document is a landmark and has a name. If it does, it emits the landmark’s name into the index. This view allows landmarks to be queried for by it’s "name" field.

function (doc, meta) {
    if (doc.type && doc.type == "landmark" && doc.name) {
        emit(doc.name, null);
    }
}

A Spatial View can instead be queried with a range or bounding box. For example, let’s imagine we have stored landmarks with coordinates for their home city (eg. Paris, Vienna, Berlin and New York) under geo, and each city’s coordinates is represented as two attributes, lon and lat. The following spatial view map function could be used to find landmarks within Europe, as a "by_location" view in a "spatial" design document:

function (doc, meta) {
    if (doc.type && doc.type == "landmark" && doc.geo) {
        emit([doc.geo.lon, doc.geo.lat], null);
    }
}

Querying Views from the PHP SDK

Once you have a view defined, it can be queried from the PHP SDK by using the query(CouchbaseViewQuery $query) method on the CouchbaseBucket class. Here is an example:

Querying simply MapReduce view
$query = CouchbaseViewQuery::from("landmarks", "by_name")->limit(10);
$landmarks = $bucket->query($query);
foreach ($landmarks->rows as $landmark)
{
    printf("%s\n", $landmark->key);
}

The CouchbaseBucket.query method takes an instance of CouchbaseViewQuery, executes it and returns back the result of query as either stdClass instance (which is default) or nested array depending on third argument $json_asarray. Some useful methods and properties of the CouchbaseViewQuery class include (others can be seen in the reference documentation):

  • order: set order of documents in result, accepts CouchbaseViewQuery::ORDER_ASCENDING or CouchbaseViewQuery::ORDER_DESCENDING

  • skip: skip this number of records before starting to return the results

  • limit: limit the number of the returned documents to the specified number

  • key: return only documents that match the specified key.

  • keys: return only documents that match one of keys specified within the given array

The value returned by queryimplementation that contains the results of the query and number of rows. Depending on $json_asarray they should be accessed with property (default) or array syntax.

  • rows: the results of the query.

  • total_rows: the total count of all rows.

The entry of rows collection represents each row returned by the view request and contains properties for the document id, the key and the value if it was emitted. Here is another example of a more advanced query:

Querying a MapReduce view
$query = CouchbaseViewQuery::from("landmarks", "by_name")
    ->limit(10)->range("A", "\u0fff");
$landmarks = $bucket->query($query);
foreach ($landmarks->rows as $landmark)
{
    printf("%s\n", $landmark->key);
}

Here’s some sample output for the previous query:

A R M Chicken
A. Cavalli &
A16
Abacus Books
Abbey and Palace of Holyroodhouse
Abbey Grounds
Abbot Kinney
Abbots Tandoori
ABC Bakery Cafe
Aberconwy House

Querying Geospatial Views

To query a geospatial view, you will need to construct a CouchbaseViewQuery object using fromSpatial factory method. Spatial queries accept additional builder method bbox which allow you to limit the enclosing bounding boxes of the result. The argument to this method is and array of four double's with each element corresponding to a component emitted by the key (longitude and latitude).

On output, spatial queries yield objects similar to how regular view query does, with an added Geometry property.

Advanced querying a spatial view
$query = CouchbaseViewQuery::fromSpatial("spatial", "routes")
       ->limit(10)->bbox(array(0, -90, 180, 90));
$routes = $bucket->query($query);
foreach ($routes->rows as $route)
{
    var_dump($route->value);
}

The output:

array(3) {
  [0]=>
  string(5) "US964"
  [1]=>
  string(3) "DCA"
  [2]=>
  string(3) "CRW"
}
array(3) {
  [0]=>
  string(5) "DL790"
  [1]=>
  string(3) "MSP"
  [2]=>
  string(3) "CLE"
}
...

In the example above, we construct a CouchbaseViewQuery that will target the "routes" view on the "spatial" design document.

Definition of spatial/routes view
/*
  Emit all the flights. You can filter them by day and time.
*/
function(doc, meta) {
    if (doc.type === 'route') {
        for (var i = 0; i < doc.schedule.length; i++) {
            var schedule = doc.schedule[i];
            var time = parseInt(schedule.utc.replace(/:/g, ''));
            var key = [schedule.day, time];
            var value = [
                schedule.flight,
                doc.sourceairport,
                doc.destinationairport
            ];
            emit(key, value);
        }
    }
}

We then pass the start and end coordinates in via the bbox method and finally we limit are results to 10 items with the limit method.