Mongodb 循环遍历每个不同的值并使用聚合方面选择标签

问题描述

我有这样的收藏:

{
    "labels": [{
        "description": "Dog"
    },{
        "description": "Red"
    },{
        "description": "XXX"
    }]
}
{
    "labels": [{
        "description": "Cat"
    },{
        "description": "XXX"
    },{
        "description": "Yellow"
    }]
}
{
    "labels": [{
        "description": "Dog"
    },{
        "description": "Yellow"
    }]
}
{
    "labels": [{
        "description": "Bird"
    },{
        "description": "XXX"
    }]
}

例如,我只想从所有元素和输出文档中过滤“红色”和“黄色”颜色,如下所示:

// because "Dog" appears 2 times so total = 2
{
  description: "Dog",total: 2,colors: [
      { "_id": "Red",total: 2 },{ "_id": "Yellow",total: 1 }
  ]
}

{
  description: "Cat",total: 1,colors: [
      { "_id": "Yellow",total: 1 }
  ]
}

{
  description: "Bird",colors: []
}

{
  description: "Red",colors: [
    { _id: "Yellow",total: 1 }
  ]
}
{
  description: "XXX",total: 4,total: 1 }
  ]
}

我可以通过使用 collection.distinct('labels.description') 然后遍历每个元素 + 像这样创建一个单独的 collection.count({ 'labels.description': 'Dog' }) 来做到这一点:

for (...)
db.collection.aggregate([
{   
        "$match": {
            "labels.description": valueFromLoop // (e.g. Dog)
        }
},{ $unwind : "$labels" },{
    "$group": {
        "_id": "$labels.description","count": { "$sum": 1 }
    }
},{
    "$match": {
        "$or": [
            { "_id": "Red" },{ "_id": "Yellow" }
        ]
    }
},{
    "$sort": {
        "count": -1
    }
}
])

我想在单个聚合或 mapReduce 中执行此操作,以便我可以使用 $out 轻松将其输出到新集合,而不是单独使用批量操作,但是我不知道是否可行。

解决方法

试试这个:

let filter = ["Red","Yellow"];

db.testcollection.aggregate([
    {
        $addFields: { bkp: "$labels" }
    },{ $unwind: "$labels" },{
        $addFields: {
            bkp: {
                $filter: {
                    input: "$bkp",as: "item",cond: {
                        $and: [
                            { $ne: ["$$item.description","$labels.description"] },{ $in: ["$$item.description",filter] }
                        ]
                    }
                }
            }
        }
    },{
        $unwind: {
            path: "$bkp",preserveNullAndEmptyArrays: true
        }
    },{
        $group: {
            _id: {
                key1: "$labels.description",key2: { $ifNull: ["$bkp.description",false] }
            },total: { $sum: 1 }
        }
    },{
        $group: {
            _id: "$_id.key1",description: { $first: "$_id.key1" },total: {
                $sum: {
                    $cond: {
                        if: { $first: [["$_id.key2"]] },then: 1,else: "$total"
                    }
                }
            },colors: {
                $push: {
                    $cond: {
                        if: { $first: [["$_id.key2"]] },then: {
                            _id: "$_id.key2",total: "$total"
                        },else: "$$REMOVE"
                    }
                }
            }
        }
    },{ $project: { _id: 0 } }
]);
,
db.test2.aggregate([
              {
                $project: {
                  labels:1,colours: {
                    $filter: {
                    input: "$labels",as: "label",cond: {
                      $or: [
                        {$eq:["Yellow","$$label.description"]},{$eq:["Red","$$label.description"]}
                      ]
                    }
                  }
                }
              }
              },{$unwind:"$labels"},{$group:{
                _id: "$labels.description",total: {$sum:1},colours: {$addToSet:"$colours.description"}
              }},{
                $project:{
                _id:0,description:"$_id",total:1,colours: {
                  $reduce:{
                    input: "$colours",initialValue: [],in: {$concatArrays: ["$$value","$$this"]}
                  }
                }
              }
              },{
                  $unwind: {
                   path:"$colours",preserveNullAndEmptyArrays: true
                   }
              },{
                $group:{
                _id:{
                  description:"$description",total:"$total",colour:"$colours"
                 },count: {
                    $sum: {$cond:[{$ifNull:["$colours",false]},1,0]}
                  }
              }
              },{
                  $group:{
                      _id:{
                       description:"$_id.description",total:"$_id.total"
                      },colours: {
                        $push: {
                            $cond: [{$gt:["$count",0]},{
                                "_id":"$_id.colour",total:"$count"
                            },"$$REMOVE" 
                            ]
                        }
                    }
                  }
              },{
                  $project: {
                      _id:0,description: "$_id.description",total: "$_id.total",colours: 1
                  }
              }
            ]);

**Edit 在您的 answer 中,您缺少 Red 和 Dog 的 Yellows,因为您使用 $result$arrayElemAt: ["$result.description",0] 中取出第一项。

如果描述是一种颜色,你是否也想在颜色中包括它自己的计数?

没关系,你已经更新了答案

,

出于某种原因,两个答案中的代码都没有正确计算所有标签。 我正在发布有效的内容:

db.collection.aggregate([
    {
        $project: {
            labels: 1,result: {
                $filter: {
                    input: "$labels",cond: {
                        $or: [
                            { $eq: ["$$label.description","Blue"] },{ $eq: ["$$label.description","Red"] },"Black-and-white"] },"Purple"] },"Orange"] },"Yellow"] },"Green"] },"Teal"] }
                        ]
                    }
                }
            }
        }
    },{
        $unwind: "$labels"
    },{
        "$group": {
            _id: "$labels.description",x: {
                $push: "$result.description" 
            },total: { "$sum": 1 }
        }
    },{
        $project: {
            x: {
              $reduce: {
                input: '$x',in: {$concatArrays: ['$$value','$$this']}
              }
            },total: 1
        }
    },{
        $project: {
            x: 1,y: { $setUnion: "$x" },{
        $project: {
            _id: 0,description: "$_id","colors": {
                $map: {
                    input: "$y",in: {
                        _id: "$$item",count: {
                            $size: {
                                $filter: {
                                    input: "$x",as: "itemx",cond: {
                                        $eq: ["$$item","$$itemx"]
                                    }
                                }
                            }
                        }
                    }
                }
            },{
        $out: "backgrounds_meta"
    }
])