Als u bekend bent met SQL, kent u misschien de UNION
clausule, die de resultaten van twee query's samenvoegt tot één resultaatset. In het bijzonder UNION ALL
bevat duplicaten.
In MongoDB kunnen we de $unionWith
. gebruiken aggregatiepijplijnfase om hetzelfde effect te bereiken dat UNION ALL
produceert. De $unionWith
stage voert een samenvoeging van twee collecties uit - het combineert pijplijnresultaten van twee collecties in een enkele resultaatset. En het bevat duplicaten.
Voorbeeld
Stel dat we twee collecties maken; een genaamd cats
en een andere genaamd dogs
. En we voegen de volgende documenten erin:
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 }
])
We kunnen nu een query uitvoeren op die collecties en de $unionWith
. gebruiken stap om de resultaten van elke zoekopdracht te combineren.
Voorbeeld:
db.cats.aggregate( [
{ $set: { _id: "$_id" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "$_id" } } ] } },
{ $sort: { type: 1, weight: -1, name: 1 } }
] )
Resultaat:
{ "_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 }
In dit voorbeeld heeft elk document een typeveld met ofwel cat
of dog
en zo is het vrij duidelijk welk document uit welke collectie komt.
Maar als de documenten het typeveld niet hadden, zou het moeilijker zijn om uit te zoeken waar de ene verzameling eindigt en de andere begint. In dit geval kunnen we een letterlijke tekenreeks gebruiken bij de $set
podium om de collectienaam weer te geven.
Voorbeeld:
db.cats.aggregate( [
{ $set: { _id: "cat" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "dog" } } ] } },
{ $sort: { type: 1, weight: -1, name: 1 } }
] )
Resultaat:
{ "_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 }
Sorteren over collecties
In de vorige voorbeelden waren de katten en de honden zo gesorteerd dat ze in twee verschillende groepen werden verdeeld; eerst katten, dan honden. Dit gebeurde voornamelijk omdat we sorteerden op het type
veld eerst.
Maar we kunnen het op elk ander veld sorteren, wat ertoe kan leiden dat de katten en honden worden gecombineerd.
Voorbeeld:
db.cats.aggregate( [
{ $set: { _id: "cat" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "dog" } } ] } },
{ $sort: { name: 1 } }
] )
Resultaat:
{ "_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 }
Projecties
U kunt het $project
. gebruiken fase om aan te geven welke velden moeten worden doorgegeven aan de volgende fase in de pijplijn. Zo kunt u bijvoorbeeld het aantal velden dat door de zoekopdracht wordt geretourneerd, verminderen.
Voorbeeld:
db.cats.aggregate( [
{ $project: { name: 1, _id: 0 } },
{ $unionWith: { coll: "dogs", pipeline: [ { $project: { name: 1, _id: 0 } } ]} }
] )
Resultaat:
{ "name" : "Fluffy" } { "name" : "Scratch" } { "name" : "Meow" } { "name" : "Wag" } { "name" : "Bark" } { "name" : "Fluffy" }
Duplicaten verwijderen
U kunt de $group
. gebruiken fase om overbodige duplicaten uit het resultaat te verwijderen.
De vorige zoekopdracht leverde bijvoorbeeld twee huisdieren op met de naam Fluffy. We kunnen een $group
. toevoegen stap naar die zoekopdracht om het overtollige duplicaat te verwijderen, zodat er slechts één Fluffy wordt geretourneerd.
db.cats.aggregate( [
{ $project: { name: 1, _id: 0 } },
{ $unionWith: { coll: "dogs", pipeline: [ { $project: { name: 1, _id: 0 } } ]} },
{ $group: { _id: "$name" } }
] )
Resultaat:
{ "_id" : "Meow" } { "_id" : "Bark" } { "_id" : "Scratch" } { "_id" : "Wag" } { "_id" : "Fluffy" }
Deze keer wordt er maar één Fluffy geretourneerd.
Niet-overeenkomende kolommen
Een van de voordelen die MongoDB's $unionWith
heeft meer dan SQL's UNION ALL
is dat het kan worden gebruikt met niet-overeenkomende kolommen.
De SQL UNION
clausule vereist dat:
- Beide zoekopdrachten retourneren hetzelfde aantal kolommen
- De kolommen in dezelfde volgorde
- De overeenkomende kolommen moeten van een compatibel gegevenstype zijn
De MongoDB $unionWith
stage legt deze beperkingen niet op.
Daarom zouden we $unionWith
. kunnen gebruiken om zoiets als dit te doen:
db.cats.aggregate( [
{ $set: { _id: "$_id" } },
{ $unionWith: { coll: "employees", pipeline: [ { $set: { _id: "$_id" } } ] } },
{ $sort: { type: 1, salary: -1 } }
] )
Resultaat:
{ "_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 }
In dit geval hebben we ons aangesloten bij de cats
collectie met de employees
verzameling. De employees
collectie had niet dezelfde velden als de cats
verzameling, maar dat is prima - het werkte nog steeds.