sql >> Databáze >  >> NoSQL >> MongoDB

Aktualizujte vnořené vnořené dokumenty v MongoDB pomocí arrayFilters

Tedy arrayFilters možnost s pozičně filtrovaným $[<identifier>] skutečně funguje správně s vývojovou sérií vydání od MongoDB 3.5.12 a také s aktuálními kandidáty na vydání pro sérii MongoDB 3.6, kde bude ve skutečnosti oficiálně vydána. Jediný problém je samozřejmě v tom, že používané "ovladače" to ve skutečnosti ještě nedohnaly.

Opakované opakování stejného obsahu, který jsem již umístil na Aktualizaci vnořeného pole pomocí MongoDB:

POZNÁMKA Poněkud ironicky, protože je to uvedeno v argumentu "options" pro .update() a podobně jako u metod je syntaxe obecně kompatibilní se všemi nejnovějšími verzemi ovladačů.

To však neplatí pro mongo shell, protože způsob, jakým je tam metoda implementována ( "ironicky pro zpětnou kompatibilitu"), arrayFilters argument není rozpoznán a odstraněn interní metodou, která analyzuje možnosti, aby byla zajištěna "zpětná kompatibilita" s předchozími verzemi serveru MongoDB a "starší" .update() Syntaxe volání API.

Pokud tedy chcete použít příkaz v mongo shell nebo jiné "shell based" produkty (zejména Robo 3T) potřebujete nejnovější verzi buď z vývojové větve nebo z produkčního vydání od 3.6 nebo vyšší.

To vše znamená, že aktuální implementace "ovladače" .update() ve skutečnosti "odstraní" potřebné argumenty s definicí arrayFilters . Pro NodeJS to bude řešeno v řadě vydání ovladače 3.x a samozřejmě „mongoose“ bude pravděpodobně nějakou dobu po tomto vydání trvat, než implementuje své vlastní závislosti na aktualizovaném ovladači, který by se pak již „neodstranil“ takové akce.

Stále to však můžete spustit na podporovaném instance serveru přechodem zpět na základní použití syntaxe "příkaz aktualizace", protože se tím obešla implementovaná metoda ovladač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();
  }

})()

Protože to posílá „příkaz“ přímo na server, vidíme, že k očekávané aktualizaci skutečně dojde:

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

Takže správně "teď" ovladače dostupné "z regálu" ve skutečnosti neimplementují .update() nebo jde o jiné implementační protějšky způsobem, který je kompatibilní se skutečným procházením nezbytných arrayFilters argument. Takže pokud si "hrajete s" vývojovou sérií nebo serverem s verzí kandidáta na vydání, pak byste měli být opravdu připraveni pracovat s "nevydanou hranou" a také nevydanými ovladači.

Ale ve skutečnosti to můžete udělat, jak je ukázáno v jakémkoli ovladači, ve správném tvaru, kde se vydávaný příkaz nezmění.

K datu psaní 11. listopadu 2017 neexistuje žádný "oficiální" vydání MongoDB nebo podporovaných ovladačů, které to skutečně implementují. Produkční využití by mělo být založeno pouze na oficiálních vydáních serveru a podporovaných ovladačích.




  1. Ekvivalent runCommand pro nodejs-native-mongodb

  2. $unionWith – MongoDB je ekvivalent UNION ALL

  3. MongoDB:spočítejte počet položek v poli

  4. Jak vrátit data JSON z php MongoCursor