使用MongoDB按计数排序检索文档

5

我有一个包含子文档的文档,大致如下:

{ 
    "name" : "some name1" 
    "like" : [      
            {  "date" : ISODate("2012-11-30T19:00:00Z") },
            {  "date" : ISODate("2012-12-02T19:00:00Z") },     
            {  "date" : ISODate("2012-12-01T19:00:00Z") },
            {  "date" : ISODate("2012-12-03T19:00:00Z") } 
    ]       
}

能否按照“最受欢迎”(过去7天的平均值)获取文档并按计数排序?


2
是的,您可以使用聚合框架来执行这样的查询。 - JohnnyHK
3个回答

11

解决这个问题有几种不同的方法。我将重点介绍使用mongodb的聚合框架来解决问题的解决方案。首先,以下是一个聚合管道,可以解决你的问题,接下来会对命令中发生了什么进行解释/分解。

db.testagg.aggregate( 
    { $unwind : '$likes' }, 
    { $group : {  _id : '$_id', numlikes : { $sum : 1 }}}, 
    { $sort : { 'numlikes' : 1}})

这个管道有三个主要命令:

1)Unwind:将“likes”字段拆分,使每个文档中只有一个“like”元素

2)Group:使用_id字段重新对文档进行分组,为找到的每个文档递增numLikes字段。这会导致numLikes填充一个与“likes”中元素数量相等的数字

3)Sort:最后,我们根据numLikes以升序方式对返回值进行排序。在我运行的测试中,此命令的输出结果是:

{"result" : [
    {
        "_id" : 1,
        "numlikes" : 1
    },
    {
        "_id" : 2,
        "numlikes" : 2
    },
    {
        "_id" : 3,
        "numlikes" : 3
    },
    {
        "_id" : 4,
        "numlikes" : 4
    }....

这是用于通过以下方式插入的数据:

for (var i=0; i < 100; i++) {
    db.testagg.insert({_id : i})
    for (var j=0; j < i; j++) {
        db.testagg.update({_id : i}, {'$push' : {'likes' : j}})
    }
}
请注意,这并没有完全回答您的问题,因为它避免了选择日期范围的问题,但它希望能让您开始并朝着正确的方向前进。
当然,还有其他解决这个问题的方法。其中一个解决方案可能是在客户端执行所有排序和操作。这只是获取所需信息的一种方法。
编辑:如果您觉得这有点繁琐,那么在聚合框架中添加$size运算符的工单已经发布,我邀请您观看并在感兴趣时投票支持以加快此新运算符的添加速度。 https://jira.mongodb.org/browse/SERVER-4899

8
更好的解决方案是保留一个计数字段,记录该文档的点赞数量。虽然您可以使用聚合来完成此操作,但性能可能不太好。 在计数字段上建立索引将加快读取操作速度,并且在插入新的点赞时,可以使用原子操作来增加计数器。

1
您可以在MongoDB v3.4及以上版本中使用以下方法简化上述聚合查询:
> db.test.aggregate([
    { $unwind: "$like" },
    { $sortByCount: "$_id" }
 ]).pretty()

{ "_id" : ObjectId("5864edbfa4d3847e80147698"), "count" : 4 }

正如@ACE所说,您现在可以在投影中使用$size:

db.test.aggregate([
    { $project: { count: { $size : "$like" } } }
]);

{ "_id" : ObjectId("5864edbfa4d3847e80147698"), "count" : 4 }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接