我有以下文件:
{
_id: 123,
state: "AZ",
products: [
{
product_id: 1,
desc: "P1"
},
{
product_id: 2,
desc: "P2"
}
]
}
我需要编写一个查询以从状态为“ AZ”且product_id为2的products数组返回单个元素。如果找不到匹配的product_id,则从products数组返回第一个(或任何一个)元素
例如:如果product_id为2(找到匹配项),则结果应为:
products: [
{
product_id: 2,
desc: "P2"
}
]
如果product_id为3(未找到),则结果应为:
products: [
{
product_id: 1,
desc: "P1"
}
]
找到匹配项后,我能够满足一个条件,但不确定在同一查询中如何满足第二个条件:
db.getCollection('test').find({"state": "AZ"}, {_id: 0, state: 0, products: { "$elemMatch": {"product_id": "2"}}})
我也尝试使用聚合管道,但是找不到可行的解决方案。
注意:这与以下问题不同,因为如果找不到匹配项,我需要返回一个默认元素: Retrieve only the queried element in an object array in MongoDB collection
答案 0 :(得分:2)
您可以尝试以下汇总
基本上,您需要$filter
个products
数组并检查$cond
位是否包含任何元素或不等于 []
,那么您必须$slice
和products
数组的第一个元素。
db.collection.aggregate([
{ "$addFields": {
"products": {
"$cond": [
{
"$eq": [
{ "$filter": {
"input": "$products",
"cond": { "$eq": ["$$this.product_id", 2] }
}},
[]
]
},
{ "$slice": ["$products", 1] },
{ "$filter": {
"input": "$products",
"cond": { "$eq": ["$$this.product_id", 2] }
}}
]
}
}}
])
甚至使用$let
聚合
db.collection.aggregate([
{ "$addFields": {
"products": {
"$let": {
"vars": {
"filt": {
"$filter": {
"input": "$products",
"cond": { "$eq": ["$$this.product_id", 2] }
}
}
},
"in": {
"$cond": [
{ "$eq": ["$$filt", []] },
{ "$slice": ["$products", 1] },
"$$filt"
]
}
}
}
}}
])
答案 1 :(得分:1)
如果您不在乎要返回哪个元素,那么这就是要走的路(如果$indexOfArray将返回-1,则在不匹配的情况下,您将获得数组中的最后一个元素):< / p>
db.getCollection('test').aggregate([{
$addFields: {
"products": {
$arrayElemAt: [ "$products", { $indexOfArray: [ "$products.product_id", 2 ] } ]
},
}
}])
如果要使用第一个元素,则可以这样做($max将把-1转换为第一个元素的索引0):
db.getCollection('test').aggregate([{
$addFields: {
"products": {
$arrayElemAt: [ "$products", { $max: [ 0, { $indexOfArray: [ "$products.product_id", 2 ] } ] } ]
},
}
}])
以下是也应在v3.2上运行的版本:
db.getCollection('test').aggregate([{
"$project": {
"products": {
$slice: [{
$concatArrays: [{
$filter: {
"input": "$products",
"cond": { "$eq": ["$$this.product_id", 2] }
}},
"$products" // simply append the "products" array
// alternatively, you could append only the first or a specific item like this [ { $arrayElemAt: [ "$products", 0 ] } ]
]
},
1 ] // take first element only
}
}
}])