【问题标题】:Aggregation at each document level mongodb每个文档级别的聚合 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}}]

标签: mongodb mongoose mongodb-query aggregation-framework aggregate


【解决方案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 } } 并忽略 "" 键。我们还有其他方法可以包含这些文档吗?
  • 我试过了 :) 这里是 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
}

【讨论】:

    猜你喜欢
    • 2015-04-01
    • 2020-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-15
    • 2017-03-18
    • 1970-01-01
    相关资源
    最近更新 更多