SemperFly
SemperFly

Reputation: 1583

How do I use CouchDB's reduce() function?

I'm trying to aggregate some data I have in CouchDB. There are photo documents in the database and rating documents in the database. Each rating document looks like so (sans _id and _rev):

{
   "type": "rating",
   "rating": 3 // Integer values are identifiers that map to a string; e.g 1 might mean 'funny'
   "photo": "as9i83ufsafa09uj" // The id of the photo that this rating points to.
}

What I want to do is a get a count of each rating type for each photo.

{
   "key": "as9i83ufsafa09uj", "value": [1, 7, 8, 6] // 1 '0' rating, 7 '1' ratings, etc.
   "key": "photoid2", "value": [3, 0, 0, 8]
}

I'm using CouchDB views' MapReduce to achieve this aggregation.

"map": "function(doc) {
   if(doc.type == 'rating')
   {
      emit(doc.photo, doc.rating);
   }
}",

"reduce": "function(keys, values, rereduce) {
   var result = new Array(0, 0, 0, 0);

   values.forEach( function(key, value)
   {
      result[value]+=1;
   });

   return result;
}"

The map returns:

{"total_rows":55,"offset":0,"rows":[
{"id":"0aa2c4c9a031eedbcf2795cabc1679be","key":"4aa5ec26-26b8-490a-a9cc-620a0d2136b9","value":0},
{"id":"29f363432e008f5934b4160292e18680","key":"4aa5ec26-26b8-490a-a9cc-620a0d2136b9","value":3},
{"id":"646d0d764623bc2f3ed1354ac03b583e","key":"4aa5ec26-26b8-490a-a9cc-620a0d2136b9","value":2},
...
{"id":"fa5be78402171e3bf1eb1cf91c5fda6e","key":"c63b78b6-ad92-426c-ab64-c9a6ae229b31","value":1}
]}

The reduce returns witb group_level=0:

{"rows":[
{"key":null,"value":[1,1,1,1]}
]}

with group_level=1:

{"rows":[
{"key":"4aa5ec26-26b8-490a-a9cc-620a0d2136b9","value":[2,2,2,0]},
{"key":"5ad3de4b-d25b-42d3-95e0-df7661becbf3","value":[2,2,2,2]},
{"key":"7600710b-9ae3-4312-876c-ad352722dac3","value":[2,2,2,2]},
{"key":"959f48a2-5018-4938-aab4-086d8824dd75","value":[2,0,0,0]},
{"key":"c63b78b6-ad92-426c-ab64-c9a6ae229b31","value":[2,2,2,2]}
]}

I'm familiar with MongoDB's map reduce and this function would work using their schema. What do I need to tweak for this to work in CouchDB?

UPDATE This is the final reduce function that worked for me. I wasn't addressing the rereduce parameter correctly. Thanks goes to Marcin Skórzewski for helping me to better understand rereduce.

"reduce": "function(key, values, rereduce) {

        var result = new Array(0, 0, 0, 0);

        if(rereduce == true)
        {
            for(var i = 0; i < values.length; i++)
            {
                var value = values[i];

                for (var j = 0; j < value.length; j++)
                {
                    result[j] += value[j];
                }
            }

            return result;
        }

        for(var i = 0; i < values.length; i++)
        {
            value = values[i];
            result[value]+=1;
        }

        return result;

        }"

Upvotes: 4

Views: 2484

Answers (1)

Marcin Sk&#243;rzewski
Marcin Sk&#243;rzewski

Reputation: 2854

I think you are not using rereduce properly. The values element and returned data is not the same type. In case of only one group level and small size of data (to fit in the single B-tree node) it could work fine, because no rereduce has to be run. Take a look at the reduce doc and the meaning of rereduce argument for reduce().

In case reduce() is run for values emitted from map() they are integers, but if you reduce values obtained by prior reduce() they are arrays. You can make use of the rereduce and in case it is true add arrays. Or you can emit arrays in map (e.g. to get [0,0,0,1] instead of 3) and always add arrays in reduce() without worrying about rereduce argument.

Upvotes: 4

Related Questions