Mongo聚合和带有$ lookup的$ group嵌套数组

时间:2019-12-10 10:57:54

标签: javascript node.js mongodb mongoose aggregation-framework

我有以下产品架构:(部分使用猫鼬)

 attributes: [
      {
        set: {
          ref: 'AttributeSet',
          type: Schema.Types.ObjectId
        },
        items: [
          {
            attribute: {
              ref: 'Attributes',
              type: Schema.Types.ObjectId
            },
            values: [
              {
                ref: 'AttributeValues',
                type: Schema.Types.ObjectId
              }
            ]
          }
        ],
        _id: 0
      }
    ],

示例文档1 :(部分)

"attributes" : [
        {
            "set" : ObjectId("5ccc079c846de44116182890"),
            "items" : [
                {
                    "values" : [
                        ObjectId("5ccc0900846de441161828a1")
                    ],
                    "_id" : ObjectId("5dee638d72fa520f53d0d1c4"),
                    "attribute" : ObjectId("5ccc0900846de441161828a0")
                },
                {
                    "values" : [
                        ObjectId("5ccc0a51846de441161828cc")
                    ],
                    "_id" : ObjectId("5dee638d72fa520f53d0d1c3"),
                    "attribute" : ObjectId("5ccc0a51846de441161828cb")
                },
                {
                    "values" : [
                        ObjectId("5ccc0c7d846de44116182906")
                    ],
                    "_id" : ObjectId("5dee638d72fa520f53d0d1c2"),
                    "attribute" : ObjectId("5ccc0c7d846de44116182904")
                },
                {
                    "values" : [
                        ObjectId("5ccc0d64846de44116182911")
                    ],
                    "_id" : ObjectId("5dee638d72fa520f53d0d1c1"),
                    "attribute" : ObjectId("5ccc0d64846de4411618290f")
                },
                {
                    "values" : [
                        ObjectId("5ccc079f846de44116182892")
                    ],
                    "_id" : ObjectId("5def6acf66910405e07e1e9f"),
                    "attribute" : ObjectId("5ccc079f846de44116182891")
                }
            ]
        }
    ]

示例文档2 :(部分)

"attributes" : [
        {
            "set" : ObjectId("5ccc079c846de44116182890"), 
            "items" : [
                {
                    "values" : [
                        ObjectId("5ccc079f846de44116182892")
                    ], 
                    "_id" : ObjectId("5dee635c72fa520f53d0d1c0"), 
                    "attribute" : ObjectId("5ccc079f846de44116182891")
                }, 
                {
                    "values" : [
                        ObjectId("5ccc0900846de441161828a2")
                    ], 
                    "_id" : ObjectId("5dee635c72fa520f53d0d1bf"), 
                    "attribute" : ObjectId("5ccc0900846de441161828a0")
                }, 
                {
                    "values" : [
                        ObjectId("5ccc0ea4846de44116182941")
                    ], 
                    "_id" : ObjectId("5dee635c72fa520f53d0d1be"), 
                    "attribute" : ObjectId("5ccc0ea4846de44116182940")
                }, 
                {
                    "values" : [
                        ObjectId("5ccc08ba846de4411618289c")
                    ], 
                    "_id" : ObjectId("5def56c537e877042d5abeb5"), 
                    "attribute" : ObjectId("5ccc08ba846de4411618289a")
                }, 
                {
                    "values" : [
                        ObjectId("5ccc09ca846de441161828aa"), 
                        ObjectId("5ccc09ca846de441161828a9")
                    ], 
                    "_id" : ObjectId("5def56c537e877042d5abeb4"), 
                    "attribute" : ObjectId("5ccc09ca846de441161828a7")
                }
            ]
        }
    ], 

我想汇总并找到所有具有属性的产品,然后在输出中对属性进行分组。

管道:

db.getCollection("products").aggregate(
  [
    { $unwind: "$attributes" },
    {
      $group: {
        _id: "$attributes",
        attributes: { $first: "$attributes.items" }
      }
    },
    { $unwind: "$attributes" },
    {
      $lookup: {
        from: "attributes",
        let: { attribute: "$attributes.attribute" },
        pipeline: [
          {
            $match: {
              $expr: {
                $eq: ["$_id", "$$attribute"]
              }
            }
          },
          { $project: { display_name: 1, _id: 1 } }
        ],
        as: "attrs"
      }
    },
    {
      $lookup: {
        from: "attributevalues",
        let: { attribute: "$attributes.values" },
        pipeline: [
          {
            $match: {
              $expr: {
                $in: ["$_id", "$$attribute"]
              }
            }
          }
        ],
        as: "values"
      }
    },
    { $project: { attrs: 1, values: 1, _id: 0 } },
    {
      $group: { _id: "$attrs", items: { $push: "$values" }, total: { $sum: 1 } }
    }
  ],
  {
    allowDiskUse: true
  }
);

管道输出:

