Mongodb 使用聚合(构面)遍历每个不同的值和 select 标签

Mongodb loop through every distinct values and select tags using aggregate (facet)

我有 collection 这样的:

{
    "labels": [{
        "description": "Dog"
    }, {
        "description": "Red"
    }, {
        "description": "XXX"
    }]
}
{
    "labels": [{
        "description": "Cat"
    }, {
        "description": "XXX"
    }, {
        "description": "Yellow"
    }]
}
{
    "labels": [{
        "description": "Dog"
    }, {
        "description": "Red"
    }, {
        "description": "Yellow"
    }]
}
{
    "labels": [{
        "description": "Bird"
    }, {
        "description": "XXX"
    }, {
        "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",
  total: 1,
  colors: []
}

{
  description: "Red",
  total: 2,
  colors: [
    { _id: "Yellow", total: 1 }
  ]
}
{
  description: "XXX",
  total: 4,
  colors: [
    { _id: "Yellow", 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 轻松地将它输出到新的 collection,而不是单独使用批量操作,但我不知道这是否可能。

试试这个:

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
                  }
              }
            ]);

**编辑 在您的 中,您缺少红色和狗的黄色,因为您使用 $arrayElemAt: ["$result.description", 0]$result 中获取第一个项目。

如果描述是一种颜色,您是否也想在颜色中包含自身的数量?

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

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

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