MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

pushOrModify como operador para subdocumento mongo


Na verdade, isso requer "dois" (ou "três" com upsert ) instruções de atualização e é uma das boas razões pelas quais as operações "em massa" existem.
db.collection.bulkWrite([
  // Attempt to update the matched element
  { "updateOne": {
    "filter": { 
      "name": "SweetTown",
      "residents.name": "Bob"
    },
    "update": {
      "$set": { "residents.$.reputation": 30 }    
    }
  },
  // $push the element where not matched
  { "updateOne": {
    "filter": {
      "name": "SweetTown",
      "residents.name": { "$ne": "Bob" } 
    },
    "update": { 
      "$push": { 
        "residents": { "name": "Bob", "reputation": 30 } 
      }
    } 
  }}
])

Ou se você realmente quiser incluir um "upsert" para o documento básico de "SweetTown" então você precisa separar essa preocupação em seu próprio teste:
db.collection.bulkWrite([
  // Attempt to update the matched element
  { "updateOne": {
    "filter": { 
      "name": "SweetTown",
      "residents.name": "Bob"
    },
    "update": {
      "$set": { "residents.$.reputation": 30 }    
    }
  },
  // $push the element where not matched
  { "updateOne": {
    "filter": {
      "name": "SweetTown",
      "residents.name": { "$ne": "Bob" } 
    },
    "update": { 
      "$push": { 
        "residents": { "name": "Bob", "reputation": 30 } 
      }
    } 
  }},
  // Only use $setOnInsert when actually an upsert
  { "updateOne": {
      "filter": {
        "name": "SweetTown"
      },
      "update": {
        "$setOnInsert": {
           "residents": [{ "name": "Bob", "reputation": 30 }]
        }
      },
      "upsert": true
  }}
])

Portanto, o conceito geral é somente aplique o $setOnInsert ação quando um "upsert" realmente ocorre. Para garantir que isso aconteça apenas neste caso, as outras operações que realmente estão olhando para o elemento do array não são marcadas com o "upsert" opção. Essa parte é proposital.

Seja qual for a maneira como você olhe, só é possível para um dessas operações para realmente fazer alguma modificação no banco de dados, pois ou o elemento é encontrado ou não, ou mesmo o documento não é encontrado e um novo é criado.

Em nenhum caso é possível fazer esse tipo de operação em uma única instrução de atualização. No entanto, como as operações "em massa" são realmente apenas uma solicitação com um resposta, então, no que diz respeito ao seu aplicativo, ele só precisa conversar com o servidor uma vez para que o servidor tente todas essas três coisas e retorne uma resposta.

Para uso anterior da API em massa direta, a sintaxe alternativa é:
var bulk = db.collection.initializeOrderedBulkOp();

// $set matched where existing
bulk.find({ "name": "SweetTown", "residents.name": "Bob" }).updateOne({
  "$set": { "residents.$.reputation": 30 }
});

// $push where not existing
bulk.find({ "name": "SweetTown", "residents.name": { "$ne": "Bob" } }).updateOne({
  "$push": { "residents": { "name": "Bob", "reputation": 30 } }
});

// Attempt to upsert only
bulk.find({ "name": "SweetTown" }).upsert().updateOne({
  "$setOnInsert": {
    "residents": [{ "name": "Bob", "reputation": 30 }]
  }
})

bulk.execute();