mongodb 为每个检索到的文档添加计数器

Posted

技术标签:

【中文标题】mongodb 为每个检索到的文档添加计数器【英文标题】:mongodb add counter to each retrieved document 【发布时间】:2020-07-05 04:47:48 【问题描述】:

嘿,有没有办法为从 mongo 检索到的每个文档添加一个计数器?

假设我们在 mongo 中有用户:_id, name。我想全部获取它们,并且我想为每个检索到的文档添加一个计数器字段并在获取文档时增加它。

所以结果是

users : [_id: "some_id_1", name: "john", counter: 1, _id: "some_id_2", name: "bob", counter: 2]

所以这个计数器字段会即时生成?

【问题讨论】:

是的,你可以。这个帖子有类似的问答:Add a field with increasing value in MongoDB Aggregation based on condition 【参考方案1】:

虽然@prasad_ 提到的答案似乎有效,但 $unwind 自 Mongodb 3.2 includeArrayIndex option 起就更容易实现了:

db.collection.aggregate([
  
    $group: 
      _id: null,
      data: 
        $push: "$$ROOT"
      
    
  ,
  
    $unwind: 
      path: "$data",
      includeArrayIndex: "counter",

    
  ,
  
    $replaceRoot: 
      newRoot: 
        $mergeObjects: [
          "$data",
          
            counter: 
              $add: [
                "$counter",
                1
              ]
            
          
        ]
      
    
  
])

最后阶段只是为了重塑您的文档,并为每个计数器加 1,因为它是从 0 开始的(基于数组索引)

You can test it here.

【讨论】:

【参考方案2】:

我写另一个答案是为了完成前三个答案。

我意识到这三个答案的一些(简单)基准测试。

数据集:10000 个文档,如下所示:

 
    "_id" : "5e7b55cb911ef7ebdfd72c08", 
    "name" : "Dominguez"

我在一个循环中运行了 3 个聚合集 1000 次。

var results=[];
for(i=0;i<1000;i++) 

    var before = new Date();
    tmp_res_matthPen=db.testing.aggregate(aggregation_mpenicaud);
    var after = new Date();
    var mpe = after-before;

    var before = new Date();
    tmp_res_prasad=db.testing.aggregate(aggregation_prasad);
    var after = new Date();
    var pra = after-before;

    var before = new Date();
    tmp_res_valijon=db.testing.aggregate(aggregation_valijon);
    var after = new Date();
    var val = after-before;

    results.push("pra":pra,"val":val,"mpe":mpe);

db.results.insert(results);

这是提供答案的平均值(以毫秒为单位):

        "mpe" : 7.725, 
        "val" : 15.441, 
        "pra" : 14.911

但是... 查看 Valijon 的答案,我注意到这两个 $sort 阶段在这里都不是很有用,因为没有被问到(并且 _id 字段通常已经根据时间按 _id 生成排序)。有些我尝试将它们从管道中删除。

没有 $sort 阶段的平均值:

    "mpe" : 7.399, 
    "val" : 7.149, 
    "pra" : 13.541

并在所有管道中的 _id 上添加 $sort 阶段(这里 _id 充当索引字段)

索引字段上具有 $sort 阶段的平均值:

    "mpe" : 17.518, 
    "val" : 16.166, 
    "pra" : 23.078

最后,我按名称排序,因为它不是索引和随机生成的。

$sort 阶段在未索引字段上的平均值:

"mpe" : 16.858, 
"val" : 14.27, 
"pra" : 24.777

【讨论】:

【参考方案3】:

以下聚合为每个文档添加了计数器字段:

db.users.aggregate( [
   
      $group:  
          _id: null, 
          docs:  $push: "$$ROOT"  
       
  ,
   
      $project:  
         _id: 0,
         docs:  
             $map: 
                 input:  $range: [ 0,  $size: "$docs"  ] ,
                 in: 
                     $mergeObjects: [ 
                          $arrayElemAt: [ "$docs", "$$this" ] ,
                          counter:  $add: [ "$$this", 1 ]  
                     ]
                 
             
         
      
  ,
   
      $unwind: "$docs" 
  ,
   
      $replaceRoot:  newRoot: "$docs"  
  
] )

【讨论】:

【参考方案4】:

我不会错过这个聚会的。

从 MongoDB v3.6 开始,我们可以使用同一个集合进行内部连接,并且对于每个项目 i,我们从下一个集合中计算项目 0 - i

一旦我们得到counter,我们需要将它与ROOT文档合并。

db.collection.aggregate([
  
    $sort: 
      _id: 1
    
  ,
  
    $lookup: 
      from: "collection",
      let: 
        until: "$_id"
      ,
      pipeline: [
        
          $sort: 
            _id: 1
          
        ,
        
          $match: 
            $expr: 
              $lte: [
                "$_id",
                "$$until"
              ]
            
          
        ,
        
          $group: 
            _id: null,
            counter: 
              $sum: 1
            
          
        ,
        
          $project: 
            "_id": 0
          
        
      ],
      as: "counter"
    
  ,
  
    $replaceRoot: 
      newRoot: 
        $mergeObjects: [
          "$$ROOT",
          
            $arrayElemAt: [
              "$counter",
              0
            ]
          
        ]
      
    
  
])

MongoPlayground

【讨论】:

您是否对这些解决方案进行了基准测试?我对结果很感兴趣。 请不要通过删除 $sort 阶段来编辑您的答案,否则我的第二个答案将不相关 @matthPen 我已经尝试了 2,102,833 条记录,并且除我之外的所有解决方案都挂起:D 支持基准测试

以上是关于mongodb 为每个检索到的文档添加计数器的主要内容,如果未能解决你的问题,请参考以下文章

如何根据 MongoDB 中的列表计数过滤文档? [复制]

如何根据 MongoDB 中的列表计数过滤文档? [复制]

获取数组中每个索引的子文档元素计数并更新子文档键 - 数组中的子文档(IN MONGODB)

MongoDB - 加载文档子集以进行查询

检索与 $group 聚合中的指定条件匹配的计数

在 Joomla 中添加命中计数器