MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

pushOrModify comme opérateur pour le sous-document mongo

Cela nécessite en fait "deux" (ou "trois" avec upsert ) instructions de mise à jour et c'est l'une des très bonnes raisons pour lesquelles les opérations "en masse" existent.

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 si vous vouliez réellement inclure un "upsert" pour le document de base de "SweetTown" alors vous devez séparer cette préoccupation dans son propre 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
  }}
])

Donc, le concept général est de seulement appliquer le $setOnInsert action lors d'un "upsert" se produit réellement. Afin de s'assurer que cela ne se produit que dans ce cas, les autres opérations qui regardent réellement l'élément du tableau ne sont pas marquées avec le "upsert" option. Cette partie est exprès.

Quelle que soit la façon dont vous le voyez, cela n'est possible que pour un de ces opérations pour réellement apporter des modifications dans la base de données, que l'élément soit trouvé ou non, ou même que le document ne soit pas trouvé et qu'un nouveau soit créé.

En aucun cas, il n'est possible d'effectuer ce type d'opération dans une seule instruction de mise à jour. Cependant, étant donné que les opérations "en masse" ne sont vraiment qu'une une demande avec un réponse, alors en ce qui concerne votre application, il suffit de parler au serveur une fois pour que le serveur essaie ces trois choses et renvoie une réponse.

Pour une utilisation antérieure de l'API Bulk directe, la syntaxe alternative est :

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