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 天的平均值)并按计数排序?

4

3 回答 3

11

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

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

该管道有 3 个主要命令:

1) 展开:这会拆分“喜欢”字段,以便每个文档有 1 个“喜欢”元素

2) 组:这使用 _id 字段重新组合文档,为找到的每个文档增加 numLikes 字段。这将导致 numLikes 被填充为等于之前“喜欢”中的元素数量的数字

3)排序:最后,我们根据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

于 2012-12-24T18:31:24.203 回答
8

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

于 2012-12-24T18:31:53.090 回答
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 }
于 2016-12-29T16:55:10.977 回答