Вопрос или проблема
У меня есть вложенный массив mongo
{
"_id": 1,
"date": "2024-09-30",
"employees": [
{
"_id": 101,
"name": "Джон Доу",
"company": "ABC Corp",
"department": "HR",
"salary": 50000,
"contactNumber": "123-456-7890"
},
{
"_id": 102,
"name": "A",
"company": "XYZ Corp",
"department": "HR",
"salary": 500100,
"contactNumber": "1333-456-7890"
},
{
"_id": 103,
"name": "B",
"company": "XYZ Corp",
"department": "HR",
"salary": 500100,
"contactNumber": "1333-456-7890"
}
]
}
Мне нужно обновить зарплату для 101 и также добавить нового сотрудника 104. Как я могу сделать это в одном обновлении?
Я могу сделать это с помощью 2 обновлений. Одно для обновления зарплаты 101 с использованием ArrayFilters и другое для добавления 104 в массив. Ниже приведены запросы,
db.collection.updateOne(
{ _id: 1 }, // Найти документ с _id: 1
{
$set: { "employees.$[emp].salary": 60000 } // Установить новую зарплату для сотрудника с _id: 101
},
{
arrayFilters: [ { "emp._id": 101 } ] // Фильтр массива для целевого сотрудника с _id: 101
}
)
db.collection.updateOne(
{ _id: 1 }, // Найти документ с _id: 1
{
$push: {
employees: {
_id: 104,
name: "Новый Сотрудник",
company: "Новая Корп",
department: "HR",
salary: 70000,
contactNumber: "987-654-3210"
}
}
}
)
Есть ли способ делать добавление нового сотрудника и обновление существующего сотрудника в одном запросе?
Я думаю, что я могу использовать агрегацию, чтобы сделать это. Но что лучше с точки зрения производительности?
Это не может быть выполнено в одном запросе обновления – “Обновление пути ’employees’ создаст конфликт в ’employees'” – что происходит, потому что обе операции выполняются над полем ’employees’. Однако, если бы вы добавляли в другое поле, например в поле newEmployees
, тогда это бы сработало. Единственное различие между двумя связанными запросами – это поле для $push
.
В любом случае, относительно выполнения обеих операций с помощью агрегации, одна агрегация будет быстрее чем два отдельных обновления, но, вероятно, это не стоит той сложности. Нужен ли новый сотрудник, чтобы добавляться каждый раз при изменении зарплаты другого сотрудника?
Если вам нужно, чтобы “либо оба действия сработали, либо оба провалились”, тогда рассмотрите возможность использования транзакций.
Вы можете сделать следующее в обновлении с помощью конвейера агрегации.
$concatArrays
для добавления новой записи 104 в конец массиваemployees
$map
для итерации по результирующему массиву. Условно обновить запись массива с помощью$mergeObjects
, если id записи равен 101
db.collection.update({
"_id": 1
},
[
{
"$set": {
"employees": {
"$map": {
"input": {
"$concatArrays": [
"$employees",
[
// новый сотрудник для вставки здесь
{
_id: 104,
name: "Новый Сотрудник",
company: "Новая Корп",
department: "HR",
salary: 70000,
contactNumber: "987-654-3210"
}
]
]
},
"as": "e",
"in": {
"$cond": {
"if": {
"$eq": [
101,
"$$e._id"
]
},
"then": {
"$mergeObjects": [
"$$e",
{
// обновления, которые вы хотите выполнить здесь
"salary": 60000
}
]
},
"else": "$$e"
}
}
}
}
}
}
])
Ответ или решение
Вопрос о том, как обновить зарплату одного сотрудника и одновременно добавить нового участника в массив сотрудников в MongoDB, может быть решен с использованием конвейера обновлений (aggregation pipeline). Хотя стандартные операции $set
и $push
не могут быть использованы в рамках одной команды из-за конфликта, вы можете воспользоваться методом, который позволяет достигнуть желаемого результата.
Вот пример запроса, который решает эту задачу:
db.collection.update(
{ "_id": 1 },
[
{
"$set": {
"employees": {
"$map": {
"input": {
"$concatArrays": [
"$employees",
[
{
_id: 104,
name: "New Employee",
company: "New Corp",
department: "HR",
salary: 70000,
contactNumber: "987-654-3210"
}
]
]
},
"as": "e",
"in": {
"$cond": {
"if": {
"$eq": [
101,
"$$e._id"
]
},
"then": {
"$mergeObjects": [
"$$e",
{
"salary": 60000 // Обновление зарплаты для сотрудника с _id: 101
}
]
},
"else": "$$e"
}
}
}
}
}
}
]
)
Объяснение запроса:
-
$set
: Этот оператор обновляет полеemployees
. -
$map
: Мы проходим по массиву сотрудников, который собираем с помощью$concatArrays
. -
$concatArrays
: Здесь мы объединяем существующий массивemployees
с новым массивом, содержащим нового сотрудника с_id: 104
. -
$cond
: Условный оператор, который проверяет, равен ли_id
сотрудника 101. Если да, то мы используем$mergeObjects
, чтобы объединить старый объект сотрудника с новыми данными, обновляя только полеsalary
. -
else
: Если_id
не 101, мы просто возвращаем текущий объект без изменений.
Этот метод позволяет вам обновить информацию о зарплате для сотрудника с _id: 101
и одновременно добавить нового сотрудника в массив employees
в одной операции.
Преимущества использования:
- Производительность: Выполнение всего в одном запросе сводит к минимуму количество операций. Это имеет значение, когда вы работаете с большими объемами данных или частыми обновлениями.
- Упрощение логики: Упрощает логику обработки данных, устраняя необходимость в дополнительных запросах.
Если у вас есть еще вопросы по MongoDB или другим аспектам работы с базами данных, не стесняйтесь их задавать!