MongoDB Academy · Lektion

Mönstren Extended Reference och Subset

Ni kommer att bädda in en utvald delmängd av ofta använda fält från ett refererat dokument för att eliminera $lookup-joiner i läsvägar med hög belastning.

Lektion 2 av 413 steg

Mönstren Extended Reference och Subset är en gratis lektion i MongoDB Academy på CoddyKit. Detta är lektion 2 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för MongoDB Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i MongoDB Academy innehåller totalt 4 lektioner.

Prestandaproblemet med $lookup

MongoDB:s steg $lookup sammanfogar samlingar vid frågetillfället genom att utföra en hash-join i minnet. För frekventa frågor med hög trafik kan kostnaden för denna sammanfogning dominera svarstiden. Om din endpoint för orderlistor utför en $lookup för att hämta kundnamn och e-postadresser för varje order i varje anrop betalar du kostnaden för sammanfogningen varje gång. Extended Reference-mönstret eliminerar denna kostnad genom att bädda in en riktad delmängd av det refererade dokumentets fält.

Extended Reference-mönstret

Extended Reference-mönstret bäddar in en noggrant utvald delmängd av fälten från ett refererat dokument direkt i det refererande dokumentet. I stället för att bara lagra kundens _id i en order och slå upp kunden varje gång ordern visas, bäddar du in kundens name och email – de fält som behövs för att visa ordern – direkt i orderdokumentet. Den fullständiga kundposten finns fortfarande separat för uppdateringar.

// Without Extended Reference: requires $lookup on every order read
{ orderId: 'ORD-1234', customerId: ObjectId('...'), total: 99.99 }

// With Extended Reference: order carries the fields it needs
{
  orderId: 'ORD-1234',
  customer: {
    _id: ObjectId('...'),       // reference for updates
    name: 'Alice Smith',        // duplicated for fast reads
    email: 'alice@example.com'  // duplicated for fast reads
  },
  total: 99.99,
  status: 'shipped'
}

Välja vilka fält som ska dupliceras

Duplicera bara fält som läses ofta tillsammans med det refererande dokumentet och ändras sällan. En kunds namn och e-postadress är bra kandidater – de behövs för att visa order och ändras sällan. Kundens faktureringsadress eller saldo av lojalitetspoäng är sämre kandidater – de ändras ofta och skulle kräva att alla inbäddade kopior uppdateras varje gång. Målet är att eliminera sammanfogningar i den kritiska läsvägen utan att skapa en omöjlig synkroniseringsbörda.

Hantera uppdateringar av duplicerade fält

När en kund ändrar sitt namn, vilket är en ovanlig händelse, måste du uppdatera kundens huvuddokument och alla orderdokument som bäddar in namnet. Detta kallas write fan-out. Använd updateMany() för att sprida ändringen. Den viktiga insikten är att denna sällsynta skrivkostnad är värd att ta om den eliminerar kostnaden för sammanfogningar i tusentals dagliga läsningar.

// Update the source document first
await db.collection('customers').updateOne(
  { _id: customerId },
  { $set: { name: 'Alice Johnson' } }
)

// Then propagate to all embedded references
await db.collection('orders').updateMany(
  { 'customer._id': customerId },
  { $set: { 'customer.name': 'Alice Johnson' } }
)

Subset-mönstret: korta ned stora arrayer

Subset-mönstret hanterar en annan utmaning: när ett dokument bäddar in en array som växer sig mycket stor över tid hämtar varje läsning hela arrayen – även om programmet bara visar de första N elementen. En produkt med 5 000 recensioner bäddar in alla 5 000 i ett enda dokument, men användargränssnittet visar bara de 10 främsta. Subset-mönstret delar upp arrayen: behåll de senast tillagda eller mest relevanta N elementen i huvud dokumentet och flytta resten till en separat samling.

// Anti-pattern: all 5000 reviews in the product document
{
  _id: productId,
  name: 'Widget Pro',
  reviews: [ /* 5000 review objects */ ]  // fetched on every product read
}

// Subset Pattern: only recent 10 reviews in main document
{
  _id: productId,
  name: 'Widget Pro',
  recentReviews: [ /* 10 most recent */ ],  // fast, always-displayed
  reviewCount: 5000,
  avgRating: 4.3
  // full reviews in separate 'reviews' collection
}

Underhåll delmängden med $push och $slice

Håll den inbäddade delmängden uppdaterad genom att använda $push med modifieraren $slice. När du har lagt till en ny recension i arrayen recentReviews med $push använder du $slice: -10 för att begränsa arrayen till de 10 sista elementen. Detta är en enda atomisk operation – MongoDB utför push och slice atomiskt i samma uppdatering.

const newReview = { userId: ObjectId('...'), rating: 5, comment: 'Great product!', date: new Date() }

// Add to main reviews collection
await db.collection('reviews').insertOne({ productId, ...newReview })

// Update subset in product document — keep last 10
await db.collection('products').updateOne(
  { _id: productId },
  {
    $push: {
      recentReviews: {
        $each: [newReview],
        $sort: { date: -1 },
        $slice: 10
      }
    },
    $inc: { reviewCount: 1 }
  }
)

Fråga utanför delmängden

