Hvis du er fortrolig med SQL, kender du måske til UNION
klausul, som sammenkæder resultaterne af to forespørgsler til et enkelt resultatsæt. Især UNION ALL
omfatter dubletter.
I MongoDB kan vi bruge $unionWith
aggregeringspipeline-stadiet for at opnå den samme effekt som UNION ALL
producerer. $unionWith
stage udfører en forening af to samlinger – den kombinerer pipelineresultater fra to samlinger til et enkelt resultatsæt. Og det inkluderer dubletter.
Eksempel
Antag, at vi opretter to samlinger; en kaldet cats
og en anden kaldet dogs
. Og vi indsætter følgende dokumenter i dem:
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 }
])
Vi kan nu køre en forespørgsel mod disse samlinger og bruge $unionWith
trin for at kombinere resultaterne af hver forespørgsel.
Eksempel:
db.cats.aggregate( [
{ $set: { _id: "$_id" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "$_id" } } ] } },
{ $sort: { type: 1, weight: -1, name: 1 } }
] )
Resultat:
{ "_id" :3, "name" :"Mjav", "type" :"Kat", "vægt" :7 }{ "_id" :1, "name" :"Fluffy", "type" :"Kat", "vægt" :5 }{ "_id" :2, "navn" :"Krat", "type" :"Kat", "vægt" :3 }{ "_id" :3, "navn" :"Fluffy", "type" :"Hund", "weight" :40 }{ "_id" :1, "name" :"Wag", "type" :"Hund", "weight" :20 }{ " _id" :2, "name" :"Bark", "type" :"Hund", "vægt" :10 }
I dette eksempel har hvert dokument et typefelt med enten cat
eller dog
og så det er ret tydeligt, hvilket dokument der kommer fra hvilken samling.
Men hvis dokumenterne ikke havde typefeltet, ville det være sværere at finde ud af, hvor en indsamling slutter, og en anden starter. I dette tilfælde kan vi bruge en streng literal ved $set
fase for at repræsentere samlingens navn.
Eksempel:
db.cats.aggregate( [
{ $set: { _id: "cat" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "dog" } } ] } },
{ $sort: { type: 1, weight: -1, name: 1 } }
] )
Resultat:
{ "_id" :"cat", "name" :"Mjav", "type" :"Kat", "vægt" :7 }{ "_id" :"kat", "name" :"Fluffy" , "type" :"Kat", "vægt" :5 }{ "_id" :"kat", "navn" :"Krat", "type" :"Kat", "vægt" :3 }{ "_id" :"hund", "name" :"Fluffy", "type" :"Hund", "vægt" :40 }{ "_id" :"hund", "name" :"Wag", "type" :"Hund ", "weight" :20 }{ "_id" :"dog", "name" :"Bark", "type" :"Hund", "weight" :10 }
Sortering på tværs af samlinger
I de foregående eksempler blev kattene og hundene sorteret på en måde, der adskilte dem i to adskilte grupper; først katte, så hunde. Dette skete primært fordi vi sorterede efter type
felt først.
Men vi kan sortere det på et hvilket som helst andet felt, hvilket kan resultere i, at katte og hunde bliver kombineret.
Eksempel:
db.cats.aggregate( [
{ $set: { _id: "cat" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "dog" } } ] } },
{ $sort: { name: 1 } }
] )
Resultat:
{ "_id" :"hund", "name" :"Bark", "type" :"Hund", "vægt" :10 }{ "_id" :"kat", "name" :"Fluffy" , "type" :"Kat", "vægt" :5 }{ "_id" :"hund", "name" :"Fluffy", "type" :"Hund", "vægt" :40 }{ "_id" :"kat", "name" :"Mjav", "type" :"Kat", "vægt" :7 }{ "_id" :"kat", "name" :"Krat", "type" :"Kat ", "weight" :3 }{ "_id" :"dog", "name" :"Wag", "type" :"Hund", "weight" :20 }
Projektioner
Du kan bruge $project
trin for at angive, hvilke felter der skal videregives til næste trin i pipelinen. For eksempel kan du derfor reducere antallet af felter, der returneres af forespørgslen.
Eksempel:
db.cats.aggregate( [
{ $project: { name: 1, _id: 0 } },
{ $unionWith: { coll: "dogs", pipeline: [ { $project: { name: 1, _id: 0 } } ]} }
] )
Resultat:
{ "name" :"Fluffy" }{ "name" :"Scratch" }{ "name" :"Meow" }{ "name" :"Wag" }{ "name" :"Bark" }{ " name" :"Fluffy" }
Fjern dubletter
Du kan bruge $group
fase for at eliminere overflødige dubletter fra resultatet.
For eksempel returnerede den forrige forespørgsel to kæledyr kaldet Fluffy. Vi kan tilføje en $group
trin til den forespørgsel for at eliminere den overflødige duplikat, så kun én Fluffy returneres.
db.cats.aggregate( [
{ $project: { name: 1, _id: 0 } },
{ $unionWith: { coll: "dogs", pipeline: [ { $project: { name: 1, _id: 0 } } ]} },
{ $group: { _id: "$name" } }
] )
Resultat:
{ "_id" :"Meow" }{ "_id" :"Bark" }{ "_id" :"Scratch" }{ "_id" :"Wag" }{ "_id" :"Fluffy" }Denne gang returneres kun én Fluffy.
Ikke-matchende kolonner
En af fordelene ved MongoDB's
$unionWith
har over SQL'sUNION ALL
er, at den kan bruges med ikke-matchende kolonner.SQL
UNION
klausul kræver, at:
- Begge forespørgsler returnerer det samme antal kolonner
- Kolonnerne i samme rækkefølge
- De matchende kolonner skal være af en kompatibel datatype
MongoDB $unionWith
fase pålægger ikke disse begrænsninger.
Derfor kunne vi bruge $unionWith
at gøre noget som dette:
db.cats.aggregate( [
{ $set: { _id: "$_id" } },
{ $unionWith: { coll: "employees", pipeline: [ { $set: { _id: "$_id" } } ] } },
{ $sort: { type: 1, salary: -1 } }
] )
Resultat:
{ "_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" :"Kat", "vægt" :5 }{ "_id" :2, "name" :"Scratch", "type" :"Kat", "vægt" :3 }{ "_id" :3, "name" :"Mjav", "type" :"Kat", "vægt" :7 }
I dette tilfælde sluttede vi os til cats
indsamling med employees
kollektion. employees
samlingen havde ikke de samme felter som cats
indsamling, men det er fint – det virkede stadig.