我有一个包含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);
});
});
我看过聚合,但我无法弄清楚如何做同样的事情。或者这里的文件结构都错了?
感谢您的回复!
答案 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}
我希望这个答案有帮助!如果您还有其他问题,请与我们联系。
布鲁斯