I det vanligaste fallet – att visa en produktsida med de senaste recensionerna – läser du produktens huvuddokument och använder den inbäddade recentReviews. För ”läs in fler” eller paginering frågar du samlingen reviews med produktens _id. Denna tvånivåmetod ger snabb prestanda i det vanligaste fallet utan att begränsa möjligheten att komma åt alla recensioner när det behövs.

// Fast product page: use embedded subset
const product = await db.collection('products').findOne(
  { _id: productId },
  { projection: { name: 1, price: 1, avgRating: 1, recentReviews: 1 } }
)

// Load more reviews: query the full reviews collection
const moreReviews = await db.collection('reviews')
  .find({ productId })
  .sort({ date: -1 })
  .skip(10)
  .limit(10)
  .toArray()

Jämförelse mellan Extended Reference och Subset

Extended Reference duplicerar fält från ett refererat dokument till det refererande dokumentet för att eliminera sammanfogningar vid läsning. Det hanterar fallet med en-till-många-relationer där ”ena”-sidans fält behövs tillsammans med varje post på ”många”-sidan. Subset-mönstret duplicerar de mest relevanta N elementen i en stor array till huvud dokumentet för att undvika att tusentals element läses in vid varje läsning. Båda mönstren byter ökad komplexitet vid skrivning mot bättre läsprestanda.

När dessa mönster INTE ska användas

Undvik dessa mönster när de duplicerade uppgifterna ändras ofta – kostnaden för write fan-out blir då orimligt hög. Om kunders e-postadresser ändras dagligen är det opraktiskt att sprida ändringarna till miljontals order. Undvik dem också när datakonsistens är avgörande och fördröjd spridning inte kan accepteras – under tiden mellan en namnändring och slutförandet av write fan-out kommer vissa order att visa gamla uppgifter. För krav på strikt konsistens bör du använda referenser och acceptera kostnaden för sammanfogningen.

Praktiskt exempel: e-handelsorder

I en e-handelsplattform läses order mycket oftare än kunder uppdaterar sina profiler. Att bädda in en ögonblicksbild av kundens leveransadress när ordern skapas (Extended Reference) är faktiskt korrekt ur verksamhetens perspektiv – ordern bör behålla adressen som användes vid köpet även om kunden flyttar senare. Detta är ett fall där mönstret följer domänlogiken, inte bara prestandakraven, vilket gör det dubbelt lämpligt.

// Order with Extended Reference — snapshot of address at purchase time
{
  orderId: 'ORD-5678',
  customer: {
    _id: ObjectId('...'),
    name: 'Bob Chen',
    shippingAddress: {
      street: '42 Elm Street',
      city: 'Istanbul',
      country: 'TR'
    }  // snapshot at order time — correct even if customer moves later
  },
  items: [{ sku: 'WGT-001', qty: 2, price: 49.99 }],
  total: 99.98
}

Mätning och validering av prestanda

Innan du tillämpar dessa mönster mäter du baslinjeprestandan med explain('executionStats') för att identifiera var läsfördröjningen faktiskt uppstår. Mät sedan på nytt efter att du har tillämpat ett mönster för att bekräfta förbättringen. Använd db.collection.stats() för att jämföra genomsnittliga dokumentstorlekar och kontrollera att de inbäddade fälten inte gör att dokumenten närmar sig gränsen på 16 MB. Mönster bör motiveras av data, inte antas vara till hjälp i alla situationer.

// Measure before and after applying Extended Reference
db.orders.find({ 'customer._id': someId }).explain('executionStats')

// Check average document size after embedding
db.orders.stats().avgObjSize  // in bytes

Snabbtest

Testa dina kunskaper om begreppen MongoDB och NoSQL Databases från den här lektionen.

Sammanfattning av lektionen

I den här lektionen lärde du dig att Extended Reference-mönstret bäddar in utvalda fält från ett refererat dokument för att eliminera sammanfogningar i kritiska läsvägar – bäst för fält som läses ofta och ändras sällan, att Subset-mönstret behåller de mest relevanta N elementen i en array i huvud dokumentet och arkiverar resten i en separat samling, och att båda mönstren byter sällsynta write fan-out-operationer mot betydligt snabbare läsningar. Härnäst utforskar vi Polymorphic- och Schema Versioning-mönstren.

Gratis att börja

Lär dig JavaScript med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
30
Lektioner
120

Vanliga frågor

Är lektionen ”Mönstren Extended Reference och Subset” gratis?

Ja – hela texten till ”Mönstren Extended Reference och Subset” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i MongoDB Academy, kan Ni uppgradera till CoddyKit PRO. Kursen i MongoDB Academy innehåller totalt 4 lektioner.

Vad lär jag mig i ”Mönstren Extended Reference och Subset”?

Ni kommer att bädda in en utvald delmängd av ofta använda fält från ett refererat dokument för att eliminera $lookup-joiner i läsvägar med hög belastning. Ni övar på MongoDB Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig MongoDB Academy?

Du behöver inga förkunskaper. Utbildningen i MongoDB Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 2 av 4.

Hur lång tid tar lektionen ”Mönstren Extended Reference och Subset”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här MongoDB Academy-lektionen?

Ja. Varje MongoDB Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Mönstren Bucket och Computed
  2. Mönstren Extended Reference och Subset
  3. Mönstren Polymorphic och Schema Versioning
  4. Mönstren Outlier och Tree Structure
← Tillbaka till MongoDB Academy