Jeśli znasz SQL, możesz wiedzieć o UNION
klauzula, która łączy wyniki dwóch zapytań w jeden zestaw wyników. W szczególności UNION ALL
zawiera duplikaty.
W MongoDB możemy użyć $unionWith
etap potoku agregacji, aby osiągnąć ten sam efekt, co UNION ALL
produkuje. $unionWith
stage wykonuje połączenie dwóch kolekcji — łączy wyniki potoku z dwóch kolekcji w jeden zestaw wyników. I zawiera duplikaty.
Przykład
Załóżmy, że tworzymy dwie kolekcje; jeden o nazwie cats
i inny o nazwie dogs
. Wstawiamy do nich następujące dokumenty:
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 }
])
Możemy teraz uruchamiać zapytania względem tych kolekcji i używać $unionWith
etap łączenia wyników każdego zapytania.
Przykład:
db.cats.aggregate( [
{ $set: { _id: "$_id" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "$_id" } } ] } },
{ $sort: { type: 1, weight: -1, name: 1 } }
] )
Wynik:
{ "_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 }
W tym przykładzie każdy dokument ma pole typu zawierające cat
lub dog
więc jest całkiem oczywiste, który dokument pochodzi z której kolekcji.
Ale gdyby dokumenty nie miały pola typu, to byłoby trudniej ustalić, gdzie kończy się jedna kolekcja, a zaczyna druga. W tym przypadku możemy użyć literału ciągu w $set
etap reprezentujący nazwę kolekcji.
Przykład:
db.cats.aggregate( [
{ $set: { _id: "cat" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "dog" } } ] } },
{ $sort: { type: 1, weight: -1, name: 1 } }
] )
Wynik:
{ "_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 }
Sortowanie w kolekcjach
W poprzednich przykładach koty i psy zostały posortowane w sposób, który podzielił je na dwie odrębne grupy; najpierw koty, potem psy. Stało się tak głównie dlatego, że sortowaliśmy według type
najpierw pole.
Ale możemy to posortować na dowolnym innym polu, co może skutkować połączeniem kotów i psów.
Przykład:
db.cats.aggregate( [
{ $set: { _id: "cat" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "dog" } } ] } },
{ $sort: { name: 1 } }
] )
Wynik:
{ "_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 }
Projekcje
Możesz użyć $project
etap, aby określić, które pola mają zostać przekazane do następnego etapu w potoku. Na przykład możesz zmniejszyć liczbę pól zwracanych przez zapytanie.
Przykład:
db.cats.aggregate( [
{ $project: { name: 1, _id: 0 } },
{ $unionWith: { coll: "dogs", pipeline: [ { $project: { name: 1, _id: 0 } } ]} }
] )
Wynik:
{ "name" : "Fluffy" } { "name" : "Scratch" } { "name" : "Meow" } { "name" : "Wag" } { "name" : "Bark" } { "name" : "Fluffy" }
Usuń duplikaty
Możesz użyć $group
etap, aby wyeliminować zbędne duplikaty z wyniku.
Na przykład poprzednie zapytanie zwróciło dwa zwierzaki o nazwie Fluffy. Możemy dodać $group
etap do tego zapytania, aby wyeliminować zbędny duplikat, tak aby zwracany był tylko jeden Fluffy.
db.cats.aggregate( [
{ $project: { name: 1, _id: 0 } },
{ $unionWith: { coll: "dogs", pipeline: [ { $project: { name: 1, _id: 0 } } ]} },
{ $group: { _id: "$name" } }
] )
Wynik:
{ "_id" : "Meow" } { "_id" : "Bark" } { "_id" : "Scratch" } { "_id" : "Wag" } { "_id" : "Fluffy" }
Tym razem zwracany jest tylko jeden Fluffy.
Niepasujące kolumny
Jedna z zalet $unionWith
MongoDB ma ponad UNION ALL
SQL jest to, że można go używać z niepasującymi kolumnami.
SQL UNION
klauzula wymaga, aby:
- Oba zapytania zwracają tę samą liczbę kolumn
- Kolumny w tej samej kolejności
- Dopasowane kolumny muszą mieć zgodny typ danych
MongoDB $unionWith
scena nie nakłada tych ograniczeń.
Dlatego możemy użyć $unionWith
zrobić coś takiego:
db.cats.aggregate( [
{ $set: { _id: "$_id" } },
{ $unionWith: { coll: "employees", pipeline: [ { $set: { _id: "$_id" } } ] } },
{ $sort: { type: 1, salary: -1 } }
] )
Wynik:
{ "_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 }
W tym przypadku dołączyliśmy do cats
zbiórka z employees
kolekcja. employees
kolekcja nie miała takich samych pól jak cats
kolekcja, ale to w porządku – nadal działało.