Om du är bekant med SQL kanske du känner till UNION
klausul, som sammanfogar resultaten av två frågor till en enda resultatuppsättning. I synnerhet UNION ALL
inkluderar dubbletter.
I MongoDB kan vi använda $unionWith
aggregeringspipeline för att uppnå samma effekt som UNION ALL
producerar. $unionWith
stage genomför en sammanslutning av två samlingar – den kombinerar pipelineresultat från två samlingar till en enda resultatuppsättning. Och det inkluderar dubbletter.
Exempel
Anta att vi skapar två samlingar; en som heter cats
och en annan som heter dogs
. Och vi infogar följande dokument 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öra en sökning mot dessa samlingar och använda $unionWith
steg för att kombinera resultaten av varje fråga.
Exempel:
db.cats.aggregate( [
{ $set: { _id: "$_id" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "$_id" } } ] } },
{ $sort: { type: 1, weight: -1, name: 1 } }
] )
Resultat:
{ "_id" :3, "name" :"Mjau", "type" :"Katt", "vikt" :7 }{ "_id" :1, "name" :"Fluffig", "typ" :"Cat", "weight" :5 }{ "_id" :2, "name" :"Scratch", "type" :"Cat", "weight" :3 }{ "_id" :3, "name" :"Fluffig", "type" :"Hund", "vikt" :40 }{ "_id" :1, "name" :"Wag", "type" :"Hund", "vikt" :20 }{ " _id" :2, "name" :"Skall", "type" :"Hund", "vikt" :10 }
I det här exemplet har varje dokument ett typfält med antingen cat
eller dog
och så det är ganska uppenbart vilket dokument som kommer från vilken samling.
Men om dokumenten inte hade typfältet, skulle det vara svårare att räkna ut var en insamling slutar och en annan börjar. I det här fallet kan vi använda en bokstavlig sträng vid $set
steg för att representera samlingens namn.
Exempel:
db.cats.aggregate( [
{ $set: { _id: "cat" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "dog" } } ] } },
{ $sort: { type: 1, weight: -1, name: 1 } }
] )
Resultat:
{ "_id" :"cat", "name" :"Mjau", "type" :"Katt", "vikt" :7 }{ "_id" :"katt", "name" :"Fluffig" , "type" :"Katt", "vikt" :5 }{ "_id" :"katt", "namn" :"Scratch", "type" :"Katt", "vikt" :3 }{ "_id" :"hund", "name" :"Fluffig", "type" :"Hund", "vikt" :40 }{ "_id" :"hund", "name" :"Wag", "type" :"Hund ", "weight" :20 }{ "_id" :"dog", "name" :"Bark", "type" :"Hund", "weight" :10 }
Sortera över samlingar
I de tidigare exemplen sorterades katterna och hundarna på ett sätt som separerade dem i två distinkta grupper; först katter, sedan hundar. Detta hände främst för att vi sorterade på type
fältet först.
Men vi kan sortera det på vilket annat fält som helst, vilket kan resultera i att katter och hundar kombineras.
Exempel:
db.cats.aggregate( [
{ $set: { _id: "cat" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "dog" } } ] } },
{ $sort: { name: 1 } }
] )
Resultat:
{ "_id" :"hund", "name" :"Skall", "type" :"Hund", "vikt" :10 }{ "_id" :"katt", "name" :"Fluffig" , "type" :"Katt", "vikt" :5 }{ "_id" :"hund", "name" :"Fluffig", "type" :"Hund", "vikt" :40 }{ "_id" :"cat", "name" :"Mjau", "type" :"Katt", "vikt" :7 }{ "_id" :"katt", "name" :"Krata", "typ" :"Katt ", "weight" :3 }{ "_id" :"dog", "name" :"Wag", "type" :"Hund", "weight" :20 }
Projektioner
Du kan använda $project
steg för att specificera vilka fält som ska skickas vidare till nästa steg i pipelinen. Till exempel kan du därför minska antalet fält som returneras av frågan.
Exempel:
db.cats.aggregate( [
{ $project: { name: 1, _id: 0 } },
{ $unionWith: { coll: "dogs", pipeline: [ { $project: { name: 1, _id: 0 } } ]} }
] )
Resultat:
{ "name" :"Fluffig" }{ "name" :"Scratch" }{ "name" :"Mjau" }{ "name" :"Wag" }{ "name" :"Bark" }{ " name" :"Fluffig" }
Ta bort dubbletter
Du kan använda $group
steg för att eliminera redundanta dubbletter från resultatet.
Till exempel returnerade den tidigare frågan två husdjur som heter Fluffy. Vi kan lägga till en $group
steg till den frågan för att eliminera den redundanta dubbletten, så att bara en Fluffy returneras.
db.cats.aggregate( [
{ $project: { name: 1, _id: 0 } },
{ $unionWith: { coll: "dogs", pipeline: [ { $project: { name: 1, _id: 0 } } ]} },
{ $group: { _id: "$name" } }
] )
Resultat:
{ "_id" :"Mjau" }{ "_id" :"Bark" }{ "_id" :"Scratch" }{ "_id" :"Wag" }{ "_id" :"Fluffig" }Den här gången returneras endast en Fluffy.
Icke-matchande kolumner
En av fördelarna med MongoDB:s
$unionWith
har över SQL:sUNION ALL
är att den kan användas med icke-matchande kolumner.SQL
UNION
klausul kräver att:
- Båda frågorna returnerar samma antal kolumner
- Kolumnerna i samma ordning
- De matchande kolumnerna måste vara av en kompatibel datatyp
MongoDB $unionWith
steg inte inför dessa begränsningar.
Därför kan vi använda $unionWith
att göra något så här:
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" :"Fluffig ", "type" :"Katt", "vikt" :5 }{ "_id" :2, "name" :"Scratch", "type" :"Katt", "vikt" :3 }{ "_id" :3, "name" :"Mjau", "typ" :"Katt", "vikt" :7 }
I det här fallet gick vi med i cats
insamling med employees
samling. employees
samlingen hade inte samma fält som cats
samling, men det är bra – det fungerade fortfarande.