sql >> Databasteknik >  >> NoSQL >> MongoDB

Uppdatera kapslade underdokument i MongoDB med arrayFilters

Alltså arrayFilters alternativ med positionsfiltrerad $[<identifier>] fungerar faktiskt korrekt med utvecklingsreleaseserien sedan MongoDB 3.5.12 och även i de aktuella släppkandidaterna för MongoDB 3.6-serien, där detta faktiskt kommer att släppas officiellt. Det enda problemet är förstås att de "drivrutiner" som används faktiskt inte har kommit ikapp detta ännu.

Upprepa samma innehåll som jag redan har placerat på Uppdatering av en kapslad array med MongoDB:

OBS Något ironiskt, eftersom detta anges i "alternativ"-argumentet för .update() och liknande metoder är syntaxen i allmänhet kompatibel med alla senaste versioner av drivrutiner.

Detta är dock inte sant för mongo skal, eftersom metoden är implementerad där ("ironiskt nog för bakåtkompatibilitet") arrayFilters argumentet känns inte igen och tas bort av en intern metod som analyserar alternativen för att leverera "bakåtkompatibilitet" med tidigare MongoDB-serverversioner och en "legacy" .update() API-anropssyntax.

Så om du vill använda kommandot i mongo skal eller andra "skalbaserade" produkter (särskilt Robo 3T) behöver du en senaste version från antingen utvecklingsgrenen eller produktionsversionen från och med 3.6 eller senare.

Allt detta betyder är att den nuvarande "drivrutinen"-implementeringen av .update() "tar bort" faktiskt de nödvändiga argumenten med definitionen av arrayFilters . För NodeJS kommer detta att tas upp i 3.x-versionen av drivrutinen, och naturligtvis kommer "mongoose" troligen att ta lite tid efter den releasen för att implementera sina egna beroenden av den uppdaterade drivrutinen, som då inte längre skulle "stripa" sådana handlingar.

Du kan dock fortfarande köra detta på en stödd serverinstans, genom att gå tillbaka till den grundläggande syntaxanvändningen för "uppdateringskommandot", eftersom detta förbigick den implementerade drivrutinsmetoden:

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

})()

Eftersom det skickar "kommandot" direkt till servern ser vi att den förväntade uppdateringen faktiskt äger rum:

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

Så just "nu" drivrutinerna som finns tillgängliga "från hyllan" implementerar faktiskt inte .update() eller så är det andra implementerande motsvarigheter på ett sätt som är kompatibelt med att faktiskt passera de nödvändiga arrayFilters argument. Så om du "leker med" en utvecklingsserie eller släppkandidatserver, bör du verkligen vara beredd på att arbeta med "bleeding edge" och outgivna drivrutiner också.

Men du kan faktiskt göra detta som visas i vilken drivrutin som helst, i korrekt form där kommandot som utfärdas inte kommer att ändras.

När det skrevs den 11 november 2017 finns det ingen "officiell" version av MongoDB eller de drivrutiner som stöds som faktiskt implementerar detta. Produktionsanvändning bör endast baseras på officiella versioner av servern och drivrutiner som stöds.




  1. Hur man försplittrar en GUID-baserad Shard Key med MongoDB

  2. Det går inte att deserialisera PyMongo ObjectId från JSON

  3. Få dokument med taggar i listan, sorterade efter totalt antal matchningar

  4. rails + docker + sidekiq + Fel vid anslutning till Redis på 127.0.0.1:6379 (Errno::ECONNREFUSED)