Múltiples condiciones de unión usando el operador $ lookup

Aquí está mi colección:

colección1:

{ user1: 1, user2: 2, percent: 0.56 } 

colección2:

 { user1: 1, user2: 2, percent: 0.3 } 

Quiero unirme a las dos colecciones por ‘user1’ y ‘user2’.

El resultado así:

 { user1: 1, user2: 2, percent1: 0.56, percent2: 0.3 } 

¿Cómo escribo el pipeline?

Podemos realizar condiciones de combinación múltiple con el operador de canalización de agregación $lookup en la versión 3.6 y posterior.

Necesitamos asignar los valores de los campos a la variable usando el campo opcional let ; a continuación, tiene acceso a esas variables en las etapas del campo de interconexión en las que especifica la interconexión para que se ejecute en las colecciones.

Tenga en cuenta que en la etapa $match , usamos el operador de consulta de evaluación $expr para comparar el valor de los campos.

La última etapa de la canalización es la etapa de canalización de agregación $replaceRoot en la que simplemente $replaceRoot el resultado $lookup con parte del documento $$ROOT utilizando el operador $mergeObjects .

 db.collection2.aggregate([ { $lookup: { from: "collection1", let: { firstUser: "$user1", secondUser: "$user2" }, pipeline: [ { $match: { $expr: { $and: [ { $eq: [ "$user1", "$$firstUser" ] }, { $eq: [ "$user2", "$$secondUser" ] } ] } } } ], as: "result" } }, { $replaceRoot: { newRoot: { $mergeObjects:[ { $arrayElemAt: [ "$result", 0 ] }, { percent1: "$$ROOT.percent1" } ] } } } ] ) 

Esta tubería produce algo que se ve así:

 { "_id" : ObjectId("59e1ad7d36f42d8960c06022"), "user1" : 1, "user2" : 2, "percent" : 0.3, "percent1" : 0.56 } 

Si no está en la versión 3.6+, primero puede unirse utilizando uno de sus campos, por ejemplo, “usuario1”; a continuación, desenrollará la matriz del documento correspondiente utilizando el operador de la $unwind agregación $unwind . La siguiente etapa en la tubería es la etapa $redact donde filtra aquellos documentos donde el valor de “usuario2” de la colección “unida” y el documento de entrada no son iguales usando las variables de sistema $$KEEP y $$PRUNE . A continuación, puede cambiar la forma de su documento en $project etapa de $project .

 db.collection1.aggregate([ { "$lookup": { "from": "collection2", "localField": "user1", "foreignField": "user1", "as": "collection2_doc" }}, { "$unwind": "$collection2_doc" }, { "$redact": { "$cond": [ { "$eq": [ "$user2", "$collection2_doc.user2" ] }, "$$KEEP", "$$PRUNE" ] }}, { "$project": { "user1": 1, "user2": 1, "percent1": "$percent", "percent2": "$collection2_doc.percent" }} ]) 

que produce:

 { "_id" : ObjectId("572daa87cc52a841bb292beb"), "user1" : 1, "user2" : 2, "percent1" : 0.56, "percent2" : 0.3 } 

Si los documentos en sus colecciones tienen la misma estructura y se encuentra realizando esta operación a menudo, entonces debería considerar fusionar las dos colecciones en una sola o insertar los documentos en esas colecciones en una nueva colección.

 db.collection3.insertMany( db.collection1.find({}, {"_id": 0}) .toArray() .concat(db.collection2.find({}, {"_id": 0}).toArray()) ) 

Luego $group sus documentos por “usuario1” y “usuario2”

 db.collection3.aggregate([ { "$group": { "_id": { "user1": "$user1", "user2": "$user2" }, "percent": { "$push": "$percent" } }} ]) 

cuyos rendimientos:

 { "_id" : { "user1" : 1, "user2" : 2 }, "percent" : [ 0.56, 0.3 ] } 

Puede hacer varias coincidencias de campo usando las tuberías de $ match y $ project . (Consulte la respuesta detallada aquí – mongoDB Join en múltiples campos )

 db.collection1.aggregate([ {"$lookup": { "from": "collection2", "localField": "user1", "foreignField": "user1", "as": "c2" }}, {"$unwind": "$c2"}, {"$project": { "user2Eq": {"$eq": ["$user2", "$c2.user2"]}, "user1": 1, "user2": 1, "percent1": "$percent", "percent2": "$c2.percent" }}, {"$match": { {"user2Eq": {"$eq": True}} }}, {"$project": { "user2Eq": 0 }} ]) 

Si está tratando de modelar sus datos, y vino aquí para verificar si mongodb puede realizar combinaciones en múltiples campos antes de decidirse a hacerlo, continúe leyendo.

Si bien MongoDB puede realizar uniones, también tiene la libertad de modelar los datos de acuerdo con el patrón de acceso de la aplicación. Si los datos son tan simples como los presentados en la pregunta, simplemente podemos mantener una sola colección que se ve así:

 { user1: 1, user2: 2, percent1: 0.56, percent2: 0.3 } 

Ahora puede realizar todas las operaciones en esta colección que habría realizado uniéndose. ¿Por qué estamos tratando de evitar las uniones? Porque no son compatibles con colecciones fragmentadas ( documentos ), lo que evitará que se amplíe cuando sea necesario. La normalización de datos (que tiene tablas / colecciones separadas) funciona muy bien en SQL, pero cuando se trata de Mongo, evitar combinaciones puede ofrecer ventajas sin consecuencias en la mayoría de los casos. Use la normalización en MongoDB solo cuando no tenga otra opción. De los documentos :

En general, use modelos de datos normalizados:

  • cuando la incrustación daría lugar a la duplicación de datos, pero no proporcionaría suficientes ventajas de rendimiento de lectura para compensar las implicaciones de la duplicación.
  • para representar relaciones más complejas de muchos a muchos.
  • para modelar grandes conjuntos de datos jerárquicos.

Consulte aquí para obtener más información sobre la inserción y por qué la elegiría en lugar de la normalización.