[{ 
    "_id" : [
        {
            "_id" : ObjectId("5ccc079f846de44116182891"), 
            "display_name" : "Caliber (cal.)"
        }
    ], 
    "items" : [
        [
            {
                "_id" : ObjectId("5ccc079f846de44116182892"), 
                "sort_order" : NumberInt(0),
                "label" : "12", 
                "attribute_id" : ObjectId("5ccc079f846de44116182891")
            }
        ], 
        [
            {
                "_id" : ObjectId("5ccc079f846de44116182892"), 
                "sort_order" : NumberInt(0),
                "label" : "12", 
                "attribute_id" : ObjectId("5ccc079f846de44116182891")
            }
        ]
    ], 
    "total" : 2.0
},
{
    "_id" : [
        {
            "_id" : ObjectId("5ccc0900846de441161828a0"),
            "display_name" : "Mechanism"
        }
    ],
    "items" : [
        [
            {
                "_id" : ObjectId("5ccc0900846de441161828a2"),
                "sort_order" : NumberInt(1),
                "label" : "Inaction",
                "attribute_id" : ObjectId("5ccc0900846de441161828a0")
            }
        ],
        [
            {
                "_id" : ObjectId("5ccc0900846de441161828a1"),
                "sort_order" : NumberInt(0),
                "label" : "Gas",
                "attribute_id" : ObjectId("5ccc0900846de441161828a0")
            }
        ]
    ],
    "total" : 2.0
}]

问题是e.x。在数组的第一个元素中,我在items数组中有一个副本。

这是所需的输出:

[{
    "_id" : [
        {
            "_id" : ObjectId("5ccc079f846de44116182891"),
            "display_name" : "Caliber (cal.)"
        }
    ],
    "items" : [
        [
            {
                "_id" : ObjectId("5ccc079f846de44116182892"),
                "sort_order" : NumberInt(0),
                "label" : "12",
                "attribute_id" : ObjectId("5ccc079f846de44116182891"),
                "total": 'current _id total, in this case it should be 2'
            }
        ],
        ...other items goes below, grouped as above
    ]
}]

1 个答案:

答案 0 :(得分:2)

添加解决方案以按属性和值对值进行分组并计算出现的次数,然后查找并推送属性的所有值及其计数。

db.products.aggregate(
[
   {"$unwind":"$attributes"},
   {"$unwind":"$attributes.items"},
   {"$replaceRoot":{"newRoot":"$attributes.items"}},
   {"$unwind":"$values"},
   {"$group":{
      "_id":{"attribute":"$attribute","values":"$values"},
      "total":{"$sum":1}
   }},
   {"$lookup":{
      "from":"attributes",
      "let":{"attribute":"$_id.attribute"},
      "pipeline":[
        {"$match":{"$expr":{"$eq":["$_id","$$attribute"]}}},
        {"$project":{"display_name":1,"_id":1}}],
      "as":"attrs"
   }},
   {"$lookup":{
      "from":"attributevalues",
      "localField":"_id.values",
      "foreignField":"_id",
      "as":"values"
   }},
   {"$unwind":"$values"},
   {"$addFields":{"values.total":"$total"}},
   {"$group":{
     "_id":{"$arrayElemAt":["$attrs", 0]},
     "values":{"$push":"$values"}
   }}
])

使用以下聚合查询。使用$ addToSet保留唯一值。

db.products.aggregate(
 [
  {"$unwind":"$attributes"},
  {"$unwind":"$attributes.items"},
  {"$replaceRoot":{"newRoot":"$attributes.items"}},
  {"$unwind":"$values"},
  {"$group":{
     "_id":"$attribute",
     "values":{"$addToSet":"$values"},
     "total":{"$sum":1}
  }},
  {"$lookup":{
     "from":"attributes",
     "let":{"attribute":"$_id"},
     "pipeline":[
       {"$match":{"$expr":{"$eq":["$_id","$$attribute"]}}},
       {"$project":{"display_name":1,"_id":1}}],
     "as":"attrs"
  }},
  {"$addFields":{"attrs":{"$arrayElemAt":["$attrs", 0]}},
  {"$lookup":{
     "from":"attributevalues",
     "localField":"values",
     "foreignField":"_id",
     "as":"values"
   }}
])

旧答案

您可以使用以下汇总查询。我试图清理您当前的查询并更改为仅按值字段分组。

类似

db.products.aggregate(
[
  {"$unwind":"$attributes"},
  {"$unwind":"$attributes.items"},
  {"$replaceRoot":{"newRoot":"$attributes.items"}},
  {"$unwind":"$values"},
  {"$group":{
    "_id":"$values",
    "items":{"$first":"$$ROOT"},
    "total":{"$sum":1}
  }},
  {"$lookup":{
    "from":"attributes",
    "let":{"attribute":"$items.attribute"},
    "pipeline":[
      {"$match":{"$expr":{"$eq":["$_id","$$attribute"]}}},
      {"$project":{"display_name":1,"_id":1}}],
    "as":"attrs"
  }},
  {"$lookup":{
     "from":"attributevalues",
     "localField":"items.values",
     "foreignField":"_id",
     "as":"values"
  }},
  {"$unwind":"$values"},
  {"$group":{
    "_id":{"$arrayElemAt":["$attrs", 0]},
    "values":{"$push":"$values"},
    "total":{"$first":"$total"}
  }},
  {"$addFields":{"_id":0, "attribute":"$_id"}}
])