0

I have a collection with feeds. The documents are structured something like this:

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

Then I have users:

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

What I would like to accomplish is to get the number of friends that has opened the feeds fetched by the user. I do this now with a mapReduce, passing the friends of the user fetching the feeds. I do not think I am doing it correctly as I reduce by only returning the emit itself and I have to convert the result back to a normal query result on the finalizer:

    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);
        });
    });

I have looked at aggregation, but I can not quite figure out how to do the same thing. Or is the structure of the documents here all wrong?

Thanks for any reply!

4

1 回答 1

1

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

我知道,给定一个用户,您想要的计算是查找该用户包含在 opensBy 数组中的所有提要,然后找到该用户的不同朋友的数量,这些好友包含在这些 opensBy 数组中。我说的对吗?

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

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

然后我们可以对 feeds 集合执行以下聚合来进行计算:

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

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

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

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

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

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

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

    {“结果”:[{“_id”:0,“计数”:2}],“确定”:1}

我希望这个答案有帮助!如果您还有其他问题,请告诉我。

布鲁斯

于 2013-09-17T20:02:36.597 回答