3

I have a collection with let's say the following content:

{
    "_id":ObjectId("5051c4778ec2487f7c000001"),
    "user_id":"978956784678",
    "likes":{
        "data":[
            {
                "name":"Store 1",
                "category":"Retail and consumer merchandise",
                "id":"354412263434",
                "created_time":"2012-09-07T11:36:05+0000"
            },
            {
                "name":"Store 2",
                "category":"Retail and consumer merchandise",
                "id":"293088074081904",
                "created_time":"2012-08-13T20:06:49+0000"
            }
        ],
        "paging":{
            "next":"https://test.com/next"
        }
    }
}

I am trying to build a Map/Reduce or aggregation in MongoDB to give me the following output (schematically):

user_id, category, "Count of likes"

Somehow I don't find an appropriate solution... What I got so far is an total aggregation of the category likes, but not per user_id:

db.runCommand({ 
mapreduce: "likes",
map: function() { 
    this.likes.data.forEach(
       function(z){
            emit( z.category , { count : 1 } );
        }
    );
},
reduce: function(key, values) {
    var total = 0;
    for ( var i=0; i<values.length; i++ )
        total += values[i].count;
    return { count : total };
},
out: 'result3',
verbose: true
});

Can somebody give me a hint? Help is much appreciated!

Tobi

3
  • what output do you get ? Commented Sep 14, 2012 at 10:45
  • The result is { "_id" : "Retail and consumer merchandise", "value" : { "count" : 2 } } Commented Sep 14, 2012 at 10:49
  • how about this emit( this.user_id , { count : 1 } );? Commented Sep 14, 2012 at 11:20

2 Answers 2

2

If you would like to use MR to count each user's likes, you could emit user_id and category as the key used to group documents in reduce:

map: function() {
    var u = this.user_id; 
    this.likes.data.forEach(
       function(z){
            emit( {category: z.category, user: u} , { count : 1 } );
        }
    );
}
Sign up to request clarification or add additional context in comments.

1 Comment

This works excellent! i use it to gather an rating average of products reviews. But you got to be sure that your data set includes (in the example) the likes object with value!
1

If possible, I would recommend using the new aggregation framework, which comes with MongoDB version 2.2, the newest stable release. The aggregation framework is written in C++ rather than Javascript, and should have better performance for many aggregation commands.

The following aggregate() counts the number of likes per category, per user. Please let me know if this is not the desired output.

Command:

    db.collection.aggregate(
        { $unwind : "$likes.data" }, 
        { $group : 
           {
             _id: {user: "$user_id", category: "$likes.data.category"}, 
             count: {$sum:1}
           }
        }
   );

Result:

{
    "result" : [
        {
            "_id" : {
                "user" : "978956784678",
                "category" : "Retail and consumer merchandise"
            },
            "count" : 2
        }
    ],
    "ok" : 1
}

4 Comments

Jenna, thanks a lot for your replies. In both solutions, the calculation is correct. I keep on struggeling in formating the output in the way I'd prefer it: { "results" : [ { "user" : "100003938243508", "categories : [ { "category" : "Retail and consumer merchandise", "count" : 2 } ] } ] }
Hi Tobi, do you want each user to have a single document listing all categories, i.e. { user: x, categories: [{category: y, count: z}, {category: l, count: j}, ...] ? Or can the result have multiple documents for each user?
Hi Jenna, I'd prefer to have as result a set of one document for each user, like your option one. Thanks a lot in advance!
Hi Tobi, I'm sorry- I'm not sure this is possible; however, if you find a solution, please let us know.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.