MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Aktualisieren Sie verschachtelte Filialdokumente in MongoDB mit arrayFilters

Also die arrayFilters Option mit positionsgefiltertem $[<identifier>] funktioniert tatsächlich einwandfrei mit der Entwicklungs-Release-Serie seit MongoDB 3.5.12 und auch in den aktuellen Release-Kandidaten für die MongoDB-3.6-Serie, wo diese tatsächlich offiziell veröffentlicht wird. Das einzige Problem ist natürlich, dass die eingesetzten "Treiber" das noch nicht wirklich nachgeholt haben.

Ich wiederhole denselben Inhalt, den ich bereits zum Aktualisieren eines verschachtelten Arrays mit MongoDB platziert habe:

HINWEIS Etwas ironisch, da dies im "options"-Argument für .update() angegeben ist und wie bei Methoden ist die Syntax im Allgemeinen mit allen neueren Treiberversionen kompatibel.

Dies gilt jedoch nicht für den mongo Shell, da die Art und Weise, wie die Methode dort implementiert ist ( "ironischerweise aus Gründen der Abwärtskompatibilität"), die arrayFilters -Argument wird nicht erkannt und von einer internen Methode entfernt, die die Optionen parst, um „Abwärtskompatibilität“ mit früheren MongoDB-Serverversionen und einem „alten“ .update() bereitzustellen API-Aufrufsyntax.

Wenn Sie also den Befehl in der mongo verwenden möchten Shell oder andere "Shell-basierte" Produkte (insbesondere Robo 3T ) benötigen Sie eine neueste Version entweder aus dem Entwicklungszweig oder eine Produktionsversion ab 3.6 oder höher.

All dies bedeutet, dass die aktuelle "Treiber"-Implementierung von .update() tatsächlich "entfernt" die notwendigen Argumente mit der Definition von arrayFilters . Für NodeJS wird dies in der 3.x-Release-Serie des Treibers angegangen, und natürlich wird "Mungo" nach dieser Veröffentlichung wahrscheinlich einige Zeit brauchen, um seine eigenen Abhängigkeiten vom aktualisierten Treiber zu implementieren, der dann nicht mehr "stripen" würde. solche Aktionen.

Sie können dies jedoch weiterhin auf einem unterstützten ausführen Serverinstanz, indem Sie auf die grundlegende Verwendung der "Update-Befehl"-Syntax zurückgreifen, da dies die implementierte Treibermethode umgeht:

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

})()

Da dieser den "Befehl" direkt an den Server sendet, sehen wir, dass die erwartete Aktualisierung tatsächlich stattfindet:

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

Also richtig "jetzt" die "von der Stange" erhältlichen Treiber implementieren .update() nicht wirklich oder es sind andere Gegenstücke, die auf eine Weise implementiert werden, die mit dem tatsächlichen Durchlaufen der erforderlichen arrayFilters kompatibel ist Streit. Wenn Sie also mit einer Entwicklungsserie oder einem Release-Kandidaten-Server "spielen", sollten Sie wirklich darauf vorbereitet sein, auch mit "Bleeding Edge" und unveröffentlichten Treibern zu arbeiten.

Aber Sie können dies tatsächlich tun, wie es in jedem Treiber demonstriert wird, in der richtigen Form, in der der ausgegebene Befehl nicht geändert wird.

Zum Zeitpunkt des Schreibens am 11. November 2017 gibt es kein "offizielles" Version von MongoDB oder die unterstützten Treiber, die dies tatsächlich implementieren. Die Produktionsnutzung sollte nur auf offiziellen Versionen des Servers und unterstützten Treibern basieren.