The problem lies within your custom reduce function: you're not handling the case when it's being called as part of a re-reduce.
As per Couchbase documentation:
The base format of the reduce() function is as follows:
function(key, values, rereduce) {
...
return retval;
}
The reduce function is supplied three arguments:
key
: The key is the unique key derived from the map() function and the
group_level parameter.
values
: The values argument is an array of all of the values that match
a particular key. For example, if the same key is output three times,
data will be an array of three items containing, with each item
containing the value output by the emit() function.
rereduce
: The rereduce indicates whether the function is being called
as part of a re-reduce, that is, the reduce function being called
again to further reduce the input data.
When rereduce
is false:
The supplied key
argument will be an array where the first argument is the key
as emitted by the map function, and the id
is the document ID that generated the key.
The values is an array of values where each element of the array matches the corresponding element within the array of keys
.
When rereduce
is true:
Bold formatting is mine, and the highlighted words are quite important: you should consider that sometimes, you'll receive the keys
argument with a value of null
.
According to the docs, you should handle the case when rereduce
is true
within your reduce()
function, and you should know that in this case, keys
will be null
. In the case of your reduce()
function, you could do something like this:
function(keys, values, rereduce) {
if (rereduce) {
var result = [];
for (var i = 0; i < values.length; i++) {
var distinct = values[i];
for (var j = 0; j < distinct.length; j++) {
result.push(distinct[j]);
}
}
return result.filter(function (e, i, arr) {
return arr.lastIndexOf(e) === i;
});
}
return keys.filter(function (e, i, arr) {
return arr.lastIndexOf(e) === i;
});
}
Here, I'm firstly handling the re-reduce phase. For this I'm flattening the array of arrays that I'm receiving in the values
argument and then I'm removing the duplicates that might have appeared after the merge.
Then it comes your original code, which returns the keys
argument array without duplicates.
To test that this reduce()
function actually works, I've used the following map()
function:
function(doc, meta) {
switch(doc.type) {
case "brewery":
emit(meta.id, null);
emit(meta.id, null);
break;
}
}
This intentionally generates duplicates, which then are removed by the reduce()
function.