2

I have a movie collection, each movie has several stars, for example:

{
    movie: "Pride & Prejudice",
    starList: ["Keira Knightley", "Matthew Macfadyen"]
}


{
    movie: "Begin Again",
    starList: ["Keira Knightley", "Mark Ruffalo"]
}

And I want to get an Inverted Index, each star has several movies

{ star: "Keira Knightley", movieList: ["Pride & Prejudice", "Begin Again"] }

This can be accomplished by using mapreduce on mongodb. I use nodejs with mongoose driver. Following is the code:

var _ = require("underscore");

var o = {};

o.scope = { _: _ };

o.map = function()
{
    var movie = this.movie;
    this.starList.forEach(function(star)
    {
        emit(star, { movieList: [movie]});
    });
};

o.reduce = function(k, vals)
{
    var movieList = _.flatten(_.pluck(vals, "movieList"));
    return { movieList: movieList};
};

Movie.mapReduce(o).then(function(results)
{
    console.log(JSON.stringify(results, null, 4));
});

I got an error which shows I cannot use underscore.js in reduce function.

MongoError: TypeError: _.pluck is not a function :
_funcs2@:8:34

    at Function.MongoError.create (/app/node_modules/mongoose/node_modules/mongodb/node_modules/mongodb-core/lib/error.js:31:11)
    at commandCallback (/app/node_modules/mongoose/node_modules/mongodb/node_modules/mongodb-core/lib/topologies/server.js:1187:66)
    at Callbacks.emit (/app/node_modules/mongoose/node_modules/mongodb/node_modules/mongodb-core/lib/topologies/server.js:119:3)
    at null.messageHandler (/app/node_modules/mongoose/node_modules/mongodb/node_modules/mongodb-core/lib/topologies/server.js:358:23)
    at Socket.<anonymous> (/app/node_modules/mongoose/node_modules/mongodb/node_modules/mongodb-core/lib/connection/connection.js:292:22)
    at emitOne (events.js:77:13)
    at Socket.emit (events.js:169:7)
    at readableAddChunk (_stream_readable.js:153:18)
    at Socket.Readable.push (_stream_readable.js:111:10)
    at TCP.onread (net.js:536:20)

2 Answers 2

1

A very simple approach which uses the aggregation framework follows:

Movie.aggregate([
    { "$unwind": "$starsList" },
    {
        "$group": {
            "_id": "$starsList",
            "movieList": {"$push": "$movie"}
        }
    },
    {
        "$project": {
            "_id": 0,
            "star": "$_id",
            "movieList": 1
        }
    }
]).exec(callback);

or using the fluent aggregate() builder API:

Movie.aggregate()
     .unwind("starsList")
     .group({
        "_id": "$starsList",
        "movieList": {"$push": "$movie"}
     })
     .project({
        "_id": 0,
        "star": "$_id",
        "movieList": 1
     })
     .exec(callback);
Sign up to request clarification or add additional context in comments.

2 Comments

In addition, the "startsList" should be "starList"
Thanks, it works, but I cannot accept your answer because the problem is about "using underscore in mapreduce"
0

I may be wrong here. But i think when you try to acess underscore methods in reduce method your _ variable doesn't have all methods or for access you must use different approach. I would suggest to try like this

_['pluck'](....)

And also check _ variable to see if you got all methods in scope correctly.

Hope this helps.

1 Comment

How to check _ variable in reduce function? It seems I can not using console.log() in reduce function

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.