Using built-in reduces

We discussed earlier how a Couchbase view includes a map function for finding and extracting into an index. Reduce functions are optional functions which can perform calculations and other operations on items in an index. There are two types of reduce functions: those that are provided by Couchbase Server, known as built-in reduces, and reduce functions you create as custom JavaScript. The built-in reduce functions include:

  • _count—this function counts the number of emitted items. For instance if you perform a query on a view and provide a start key and end key resulting in 10 items in a result set, you will get the value 10 as a result of the reduce.

  • _sum—adds up all values emitted to an index. For instance, for the values 3, 4, and 5 in a result set the result of the reduce function will be 12.

  • _stats—calculates statistics on your emitted values, including sum of emitted values, count of emitted items, minimum emitted value, maximum emitted value and sum of squares for emitted values.

To understand how a built-in reduce works, imagine an application for beers and breweries. Each brewery document would appear as follows in JSON:

{
"name":"Allguer Brauhaus AG Kempten",
"state":"Bayern",
"code":"",
"country":"Germany",
"phone":"49-(0)831-/-2050-0",
"website":"",
"type":"brewery",
"updated":"2010-07-22 20:00:20",
"description":"",
"address":["Beethovenstrasse 7"],
"geo":{"accuracy":"ROOFTOP","lat":47.7487,"lng":10.5694}
}

This specific brewery document contains information for a brewery in Bavaria, but all other breweries in our application would follow the same document model. Imagine we want to be able to count the number of breweries in each unique city, state or country. In this case we need both a map and reduce function; in this case the map function of our view would look like this:

function (doc, meta) {
  if (doc.country, doc.state, doc.city) {
    emit([doc.country, doc.state, doc.city], 1);
  } else if (doc.country, doc.state) {
    emit([doc.country, doc.state], 1);
  } else if (doc.country) {
    emit([doc.country], 1);
  }
}

As a best practice we want make sure that the fields we want to include in our index actually exist. Therefore we have our map function build on index based on a conditional, and the same conditional ensures the presence of the items we want to index. This ensures the fields exist in documents when we query the view and we therefore avoid a view failure when Couchbase Server generates the index.

If a brewery has all categories of information, namely country, state, and city, we will create an index with the country, state and city as key with the value equal to one. If the brewery only has country and state, we create an index with country and state as key with the value equal to one. Finally if we only have the country of origin, we create an index with only the country as key and the value set to one. As a result of the map function, we would have an index that appears as follows:

KeyValue
["Germany", "Bayern"]            1
["Belgium", "Namur"]            1
["Germany", "Bayern"]            1
....

For our reduce function we use a built-in reduce function, _count. This function which will sum the values for all unique keys. In this case, the result set for our view query will be as follows:

KeyValue
["Germany", "Bayern"]            2
["Belgium", "Namur"]            1

When you create reduce function and store it in a design document, it will appear in JSON as follows:

{
  "_id": "_design/beers",
  "language": "javascript",
  "views": {
    "titles": {
      "map": "function(doc, meta){
        if (meta.type == "json" && doc.date && doc.title) {
        // Check if doc is JSON
        emit(doc.date, doc.title);
         } else {
        // do something with binary value
         }
    }
  }
  "reduce" : "_count"
}