#mongodb #aggregation-framework
Вопрос:
У меня есть массив, доступный в БД с ключевыми героями.
Heros: [
{
name: 'Ironman',
country: 'USA'
},
{
name: 'Shaktiman',
country: 'India'
},
{
name: 'Black Panther',
country: 'Wakanda'
}
]
и у меня есть новый массив, в котором в основном есть дополнительные герои:
additionalHeros = [{ name: 'Wonder woman', country: 'USA' }, { name: 'Kenshiro', country: 'Japan' }]
Я хочу обновить имя и страну героя по определенному индексу, скажем, 1, и заменить его новыми значениями. После этого я должен поместить всех дополнительных героев в один и тот же запрос.
После выполнения запроса на обновление ожидаемый результат будет:
At index 2 -> update name: 'Krishh', country: 'India' -> put rest of the heros
Обновленное значение базы данных для героев составляет:
Heros: [
{
name: 'Ironman',
country: 'USA'
},
{
name: 'Shaktiman',
country: 'India'
},
{
name: 'Krishh',
country: 'India'
},
{ name: 'Wonder woman',
country: 'USA'
},
{ name: 'Kenshiro',
country: 'Japan'
}
]
Я знаю о части, которая должна указывать значение, но я не могу найти никакого эффективного способа обновить и вставить сразу
$push: { heros: { $each: additionalHeros } }
Комментарии:
1. 1) регулярный запрос на обновление не позволит выполнять 2 операции в одном поле — это создаст конфликт 2) вы можете использовать обновление с конвейером агрегации — но это не эффективный способ выполнить эту операцию.
2. @turivishal Тогда что было бы лучше, любое другое хорошее решение
Ответ №1:
- Обычный запрос на обновление не позволит выполнить 2 операции в одном поле, это приведет к ошибке конфликта
- Вы можете использовать обновление с конвейером агрегации, начиная с MongoDB 4.2, но я думаю, что этот подход неэффективен, и вам нужен эффективный подход,
$map
для повторения циклаheros
массива$indexOfArray
чтобы получить индекс текущего объекта вheros
массиве$cond
чтобы проверить, совпадает ли приведенное вышеindex
значение с нашим входным индексом, затем обновите последнее значение, в противном случае верните тот же объект- второй
$set
этап для добавления новогоheros
$concatArrays
оператора using
additionalHeros = [{ name: 'Wonder woman', country: 'USA' }, { name: 'Kenshiro', country: 'Japan' }]
updateIndex = {
name: "Krishh",
country: "India"
}
index = 1
db.collection.updateOne(
{}, // your query
[{
$set: {
heros: {
$map: {
input: "$heros",
in: {
$cond: [
{
$eq: [
{
$indexOfArray: [
"$heros",
{
name: "$this.name",
country: "$this.country"
}
]
},
index
]
},
updateIndex,
"$this"
]
}
}
}
}
},
{
$set: {
heros: {
$concatArrays: ["$heros", additionalHeros]
}
}
}]
)
- Вы можете выполнить два отдельных запроса на обновление или запрос на массовую запись
- обновите конкретное свойство индекса
db.collection.updateOne(
{}, // your query
{
$set: {
"heros.1.name": "Krishh",
"heros.1.country": "India"
}
}
)
- вставьте массив в
heros
db.collection.updateOne(
{}, // your query
{
$push: {
heros: {
$each: [
{
name: "Wonder woman",
country: "USA"
},
{
name: "Kenshiro",
country: "Japan"
}
]
}
}
}
)
Ответ №2:
это сработает, пусть документ будет
{
"_id" : "heroes",
"Heros" : [
{
"name" : "Ironman",
"country" : "USA"
},
{
"name" : "Shaktiman",
"country" : "India"
},
{
"name" : "Black Panther",
"country" : "Wakanda"
}
]
}
Код был бы
let additionalHeros = [{ name: 'Wonder woman', country: 'USA' }, { name: 'Kenshiro', country: 'Japan' }]
let index = 2;
let data = { name: 'Krishh', country: 'India' }
let updateQuery = {};
updateQuery[`Heros.${index}`] = data;
let ops = [];
ops.push(
{
updateOne: {
filter: { _id: "heroes" },
update: {
$set: updateQuery,
},
upsert: true
}
},
);
ops.push(
{
updateOne: {
filter: { _id: "heroes" },
update: {
$push: { Heros: { $each: additionalHeros } }
},
upsert: true
}
},
);
let x = await db1.bulkWrite(ops, { ordered: true, upsert: true })
Тогда выход был бы
{
"_id" : "heroes",
"Heros" : [
{
"name" : "Ironman",
"country" : "USA"
},
{
"name" : "Shaktiman",
"country" : "India"
},
{
"name" : "Krishh",
"country" : "India"
},
{
"name" : "Wonder woman",
"country" : "USA"
},
{
"name" : "Kenshiro",
"country" : "Japan"
}
]
}