MongoDB
 sql >> Baza danych >  >> NoSQL >> MongoDB

Zaktualizuj zagnieżdżone poddokumenty w MongoDB za pomocą arrayFilters

Więc arrayFilters opcja z filtrem pozycyjnym $[<identifier>] faktycznie działa poprawnie z serią wydań rozwojowych od MongoDB 3.5.12, a także z obecnymi kandydatami do wydania dla serii MongoDB 3.6, gdzie zostanie to oficjalnie wydane. Jedynym problemem jest oczywiście to, że „kierowcy” w użyciu jeszcze tego nie dostali.

Powtarzając tę ​​samą treść, którą już umieściłem podczas aktualizacji zagnieżdżonej tablicy za pomocą MongoDB:

UWAGA Trochę ironicznie, ponieważ jest to określone w argumencie „opcje” dla .update() i podobnie jak metody, składnia jest ogólnie zgodna ze wszystkimi najnowszymi wersjami sterowników.

Jednak nie dotyczy to mongo powłoki, ponieważ sposób, w jaki metoda jest tam zaimplementowana ( "jak na ironię dla wstecznej kompatybilności"), arrayFilters argument nie jest rozpoznawany i usuwany przez wewnętrzną metodę, która analizuje opcje w celu zapewnienia „zgodności wstecznej” z wcześniejszymi wersjami serwera MongoDB i „starszego” .update() Składnia wywołań API.

Więc jeśli chcesz użyć polecenia w mongo shell lub inne produkty "oparte na powłoce" (zwłaszcza Robo 3T), potrzebujesz najnowszej wersji z gałęzi rozwojowej lub wydania produkcyjnego w wersji 3.6 lub nowszej.

Wszystko to oznacza, że ​​obecna implementacja „sterownika” .update() faktycznie "usuwa" niezbędne argumenty z definicją arrayFilters . W przypadku NodeJS zostanie to rozwiązane w wydaniu 3.x sterownika i oczywiście „mongusta” prawdopodobnie zajmie trochę czasu po tym wydaniu, aby zaimplementować własne zależności od zaktualizowanego sterownika, który nie będzie już „rozbierał się” takie działania.

Możesz jednak nadal uruchomić to na obsługiwanym instancji serwera, wracając do podstawowej składni „polecenia aktualizacji”, ponieważ omijało to zaimplementowaną metodę sterownika:

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

})()

Ponieważ wysyła to „polecenie” bezpośrednio do serwera, widzimy, że oczekiwana aktualizacja faktycznie ma miejsce:

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

Więc dobrze „teraz” sterowniki dostępne "z półki" w rzeczywistości nie implementują .update() lub są to inne implementujące odpowiedniki w sposób zgodny z faktycznym przechodzeniem przez niezbędne arrayFilters argument. Więc jeśli "bawisz się" serią rozwojową lub serwerem kandydującym do wydania, naprawdę powinieneś być przygotowany na pracę z "krwawioną krawędzią" i niewydanymi sterownikami.

Ale możesz to zrobić, jak pokazano w dowolnym sterowniku, we właściwej formie, w której wydawane polecenie nie zostanie zmienione.

W dniu pisania 11 listopada 2017 r. nie ma „oficjalnych” wydanie MongoDB lub obsługiwanych sterowników, które faktycznie to implementują. Wykorzystanie produkcyjne powinno opierać się wyłącznie na oficjalnych wydaniach serwera i obsługiwanych sterownikach.




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Jak znaleźć nazwę pola MongoDB na dowolnej głębokości?

  2. MongoDB $rok

  3. Utrzymywanie otwartego połączenia z bazą danych MongoDB

  4. Jaki jest maksymalny rozmiar operacji wsadowej MongoDB?

  5. Powstrzymaj Mongoose przed tworzeniem właściwości _id dla elementów tablicy dokumentów podrzędnych