在聚合分组期间,我遇到了重塑文档的问题。基本上我想根据类型将条目推送到字段。我的结构如下:
_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不会让我指定一个空的/“什么也不做”的其他情况。
我感觉它可以通过类型和内容进行分组以及所需类型的匹配,但由于我有许多类型和任意分组导致分组树,这可能会变得非常复杂。对错误的想法或提示将非常受欢迎。
谢谢
编辑:基于已接受的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" }
}}
答案 0 :(得分:14)
您似乎走在正确的轨道上,只有不同的方法可以从条件中删除false
的值。你不能没有任何回报,但你可以摆脱你不想要的价值。
如果你真的想要"套"并且您可以使用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] ]
}
}}
])
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" }
}}
])