首页 文章

MongoDB聚合选择性项目

提问于
浏览
7

在聚合分组期间,我遇到了重塑文档的问题 . 基本上我想根据类型将条目推送到字段 . 我的结构如下:

_id: P1
   entities: [{type: A, val: X}, {type: B, val: X}, {type: A, val: Y}]
   ...

我想$ unwind和$投影这些实体,以便获得如下结构:

_id: P1
   A: [X]
   B: []
_id: P1
   A: [Y]
   B: []
_id: P1
   A: []
   B: [X]

所以我可以用A或B或两者进行分组,即

$group: {
   _id: {
       A: $A, 
       B: $B
    }
    count: {$sum : 1}

我以为我可以做到:

$unwind: $entities
$project: {
   id: §id
   A: {"$cond":[{"$eq":["$type","A"]},"$code"]}
   B: {"$cond":[{"$eq":["$type","B"]},"$code"]}
}
$group: {
  _id: "$id"
  A: {$addToSet : "$A"}
}

或失败的东西

$unwind: $entities
$group: {
  _id: "$id"
  A: {"$cond":[{"$eq":["$type","A"]},$push: "$code", null]}
  ...
}

但是这两个版本都失败了,因为我对其他内容无能为力,而且我没有设法在条件内使用$ push . 我得到的最接近的是项目取决于类型,但由于我找不到在没有匹配时不向场添加任何内容的方法,我最终得到:

_id: P1
   A: [X,null,Y]
   B: [null,X,null]

这弄乱了计数 . 我的第二个想法是过滤数组以删除null . 但我没有找到删除实体的方法,因为再次$ cond不会让我指定一个空/“什么都不做”的情况 .

我觉得它可以通过类型和内容进行分组以及所需类型的匹配,但由于我有许多类型和任意分组导致分组树,这可能会变得非常复杂 . 对错误的想法或提示将非常受欢迎 .

谢谢

EDIT: 基于接受的anwser的解决方案

我不得不稍微调整它,以过滤一个类型的所有内容都为null的情况,因为否则它会在匹配期间丢失,因为我想保留这些知识 . 谢谢!

{$project:{
  A: {$cond: [
      {$eq: ["$A", [false]]},
      ["N/A"],
      "$A"
  ]},
  B: {$cond: [
      {$eq: ["$B", [false]]},
      ["N/A"],
      "$B"
  ]},
}},
{ "$unwind": "$A" },
{ "$match": { "A": { "$ne": false } } },
{ "$group": {
    "_id": "$_id",
    "A": { "$push": "$A" },
    "B": { "$first": "$B" }
}},
{ "$unwind": "$B" },
{ "$match": { "B": { "$ne": false } } },
{ "$group": {
    "_id": "$_id",
    "A": { "$first": "$A" },
    "B": { "$push": "$B" }
}}

1 回答

  • 13

    你似乎走在正确的轨道上,只有不同的方法从条件中删除 false 的值 . 你不能让它什么都不返回,但你可以摆脱你不想要的值 .

    如果你真的想要"sets"并且你有MongoDB 2.6或更高版本可用,那么你基本上使用$setDifference过滤掉 false 值:

    db.entities.aggregate([
        { "$unwind": "$entities" },
        { "$group": {
            "_id": "$_id",
            "A": { 
                "$addToSet": {
                    "$cond": [
                        { "$eq": [ "$entities.type", "A" ] },
                        "$entities.val",
                        false
                    ]
                }
            },
            "B": { 
                "$addToSet": {
                    "$cond": [
                        { "$eq": [ "$entities.type", "B" ] },
                        "$entities.val",
                        false
                    ]
                }
            }
        }},
        { "$project": {
            "A": {
                "$setDifference": [ "$A", [false] ]
            },
            "B": {
                "$setDifference": [ "$B", [false] ]
            }
        }}
    ])
    

    或者只是在$project中使用$map运算符的一步:

    db.entities.aggregate([
        {"$project": {
            "A": {
                 "$setDifference": [
                     {
                         "$map": {
                             "input": "$entities",
                             "as": "el",
                             "in": {
                                 "$cond": [
                                     { "$eq": [ "$$el.type", "A" ] },
                                     "$$el.val",
                                     false
                                 ]
                             }
                         }
                     },
                     [false]
                 ]
             },
            "B": {
                 "$setDifference": [
                     {
                         "$map": {
                             "input": "$entities",
                             "as": "el",
                             "in": {
                                 "$cond": [
                                     { "$eq": [ "$$el.type", "B" ] },
                                     "$$el.val",
                                     false
                                 ]
                             }
                         }
                     },
                     [false]
                 ]
             }
        }}
    ])
    

    或者与一般的$unwind$match运算符一起使用来过滤这些:

    db.entities.aggregate([
        { "$unwind": "$entities" },
        { "$group": {
            "_id": "$_id",
            "A": { 
                "$push": {
                    "$cond": [
                        { "$eq": [ "$entities.type", "A" ] },
                        "$entities.val",
                        false
                    ]
                }
            },
            "B": { 
                "$push": {
                    "$cond": [
                        { "$eq": [ "$entities.type", "B" ] },
                        "$entities.val",
                        false
                    ]
                }
            }
        }},
        { "$unwind": "$A" },
        { "$match": { "A": { "$ne": false } } },
        { "$group": {
            "_id": "$_id",
            "A": { "$push": "$A" },
            "B": { "$first": "$B" }
        }},
        { "$unwind": "$B" },
        { "$match": { "B": { "$ne": false } } },
        { "$group": {
            "_id": "$_id",
            "A": { "$first": "$A" },
            "B": { "$push": "$B" }
        }}
    ])
    

    对正常数组使用$push或对于唯一集使用$addToSet .

相关问题