sql >> Databasteknik >  >> NoSQL >> MongoDB

$lookup på ObjectIds i en array

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".

$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.



  1. Bygg en reaktiv publikation med ytterligare fält i varje dokument

  2. Redis – Överväg att byta namn på en av bönorna eller aktivera åsidosättning genom att ställa in spring.main.allow-bean-definition-overriding=true

  3. Använda MongoDB $pull för att ta bort dokument inom en Array

  4. Att avgöra varför Redis tar emot en SIGTERM varannan minut