sql >> Database >  >> NoSQL >> MongoDB

Update geneste subdocumenten in MongoDB met arrayFilters

Dus de arrayFilters optie met positioneel gefilterd $[<identifier>] werkt echt goed met de ontwikkelingsrelease-serie sinds MongoDB 3.5.12 en ook in de huidige release-kandidaten voor de MongoDB 3.6-serie, waar deze officieel zal worden vrijgegeven. Het enige probleem is natuurlijk dat de gebruikte "drivers" dit nog niet echt hebben ingehaald.

Ik herhaal dezelfde inhoud die ik al heb geplaatst op Een geneste array bijwerken met MongoDB:

OPMERKING Enigszins ironisch, aangezien dit is gespecificeerd in het argument "opties" voor .update() en net als bij methoden is de syntaxis over het algemeen compatibel met alle recente versies van stuurprogramma's.

Dit geldt echter niet voor de mongo shell, aangezien de manier waarop de methode daar is geïmplementeerd ("ironisch genoeg voor achterwaartse compatibiliteit") de arrayFilters argument wordt niet herkend en verwijderd door een interne methode die de opties parseert om "achterwaartse compatibiliteit" te leveren met eerdere MongoDB-serverversies en een "legacy" .update() API-aanroepsyntaxis.

Dus als je het commando in de mongo . wilt gebruiken shell of andere "shell-gebaseerde" producten (met name Robo 3T) je hebt een laatste versie nodig van de ontwikkelingstak of de productierelease vanaf 3.6 of hoger.

Dit alles betekent dat de huidige "driver"-implementatie van .update() daadwerkelijk "verwijdert" de noodzakelijke argumenten met de definitie van arrayFilters . Voor NodeJS zal dit worden aangepakt in de 3.x release-serie van de driver, en natuurlijk zal "mongoose" dan waarschijnlijk enige tijd nodig hebben na die release om zijn eigen afhankelijkheden van de bijgewerkte driver te implementeren, die dan niet langer "stript" dergelijke acties.

U kunt dit echter nog steeds uitvoeren op een ondersteunde serverinstantie, door terug te vallen op het basisgebruik van de "update-opdracht"-syntaxis, aangezien dit de geïmplementeerde stuurprogrammamethode omzeilde:

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

})()

Aangezien dat het "commando" rechtstreeks naar de server stuurt, zien we dat de verwachte update inderdaad plaatsvindt:

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

Dus juist "nu" de stuurprogramma's die "van de plank" beschikbaar zijn, implementeren .update() niet daadwerkelijk of het zijn andere implementerende tegenhangers op een manier die compatibel is met het daadwerkelijk passeren van de benodigde arrayFilters argument. Dus als je "speelt" met een ontwikkelingsreeks of een kandidaat-server vrijgeeft, moet je echt bereid zijn om ook met de "bleeding edge" en niet-uitgebrachte stuurprogramma's te werken.

Maar u kunt dit in feite doen, zoals in elk stuurprogramma wordt aangetoond, in de juiste vorm waarbij de opdracht die wordt gegeven niet wordt gewijzigd.

Op het moment van schrijven op 11 november 2017 is er geen "officiële" release van MongoDB of de ondersteunde stuurprogramma's die dit daadwerkelijk implementeren. Het productiegebruik mag alleen gebaseerd zijn op officiële releases van de server en ondersteunde stuurprogramma's.




  1. Redis Sentinels met TLS

  2. Hoe JSON-gegevens te retourneren vanuit php MongoCursor

  3. Redis-cluster/taakverdeling

  4. Wat is de standaard sessietime-out en hoe deze te configureren bij gebruik van de Spring Session met Redis als backend