Rah*_*rma 4 arrays mongodb mongodb-query aggregation-framework
我有一个questions用这样的文档命名的集合:
{
"formats": [
{
"language_id": 1,
"text": "question text1"
},
{
"language_id": 2,
"text": "question text 2"
}
],
"qid": "HQSRFA3T"
}
Run Code Online (Sandbox Code Playgroud)
我想写一个查询,如果特定language_id的不存在,那么language_id默认情况下应该返回 1 。
到目前为止,我已经尝试了两个查询:
db.questions.aggregate([
{
$match: {
'qid': 'HQSRFA3T'
}
},
{
$project: {
formats: {
$ifNull: [
{ $filter: { input: '$formats', as: 'format', cond: {$eq: ['$$format.language_id', 3]}} },
{ $filter: { input: '$formats', as: 'format', cond: {$eq: ['$$format.language_id', 1]}} }
]
},
_id: 0
}
}
])
Run Code Online (Sandbox Code Playgroud)
这个查询的结果是这样的:
{ "formats" : [ ] }
然后还有另一个查询是这样的:
db.questions.aggregate([ { $match: {'qid': 'HQSRFA3T'}}, { $project: {
formats: {
$filter: {
input: '$formats',
as: 'format',
cond: {
$or: [
{ $eq: ['$$format.language_id', 1] },
{ $eq: ['$$format.language_id', 3] }
]
}
}
},
_id: 0
}}])
Run Code Online (Sandbox Code Playgroud)
如果两个元素language_id都存在于数组中,则此查询将返回两个元素。
有“几种”方式:
理想情况下,您$indexOfArray从 MongoDB 3.4 开始,然后您可以将其与$in以下内容结合使用:
db.questions.aggregate([
{ "$match": { "qid": "HQSRFA3T" } },
{ "$project": {
"formats": {
"$cond": {
"if": { "$in": [ 3, "$formats.language_id"] },
"then": {
"$arrayElemAt": [
"$formats",
{ "$indexOfArray": [ "$formats.language_id", 3 ] }
]
},
"else": {
"$arrayElemAt": [
"$formats",
{ "$indexOfArray": [ "$formats.language_id", 1 ] }
]
}
}
}}
}
])
Run Code Online (Sandbox Code Playgroud)
如果你真正想要的是匹配"text",那么稍微改变一下:
db.questions.aggregate([
{ "$match": { "qid": "HQSRFA3T" } },
{ "$project": {
"text": {
"$cond": {
"if": { "$in": [ 3, "$formats.language_id"] },
"then": {
"$arrayElemAt": [
"$formats.text",
{ "$indexOfArray": [ "$formats.language_id", 3 ] }
]
},
"else": {
"$arrayElemAt": [
"$formats.text",
{ "$indexOfArray": [ "$formats.language_id", 1 ] }
]
}
}
}}
}
])
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为如果$indexOfArray返回-1指示“未找到”,$cond则将相应地分支:
db.questions.aggregate([
{ "$match": { "qid": "HQSRFA3T" } },
{ "$project": {
"formats": {
"$cond": {
"if": { "$gt": [
{ "$size": {
"$filter": {
"input": "$formats",
"cond": { "$eq": [ "$$this.language_id", 3 ] }
}
}},
0
]},
"then": {
"$filter": {
"input": "$formats",
"cond": { "$eq": [ "$$this.language_id", 3 ] }
}
},
"else": {
"$filter": {
"input": "$formats",
"cond": { "$eq": [ "$$this.language_id", 1 ] }
}
}
}
}
}}
])
Run Code Online (Sandbox Code Playgroud)
如果您至少拥有 MongoDB 3.2,您甚至可以改变最后一种形式,$arrayElemAt只返回位置处的“单个”匹配数组元素0。
db.questions.aggregate([
{ "$match": { "qid": "HQSRFA3T" } },
{ "$project": {
"formats": {
"$cond": {
"if": { "$gt": [
{ "$size": {
"$filter": {
"input": "$formats",
"cond": { "$eq": [ "$$this.language_id", 3 ] }
}
}},
0
]},
"then": {
"$arrayElemAt": [
{ "$filter": {
"input": "$formats",
"cond": { "$eq": [ "$$this.language_id", 3 ] }
}},
0
]
},
"else": {
"$arrayElemAt": [
{ "$filter": {
"input": "$formats",
"cond": { "$eq": [ "$$this.language_id", 1 ] }
}},
0
]
}
}
}
}}
])
Run Code Online (Sandbox Code Playgroud)
在其它替代$cond的if条件使用$in,以匹配的数组元素的比较:
"if": { "$in": [ 3, "$formats.language_id" ] }
Run Code Online (Sandbox Code Playgroud)
但由于这需要 MongoDB 3.4,那么您也可以改用$indexOfArray运算符。
尝试“强制”多个匹配项$filter并最终希望丢弃其中一个几乎没有意义,但是您“可以”这样做$let:
db.questions.aggregate([
{ "$match": { "qid": "HQSRFA3T" } },
{ "$project": {
"formats": {
"$let": {
"vars": {
"formats": {
"$filter": {
"input": "$formats",
"cond": {
"$or": [
{ "$eq": [ "$$this.language_id", 1 ] },
{ "$eq": [ "$$this.language_id", 3 ] }
]
}
}
}
},
"in": {
"$cond": {
"if": {
"$gt": [
{ "$size": {
"$filter": {
"input": "$$formats",
"cond": { "$eq": [ "$$this.language_id", 3 ] }
}
}},
0
]
},
"then": {
"$filter": {
"input": "$$formats",
"cond": { "$eq": [ "$$this.language_id", 3 ] }
}
},
"else": {
"$filter": {
"input": "$$formats",
"cond": { "$eq": [ "$$this.language_id", 1 ] }
}
}
}
}
}
}
}}
])
Run Code Online (Sandbox Code Playgroud)
所以它就在那里,但它只是额外的工作,收益很小,因为$or条件充其量与“默认”情况匹配,并且无论如何您仍然需要“过滤掉”仅“首选”匹配。