Si está familiarizado con SQL, es posible que conozca UNION
cláusula, que concatena los resultados de dos consultas en un único conjunto de resultados. En particular, UNION ALL
incluye duplicados.
En MongoDB, podemos usar $unionWith
etapa de canalización de agregación para lograr el mismo efecto que UNION ALL
produce. El $unionWith
stage realiza una unión de dos colecciones:combina los resultados de canalización de dos colecciones en un único conjunto de resultados. E incluye duplicados.
Ejemplo
Supongamos que creamos dos colecciones; uno llamado cats
y otro llamado dogs
. E insertamos los siguientes documentos en ellos:
db.cats.insertMany([
{ _id: 1, name: "Fluffy", type: "Cat", weight: 5 },
{ _id: 2, name: "Scratch", type: "Cat", weight: 3 },
{ _id: 3, name: "Meow", type: "Cat", weight: 7 }
])
db.dogs.insertMany([
{ _id: 1, name: "Wag", type: "Dog", weight: 20 },
{ _id: 2, name: "Bark", type: "Dog", weight: 10 },
{ _id: 3, name: "Fluffy", type: "Dog", weight: 40 }
])
Ahora podemos ejecutar consultas en esas colecciones y usar $unionWith
etapa para combinar los resultados de cada consulta.
Ejemplo:
db.cats.aggregate( [
{ $set: { _id: "$_id" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "$_id" } } ] } },
{ $sort: { type: 1, weight: -1, name: 1 } }
] )
Resultado:
{ "_id" : 3, "name" : "Meow", "type" : "Cat", "weight" : 7 } { "_id" : 1, "name" : "Fluffy", "type" : "Cat", "weight" : 5 } { "_id" : 2, "name" : "Scratch", "type" : "Cat", "weight" : 3 } { "_id" : 3, "name" : "Fluffy", "type" : "Dog", "weight" : 40 } { "_id" : 1, "name" : "Wag", "type" : "Dog", "weight" : 20 } { "_id" : 2, "name" : "Bark", "type" : "Dog", "weight" : 10 }
En este ejemplo, cada documento tiene un campo de tipo con cat
o dog
por lo que es bastante evidente qué documento proviene de qué colección.
Pero si los documentos no tuvieran el campo de tipo, entonces sería más difícil determinar dónde termina una colección y comienza otra. En este caso, podemos usar un literal de cadena en el $set
escenario para representar el nombre de la colección.
Ejemplo:
db.cats.aggregate( [
{ $set: { _id: "cat" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "dog" } } ] } },
{ $sort: { type: 1, weight: -1, name: 1 } }
] )
Resultado:
{ "_id" : "cat", "name" : "Meow", "type" : "Cat", "weight" : 7 } { "_id" : "cat", "name" : "Fluffy", "type" : "Cat", "weight" : 5 } { "_id" : "cat", "name" : "Scratch", "type" : "Cat", "weight" : 3 } { "_id" : "dog", "name" : "Fluffy", "type" : "Dog", "weight" : 40 } { "_id" : "dog", "name" : "Wag", "type" : "Dog", "weight" : 20 } { "_id" : "dog", "name" : "Bark", "type" : "Dog", "weight" : 10 }
Ordenar entre colecciones
En los ejemplos anteriores, los gatos y los perros se clasificaron de una manera que los separó en dos grupos distintos; Primero los gatos, luego los perros. Esto sucedió principalmente porque ordenamos el type
campo primero.
Pero podemos ordenarlo en cualquier otro campo, lo que podría dar como resultado que se combinen los gatos y los perros.
Ejemplo:
db.cats.aggregate( [
{ $set: { _id: "cat" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "dog" } } ] } },
{ $sort: { name: 1 } }
] )
Resultado:
{ "_id" : "dog", "name" : "Bark", "type" : "Dog", "weight" : 10 } { "_id" : "cat", "name" : "Fluffy", "type" : "Cat", "weight" : 5 } { "_id" : "dog", "name" : "Fluffy", "type" : "Dog", "weight" : 40 } { "_id" : "cat", "name" : "Meow", "type" : "Cat", "weight" : 7 } { "_id" : "cat", "name" : "Scratch", "type" : "Cat", "weight" : 3 } { "_id" : "dog", "name" : "Wag", "type" : "Dog", "weight" : 20 }
Proyecciones
Puedes usar el $project
etapa para especificar qué campos se pasarán a la siguiente etapa en la canalización. Por ejemplo, puede reducir el número de campos devueltos por la consulta.
Ejemplo:
db.cats.aggregate( [
{ $project: { name: 1, _id: 0 } },
{ $unionWith: { coll: "dogs", pipeline: [ { $project: { name: 1, _id: 0 } } ]} }
] )
Resultado:
{ "name" : "Fluffy" } { "name" : "Scratch" } { "name" : "Meow" } { "name" : "Wag" } { "name" : "Bark" } { "name" : "Fluffy" }
Eliminar duplicados
Puedes usar el $group
etapa para eliminar duplicados redundantes del resultado.
Por ejemplo, la consulta anterior devolvió dos mascotas llamadas Fluffy. Podemos agregar un $group
Etapa a esa consulta para eliminar el duplicado redundante, de modo que solo se devuelva un Fluffy.
db.cats.aggregate( [
{ $project: { name: 1, _id: 0 } },
{ $unionWith: { coll: "dogs", pipeline: [ { $project: { name: 1, _id: 0 } } ]} },
{ $group: { _id: "$name" } }
] )
Resultado:
{ "_id" : "Meow" } { "_id" : "Bark" } { "_id" : "Scratch" } { "_id" : "Wag" } { "_id" : "Fluffy" }
Esta vez, solo se devuelve un Fluffy.
Columnas que no coinciden
Una de las ventajas de $unionWith
de MongoDB tiene sobre UNION ALL
de SQL es que se puede usar con columnas que no coinciden.
SQL UNION
cláusula requiere que:
- Ambas consultas devuelven el mismo número de columnas
- Las columnas en el mismo orden
- Las columnas coincidentes deben ser de un tipo de datos compatible
MongoDB $unionWith
etapa no impone estas limitaciones.
Por lo tanto, podríamos usar $unionWith
para hacer algo como esto:
db.cats.aggregate( [
{ $set: { _id: "$_id" } },
{ $unionWith: { coll: "employees", pipeline: [ { $set: { _id: "$_id" } } ] } },
{ $sort: { type: 1, salary: -1 } }
] )
Resultado:
{ "_id" : 2, "name" : "Sarah", "salary" : 128000 } { "_id" : 5, "name" : "Beck", "salary" : 82000 } { "_id" : 4, "name" : "Chris", "salary" : 45000 } { "_id" : 3, "name" : "Fritz", "salary" : 25000 } { "_id" : 1, "name" : "Fluffy", "type" : "Cat", "weight" : 5 } { "_id" : 2, "name" : "Scratch", "type" : "Cat", "weight" : 3 } { "_id" : 3, "name" : "Meow", "type" : "Cat", "weight" : 7 }
En este caso, nos unimos a los cats
recogida con los employees
recopilación. Los employees
la colección no tenía los mismos campos que los cats
colección, pero está bien, todavía funcionó.