CouchDB: Merging Objects in Reduce Function

311 Views Asked by At

I'm new to CouchDB, so bear with me. I searched SO for an answer, but couldn't narrow it down to this specifically.

I have a mapper function which creates values for a user. The users have seen different product pages, and we want to tally the type and products they've seen.

var emit_values = {};
emit_values.name = doc.name;
...
emit_values.productsViewed = {};
emit_values.productsViewed[doc.product] = 1

emit([doc.id, doc.customer], emit_values);

In the reduce function, I want to gather different values into that productsViewed object for that given user. So after the reduce, I have this:

productsViewed: {
   book1: 1,
   book3: 2, 
   book8: 1
}

Unfortunately, doing this creates a reduce overflow error. According to the other posts, this is because the productsViewed object is growing in size in the reduce function, and Couch doesn't like that. Specifically:

A common mistake new CouchDB users make is attempting to construct complex aggregate values with a reduce function. Full reductions should result in a scalar value, like 5, and not, for instance, a JSON hash with a set of unique keys and the count of each.

So, I understand this is not the right way to do this in Couch. Does anyone have any insight into how to properly gather values into a document after reduce?

1

There are 1 best solutions below

1
On BEST ANSWER

You simple build a view with the customer as key

emit(doc.customer, doc.product);

Then you can call

/:db/_design/:name/_view/:name?key=":customer"

to get all products an user has viewed.

If a customer can have viewed a product several times you can build a multi-key view

emit([doc.customer, doc.product], null);

and reduce it with the built-in function _count

 /:db/_design/:name/_view/:name?startkey=[":customer","\u0000"]&endkey=[":customer","\u9999"]&reduce=true&group_level=2

You have to accept that you cannot

construct complex aggregate values

with CouchDB by requesting the view. If you want to have a data structure like your wished payload

productsViewed: {
  book1: 1,
  book3: 2, 
  book8: 1
}

i recommend to use an _update handler on the customer doc. Every request that logs a product visit adds a value to the customer.productsViewed property instead of creating a new doc.