$unwind: dela upp arrayfält
Ni plattar ut ett arrayfält till enskilda dokument med $unwind och kombinerar det med $group för analys per element.
$unwind: dela upp arrayfält ä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.
Vad gör $unwind?
Steget $unwind delar upp ett arrayfält i ett dokument i flera utdatadokument – ett per arrayelement. Varje utdatadokument är en kopia av originaldokumentet där arrayfältet har ersatts med ett enskilt element från arrayen. Detta är viktigt för analyser per element, till exempel för att räkna hur många gånger varje tagg förekommer i alla artiklar i en samling.
// Input document:
// { title: 'MongoDB Guide', tags: ['nosql', 'database', 'mongodb'] }
db.articles.aggregate([
{ $unwind: '$tags' }
]);
// Output: THREE documents:
// { title: 'MongoDB Guide', tags: 'nosql' }
// { title: 'MongoDB Guide', tags: 'database' }
// { title: 'MongoDB Guide', tags: 'mongodb' }Grundläggande syntax för $unwind
Den enklaste formen av $unwind är en sträng med den dollarprefixade fältsökvägen: { $unwind: '$arrayField' }. Den utökade objektsyntaxen ger tillgång till ytterligare alternativ, till exempel att bevara tomma arrayer och inkludera arrayindexet. I de flesta fall räcker den enkla strängformen.
// Simple string form
db.products.aggregate([
{ $unwind: '$reviews' }
]);
// Extended object form with options
db.products.aggregate([
{ $unwind: {
path: '$reviews',
includeArrayIndex: 'reviewIndex', // add index field
preserveNullAndEmptyArrays: true // keep docs with no reviews
}}
]);Problemet med saknade arrayer
Som standard tar $unwind bort dokument där det angivna fältet saknas, är null eller är en tom array. Detta beteende liknar en inner join: endast dokument med icke-tomma arrayer släpps igenom. Om du vill bevara dokument med saknade eller tomma arrayer anger du preserveNullAndEmptyArrays: true. Detta är viktigt när arrayfältet är valfritt och du inte vill förlora det överordnade dokumentet.
// Default: documents without 'tags' are DROPPED
db.articles.aggregate([{ $unwind: '$tags' }]);
// Article with no tags: { title: 'Untitled' } -> EXCLUDED
// preserveNullAndEmptyArrays: documents are KEPT
db.articles.aggregate([{
$unwind: {
path: '$tags',
preserveNullAndEmptyArrays: true // keep docs with no tags
}
}]);
// { title: 'Untitled' } -> INCLUDED with tags: nullincludeArrayIndex för elementets position
Alternativet includeArrayIndex lägger till ett nytt fält i varje utdatadokument som innehåller elementets nollbaserade index i originalarrayen. Detta är användbart när positionen i arrayen har betydelse – till exempel ordningen på stegen i en process, rankningen för ett sökresultat eller händelseförloppet i en logg.
// Track which position each tag appeared at in the original array
db.articles.aggregate([{
$unwind: {
path: '$tags',
includeArrayIndex: 'tagPosition'
}
}]);
// { title: 'Guide', tags: 'nosql', tagPosition: 0 }
// { title: 'Guide', tags: 'database', tagPosition: 1 }
// { title: 'Guide', tags: 'mongodb', tagPosition: 2 }$unwind följt av $group
Det vanligaste mönstret i MongoDB-aggregationer är $unwind + $group: först plattar du ut arrayen för att få ett dokument per element och grupperar sedan för att beräkna statistik per element. Om du till exempel delar upp arrayen tags och därefter grupperar efter taggnamn får du antalet artiklar per tagg.
// Count how many articles use each tag
db.articles.aggregate([
{ $unwind: '$tags' }, // one doc per tag
{ $group: {
_id: '$tags', // group by tag value
count: { $sum: 1 } // count articles per tag
}},
{ $sort: { count: -1 } }, // most popular first
{ $limit: 20 } // top 20 tags
]);$unwind med resultat från $lookup
Efter en $lookup är det sammanfogade fältet alltid en array. När joinen är en-till-en (en join via ett unikt _id) brukar du direkt använda $unwind för att platta ut arrayen med ett enda element till ett inbäddat objekt. Detta är ett så vanligt mönster att du ser det i nästan varje pipeline som använder $lookup.
db.orders.aggregate([
{ $lookup: {
from: 'customers',
localField: 'customerId',
foreignField: '_id',
as: 'customer'
}},
// Flatten the single-element array
{ $unwind: '$customer' },
// Now access customer fields as objects
{ $project: {
orderId: '$_id',
amount: 1,
'customer.name': 1,
'customer.email': 1
}}
]);Platta ut nästlade arrayer
För dokument med nästlade arrayer (arrayer inuti arrayer) kan du använda flera $unwind-steg efter varandra. Det första $unwind-steget delar upp den yttre arrayen och det andra delar upp den inre arrayen. Varje steg multiplicerar antalet utdatadokument, så var försiktig med djup nästling för att undvika en explosionsartad ökning av antalet dokument.
// Document: { course: 'Math', modules: [{ name: 'Algebra', lessons: ['L1', 'L2'] }] }
db.courses.aggregate([
{ $unwind: '$modules' }, // deconstruct modules array
{ $unwind: '$modules.lessons' } // deconstruct nested lessons array
]);
// Output:
// { course: 'Math', modules: { name: 'Algebra', lessons: 'L1' } }
// { course: 'Math', modules: { name: 'Algebra', lessons: 'L2' } }Beräkna statistik för arrayelement
Ett kraftfullt användningsområde för $unwind är att beräkna statistik för enskilda arrayelement. I en e-handelsdatabas med order som innehåller orderrader (en array med underdokument) kan du till exempel dela upp orderraderna för att beräkna total intäkt, genomsnittligt antal eller de mest sålda artiklarna för alla orderrader i alla order – inte bara per order.
// Total revenue and quantity per product across all orders
db.orders.aggregate([
{ $unwind: '$lineItems' }, // deconstruct line items
{ $group: {
_id: '$lineItems.productId',
totalRevenue: { $sum: { $multiply: ['$lineItems.price', '$lineItems.qty'] } },
totalQty: { $sum: '$lineItems.qty' },
orderCount: { $sum: 1 } // how many orders contained this product
}},
{ $sort: { totalRevenue: -1 } }
]);Undvik $unwind när det är möjligt
$unwind kan multiplicera antalet dokument i pipelinen kraftigt (ett dokument med en array på 100 element blir 100 dokument efter unwind). Detta ökar minnesanvändningen och bearbetningstiden för alla efterföljande steg. Fråga alltid: ”Kan jag uppnå detta med en arrayuttrycksoperator som $size, $filter eller $map i ett $project-steg i stället för att använda unwind?” Arrayuttrycksoperatorer är ofta snabbare eftersom de inte ökar antalet dokument.
// Better: use $size in $project instead of $unwind + $count
// Avoid:
db.articles.aggregate([
{ $unwind: '$tags' },
{ $group: { _id: '$_id', tagCount: { $sum: 1 } } } // slow
]);
// Better:
db.articles.aggregate([{
$project: {
title: 1,
tagCount: { $size: { $ifNull: ['$tags', []] } } // fast
}
}]);Prestandapåverkan av $unwind
Eftersom $unwind multiplicerar antalet dokument är det vanligtvis det dyraste steget i en pipeline när det används på stora arrayer. Prestandatips: använd $match och $project före $unwind för att minska storleken och antalet indatadokument; lägg till ett $match direkt efter $unwind om du bara är intresserad av vissa element; och begränsa antalet dokument som går in i unwind med ett tidigt $limit när det är lämpligt.
// Optimized order for $unwind pipelines:
db.orders.aggregate([
{ $match: { status: 'completed', createdAt: { $gte: thisMonth } } }, // 1. filter first
{ $project: { lineItems: 1, _id: 0 } }, // 2. project only needed fields
{ $unwind: '$lineItems' }, // 3. expand (smaller docs now)
{ $match: { 'lineItems.qty': { $gt: 5 } } }, // 4. filter expanded results
{ $group: { _id: '$lineItems.productId', count: { $sum: 1 } } }
]);Återskapa arrayer efter $unwind
Efter en pipeline med $unwind + $group kan du använda $push i $group-steget för att bygga upp en array igen från de bearbetade elementen. Detta mönster är användbart för att filtrera eller omvandla arrayelement: dela upp arrayen för att få enskilda element, tillämpa omvandlingar eller filter per element och lägg sedan tillbaka elementen i en ny array. Detta är mer flexibelt än att använda $filter eller $map för komplex logik per element.
// Filter out low-rated reviews per product, keep only rating >= 4
db.products.aggregate([
{ $unwind: '$reviews' },
{ $match: { 'reviews.rating': { $gte: 4 } } }, // per-element filter
{ $group: {
_id: '$_id',
name: { $first: '$name' },
goodReviews: { $push: '$reviews' } // rebuild filtered array
}}
]);
// Each product now has only its 4+ star reviews in goodReviewsSnabbtest
Testa din förståelse av $unwind i aggregationspipelinen.
Lektionssammanfattning
I den här lektionen har du lärt dig att $unwind skapar ett utdatadokument per arrayelement, att preserveNullAndEmptyArrays: true bevarar dokument med saknade eller tomma arrayer och att det klassiska $unwind + $group-mönstret möjliggör analyser per element, till exempel frekvensräkning av taggar. Härnäst utforskar vi $addFields, $replaceRoot och $mergeObjects.
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 ”$unwind: dela upp arrayfält” gratis?
Ja – hela texten till ”$unwind: dela upp arrayfält” 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 ”$unwind: dela upp arrayfält”?
Ni plattar ut ett arrayfält till enskilda dokument med $unwind och kombinerar det med $group för analys per element. 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 ”$unwind: dela upp arrayfält”?
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
- $lookup: koppla samman samlingar i pipelinen
- $unwind: dela upp arrayfält
- $addFields, $replaceRoot och $mergeObjects
- $out och $merge: skriva pipelinens resultat