Como actualizo parcialmente um objecto no MongoDB para que o novo objecto se sobreponha / fundir com o existente

dado este documento gravado em MongoDB

{
   _id : ...,
   some_key: { 
        param1 : "val1",
        param2 : "val2",
        param3 : "val3"
   }
}

um objecto com novas informações sobre param2 e param3 do mundo exterior precisa de ser salvo

var new_info = {
    param2 : "val2_new",
    param3 : "val3_new"
};

quero juntar / sobrepor os novos campos sobre o estado existente do objecto para que o param1 não seja removido

a fazer isto

db.collection.update(  { _id:...} , { $set: { some_key : new_info  } } 

vai levar ao MongoDB está fazendo exatamente como foi pedido, e define some_key para esse valor. a substituir o antigo.

{
   _id : ...,
   some_key: { 
      param2 : "val2_new",
      param3 : "val3_new"
   }
}
Qual é a maneira de ter MongoDB? actualizar apenas novos campos (sem os indicar um a um explicitamente)? para obter isso:

{
   _id : ...,
   some_key: { 
        param1 : "val1",
        param2 : "val2_new",
        param3 : "val3_new"
   }
}

estou a usar o cliente Java, mas qualquer exemplo será apreciado {[[9]}

Author: Eran Medan, 2012-04-24

12 answers

Se eu entender a pergunta corretamente, você quer atualizar um documento com o conteúdo de outro documento, mas apenas os campos que ainda não estão presentes, e ignorar completamente os campos que já estão definidos (mesmo que para outro valor).

Não há maneira de fazer isso num único comando.

Primeiro tem de consultar o documento, descobrir o que deseja $set e depois actualizá-lo (usando os valores antigos como um filtro correspondente para se certificar de que não fica concorrente atualizações entretanto).


Outra leitura da sua pergunta seria que você está feliz com $set, mas não quer definir explicitamente todos os campos. Como é que Passarias os dados?

Sabes que podes fazer o seguinte:

db.collection.update(  { _id:...} , { $set: someObjectWithNewData } 
 80
Author: Thilo, 2012-04-24 01:42:24
Resolvi - o com a minha própria função. Se você quiser atualizar o campo especificado no documento, você precisa endereçá-lo claramente.

Exemplo:

{
    _id : ...,
    some_key: { 
        param1 : "val1",
        param2 : "val2",
        param3 : "val3"
    }
}

Se quiser actualizar apenas o param2, é errado fazer:

db.collection.update(  { _id:...} , { $set: { some_key : new_info  } }  //WRONG

Deve utilizar:

db.collection.update(  { _id:...} , { $set: { some_key.param2 : new_info  } } 

Então eu escrevi uma função assim:
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'));
 80
Author: mehmatrix, 2013-04-20 10:38:25

Pode usar a notação de ponto para aceder e definir campos no interior de objectos, sem afectar as outras propriedades desses objectos.

Dado o objecto acima indicado:

> db.test.insert({"id": "test_object", "some_key": {"param1": "val1", "param2": "val2", "param3": "val3"}})
WriteResult({ "nInserted" : 1 })

Podemos actualizar apenas some_key.param2 e 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"
    }
}
Podes mergulhar o mais fundo que quiseres. Isto também é útil para adicionar novas propriedades a um objeto sem afetar as existentes.
 10
Author: Samir Talwar, 2015-11-14 17:33:13

A melhor solução é extrair propriedades do objecto e torná-las pares chave-valor de notação plana. Você poderia usar, por exemplo, esta biblioteca:

Https://www.npmjs.com/package/mongo-dot-notation

Tem uma função .flatten que lhe permite mudar o objecto para um conjunto plano de propriedades que poderão ser então dadas ao modificador $set, sem preocupações de que qualquer propriedade do seu objecto DB existente seja apagada/substituída sem necessidade.

Retirado de 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
  }
}
*/

E depois forma um selector perfeito-irá actualizar apenas as propriedades indicadas. EDIT: gosto de ser arqueólogo algumas vezes;)

 8
Author: SzybkiSasza, 2016-09-06 20:43:02

O Mongo permite-lhe actualizar os documentos aninhados usando uma convenção .. Dê uma olhada: atualizando documentos aninhados em mongodb . Aqui está outra pergunta do passado sobre uma atualização de merge, como a que você está procurando Eu acredito: a atualização atômica do MongoDB através do documento 'merge'

 7
Author: Pavel Veller, 2017-05-23 12:10:42

Parece que você pode definir isPartialObject que pode realizar o que você quer.

 1
Author: Patrick Tescher, 2012-04-24 01:41:52
Tive sucesso ao fazê - lo desta maneira.
db.collection.update(  { _id:...} , { $set: { 'key.another_key' : new_info  } } );

Tenho uma função que lida com o meu perfil actualiza dinamicamente

function update(prop, newVal) {
  const str = `profile.${prop}`;
  db.collection.update( { _id:...}, { $set: { [str]: newVal } } );
}

Nota:' perfil ' é específico para a minha implementação, é apenas a sequência da chave que você gostaria de modificar.

 1
Author: Matt Smith, 2016-07-24 16:48:52

Sim, a melhor maneira é converter a notação do objecto para uma representação de texto de valor-chave plana, como mencionado neste Comentário: https://stackoverflow.com/a/39357531/2529199

Eu queria destacar um método alternativo usando esta biblioteca NPM: https://www.npmjs.com/package/dot-object o que lhe permite manipular diferentes objectos usando a notação dos pontos.

Usei este padrão para criar programaticamente uma propriedade de objectos aninhados ao aceitar o valor-chave como uma variável de função, como se segue:

const dot = require('dot-object');

function(docid, varname, varvalue){
  let doc = dot.dot({
      [varname]: varvalue 
  });

  Mongo.update({_id:docid},{$set:doc});
}

Este padrão permite-me usar propriedades aninhadas, bem como propriedades de nível único, intercambiáveis, e inseri-las de forma limpa no Mongo.

Se precisar de brincar com objectos JS para além de Mongo, especialmente do lado do cliente, mas tiver consistência ao trabalhar com o Mongo, esta biblioteca dá-lhe mais opções do que o módulo mongo-dot-notation NPM anteriormente mencionado.

P. S I originally wanted to just mention this as a comment but apparently my O representante s/o não é alto o suficiente para postar um comentário. Então, não tentando interferir no Comentário de SzybkiSasza, apenas queria destacar a disponibilização de um módulo alternativo.

 1
Author: Ashwin Shankar, 2018-08-01 19:37:01

Use $set faça este processo

.update({"_id": args.dashboardId, "viewData._id": widgetId}, {$set: {"viewData.$.widgetData": widgetDoc.widgetData}})
.exec()
.then(dashboardDoc => {
    return {
        result: dashboardDoc
    };
}); 
 0
Author: KARTHIKEYAN.A, 2017-02-03 12:19:15

Faça um objecto de actualização com os nomes das propriedades, incluindo a localização dos pontos necessária. ("somekey."+do exemplo do OP), e então usar que fazer a atualização.

//the modification that's 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){...});
 0
Author: Andrew, 2018-06-08 23:23:17
    // 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

Se tiver vários campos a actualizar

        Document doc = new Document();
        doc.put("param2","value2");
        doc.put("param3","value3");
        update.put("$set", doc);
 0
Author: Vino, 2018-07-28 16:42:38

Em Mongo 3.6 existe uma função mergeObjects fazendo exatamente o que você precisa:

Https://docs.mongodb.com/manual/reference/operator/aggregation/mergeObjects/

 -1
Author: Tim Babych, 2018-04-12 09:01:17