2017 uppdatering
$lookup kan nu direkt använda en array som det lokala fältet. $unwind
behövs inte längre.
Gammalt svar
$lookup
aggregeringspipelinesteget fungerar inte direkt med en array. Huvudsyftet med designen är en "vänster koppling" som en "en till många" typ av koppling (eller egentligen en "uppslagning") på möjliga relaterade data. Men värdet är avsett att vara singular och inte en array.
Därför måste du "avnormalisera" innehållet först innan du utför $lookup
operation för att detta ska fungera. Och det betyder att du använder $unwind
:
db.orders.aggregate([
// Unwind the source
{ "$unwind": "$products" },
// Do the lookup matching
{ "$lookup": {
"from": "products",
"localField": "products",
"foreignField": "_id",
"as": "productObjects"
}},
// Unwind the result arrays ( likely one or none )
{ "$unwind": "$productObjects" },
// Group back to arrays
{ "$group": {
"_id": "$_id",
"products": { "$push": "$products" },
"productObjects": { "$push": "$productObjects" }
}}
])
Efter $lookup
matchar varje arraymedlem, resultatet är en array själv, så du $unwind
igen och $group
till $push
nya arrayer för slutresultatet.
Observera att alla "left join"-matchningar som inte hittas kommer att skapa en tom array för "productObjects" på den givna produkten och därmed negera dokumentet för "product"-elementet när den andra $unwind
kallas.
Även om en direkt applicering på en array skulle vara bra, är det bara så det här fungerar för närvarande genom att matcha ett singulärt värde med ett antal möjliga.
Som $lookup
är i grunden väldigt nytt, det fungerar för närvarande som skulle vara bekant för dem som är bekanta med mongoose som en "fattigmansversion" av .populate()
metod som erbjuds där. Skillnaden är att $lookup
erbjuder "serverside"-behandling av "join" i motsats till på klienten och att en del av "mognad" i $lookup
saknas för närvarande från vad .populate()
erbjudanden (som att interpolera uppslagningen direkt på en array).
Detta är faktiskt ett tilldelat problem för förbättring SERVER-22881, så med lite tur skulle detta komma till nästa utgåva eller en kort därefter.
Som designprincip är din nuvarande struktur varken bra eller dålig, utan bara föremål för omkostnader när du skapar någon "join". Som sådan gäller den grundläggande stående principen för MongoDB i början, där om du "kan" leva med data "föranslutna" i en samling, då är det bäst att göra det.
En annan sak som kan sägas om $lookup
som en generell princip är att avsikten med "join" här är att fungera tvärtom än vad som visas här. Så istället för att behålla de "relaterade id:t" för de andra dokumenten i "förälder"-dokumentet, är den allmänna principen som fungerar bäst när de "relaterade dokumenten" innehåller en hänvisning till "föräldern".
Så $lookup
kan sägas "fungera bäst" med en "relationsdesign" som är det omvända till hur något som mongoose .populate()
utför sin klientsida ansluter sig. Genom att identifiera "en" inom varje "många" istället, drar du bara in de relaterade föremålen utan att behöva $unwind
arrayen först.