MongoDB: 第二次匹配聚合失败后保留第一次匹配的结果
MongoDB: Keep result of first match after second match failed in aggregation
在 MongoDB 中,我必须从文档中仅推送与对象 属性 匹配的对象数组。使用聚合我可以实现这一目标。
但是我对匹配有疑问。
Users
.aggregate()
.match({_id: userId})
.unwind('tags')
.match({"tags.name": "verified"})
.group({
"_id": {"id": "$_id", "type": "$type"},
"tags": {$push: "$tags"}
})
当用户有 "verified" 标签时,这工作正常。但是当用户没有时,结果只是 []
(空数组)。
我知道这是已知的行为,但是我如何获得匹配的用户(因为第一个匹配将 return 如果有效用户)单独使用 tags
作为空数组。
样本集:
{
_id: 123,
type: "normal",
tags: [{name: "verified", "tagId": 1}, {name: "good", "tagId": 2}]
}
{
_id: 122,
type: "normal",
tags: [{name: "good", "tagId": 2}]
}
所以当我对 userId = 123 应用上述聚合时,结果是,
{
result: [
{
"_id": {
"id": 123,
"type": "normal"
}
"tags": [{name: "verified", "tagId": 1}]
}
],
"ok": 1
}
同理如果我用userId = 122申请,结果是[]
但预期的结果是,
{
result: [
{
"_id": {
"id": 122,
"type": "normal"
}
"tags": []
}
],
"ok": 1
}
您基本上需要测试匹配数是否为 0
并将结果数组换成一个空数组,否则为空数组。将不匹配的值替换为 null
insead:
这是一种方法,但如果可能的话,另一种方法是只使用 $redact
来过滤数组:
Users.aggregate(
[
{ "$match": { "_id": userId } },
{ "$redact": {
"$cond": {
"if": {
"$eq": [ { "$ifNull": [ "$name", "verified" ] }, "verified" ]
},
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}}
],
function(err,results) {
}
);
在即将发布的 MongoDB 版本中(截至撰写本文时),还将有一个 $filter
聚合运算符。这很简单,并且也会留下空数组:
Users.aggregate(
[
{ "$project": {
"type": 1,
"tags": {
"$filter": {
"input": "$tags",
"as": "el",
"cond": {
"$eq": [ "$$el.name", "verified" ]
}
}
}
}}
],
function(err,results) {
}
);
但除此之外,您基本上计算匹配的数量并在 $cond
中交换数组,其中匹配计数为 0
:
Users.aggregate(
[
{ "$match": { "_id": userId } },
{ "$unwind": "$tags" },
{ "$group": {
"_id": {
"_id": "$_id",
"type": "$type"
},
"tags": {
"$push": {
"$cond": [
{ "$eq": [ "$tags.name", "verified" ] },
"$tags",
null
]
}
},
"count": {
"$sum": {
"$cond": [
{ "$eq": [ "$tags.name", "verified" ] },
1,
0
]
}
}
}},
{ "$unwind": "$tags" },
{ "$match": {
"$or": [
{ "tags.name": "verified" },
{ "count": 0 }
]
}},
{ "$group": {
"_id": "$_id",
"tags": { "$push": "$tags" },
"count": { "$first": "$count" }
}},
{ "$project": {
"tags": {
"$cond": [
{ "$eq": [ "$count", 0 ] },
[],
"$tags"
]
}
}}
],
function(err,results) {
}
);
如果可以的话,我会选择前者。然后在 MongoDB 的生产版本支持它后使用 $filter
。
在 MongoDB 中,我必须从文档中仅推送与对象 属性 匹配的对象数组。使用聚合我可以实现这一目标。
但是我对匹配有疑问。
Users
.aggregate()
.match({_id: userId})
.unwind('tags')
.match({"tags.name": "verified"})
.group({
"_id": {"id": "$_id", "type": "$type"},
"tags": {$push: "$tags"}
})
当用户有 "verified" 标签时,这工作正常。但是当用户没有时,结果只是 []
(空数组)。
我知道这是已知的行为,但是我如何获得匹配的用户(因为第一个匹配将 return 如果有效用户)单独使用 tags
作为空数组。
样本集:
{
_id: 123,
type: "normal",
tags: [{name: "verified", "tagId": 1}, {name: "good", "tagId": 2}]
}
{
_id: 122,
type: "normal",
tags: [{name: "good", "tagId": 2}]
}
所以当我对 userId = 123 应用上述聚合时,结果是,
{
result: [
{
"_id": {
"id": 123,
"type": "normal"
}
"tags": [{name: "verified", "tagId": 1}]
}
],
"ok": 1
}
同理如果我用userId = 122申请,结果是[]
但预期的结果是,
{
result: [
{
"_id": {
"id": 122,
"type": "normal"
}
"tags": []
}
],
"ok": 1
}
您基本上需要测试匹配数是否为 0
并将结果数组换成一个空数组,否则为空数组。将不匹配的值替换为 null
insead:
这是一种方法,但如果可能的话,另一种方法是只使用 $redact
来过滤数组:
Users.aggregate(
[
{ "$match": { "_id": userId } },
{ "$redact": {
"$cond": {
"if": {
"$eq": [ { "$ifNull": [ "$name", "verified" ] }, "verified" ]
},
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}}
],
function(err,results) {
}
);
在即将发布的 MongoDB 版本中(截至撰写本文时),还将有一个 $filter
聚合运算符。这很简单,并且也会留下空数组:
Users.aggregate(
[
{ "$project": {
"type": 1,
"tags": {
"$filter": {
"input": "$tags",
"as": "el",
"cond": {
"$eq": [ "$$el.name", "verified" ]
}
}
}
}}
],
function(err,results) {
}
);
但除此之外,您基本上计算匹配的数量并在 $cond
中交换数组,其中匹配计数为 0
:
Users.aggregate(
[
{ "$match": { "_id": userId } },
{ "$unwind": "$tags" },
{ "$group": {
"_id": {
"_id": "$_id",
"type": "$type"
},
"tags": {
"$push": {
"$cond": [
{ "$eq": [ "$tags.name", "verified" ] },
"$tags",
null
]
}
},
"count": {
"$sum": {
"$cond": [
{ "$eq": [ "$tags.name", "verified" ] },
1,
0
]
}
}
}},
{ "$unwind": "$tags" },
{ "$match": {
"$or": [
{ "tags.name": "verified" },
{ "count": 0 }
]
}},
{ "$group": {
"_id": "$_id",
"tags": { "$push": "$tags" },
"count": { "$first": "$count" }
}},
{ "$project": {
"tags": {
"$cond": [
{ "$eq": [ "$count", 0 ] },
[],
"$tags"
]
}
}}
],
function(err,results) {
}
);
如果可以的话,我会选择前者。然后在 MongoDB 的生产版本支持它后使用 $filter
。