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

$unionWith:el equivalente de MongoDB de UNION ALL

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ó.