MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

pushOrModify come operatore per il sottodocumento mongo

Ciò in realtà richiede "due" (o "tre" con upsert ) istruzioni di aggiornamento ed è uno degli ottimi motivi per cui esistono operazioni "in blocco".

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 se volevi effettivamente includere un "upsert" per il documento base di "SweetTown" quindi è necessario separare quella preoccupazione nel proprio test:

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

Quindi il concetto generale è quello di solo applica il $setOnInsert azione quando un "upsert" effettivamente si verifica. Per assicurarsi che ciò avvenga solo in questo caso, le altre operazioni che esaminano effettivamente l'elemento dell'array non sono contrassegnate con il "upsert" opzione. Quella parte è apposta.

Qualunque sia il modo in cui la guardi, è possibile solo per uno di tali operazioni per apportare effettivamente qualsiasi modifica al database, in quanto l'elemento viene trovato o meno, oppure anche il documento non viene trovato e ne viene creato uno nuovo.

In nessun caso è possibile eseguire quel tipo di operazione in una singola istruzione di aggiornamento. Tuttavia, poiché le operazioni "bulk" sono in realtà solo una richiesta con uno risposta, quindi per quanto riguarda la tua applicazione, devi solo parlare con il server una volta per fare in modo che il server provi tutte queste tre cose e restituisca una risposta.

Per un utilizzo precedente dell'API in blocco diretta, la sintassi 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();