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

Mettre à jour les sous-documents imbriqués dans MongoDB avec arrayFilters

Ainsi, les arrayFilters option avec filtrage positionnel $[<identifier>] fonctionne correctement avec la série de versions de développement depuis MongoDB 3.5.12 et également dans les versions candidates actuelles de la série MongoDB 3.6, où cela sera officiellement publié. Le seul problème est bien sûr que les "pilotes" utilisés n'ont pas encore rattrapé cela.

Réitérer le même contenu que j'ai déjà placé sur la mise à jour d'un tableau imbriqué avec MongoDB :

REMARQUE Un peu ironiquement, puisque cela est spécifié dans l'argument "options" pour .update() et comme les méthodes, la syntaxe est généralement compatible avec toutes les versions récentes du pilote.

Cependant, ce n'est pas le cas du mongo shell, puisque la façon dont la méthode y est implémentée ("ironiquement pour la compatibilité ascendante") le arrayFilters l'argument n'est pas reconnu et supprimé par une méthode interne qui analyse les options afin de fournir une "compatibilité descendante" avec les versions antérieures du serveur MongoDB et un "héritage" .update() Syntaxe d'appel API.

Donc, si vous voulez utiliser la commande dans le mongo shell ou d'autres produits "à base de shell" (notamment Robo 3T), vous avez besoin d'une dernière version de la branche de développement ou de la version de production à partir de 3.6 ou supérieure.

Tout cela signifie que l'implémentation actuelle du "pilote" de .update() en fait "supprime" les arguments nécessaires avec la définition de arrayFilters . Pour NodeJS, cela sera résolu dans la série de versions 3.x du pilote, et bien sûr, "mongoose" prendra probablement un certain temps après cette version pour implémenter ses propres dépendances sur le pilote mis à jour, qui ne "suppression" alors plus de telles actions.

Vous pouvez cependant toujours l'exécuter sur un supporté instance de serveur, en revenant à l'utilisation de base de la syntaxe "commande de mise à jour", car cela contournait la méthode de pilote implémentée :

const mongoose = require('mongoose'),
      Schema = mongoose.Schema,
      ObjectId = mongoose.Types.ObjectId;

mongoose.Promise = global.Promise;
mongoose.set('debug',true);

const uri = 'mongodb://localhost/test',
      options = { useMongoClient: true };

const contactSchema = new Schema({
  data: String,
  type: String,
  priority: String,
  retries: String
});

const personSchema = new Schema({
  name: String,
  level: String,
  priority: String,
  enabled: Boolean,
  contacts: [contactSchema]
});

const groupSchema = new Schema({
  name: String,
  people: [personSchema],
  workingHours: { start: String, end: String },
  workingDays: { type: [Number], default: undefined },
  contactTypes: {
    workingHours: { type: [String], default: undefined },
    contactTypes: { type: [String], default: undefined }
  }
});

const Group = mongoose.model('Group', groupSchema);

function log(data) {
  console.log(JSON.stringify(data, undefined, 2))
}

(async function() {

  try {

    const conn = await mongoose.connect(uri,options);

    // Clean data
    await Promise.all(
      Object.entries(conn.models).map(([k,m]) => m.remove() )
    );

    // Create sample

    await Group.create({
      name: "support",
      people: [
        {
          "_id": ObjectId("5a05a8c3e0ce3444f8ec5bd8"),
          "enabled": true,
          "level": "1",
          "name": "Someone",
          "contacts": [
            {
              "type": "email",
              "data": "[email protected]"
            },
            {
              "_id": ObjectId("5a05a8dee0ce3444f8ec5bda"),
              "retries": "1",
              "priority": "1",
              "type": "email",
              "data": "[email protected]"
            }
          ]
        }
      ]
    });

    let result = await conn.db.command({
      "update": Group.collection.name,
      "updates": [
        {
          "q": {},
          "u": { "$set": { "people.$[i].contacts.$[j].data": "new data" } },
          "multi": true,
          "arrayFilters": [
            { "i._id": ObjectId("5a05a8c3e0ce3444f8ec5bd8") },
            { "j._id": ObjectId("5a05a8dee0ce3444f8ec5bda") }
          ]
        }
      ]
    });

    log(result);

    let group = await Group.findOne();
    log(group);

  } catch(e) {
    console.error(e);
  } finally {
    mongoose.disconnect();
  }

})()

Puisque cela envoie la "commande" directement au serveur, nous voyons que la mise à jour attendue a bien lieu :

Mongoose: groups.remove({}, {})
Mongoose: groups.insert({ name: 'support', _id: ObjectId("5a06557fb568aa0ad793c5e4"), people: [ { _id: ObjectId("5a05a8c3e0ce3444f8ec5bd8"), enabled: true, level: '1', name: 'Someone', contacts: [ { type: 'email', data: '[email protected]', _id: ObjectId("5a06557fb568aa0ad793c5e5") }, { _id: ObjectId("5a05a8dee0ce3444f8ec5bda"), retries: '1', priority: '1', type: 'email', data: '[email protected]' } ] } ], __v: 0 })
{ n: 1,
  nModified: 1,
  opTime:
   { ts: Timestamp { _bsontype: 'Timestamp', low_: 3, high_: 1510364543 },
     t: 24 },
  electionId: 7fffffff0000000000000018,
  ok: 1,
  operationTime: Timestamp { _bsontype: 'Timestamp', low_: 3, high_: 1510364543 },
  '$clusterTime':
   { clusterTime: Timestamp { _bsontype: 'Timestamp', low_: 3, high_: 1510364543 },
     signature: { hash: [Object], keyId: 0 } } }
Mongoose: groups.findOne({}, { fields: {} })
{
  "_id": "5a06557fb568aa0ad793c5e4",
  "name": "support",
  "__v": 0,
  "people": [
    {
      "_id": "5a05a8c3e0ce3444f8ec5bd8",
      "enabled": true,
      "level": "1",
      "name": "Someone",
      "contacts": [
        {
          "type": "email",
          "data": "[email protected]",
          "_id": "5a06557fb568aa0ad793c5e5"
        },
        {
          "_id": "5a05a8dee0ce3444f8ec5bda",
          "retries": "1",
          "priority": "1",
          "type": "email",
          "data": "new data"            // <-- updated here
        }
      ]
    }
  ]
}

Alors d'accord "maintenant" les pilotes disponibles "sur étagère" n'implémentent pas réellement .update() ou il s'agit d'autres homologues implémentés d'une manière compatible avec le passage des arrayFilters nécessaires argument. Donc, si vous "jouez avec" une série de développement ou un serveur candidat à la publication, vous devez vraiment être prêt à travailler avec les pilotes "à la pointe de la technologie" et les pilotes inédits.

Mais vous pouvez réellement le faire comme démontré dans n'importe quel pilote, sous la forme correcte où la commande émise ne sera pas modifiée.

Au moment de la rédaction du 11 novembre 2017, il n'y a pas d'élément "officiel" version de MongoDB ou des pilotes pris en charge qui l'implémentent réellement. L'utilisation en production doit être basée uniquement sur les versions officielles du serveur et les pilotes pris en charge.