У меня есть следующий документ:
{
_id: 123,
state: "AZ",
products: [
{
product_id: 1,
desc: "P1"
},
{
product_id: 2,
desc: "P2"
}
]
}
Мне нужно написать запрос, чтобы вернуть один элемент из массива продуктов, где состояние «AZ», а product_id равно 2. Если соответствующий product_id не найден, верните первый (или любой) элемент из массива продуктов.
Например: Если 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"}}})
Я также попытался использовать конвейер агрегации, но не смог найти рабочего решения.
Примечание. Это отличается от следующего вопроса, поскольку мне нужно вернуть элемент по умолчанию, если совпадение не найдено: получить только запрошенный элемент в массиве объектов в коллекции MongoDB
Всего 2 ответа
Если вам неважно, какой элемент вы вернетесь, тогда это будет путь (вы получите последний элемент в массиве в случае отсутствия соответствия, поскольку $ indexOfArray вернет -1):
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
}
}
}])
Вы можете попробовать выполнить агрегацию
В основном вам нужно $filter
массив products
и проверять значение $cond
ition, если оно не содержит никакого элемента или равно []
тогда вы должны $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
aggregation
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"
]
}
}
}
}}
])