Как частично обновить объект в MongoDB, чтобы новый объект перекрывал / сливался с существующим

102

Учитывая этот документ, сохраненный в 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, но любой пример будет оценен

Теги:
mongodb-java

8 ответов

74
Лучший ответ

Если я правильно понял вопрос, вы хотите обновить документ с содержимым другого документа, но только те поля, которые еще не присутствуют, и полностью игнорировать поля, которые уже установлены (даже если это другое значение).

Невозможно сделать это в одной команде.

Сначала вы должны сначала запросить документ, выяснить, что хотите, $set, а затем обновить его (используя старые значения в качестве фильтра соответствия, чтобы убедиться, что вы не получаете параллельных обновлений между ними). ​​


Другое чтение вашего вопроса состояло бы в том, что вы довольны $set, но не хотите явно устанавливать все поля. Как бы вы передали данные тогда?

Вы знаете, что можете сделать следующее:

db.collection.update(  { _id:...} , { $set: someObjectWithNewData } 
  • 2
    Что делать, если вы хотите явно установить все поля ...
  • 0
    Если вы хотите установить все поля безоговорочно, вы можете использовать параметр $ multi , например: db.collection.update( { _id:...} , { $set: someObjectWithNewData, { $multi: true } } )
Показать ещё 5 комментариев
70

Я решил это своей собственной функцией. Если вы хотите обновить указанное поле в документе, вам необходимо четко указать его.

Пример:

{
    _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'));
  • 9
    Это должен быть принятый ответ. Для лучшей функции flattenObject см. Gist.github.com/penguinboy/762197
  • 0
    Спасибо, ваш ответ помог мне. Мне удалось изменить значение определенного ключа пользователя в коллекции пользователей следующим образом: db.users.update( { _id: ObjectId("594dbc3186bb5c84442949b1") }, { $set: { resume: { url: "http://i.imgur.com/UFX0yXs.jpg" } } } )
7

Вы можете использовать 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"
    }
}

Вы можете вникать так глубоко, как вам нравится. Это также полезно для добавления новых свойств к объекту, не затрагивая существующие.

7

Mongo позволяет вам обновлять вложенные документы, используя соглашение .. Посмотрите: Обновление вложенных документов в mongodb. Вот еще один вопрос из прошлого об обновлении слияния, например тот, который вы ищете, я верю: атомное обновление MongoDB через документ 'merge'

  • 3
    Это хорошо знать, но не мой вопрос :)
5

Лучшим решением является извлечение свойств из объекта и создание пары пар ключ-значение с точными точками. Вы можете использовать, например, эту библиотеку:

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: Мне нравится быть археологом несколько раз;)

  • 0
    работал как шарм, спасибо!
1

Я добился успеха таким образом:

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 } } );
}

Примечание. "Профиль" специфичен для моей реализации, это просто строка ключа, которую вы хотели бы изменить.

1

Похоже, вы можете установить isPartialObject, который может выполнить то, что вы хотите.

  • 0
    попробовал это. это не позволяет вам сохранить частичные объекты, если я не ошибаюсь ... но спасибо
0

использовать $set сделать этот процесс

.update({"_id": args.dashboardId, "viewData._id": widgetId}, {$set: {"viewData.$.widgetData": widgetDoc.widgetData}})
.exec()
.then(dashboardDoc => {
    return {
        result: dashboardDoc
    };
}); 

Ещё вопросы

Сообщество Overcoder
Наверх
Меню