Mongoose mapReduce:reduce返回对象或数组?

时间:2014-05-21 03:57:57

标签: node.js mongodb mapreduce mongoose aggregation-framework

我有以下收藏:

/* 0 */
{
    "clientID" : ObjectId("51b9c10d91d1a3a52b0000b8"),
    "_id" : ObjectId("532b4f1cb3d2eacb1300002b"),
    "answers" : [],
    "questions" : []
}

/* 1 */
{
    "clientID" : ObjectId("51b9c10d91d1a3a52b0000b8"),
    "_id" : ObjectId("532b6b9eb3d2eacb1300002c"),
    "answers" : [ 
        "1", 
        "8"
    ],
    "questions" : [ 
        "1", 
        "2", 
        "3"
    ]
}

/* 2 */
{
    "clientID" : ObjectId("51b9c10d91d1a3a52b0000b8"),
    "_id" : ObjectId("532b6baeb3d2eacb1300002d"),
    "answers" : [ 
        "1", 
        "8"
    ],
    "questions" : [ 
        "1", 
        "2", 
        "3"
    ]
}

/* 3 */
{
    "clientID" : ObjectId("5335f9d864e2b1290c00012e"),
    "_id" : ObjectId("533b828146ca43634000002d"),
    "answers" : [ 
        "ORANGE"
    ],
    "questions" : [ 
        "Color"
    ]
}

/* 4 */
{
    "clientID" : ObjectId("5335f9d864e2b1290c00012e"),
    "_id" : ObjectId("5351be327b539a4d1a00002b"),
    "answers" : [ 
        "ORANGE"
    ],
    "questions" : [ 
        "Color"
    ]
}

/* 5 */
{
    "clientID" : ObjectId("5335f9d864e2b1290c00012e"),
    "_id" : ObjectId("5351be5ec89d717d1a00002b"),
    "answers" : [ 
        "ORANGE"
    ],
    "questions" : [ 
        "Color"
    ]
}

我正在运行以下代码,以便查找(问题,答案)组合在集合中出现的次数:

o.map= function(){ 
    emit({"questions" : this.questions, "answers" :this.answers },this.clientID) 
};

o.reduce = function(answers, collection){
    return collection.length; 
};

logSearchDB.mapReduce(o,function (err, results) {
    results.sort(function(a, b){return b.value-a.value});
    for (var i = 0; i < results.length; i++) {
        console.log(JSON.stringify(results[i]))
    };

})

输出结果为:

{"_id":{"questions":[],"answers":[]},"value":"51b9c10d91d1a3a52b0000b8"}
{"_id":{"questions":["Color"],"answers":["ORANGE"]},"value":3}
{"_id":{"questions":["1","2","3"],"answers":["1","8"]},"value":2}

我预计第一行会有“值”:1

我想'reduce'函数得到了一个'collection'对象:“51b9c10d91d1a3a52b0000b8”,而不是得到一个数组:[“51b9c10d91d1a3a52b0000b8”]。

为什么map reduce不会将所有内容都收集到数组中?

1 个答案:

答案 0 :(得分:1)

您在第一行中只有一个普通值的原因是因为值只有一次出现。这通常是mapReduce的工作方式,至少在原始论文中指定的方式。

因此,当只有一个键时,实际上不会调用reduce函数。要解决此问题,请使用地图缩小中的finalize功能:

var finalize = function(key,value) {
    if ( typeof(value) != "number" )
        value = 1;

    return value;
};

db.collection.mapReduce(
    mapper,
    reducer,
    { 
      "finalize": finalize,
      "out": { "inline": 1 }
    }
);

这会在所有输出上运行,并且看到当值看起来不是nunber时,就是你正在发出的clientID,那么该值设置为1,因为这是如何哈尼正在分组。

您的查询确实比mapReduce更适合aggregation framework。聚合框架是本机代码实现,而不是使用JavaScript解释器。它的运行速度比mapReduce快得多:

db.collection.aggregate([
    { "$group": {
        "_id": {
            "questions": "$questions",
            "answers": "$answers"
        },
        "count": { "$sum": 1 }
    }}
])

所以这是更好的选择。这是后来对MongoDB的介绍,所以人们仍然倾向于考虑mapReduce,或者有早期版本的MongoDB遗留代码。但这已经存在了很长一段时间。

另请参阅operator reference了解聚合框架。