每个文档级别的聚合 mongodb

Posted

技术标签:

【中文标题】每个文档级别的聚合 mongodb【英文标题】:Aggregation at each document level mongodb 【发布时间】:2020-03-19 20:55:22 【问题描述】:

我有一个这样的文件列表

[
        "_id": "5dbc95f921d7625303fe2369",
        "name": "John",
        "itemsPurchased": [
                "offer": "o1",
                "items": ["p1"]
            ,
                "offer": "o1",
                "items": ["p1"]
            ,
            
                "offer": "o1",
                "items": ["p2"]
            ,
            
                "offer": "o2",
                "items": ["p1"]
            , 
                "offer": "o7",
                "items": ["p1"]
            
        ]
    ,
    
        "_id": "zbc95f921d7625303fe2363",
        "name": "Doe",
        "itemsPurchased": [
                "offer": "o1",
                "items": ["p11"]
            ,
                "offer": "o1",
                "items": ["p11"]
            ,
            
                "offer": "o2",
                "items": ["p13"]
            ,
            
                "offer": "o1",
                "items": ["p22"]
            ,
            
                "offer": "o2",
                "items": ["p11"]
            , 
                "offer": "o3",
                "items": ["p11"]
            
        ]
    
]

我正在尝试计算每个客户对独特产品的独特报价,期望结果如下:

[
  
    "_id": "5dbc95f921d7625303fe2369",
    "name": "John",
    "offersAndProducts": 
      "o1":2,
      "o2":2,
      "o3":1
,

  "_id": "zbc95f921d7625303fe2363",
    "name": "Doe",
    "offersAndProducts": 
      "o1":2,
      "o2":1,
      "o7":1
    
]

我想对每个文档应用聚合,在对 itemsPurchased 执行 $unwind 后,在 items 上应用 $group,然后 on offer 以消除重复:

 
            "$group" : 
                "_id" : 
                    "item" : 
                        "$arrayElemAt" : [
                            "$itemsPurchased.item", 
                            0.0
                        ]
                    , 
                    "count" : 
                        "$sum" : 1.0
                    , 
                    "offer" : "$itemsPurchased.offer"
                
            
        

那么,

 
            "$group" : 
                "_id" : "$_id.offer", 
                "count" : 
                    "$sum" : 1.0
                
            
        

这给出了所有文档的产品和报价数组:

[
o1:4,o2:3,o3:1,o7:1
] 

但我在文档级别需要它。 尝试了$addFeild,但$unwind 和$match 运算符给出了无效错误。

还有其他方法可以实现吗?

【问题讨论】:

我不了解输入和所需的输出。暂时只关注John,您如何从输入中获得"o1":2,"o2":2,"o3":1 的输出? o7 怎么了? @BuzzMoschetti:抱歉打错了。预期输出为 [ "_id": "zbc95f921d7625303fe2363", "name": "Doe", "offersAndProducts": "o1": 2, "o2": 2, "o3": 1 , "_id" :“5dbc95f921d7625303fe2369”,“名称”:“约翰”,“offersAndProducts”:“o1”:2,“o2”:1,“o7”:1] 【参考方案1】:

您需要运行$unwind 和$group 两次。要仅计算唯一的 items,您可以使用 $addToSet。要动态构建密钥,您需要使用 $arrayToObject:

db.collection.aggregate([
    
        $unwind: "$itemsPurchased"
    ,
    
        $unwind: "$itemsPurchased.items"
    ,
    
        $group: 
            _id: 
                _id: "$_id",
                offer: "$itemsPurchased.offer"
            ,
            name:  $first: "$name" ,
            items:  $addToSet: "$itemsPurchased.items" 
        
    ,
    
        $group: 
            _id: "$_id._id",
            name:  $first: "$name" ,
            offersAndProducts:  $push:  k: "$_id.offer", v:  $size: "$items"   
        
    ,
    
        $project: 
            _id: 1,
            name: 1,
            offersAndProducts:  $arrayToObject: "$offersAndProducts" 
        
    
])

Mongo Playground

【讨论】:

非常感谢您的查询,我注意到的一件事是,忽略了包含空 itemsPurchased 的文档,为了使其正常工作,我使用空 offer 和 item "_id": "zbc95f921d7625303fe2334", "name": "Doe", "itemsPurchased": [ "offer": "", "items": [ "" ] ] 返回: "_id": "zbc95f921d7625303fe2334", "name ": "Doe", "offersAndProducts": "": 1 并忽略 "" 键。我们还有其他方法可以包含这些文档吗? @user2033575 试试这个:docs.mongodb.com/manual/reference/operator/aggregation/unwind/… 我试过了 :) 这里是 sn-p:mongoplayground.net/p/ofl4h-dzwlu,它因为空数组没有提供键而失败,但我尝试用一​​些随机键 'q 填充值', 有效。如果值为 0 或 keynot null,MongoDB 是否允许条件推送? $unwind and $group back on _id 是一种反模式,因为您正在重新创建原始文档,而且效率很低。请参阅我的解决方案,了解如何通过直接处理数组来完成此操作。 @user2033575 你可以接受另一个答案,如果你觉得更好,因为我的投票被否决了【参考方案2】:

一般来说,$unwind 是一个数组,然后是 $group 在原始 _id 上的反模式,因为大多数操作可以在一个阶段直接在数组上完成。这是这样一个阶段的样子:

$addFields:
   offers:$arrayToObject:
      $map:
         input:$setUnion:"$itemsPurchased.offer",
         as:"o",
         in:[
             "$$o", 
             $size:$setUnion:$let:
                vars:items:$filter:
                    input:"$itemsPurchased",
                    cond:$eq:["$$this.offer","$$o"]
                ,
                in:$reduce:
                    input:"$$items",
                    initialValue:[],
                    in:$concatArrays:["$$value","$$items.items"]
                
             
          ]
      
   

这样做是创建一个数组,其中每个元素都是一个双元素数组(这是$arrayToObject 可以转换为第一个元素是键名,第二个是值的对象的语法)并且输入是唯一的集合报价,我们为每个产品累积一系列产品,去除重复项(使用$setUnion),然后获得结果的大小。这对您的输入产生的结果是:

"offers" : 
    "o1" : 2,
    "o2" : 2,
    "o3" : 1

【讨论】:

以上是关于每个文档级别的聚合 mongodb的主要内容,如果未能解决你的问题,请参考以下文章

ES文档级查询匹配限制

NLP的Taskflow API

文档级关系抽取:基于结构先验产生注意力偏差SSAN模型

论文速递NAACL2022- 文档级事件论元抽取的双流AMR增强模型

如何在 mlab 中解决此事务错误? [MongoError: Transaction numbers are ... 支持文档级锁定]

如何在 mlab 中解决此事务错误? [MongoError: Transaction numbers are ... 支持文档级锁定]