Se você estiver familiarizado com SQL, talvez conheça o
UNION
cláusula, que concatena os resultados de duas consultas em um único conjunto de resultados. Em particular, UNION ALL
inclui duplicatas. No MongoDB, podemos usar o
$unionWith
estágio de pipeline de agregação para obter o mesmo efeito que UNION ALL
produz. O $unionWith
stage realiza uma união de duas coleções – combina os resultados do pipeline de duas coleções em um único conjunto de resultados. E inclui duplicatas. Exemplo
Suponha que criamos duas coleções; um chamado
cats
e outro chamado dogs
. E inserimos os seguintes documentos neles: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 }
])
Agora podemos executar consultas nessas coleções e usar o
$unionWith
etapa para combinar os resultados de cada consulta. Exemplo:
db.cats.aggregate( [
{ $set: { _id: "$_id" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "$_id" } } ] } },
{ $sort: { type: 1, weight: -1, name: 1 } }
] )
Resultado:
{ "_id" :3, "name" :"Miau", "type" :"Gato", "peso" :7 }{ "_id" :1, "name" :"Fofo", "tipo" :"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" :"Latido", "type" :"Cão", "peso" :10 }
Neste exemplo, cada documento tem um campo de tipo com
cat
ou dog
e, portanto, é bastante aparente qual documento vem de qual coleção. Mas se os documentos não tivessem o campo de tipo, seria mais difícil descobrir onde uma coleção termina e outra começa. Neste caso, podemos usar uma string literal no
$set
stage para representar o nome da coleção. Exemplo:
db.cats.aggregate( [
{ $set: { _id: "cat" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "dog" } } ] } },
{ $sort: { type: 1, weight: -1, name: 1 } }
] )
Resultado:
{ "_id" :"cat", "name" :"Miau", "type" :"Cat", "weight" :7 }{ "_id" :"cat", "name" :"Fofo" , "type" :"Cat", "weight" :5 }{ "_id" :"cat", "name" :"Scratch", "type" :"Cat", "weight" :3 }{ "_id" :"cachorro", "name" :"Fofo", "type" :"Cachorro", "peso" :40 }{ "_id" :"cachorro", "name" :"Wag", "type" :"Cachorro ", "weight" :20 }{ "_id" :"dog", "name" :"Latido", "type" :"Dog", "weight" :10 }
Classificação entre coleções
Nos exemplos anteriores, os gatos e os cães foram classificados de forma a separá-los em dois grupos distintos; gatos primeiro, depois cachorros. Isso aconteceu principalmente porque classificamos no
type
campo primeiro. Mas podemos classificá-lo em qualquer outro campo, o que pode resultar na combinação de cães e gatos.
Exemplo:
db.cats.aggregate( [
{ $set: { _id: "cat" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "dog" } } ] } },
{ $sort: { name: 1 } }
] )
Resultado:
{ "_id" :"cachorro", "name" :"Latido", "tipo" :"Cachorro", "peso" :10 }{ "_id" :"gato", "name" :"Fofo" , "type" :"Cat", "weight" :5 }{ "_id" :"dog", "name" :"Fluffy", "type" :"Dog", "weight" :40 }{ "_id" :"cat", "name" :"Miau", "type" :"Cat", "weight" :7 }{ "_id" :"cat", "name" :"Scratch", "type" :"Cat ", "weight" :3 }{ "_id" :"dog", "name" :"Wag", "type" :"Dog", "weight" :20 }
Projeções
Você pode usar o
$project
stage para especificar quais campos devem ser passados para o próximo estágio no pipeline. Por exemplo, você pode reduzir o número de campos retornados pela consulta. Exemplo:
db.cats.aggregate( [
{ $project: { name: 1, _id: 0 } },
{ $unionWith: { coll: "dogs", pipeline: [ { $project: { name: 1, _id: 0 } } ]} }
] )
Resultado:
{ "name" :"Fofo" }{ "name" :"Scratch" }{ "name" :"Miau" }{ "name" :"Wag" }{ "name" :"Latido" }{ " name" :"Fofo" }
Remover duplicatas
Você pode usar o
$group
etapa para eliminar duplicatas redundantes do resultado. Por exemplo, a consulta anterior retornou dois animais de estimação chamados Fluffy. Podemos adicionar um
$group
stage para essa consulta para eliminar a duplicata redundante, de modo que apenas um Fluffy seja retornado. db.cats.aggregate( [
{ $project: { name: 1, _id: 0 } },
{ $unionWith: { coll: "dogs", pipeline: [ { $project: { name: 1, _id: 0 } } ]} },
{ $group: { _id: "$name" } }
] )
Resultado:
{ "_id" :"Miau" }{ "_id" :"Bark" }{ "_id" :"Scratch" }{ "_id" :"Wag" }{ "_id" :"Fofo" }
Desta vez, apenas um Fluffy é retornado.
Colunas não correspondentes
Uma das vantagens que o$unionWith
do MongoDB tem sobre oUNION ALL
do SQL é que ele pode ser usado com colunas não correspondentes.
O SQLUNION
cláusula exige que:
- Ambas as consultas retornam o mesmo número de colunas
- As colunas na mesma ordem
- As colunas correspondentes devem ser de um tipo de dados compatível
O MongoDB
$unionWith
palco não impõe essas limitações. Portanto, podemos usar
$unionWith
para fazer algo assim: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" :"Fofo ", "type" :"Cat", "weight" :5 }{ "_id" :2, "name" :"Scratch", "type" :"Cat", "weight" :3 }{ "_id" :3, "name" :"Miau", "type" :"Gato", "peso" :7 }
Neste caso, juntamos os
cats
coleção com os employees
coleção. Os employees
coleção não tinha os mesmos campos que os cats
coleção, mas tudo bem – ainda funcionou.