stages examples ejemplos mongodb mongodb-query aggregation-framework

examples - mongodb aggregate sum



$ búsqueda en ObjectId en una matriz (5)

¿Cuál es la sintaxis para hacer una búsqueda $ en un campo que es una matriz de ObjectIds en lugar de un solo ObjectId?

Ejemplo de documento de pedido:

{ _id: ObjectId("..."), products: [ ObjectId("..<Car ObjectId>.."), ObjectId("..<Bike ObjectId>..") ] }

Consulta que no funciona:

db.orders.aggregate([ { $lookup: { from: "products", localField: "products", foreignField: "_id", as: "productObjects" } } ])

Resultado deseado

{ _id: ObjectId("..."), products: [ ObjectId("..<Car ObjectId>.."), ObjectId("..<Bike ObjectId>..") ], productObjects: [ {<Car Object>}, {<Bike Object>} ], }


Agregar con $lookup y el posterior $group es bastante engorroso, por lo que si (y eso es un medio si) está usando node & Mongoose o una biblioteca de soporte con algunas sugerencias en el esquema, podría usar un .populate() para obtener esos documentos:

var mongoose = require("mongoose"), Schema = mongoose.Schema; var productSchema = Schema({ ... }); var orderSchema = Schema({ _id : Number, products: [ { type: Schema.Types.ObjectId, ref: "Product" } ] }); var Product = mongoose.model("Product", productSchema); var Order = mongoose.model("Order", orderSchema); ... Order .find(...) .populate("products") ...


La etapa de canalización de agregación $lookup no funcionará directamente con una matriz. La intención principal del diseño es una "unión izquierda" como un tipo de unión "uno a muchos" (o realmente una "búsqueda") en los posibles datos relacionados. Pero el valor está destinado a ser singular y no a una matriz.

Por lo tanto, debe "desnormalizar" el contenido antes de realizar la operación de $lookup para que esto funcione. Y eso significa usar $unwind :

