Z Jones
Z Jones

Reputation: 2035

mongodb mapreduce - build an array or object in the reduce?

original collection:

{ 
"_id" : ObjectId('xxxxxxxxxxxxxxxx'),
  "userId" : ObjectId('yyyyyyyyyyyyyyyy'),
  "urlId" : ObjectId('zzzzzzzzzzzzzzzzzz')
},
{ 
  "_id" : ObjectId('uuuuuuuuuuuuuuuuuu'),
  "userId" : ObjectId('aaaaaaaaaaaaaaaa'),
  "urlId" : ObjectId('zzzzzzzzzzzzzzzzzz')
}

emit:

emit(this.urlId, {userId: this.userId, visitCount: this.visitCount});

reduce:

function(key, values) {
    var visitCount = 0;
    var userVC = new Array();
    values.forEach(function(doc) {


NOT SURE WHAT TO PUT HERE TO ACHIEVE DESIRED OUTPUT COLLECTION


    });
    return {urlId: key, userVC: userVC};
};

desired MR output collection:

{
   "_id" : ObjectId('zzzzzzzzzzzzzzzzzzzz'),
   "value" : {
       "urlId" : ObjectId('zzzzzzzzzzzzzzzzzzzz'),
       "userVC" : {
              ObjectId('yyyyyyyyyyyyyyyy') : <total visit count for this userId on this urlId>,
              ObjectId('aaaaaaaaaaaaaaaa') : <total visit count for this userId on this urlId>
       }
}

Upvotes: 2

Views: 6282

Answers (2)

Darren
Darren

Reputation: 11031

For anyone else trying to solve this in the reduce function:

function reduce(key, values) {
  const result = {
    list: [],
    ...
  };

  // keep track of reduced records to avoid duplicate list entries
  const processed = {
    list: [],
    ...
  };

  values.forEach((value, index) => {
    if (value.listItem && value.listItem.length > 0) {

      // our value is an array as MongoDB needs to be strongly typed (same as reduced type)
      if (processed.list.indexOf(value.listItem[0].key) === -1) {
        result.list = result.list.concat(value.listItem);
      }

    }
    ...
  });

  return result;
}

The reduce function runs in the context of MongoDB so you need to be aware of what goes in the function that is supported.

Upvotes: 0

Chris
Chris

Reputation: 1753

You want to know how many times each user visited each individual url on your site? I think you will want to solve this differently.

Emit a count of url/user visits:

emit( { urlId: this.urlId, userId: this.userId }, { count: 1 } );

Count them with the reduce:

r = function( key , values ){
    var total = 0;
    for ( var i=0; i<values.length; i++ )
        total += values[i].count;
    return { count : total };
};

Then if you really really wanted the desired output you stated, you could do that in a finalize step. But I think it doesn't scale well to N users.

Here is a link that doesn't exactly satisfy your stated goal, but I found very useful when trying to understand how these mongodb mapreduce functions work:

http://cookbook.mongodb.org/patterns/unique_items_map_reduce/

Upvotes: 4

Related Questions