Tutorial: Map Reduce Queries

Map Reduce Queries

Map / Reduce requests

DefineView : create an index on your data.

You can create several view for each docType.

A view is composed of a map function and an optional reduce function. The map and reduce functions can be passed as string or as a Function object. The map function is applied to every document of a given docType. In this function, you can emit(key, value) This (key, value) pair is called an entry. The entry are stored sorted by key in an index. You can emit 0, 1 or N times for each doc.

I just want to sort notes by date

First, define your view (you can see it as a query where each result is indexed, that's why we need to declare it before using it):

cozysdk.defineView('Note', 'bydate', function (){
    emit(doc.date, doc);
});

And then we can query the view:

cozysdk.queryView('doctype', 'bydate', {});
// It will give you all documents for your doc type (here Note) sorted by date.

More complex example

cozysdk.defineView('doctype', 'myview', function complexMap(){
    // oscar doesnt want to be included
    if(!doc.owner == 'oscar') {
        // always be careful : docs may not have all the fields you expect.
        if(doc.tags && doc.tags.forEach){
            doc.tags.forEach(function(tag){
                // the emit(key, value) function fill up couchdb index
                emit(tag.toLowerCase(), doc.someValue);
            }
        }
    }
});
[
    { "_id": "doc1", "owner": "oscar", "tags": ["tagA"]      , "someValue": 7},
    { "_id": "doc2", "owner": "jane",  "tags": ["tagA", "tagB"] ,
                                                                "someValue": 8},
    { "_id": "doc3", "owner": "john",  "tags": ["taga"]      , "someValue": 9},
    { "_id": "doc4", "owner": "david", "tags": []         , "someValue": 10}
]

Will generate the following index

|   key  | value |  id  |
|--------|-------|------|
| taga   | 8     | doc2 |
| taga   | 9     | doc3 |
| tagb   | 8     | doc2 |

You can see that

  • doc1 has been discarded because its owner is oscar
  • doc2 has generated two entries
  • doc2 has generated one entry
  • doc4 has not generated any entry
  • Entries get sorted by their key

You can emit any value, including arrays. So if you wanted to sort by owner and date, you can

emit([doc.owner, doc.date], doc.someValue)

Querying views

Once we have defined a view, we can then query it.

To get all entries for a key, we can use the key query parameter.

cozysdk.queryView('doctype', 'myview', {key: "tagb"})
//-> {key: 'tagb', value: 8, id: 'doc2'}

Key filtering

  • If you pass no parameters, you will get every entries for this view.
  • To get entries for several keys at once, we can use the keys parameter.
  • To get entries for a range of keys, we can use the startkey & endkey parameters.

Key sorting

To reverse the sorting order, use the descending query params. Beware, that startkey and endkey will need to be reversed.

Pagination

You can use the skip parameters to discard some results from the beginning and limit to discard some results from the end. It is however cleaner to paginate using startkey and endkey.

Returned values

By default, query only returns key and values. You can use the include_docs parameter to include the documents with the results.

Destroy

You can replace queryView by destroyByView to destroy documents instead of retrieving them.

Some SQL equivalents

SELECT title FROM notes WHERE author=john SORT BY date
map    function(doc) { emit( [doc.author, doc.date], doc.title ); }
query  {"startkey": ["john"], "endkey": ["john", {}]}
// {} is greater than any string in couchdb sort order.

Troubleshooting

Map & Reduce functions are executed in couchdb server.

They can't use a function from outside of themselves. Inline all tools you may need.

BAD

function isPair(x) { return x % 2; }
var map = function (doc){
    if(isPair(doc.field) emit(doc.id)
}
cozysdk.defineView('doctype', 'pair', map)

GOOD

var map = function (doc){
    if(doc.field % 2) emit(doc.id)
}
cozysdk.defineView('doctype', 'pair', map)

Map & Reduce functions can be applied to weird documents.

Beware of null pointers, it may make the view crash.

  • BAD : emit(doc.field.info)
  • GOOD: if(doc.field) emit(doc.field.info)