Fazer uniões no MongoDB no estilo 'SQL UNION' é possível usando agregações junto com pesquisas, em uma única consulta.
Algo assim:
db.getCollection("AnyCollectionThatContainsAtLeastOneDocument").aggregate(
[
{ $limit: 1 }, // Reduce the result set to a single document.
{ $project: { _id: 1 } }, // Strip all fields except the Id.
{ $project: { _id: 0 } }, // Strip the id. The document is now empty.
// Lookup all collections to union together.
{ $lookup: { from: 'collectionToUnion1', pipeline: [...], as: 'Collection1' } },
{ $lookup: { from: 'collectionToUnion2', pipeline: [...], as: 'Collection2' } },
{ $lookup: { from: 'collectionToUnion3', pipeline: [...], as: 'Collection3' } },
// Merge the collections together.
{
$project:
{
Union: { $concatArrays: ["$Collection1", "$Collection2", "$Collection3"] }
}
},
{ $unwind: "$Union" }, // Unwind the union collection into a result set.
{ $replaceRoot: { newRoot: "$Union" } } // Replace the root to cleanup the resulting documents.
]);
Segue a explicação de como funciona:
-
Instanciar umaggregate
de qualquer coleção de seu banco de dados que contém pelo menos um documento. Se você não pode garantir que nenhuma coleção de seu banco de dados não estará vazia, você pode contornar esse problema criando em seu banco de dados algum tipo de coleção 'fictícia' contendo um único documento vazio que estará lá especificamente para fazer consultas de união.
-
Faça com que o primeiro estágio do seu pipeline seja{ $limit: 1 }
. Isso removerá todos os documentos da coleção, exceto o primeiro.
-
Remova todos os campos do documento restante usando$project
estágios:
{ $project: { _id: 1 } }, { $project: { _id: 0 } }
-
Seu agregado agora contém um único documento vazio. É hora de adicionar pesquisas para cada coleção que você deseja unir. Você pode usar opipeline
campo para fazer alguma filtragem específica ou deixelocalField
eforeignField
como null para corresponder a toda a coleção.
{ $lookup: { from: 'collectionToUnion1', pipeline: [...], as: 'Collection1' } }, { $lookup: { from: 'collectionToUnion2', pipeline: [...], as: 'Collection2' } }, { $lookup: { from: 'collectionToUnion3', pipeline: [...], as: 'Collection3' } }
-
Agora você tem um agregado contendo um único documento que contém 3 arrays como este:
{ Collection1: [...], Collection2: [...], Collection3: [...] }
Você pode então mesclá-los em um único array usando um$project
stage junto com o$concatArrays
operador de agregação:
{ "$project" : { "Union" : { $concatArrays: ["$Collection1", "$Collection2", "$Collection3"] } } }
-
Agora você tem um agregado contendo um único documento, no qual está localizada uma matriz que contém sua união de coleções. O que resta a ser feito é adicionar um$unwind
e um$replaceRoot
stage para dividir sua matriz em documentos separados:
{ $unwind: "$Union" }, { $replaceRoot: { newRoot: "$Union" } }
-
Voilà. Você sabe que tem um conjunto de resultados contendo as coleções que deseja unir. Você pode adicionar mais estágios para filtrá-lo ainda mais, classificá-lo, aplicar skip() e limit(). Praticamente tudo o que você quiser.