sql >> Base de Datos >  >> NoSQL >> MongoDB

pushOrModify como operador para el subdocumento mongo

En realidad, esto requiere "dos" (o "tres" con upsert ) declaraciones de actualización y es una de las muy buenas razones por las que existen las operaciones "en bloque".

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

O si realmente quisieras incluir un "upsert" para el documento básico de "SweetTown" entonces necesitas separar esa preocupación en su propia prueba:

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

Entonces, el concepto general es solo aplique el $setOnInsert acción cuando un "upsert" en realidad ocurre. Para asegurarse de que esto solo suceda en este caso, las otras operaciones que realmente miran el elemento de la matriz no están marcadas con "upsert" opción. Esa parte es a propósito.

Se mire como se mire, solo es posible para uno de esas operaciones para hacer realmente alguna modificación en la base de datos, ya sea que se encuentre o no el elemento, o incluso que no se encuentre el documento y se cree uno nuevo.

En ningún caso es posible hacer ese tipo de operación en una sola declaración de actualización. Sin embargo, dado que las operaciones "en bloque" son en realidad solo una solicitud con uno respuesta, entonces, en lo que respecta a su aplicación, solo necesitaba hablar con el servidor una vez para que el servidor pruebe estas tres cosas y devuelva una respuesta.

Para el uso anterior de la API masiva directa, la sintaxis alternativa es:

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