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.