valor - ¿Cómo actualizo parcialmente un objeto en MongoDB para que el nuevo objeto se superponga/fusione con el existente?
mongodb modificar documento (9)
Dado este documento guardado en MongoDB
{
_id : ...,
some_key: {
param1 : "val1",
param2 : "val2",
param3 : "val3"
}
}
Se debe guardar un objeto con nueva información sobre param2
y param3
del mundo exterior
var new_info = {
param2 : "val2_new",
param3 : "val3_new"
};
Quiero fusionar / superponer los nuevos campos sobre el estado existente del objeto para que param1 no se elimine
Haciendo esto
db.collection.update( { _id:...} , { $set: { some_key : new_info } }
Llevará a que MongoDB esté haciendo exactamente lo que se le pidió y establece some_key a ese valor. reemplazando el anterior.
{
_id : ...,
some_key: {
param2 : "val2_new",
param3 : "val3_new"
}
}
¿Cuál es la forma de que MongoDB actualice solo campos nuevos (sin indicarlos uno por uno explícitamente)? para obtener esto:
{
_id : ...,
some_key: {
param1 : "val1",
param2 : "val2_new",
param3 : "val3_new"
}
}
Estoy usando el cliente de Java, pero cualquier ejemplo será apreciado
En Mongo 3.6 hay una función mergeObjects haciendo exactamente lo que necesita:
https://docs.mongodb.com/manual/reference/operator/aggregation/mergeObjects/
La mejor solución es extraer propiedades del objeto y convertirlas en pares de valores-clave de notación de puntos planos. Podría usar, por ejemplo, esta biblioteca:
https://www.npmjs.com/package/mongo-dot-notation
Tiene la función .flatten
que le permite cambiar el objeto en un conjunto plano de propiedades que podrían .flatten
al modificador $ set, sin preocupaciones de que cualquier propiedad de su objeto DB existente sea eliminada / sobrescrita sin necesidad.
Tomado de documentos de mongo-dot-notation
:
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
}
}
*/
Y luego forma el selector perfecto - actualizará SÓLO las propiedades dadas. EDITAR: me gusta ser arqueólogo algunas veces;)
Lo resolví con mi propia función. Si desea actualizar el campo especificado en el documento, debe abordarlo con claridad.
Ejemplo:
{
_id : ...,
some_key: {
param1 : "val1",
param2 : "val2",
param3 : "val3"
}
}
Si solo desea actualizar param2, es incorrecto hacerlo:
db.collection.update( { _id:...} , { $set: { some_key : new_info } } //WRONG
Debes usar:
db.collection.update( { _id:...} , { $set: { some_key.param2 : new_info } }
Entonces escribí una función algo así:
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''));
Mongo le permite actualizar documentos anidados usando a .
convención. Eche un vistazo: Actualización de documentos anidados en mongodb . Aquí hay otra pregunta del pasado sobre una actualización de fusión, como la que estás buscando. Creo que: MongoDB atomic update via ''merge'' document
Parece que puede establecer isPartialObject que podría lograr lo que desea.
Puede usar la notación de puntos para acceder y establecer campos en el interior de los objetos, sin afectar las otras propiedades de esos objetos.
Dado el objeto que especificó arriba:
> db.test.insert({"id": "test_object", "some_key": {"param1": "val1", "param2": "val2", "param3": "val3"}})
WriteResult({ "nInserted" : 1 })
Podemos actualizar solo some_key.param2
y 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"
}
}
Puede profundizar tan profundo como desee. Esto también es útil para agregar nuevas propiedades a un objeto sin afectar las existentes.
Si entiendo la pregunta correctamente, quiere actualizar un documento con los contenidos de otro documento, pero solo los campos que aún no están presentes, e ignorar por completo los campos que ya están configurados (incluso a otro valor).
No hay forma de hacerlo en un solo comando.
Primero tiene que consultar el documento, descubrir qué es lo que quiere $set
y luego actualizarlo (usando los valores anteriores como un filtro que hace juego para asegurarse de no obtener actualizaciones simultáneas).
Otra lectura de su pregunta sería que está satisfecho con $set
, pero no desea establecer explícitamente todos los campos. ¿Cómo pasaría los datos?
Usted sabe que puede hacer lo siguiente:
db.collection.update( { _id:...} , { $set: someObjectWithNewData }
Tuve éxito haciéndolo de esta manera:
db.collection.update( { _id:...} , { $set: { ''key.another_key'' : new_info } } );
Tengo una función que maneja dinámicamente las actualizaciones de mi perfil
function update(prop, newVal) {
const str = `profile.${prop}`;
db.collection.update( { _id:...}, { $set: { [str]: newVal } } );
}
Nota: ''perfil'' es específico para mi implementación, es solo la cadena de la clave que desea modificar.
use $ set haga este proceso
.update({"_id": args.dashboardId, "viewData._id": widgetId}, {$set: {"viewData.$.widgetData": widgetDoc.widgetData}})
.exec()
.then(dashboardDoc => {
return {
result: dashboardDoc
};
});