获取具有不同标准的最新文档

时间:2013-10-01 11:04:40

标签: javascript mongodb mongodb-query

情况

我无法想出一个很好的方法来执行某个MongoDb查询。首先,这是我想要做什么样的查询。假设一个简单的数据库通过电子卡刷卡记录进入和退出事件(可能还有其他操作,无关紧要)。所以有一个名为swipelog的集合,其中包含如下所示的简单文档:

{
    _id: ObjectId("524ab4790a4c0e402200052c")
    name: "John Doe", 
    action: "entry",
    timestamp: ISODate("2013-10-01T1:32:12.112Z") 
}

现在我想列出名称及其最后的输入时间(以及我可能想要的任何其他字段,但下面的示例仅使用这两个字段)。

当前解决方案

以下是我现在所拥有的,作为MongoDb JavaScript控制台的“一线”:

db.swipelog.distinct('name')
           .forEach( function(name) {

    db.swipelog.find( { name: name, action:"entry" } )
               .sort( { $natural:-1 } )
               .limit(1)
               .forEach( function(entry) {

        printjson( [ entry.name, entry.timestamp ] )
    }) 
})

打印的内容如下:

[ "John Doe", ISODate("2013-10-01T1:32:12.112Z")]
[ "Jane Deo", ISODate("2013-10-01T1:36:12.112Z")]
...

问题

我认为上面有明显的缩放问题。如果有一百个名称,那么将对数据库进行1 + 100次查询。那么什么是获得“每个不同timestamp的最后name”的好/正确方法?如果它更容易,那么改变数据库结构或添加一些集合是可以的。

2 个答案:

答案 0 :(得分:12)

您可以使用聚合框架来实现此目的:

 db.collection.aggregate(
          [
             {$match:
                  {action:'entry'}
             },
             {$group:
                  {_id:'$name',
                   first:
                         {$max:'$timestamp'}
                  }
             }
          ])

如果您可能在结果中包含其他字段,则可以使用$first运算符

 db.collection.aggregate(
          [
             {$match:
                  {action:'entry'}
             },
             {$sort:
                  {name:1, timestamp:-1}
             },
             {$group:
                  {_id:'$name',
                   timestamp: {$first:'$timestamp'},
                   otherField: {$first:'$otherField'},
                  }
             }
          ])

答案 1 :(得分:3)

这个答案应该是关于attish上面的答案的评论,但我没有足够的代表在这里发表评论

请记住聚合框架cannot return more than 16MB of data。如果您拥有大量用户,则可能会在生产系统上遇到此限制。

MongoDB 2.6为聚合框架添加了新功能来处理这个问题:

  • db.collection.aggregateCursor()(临时名称)与db.collection.aggregate()相同,只是它返回游标而不是文档。这避免了16MB的限制
  • $out是一个新的管道阶段,它将管道的输出定向到集合。这允许您运行聚合作业
  • $sort已得到改进,可以删除RAM limitations并提高速度

如果查询性能比数据时代更重要,您可以安排一个常规aggregate命令,将其结果存储在db.last_swipe之类的集合中,然后让您的应用程序只查询db.last_swipe相关用户。

结论:我同意attish有正确的方法。但是,在当前的MongoDB版本中扩展它可能会遇到麻烦,应该研究Mongo 2.6。