MongoDB(Node JS),如何正确地将计算字段添加到查询结果中,其中计算字段使用传递的变量

时间:2013-08-06 07:25:28

标签: node.js mongodb mapreduce aggregation-framework

我有一个包含Feed的集合。这些文件的结构如下:

{
  _id: '123',
  title: 'my title',
  openedBy: ['321', '432', '543'] // ID of users
}

然后我有用户:

{
  _id '321',
  friends: ['432'] // ID of users
}

我想要完成的是获取已打开用户提取的订阅源的朋友数量。我现在使用mapReduce执行此操作,传递用户的朋友获取提要。我不认为我做得正确,因为我只通过返回发射本身来减少,我必须将结果转换回终结器上的正常查询结果:

    db.collection(collectionName).mapReduce(function () {
        var openedByFriendsLength = 0;
        for (var x = 0; x < friends.length; x++) {
            if (this.openedBy.indexOf(friends[x]) >= 0) {
                openedByFriendsLength++;
            }
        }

        emit(this._id, {
            title: this.title,
            openedByLength: this.openedBy.length,
            openedByFriendsLength: openedByFriendsLength
        });
    }, function (key, emits) {
        return emits[0];
    }, {
        out: 'getFeeds',
        scope: {
            friends: user.friends
        },
    }, function (err, collection) {
        collection.find().toArray(function (err, feeds) {
            // Convert the _id / value to a normal find result
            var resultFeeds = [];
            for (var x = 0; x < feeds.length; x++) {
                resultFeeds.push(feeds[x].value);
                resultFeeds[resultFeeds.length - 1]._id = feeds[x]._id;
            }
            callback(err, resultFeeds);
        });
    });

我看过聚合,但我无法弄清楚如何做同样的事情。或者这里的文件结构都错了?

感谢您的回复!

1 个答案:

答案 0 :(得分:1)

您询问如何使用聚合框架进行计算。通常,聚合框架比map-reduce执行得更好。您可以在此处找到有关聚合框架的文档:http://docs.mongodb.org/manual/aggregation/

我理解,在给定用户的情况下,您想要的计算是查找openBy数组中包含该用户的所有订阅源,然后查找这些openBy数组中包含的该用户的不同朋友的数量。我有正确的吗?

聚合与map-reduce一样,一次只对一个集合进行操作,因此第一步是从users集合中获取用户的朋友列表,例如:

friends = db.users.findOne({_id:user}).friends

然后我们可以在feed集合上执行以下聚合来进行计算:

db.feeds.aggregate([
    {$match: {openedBy: user}},
    {$unwind: '$openedBy'},
    {$match: {openedBy: {$in: friends}}},
    {$group: {_id: '$openedBy'}},
    {$group: {_id: 0, count: {$sum: 1}}}
])

aggregate命令指定一个处理步骤列表,其工作方式与Unix管道非常相似,将文档流从管道的一个阶段传递到下一个阶段。

  • 管道中的第一步,$ match,将集合中的所有文档作为输入,并仅选择openBy数组中包含用户的那些文档。

  • 第二步,$ unwind,获取每个输入文档并生成多个输出文档,一个用于opensBy数组的每个成员;每个输出文档都包含一个opensBy字段,其值为单个用户。这些用户将打开与给定用户相同的Feed。此步骤将允许管道的后续步骤对openBy数组的单个值执行聚合操作。

  • 第三步,$ match,过滤那些文档,只传递openBy用户是给定用户的朋友的文档。但是,在此流中,给定的朋友可能会被多次表示,因此需要进行聚合以消除重复项。

  • 第四步$ group执行聚合,为opensBy字段的每个值生成一个输出文档。这将是已打开用户打开的订阅源的给定用户的一组唯一的朋友,没有重复。 _id字段将是朋友用户ID。

  • 最后一步,另一个$ group,计算上一步生成的文档数。它输出单个文档,_id为0(您可以使用此处所需的任何值),以及包含您希望计算的最终计数的计数字段,例如:

    {“result”:[{“_ id”:0,“count”:2}],“ok”:1}

我希望这个答案有帮助!如果您还有其他问题,请与我们联系。

布鲁斯