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

¿Cómo puedo $addToSet un objeto a una matriz y $sort también usando MongoDB?

Fuiste parte del camino al identificar correctamente las operaciones que necesitas hacer. Pero, por supuesto, $sort no es un modificador válido para $addToSet dado que el mantra de MongoDB es "los conjuntos no se consideran ordenados" :

El otro problema aquí, como lo indica el error, es que no puede usar varios operadores de actualización (como $addToSet y $push ) en la misma ruta a una propiedad al mismo tiempo. De hecho, "no hay orden" para la ejecución de diferentes operadores de actualización, por lo que no hay garantía de que $addToSet ocurre antes del $push . De hecho, es probable que estén actuando en paralelo, razón por la cual el error y que esto no está permitido.

La respuesta, por supuesto, es "dos" declaraciones de actualización. Uno para el $addToSet y otro para aplicar el $sort "empujando" una matriz vacía a través de $each ,

Pero como realmente no queremos "esperar" a que se complete cada actualización, para eso está la API de operaciones "en masa". Entonces puede enviar ambas instrucciones al servidor en uno enviar y obtener uno respuesta:

var bulk = db.perros.initializeOrderedBulkOp();
bulk.find({ "name": "Risas" }).update({ 
   "$addToSet": { 
       "propiedades": { "name": "cola", "cantidad": 1 }
   }
});
bulk.find({ "name": "Risas" }).update({ 
   "$push": { 
       "propiedades": { 
           "$each": [ ], "$sort": { "cantidad": -1 } 
        }
   }
});
bulk.execute();

Entonces, esto realmente sigue siendo solo una solicitud al servidor y una respuesta. Siguen siendo "dos" operaciones, pero la sobrecarga y la posibilidad de que algún subproceso se apodere del estado intermedio de la actualización es insignificante.

Hay una alternativa a este enfoque que es mover la lógica de "detección de conjuntos" a .find() parte de la declaración de actualización y luego simplemente aplique $push donde los miembros que se agregarán al "conjunto" aún no existen:

var bulk = db.perros.initializeOrderedBulkOp();
bulk.find({ 
    "name": "Risas", 
    "propiedades": { 
        "$not": { "$elemMatch": { "name": "cola", "cantidad": 1 } } 
    } 
}).update({ 
   "$push": { 
       "propiedades": { 
           "$each": [{ "name": "cola", "cantidad": 1 }], "$sort": { "cantidad": -1 } 
        }
   }
});
bulk.execute();

Por supuesto, la complicación es que si está agregando elementos de matriz "múltiples" aquí, deberá envolver esos $not y $elemMacth pruebas en un $and condición, y luego si "solo uno" de esos elementos era válido, entonces no se podía agregar solo.

Puede "probar" ese tipo de operación con elementos "múltiples" "primero", pero luego debería tenga una ejecución "alternativa" de cada elemento de matriz individual con la misma lógica que la anterior para "probar" la posibilidad de "empujar" para cada uno.

Entonces $addToSet hace que la segunda parte sea fácil con múltiples entradas de matriz. Para una entrada, es bastante simple simplemente "consultar" y $push , para más de uno es probablemente el camino más corto para usar el "primer" patrón con $addToSet y $push una matriz vacía para "ordenar" el resultado ya que aplicar el segundo patrón significa múltiples pruebas de actualización de todos modos.