Как частично обновить объект в MongoDB, чтобы новый объект накладывался/сливался с существующим
Учитывая этот документ, сохраненный в MongoDB
{
_id : ...,
some_key: {
param1 : "val1",
param2 : "val2",
param3 : "val3"
}
}
Необходимо сохранить объект с новой информацией о param2
и param3
из внешнего мира
var new_info = {
param2 : "val2_new",
param3 : "val3_new"
};
Я хочу объединить/наложить новые поля поверх существующего состояния объекта, чтобы param1 не удалялся
Выполнение этого
db.collection.update( { _id:...} , { $set: { some_key : new_info } }
приведет к тому, что MongoDB выполнит то, что было задано, и установит some_key для этого значения. заменив старый.
{
_id : ...,
some_key: {
param2 : "val2_new",
param3 : "val3_new"
}
}
Каким образом MongoDB обновляет только новые поля (не указывая их отдельно)? для этого:
{
_id : ...,
some_key: {
param1 : "val1",
param2 : "val2_new",
param3 : "val3_new"
}
}
Я использую клиент Java, но любой пример будет оценен
Ответы
Ответ 1
Если я правильно понял вопрос, вы хотите обновить документ с содержимым другого документа, но только те поля, которые еще не присутствуют, и полностью игнорировать поля, которые уже установлены (даже если это другое значение).
Невозможно сделать это в одной команде.
Сначала вы должны сначала запросить документ, выяснить, что хотите, $set
, а затем обновить его (используя старые значения в качестве фильтра соответствия, чтобы убедиться, что вы не получаете параллельных обновлений между ними).
Другое чтение вашего вопроса состояло бы в том, что вы довольны $set
, но не хотите явно устанавливать все поля. Как бы вы передали данные тогда?
Вы знаете, что можете сделать следующее:
db.collection.update( { _id:...} , { $set: someObjectWithNewData }
Ответ 2
Я решил это своей собственной функцией. Если вы хотите обновить указанное поле в документе, вам необходимо четко указать его.
Пример:
{
_id : ...,
some_key: {
param1 : "val1",
param2 : "val2",
param3 : "val3"
}
}
Если вы хотите только обновить param2, это неправильно:
db.collection.update( { _id:...} , { $set: { some_key : new_info } } //WRONG
Вы должны использовать:
db.collection.update( { _id:...} , { $set: { some_key.param2 : new_info } }
Итак, я написал функцию что-то вроде этого:
function _update($id, $data, $options=array()){
$temp = array();
foreach($data as $key => $value)
{
$temp["some_key.".$key] = $value;
}
$collection->update(
array('_id' => $id),
array('$set' => $temp)
);
}
_update('1', array('param2' => 'some data'));
Ответ 3
Вы можете использовать dot-notation для доступа и установки полей внутри объектов, не влияя на другие свойства этих объектов.
С учетом объекта, указанного выше:
> db.test.insert({"id": "test_object", "some_key": {"param1": "val1", "param2": "val2", "param3": "val3"}})
WriteResult({ "nInserted" : 1 })
Мы можем обновить только some_key.param2
и some_key.param3
:
> db.test.findAndModify({
... query: {"id": "test_object"},
... update: {"$set": {"some_key.param2": "val2_new", "some_key.param3": "val3_new"}},
... new: true
... })
{
"_id" : ObjectId("56476e04e5f19d86ece5b81d"),
"id" : "test_object",
"some_key" : {
"param1" : "val1",
"param2" : "val2_new",
"param3" : "val3_new"
}
}
Вы можете вникать так глубоко, как вам нравится. Это также полезно для добавления новых свойств к объекту, не затрагивая существующие.
Ответ 4
Лучшим решением является извлечение свойств из объекта и создание пары пар ключ-значение с точными точками. Вы можете использовать, например, эту библиотеку:
https://www.npmjs.com/package/mongo-dot-notation
У него есть функция .flatten
, которая позволяет вам изменять объект в плоский набор свойств, который затем может быть задан модификатору $set, не опасаясь, что любое свойство вашего существующего объекта БД будет удалено/перезаписано без необходимости.
Взято из mongo-dot-notation
docs:
var person = {
firstName: 'John',
lastName: 'Doe',
address: {
city: 'NY',
street: 'Eighth Avenu',
number: 123
}
};
var instructions = dot.flatten(person)
console.log(instructions);
/*
{
$set: {
'firstName': 'John',
'lastName': 'Doe',
'address.city': 'NY',
'address.street': 'Eighth Avenu',
'address.number': 123
}
}
*/
И затем он формирует идеальный селектор - он обновит ТОЛЬКО заданные свойства.
EDIT: Мне нравится быть археологом несколько раз;)
Ответ 5
Mongo позволяет вам обновлять вложенные документы, используя соглашение .
. Посмотрите: Обновление вложенных документов в mongodb. Вот еще один вопрос из прошлого об обновлении слияния, например тот, который вы ищете, я верю: атомное обновление MongoDB через документ 'merge'
Ответ 6
Я добился успеха таким образом:
db.collection.update( { _id:...} , { $set: { 'key.another_key' : new_info } } );
У меня есть функция, которая динамически обрабатывает мои профили.
function update(prop, newVal) {
const str = `profile.${prop}`;
db.collection.update( { _id:...}, { $set: { [str]: newVal } } );
}
Примечание. "Профиль" специфичен для моей реализации, это просто строка ключа, которую вы хотели бы изменить.
Ответ 7
Похоже, вы можете установить isPartialObject, который может выполнить то, что вы хотите.
Ответ 8
// where clause DBObject
DBObject query = new BasicDBObject("_id", new ObjectId(id));
// modifications to be applied
DBObject update = new BasicDBObject();
// set new values
update.put("$set", new BasicDBObject("param2","value2"));
// update the document
collection.update(query, update, true, false); //3rd param->upsertFlag, 4th param->updateMultiFlag
Если у вас есть несколько полей для обновления
Document doc = new Document();
doc.put("param2","value2");
doc.put("param3","value3");
update.put("$set", doc);
Ответ 9
Да, лучший способ - преобразовать нотацию объекта в строковое представление ключ-значение, как указано в этом комментарии: fooobar.com/questions/56380/...
Я хотел бы выделить альтернативный метод, используя эту библиотеку NPM: https://www.npmjs.com/package/dot-object, который позволяет вам манипулировать различными объектами, используя точечную запись.
Я использовал этот шаблон для программного создания свойства вложенного объекта при принятии значения ключа в качестве переменной функции следующим образом:
const dot = require('dot-object');
function(docid, varname, varvalue){
let doc = dot.dot({
[varname]: varvalue
});
Mongo.update({_id:docid},{$set:doc});
}
Этот шаблон позволяет мне использовать как вложенные, так и одноуровневые свойства взаимозаменяемо и аккуратно вставлять их в Mongo.
Если вам нужно поиграть с объектами JS за пределами Mongo, особенно на стороне клиента, но иметь согласованность при работе с Mongo, эта библиотека предоставляет вам больше возможностей, чем ранее упомянутый модуль NPM mongo-dot-notation
.
PS Изначально я хотел упомянуть это как комментарий, но, очевидно, мой представитель S/O недостаточно высок, чтобы оставить комментарий. Поэтому, не пытаясь использовать комментарий SzybkiSasza, я просто хотел выделить альтернативный модуль.
Ответ 10
использовать $set сделать этот процесс
.update({"_id": args.dashboardId, "viewData._id": widgetId}, {$set: {"viewData.$.widgetData": widgetDoc.widgetData}})
.exec()
.then(dashboardDoc => {
return {
result: dashboardDoc
};
});
Ответ 11
Создайте объект обновления с именами свойств, включая необходимый точечный путь. ("somekey." + из примера OP), а затем используйте, чтобы сделать обновление.
//the modification that requested
updateReq = {
param2 : "val2_new",
param3 : "val3_new"
}
//build a renamed version of the update request
var update = {};
for(var field in updateReq){
update["somekey."+field] = updateReq[field];
}
//apply the update without modifying fields not originally in the update
db.collection.update({._id:...},{$set:update},{upsert:true},function(err,result){...});
Ответ 12
Я попытался findAndModify()
чтобы обновить определенное поле в уже существующем объекте.
https://docs.mongodb.com/manual/reference/method/db.collection.findAndModify/
Ответ 13
Начиная с Mongo 4.2
, db.collection.update()
может принимать конвейер агрегации, который позволяет использовать операторы агрегации, такие как $addFields
, который выводит все существующие поля из входных документов и вновь добавленных полей:
var new_info = { param2: "val2_new", param3: "val3_new" }
// { some_key: { param1: "val1", param2: "val2", param3: "val3" } }
// { some_key: { param1: "val1", param2: "val2" } }
db.collection.update({}, [{ $addFields: { some_key: new_info } }], { multi: true })
// { some_key: { param1: "val1", param2: "val2_new", param3: "val3_new" } }
// { some_key: { param1: "val1", param2: "val2_new", param3: "val3_new" } }
-
Первая часть {}
- это запрос на совпадение, который фильтрует, какие документы обновлять (в данном случае все документы).
-
Вторая часть [{ $addFields: { some_key: new_info } }]
представляет собой конвейер агрегации обновлений:
- Обратите внимание на квадратные скобки, обозначающие использование агрегационного конвейера.
- Поскольку это конвейер агрегации, мы можем использовать
$addFields
. -
$addFields
выполняет именно то, что вам нужно: обновляет объект так, чтобы новый объект перекрывался/сливался с существующим: - В этом случае
{ param2: "val2_new", param3: "val3_new" }
будут объединены с существующим some_key
если оставить param1
нетронутым и либо добавить, либо заменить оба параметра param2
и param3
.
-
Не забудьте { multi: true }
, иначе будет обновлен только первый соответствующий документ.
Ответ 14
В Mongo 3.6 есть функция mergeObjects, которая делает именно то, что вам нужно:
https://docs.mongodb.com/manual/reference/operator/aggregation/mergeObjects/