sql >> Base de Datos >  >> NoSQL >> MongoDB

Actualizar subdocumentos anidados en MongoDB con arrayFilters

Entonces los arrayFilters opción con filtro posicional $[<identifier>] en realidad funciona correctamente con la serie de versiones de desarrollo desde MongoDB 3.5.12 y también en las versiones candidatas actuales para la serie MongoDB 3.6, donde se lanzará oficialmente. Por supuesto, el único problema es que los "controladores" en uso aún no se han dado cuenta de esto.

Reiterando el mismo contenido que ya coloqué en Actualizar una matriz anidada con MongoDB:

NOTA Irónicamente, ya que esto se especifica en el argumento "opciones" para .update() y métodos similares, la sintaxis es generalmente compatible con todas las versiones recientes del controlador.

Sin embargo, esto no es cierto para el mongo shell, ya que la forma en que se implementa el método allí ("irónicamente para compatibilidad con versiones anteriores") los arrayFilters El argumento no es reconocido y eliminado por un método interno que analiza las opciones para ofrecer "compatibilidad con versiones anteriores" con versiones anteriores del servidor MongoDB y un .update() "heredado". Sintaxis de llamada de API.

Entonces, si desea usar el comando en mongo shell u otros productos "basados ​​en shell" (especialmente Robo 3T), necesita una versión más reciente de la rama de desarrollo o la versión de producción a partir de 3.6 o superior.

Todo esto significa que la implementación actual del "controlador" de .update() en realidad "elimina" los argumentos necesarios con la definición de arrayFilters . Para NodeJS, esto se abordará en la serie de versiones 3.x del controlador y, por supuesto, "mongoose" probablemente tardará un tiempo después de esa versión en implementar sus propias dependencias en el controlador actualizado, que ya no se "despojará". tales acciones.

Sin embargo, todavía puede ejecutar esto en un compatible instancia del servidor, volviendo al uso básico de la sintaxis del "comando de actualización", ya que esto omitió el método del controlador implementado:

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

})()

Como eso envía el "comando" directamente al servidor, vemos que la actualización esperada sí se lleva a cabo:

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

Así que bien "ahora" los controladores disponibles "listos para usar" en realidad no implementan .update() o son otras contrapartes de implementación de una manera que es compatible con pasar realmente a través de los arrayFilters necesarios argumento. Entonces, si está "jugando con" una serie de desarrollo o lanzando un servidor candidato, entonces realmente debería estar preparado para trabajar con los controladores "de vanguardia" y no lanzados también.

Pero en realidad puede hacer esto como se demuestra en cualquier controlador, en la forma correcta donde el comando que se emite no se modificará.

Al momento de escribir el 11 de noviembre de 2017, no hay "oficial" lanzamiento de MongoDB o los controladores compatibles que realmente implementan esto. El uso de producción debe basarse únicamente en las versiones oficiales del servidor y los controladores compatibles.