db.orders.aggregate([ // Unwind the source { "$unwind": "$products" }, // Do the lookup matching { "$lookup": { "from": "products", "localField": "products", "foreignField": "_id", "as": "productObjects" }}, // Unwind the result arrays ( likely one or none ) { "$unwind": "$productObjects" }, // Group back to arrays { "$group": { "_id": "$_id", "products": { "$push": "$products" }, "productObjects": { "$push": "$productObjects" } }} ])

Después de que $lookup coincida con cada miembro de la matriz, el resultado es una matriz en sí misma, por lo que $unwind nuevamente y $group para $push nuevas matrices para el resultado final.

Tenga en cuenta que cualquier coincidencia de "combinación izquierda" que no se encuentre creará una matriz vacía para los "productosObjetos" en el producto dado y, por lo tanto, negará el documento para el elemento "producto" cuando se llame al segundo $unwind .

Aunque una aplicación directa a una matriz sería buena, así es como funciona actualmente haciendo coincidir un valor singular con muchos posibles.

Como $lookup es básicamente muy nuevo, actualmente funciona como sería familiar para aquellos que están familiarizados con la mongoose como una "versión pobre para hombres" del método .populate() que se ofrece allí. La diferencia es que $lookup ofrece el procesamiento "del lado del servidor" de la "unión" en comparación con el cliente y que parte de la "madurez" en $lookup carece actualmente de lo que ofrece .populate() (como la interpolación de la búsqueda directamente en una matriz).

Este es en realidad un problema asignado para la mejora del SERVER-22881 , por lo que con un poco de suerte, este llegará a la próxima versión o poco después.

Como principio de diseño, su estructura actual no es ni buena ni mala, sino que está sujeta a gastos generales al crear cualquier "unión". Como tal, se aplica el principio básico de MongoDB en su inicio, donde si "puede" vivir con los datos "pre-unidos" en la colección, entonces es mejor hacerlo.

La otra cosa que se puede decir de $lookup como principio general es que la intención de "unirse" aquí es trabajar al revés de lo que se muestra aquí. Entonces, en lugar de mantener los "identificadores relacionados" de los otros documentos dentro del documento "padre", el principio general que funciona mejor es donde los "documentos relacionados" contienen una referencia al "padre".

Por lo tanto, se puede decir que $lookup "funciona mejor" con un "diseño de relación" que es lo contrario de cómo algo como mangosta .populate() realiza sus uniones del lado del cliente. En su lugar, al identificar el "uno" dentro de cada "muchos", simplemente tira de los elementos relacionados sin necesidad de $unwind la matriz primero.


La etapa de canalización de agregación de $lookup AHORA funciona directamente con una matriz (en la versión 3.3.4).

Consulte: SERVER-22881


También puede usar la etapa de pipeline para realizar comprobaciones en una matriz de subdocumentos

Aquí está el ejemplo usando python (lo siento, soy gente serpiente).

db.products.aggregate([ { ''$lookup'': { ''from'': ''products'', ''let'': { ''pid'': ''$products'' }, ''pipeline'': [ { ''$match'': { ''$expr'': { ''$in'': [''$_id'', ''$$pid''] } } } // Add additional stages here ], ''as'':''productObjects'' } ])

El problema aquí es hacer coincidir todos los objetos en la array ObjectId (Foreign _id que está en local campo local / products utilería).

También puede limpiar o proyectar los registros extranjeros con stage adicionales, como se indica en el comentario anterior.


use $ unwind obtendrá el primer objeto en lugar de una matriz de objetos

consulta:

db.getCollection(''vehicles'').aggregate([ { $match: { status: "AVAILABLE", vehicleTypeId: { $in: Array.from(newSet(d.vehicleTypeIds)) } } }, { $lookup: { from: "servicelocations", localField: "locationId", foreignField: "serviceLocationId", as: "locations" } }, { $unwind: "$locations" } ]);

resultado:

{ "_id" : ObjectId("59c3983a647101ec58ddcf90"), "vehicleId" : "45680", "regionId" : 1.0, "vehicleTypeId" : "10TONBOX", "locationId" : "100", "description" : "Isuzu/2003-10 Ton/Box", "deviceId" : "", "earliestStart" : 36000.0, "latestArrival" : 54000.0, "status" : "AVAILABLE", "accountId" : 1.0, "locations" : { "_id" : ObjectId("59c3afeab7799c90ebb3291f"), "serviceLocationId" : "100", "regionId" : 1.0, "zoneId" : "DXBZONE1", "description" : "Masafi Park Al Quoz", "locationPriority" : 1.0, "accountTypeId" : 0.0, "locationType" : "DEPOT", "location" : { "makani" : "", "lat" : 25.123091, "lng" : 55.21082 }, "deliveryDays" : "MTWRFSU", "timeWindow" : { "timeWindowTypeId" : "1" }, "address1" : "", "address2" : "", "phone" : "", "city" : "", "county" : "", "state" : "", "country" : "", "zipcode" : "", "imageUrl" : "", "contact" : { "name" : "", "email" : "" }, "status" : "", "createdBy" : "", "updatedBy" : "", "updateDate" : "", "accountId" : 1.0, "serviceTimeTypeId" : "1" } } { "_id" : ObjectId("59c3983a647101ec58ddcf91"), "vehicleId" : "81765", "regionId" : 1.0, "vehicleTypeId" : "10TONBOX", "locationId" : "100", "description" : "Hino/2004-10 Ton/Box", "deviceId" : "", "earliestStart" : 36000.0, "latestArrival" : 54000.0, "status" : "AVAILABLE", "accountId" : 1.0, "locations" : { "_id" : ObjectId("59c3afeab7799c90ebb3291f"), "serviceLocationId" : "100", "regionId" : 1.0, "zoneId" : "DXBZONE1", "description" : "Masafi Park Al Quoz", "locationPriority" : 1.0, "accountTypeId" : 0.0, "locationType" : "DEPOT", "location" : { "makani" : "", "lat" : 25.123091, "lng" : 55.21082 }, "deliveryDays" : "MTWRFSU", "timeWindow" : { "timeWindowTypeId" : "1" }, "address1" : "", "address2" : "", "phone" : "", "city" : "", "county" : "", "state" : "", "country" : "", "zipcode" : "", "imageUrl" : "", "contact" : { "name" : "", "email" : "" }, "status" : "", "createdBy" : "", "updatedBy" : "", "updateDate" : "", "accountId" : 1.0, "serviceTimeTypeId" : "1" } }