有几种方法:
理想情况下,您可以使用 MongoDB 3.4 中的 $indexOfArray
,然后将其与 $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 ] }
]
}
}
}}
}
])
如果你只想要匹配的"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 ] }
]
}
}
}}
}
])
那是因为如果
$indexOfArray
返回
-1
表示 "未找到",那么
$cond
将相应地分支:
或者,使用
$filter
和
$size
:
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 ] }
}
}
}
}
}}
])
你甚至可以使用
$arrayElemAt
在最后一个表单上进行变化,如果你至少拥有 MongoDB 3.2,它只会返回位置为
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
]
}
}
}
}}
])
在处理if
条件时,$cond
还有其他替代方案,可以使用$in
来匹配数组元素的比较:
"if": { "$in": [ 3, "$formats.language_id" ] }
但是,由于这需要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 ] }
}
}
}
}
}
}
}}
])
所以它存在,但只是额外的工作,收益很小,因为最好的
$or
条件匹配“默认”情况,而你仍然需要“过滤掉”仅针对“首选”匹配的情况。
$indexOfArray
返回 -1,而根据文档$arrayElemAt
给出数组中的最后一个元素。 - Rahul Sharma$ifNull
可能不是最好的选择。 - Neil Lunnlanguage_id
1,这可以通过第三个查询正确实现。我只是想指出,如果 $indexOfArray 返回 -1,则 $arrayElemAt 不会返回 null。无论如何,感谢您对第三个查询的帮助。 - Rahul Sharma$ifNull
不起作用,因为没有返回null
的东西,因此$cond
在所有情况下都有效。 - Neil Lunn