Det första att förstå om mangustbeståndet är att det inte är magi, utan bara en bekvämlighetsmetod som låter dig hämta relaterad information utan att göra allt själv.
Konceptet är huvudsakligen för användning där du bestämmer dig för att du ska behöva placera data i en separat samling snarare än att bädda in den data, och dina huvudsakliga överväganden bör vanligtvis vara på dokumentstorlek eller där den relaterade informationen är föremål för frekventa uppdateringar som skulle göra upprätthålla inbäddad data svårhanterlig.
Den "inte magiska" delen är att i huvudsak vad som händer under täcket är att när du "refererar" till en annan källa, gör fyllningsfunktionen ytterligare en fråga/frågor till den "relaterade" samlingen för att "sammanfoga" dessa resultat från föräldern objekt som du har hämtat. Du kan göra detta själv, men metoden är till för att förenkla uppgiften. Det uppenbara "prestanda"-övervägandet är att det inte finns en enda tur och retur till databasen (MongoDB-instans) för att hämta all information. Det finns alltid mer än en.
Som ett exempel, ta två samlingar:
{
"_id": ObjectId("5392fea00ff066b7d533a765"),
"customerName": "Bill",
"items": [
ObjectId("5392fee10ff066b7d533a766"),
ObjectId("5392fefe0ff066b7d533a767")
]
}
Och föremålen:
{ "_id": ObjectId("5392fee10ff066b7d533a766"), "prod": "ABC", "qty": 1 }
{ "_id": ObjectId("5392fefe0ff066b7d533a767"), "prod": "XYZ", "qty": 2 }
Det "bästa" som kan göras av en "refererad" modell eller användning av populate (under huven) är detta:
var order = db.orders.findOne({ "_id": ObjectId("5392fea00ff066b7d533a765") });
order.items = db.items.find({ "_id": { "$in": order.items } ).toArray();
Så det finns helt klart "minst" två frågor och operationer för att "sammanfoga" den datan.
Inbäddningskonceptet är i huvudsak MongoDB:s svar på hur man hanterar att inte stödja "joins". Så att istället för att dela upp data i normaliserade samlingar försöker du bädda in den "relaterade" data direkt i dokumentet som använder den. Fördelarna här är att det finns en enda "läs"-operation för att hämta den "relaterade" informationen, och även en enda punkt för "skriv"-operationer för att både uppdatera "förälder" och "barn"-poster, men ofta inte möjligt att skriva till "många" barn samtidigt utan att bearbeta "listor" på klienten eller på annat sätt acceptera "flera" skrivoperationer, och helst i "batch"-bearbetning.
Data ser då snarare ut så här (jämfört med exemplet ovan):
{
"_id": ObjectId("5392fea00ff066b7d533a765"),
"customerName": "Bill",
"items": [
{ "_id": ObjectId("5392fee10ff066b7d533a766"), "prod": "ABC", "qty": 1 },
{ "_id": ObjectId("5392fefe0ff066b7d533a767"), "prod": "XYZ", "qty": 2 }
]
}
Att faktiskt hämta data är därför bara en fråga om:
db.orders.findOne({ "_id": ObjectId("5392fea00ff066b7d533a765") });
För- och nackdelarna med båda beror alltid till stor del på användningsmönstret för din applikation. Men på ett ögonkast:
Inbäddning
-
Den totala dokumentstorleken med inbäddade data överstiger vanligtvis inte 16 MB lagringsutrymme (BSON-gränsen) eller på annat sätt (som en riktlinje) har arrayer som innehåller 500 eller fler poster.
-
Data som är inbäddad kräver i allmänhet inte frekventa ändringar. Så du kan leva med "duplicering" som kommer från avnormaliseringen som inte resulterar i att du behöver uppdatera dessa "dubbletter" med samma information i många överordnade dokument bara för att åberopa en ändring.
-
Relaterad data används ofta i samarbete med föräldern. Vilket betyder att om dina "läs/skriv"-fall i stort sett alltid behöver "läsa/skriva" till både förälder och barn så är det vettigt att bädda in data för atomära operationer.
Referenser
-
Den relaterade data kommer alltid att överskrida 16 MB BSON-gränsen. Du kan alltid överväga en hybrid metod för "bucketing", men den allmänna hårda gränsen för huvuddokumentet kan inte överträdas. Vanliga fall är "inlägg" och "kommentarer" där "kommentar"-aktiviteten förväntas vara mycket stor.
-
Relaterad data behöver regelbundet uppdateras. Eller i huvudsak fallet där du "normaliserar" eftersom den informationen "delas" mellan många föräldrar och de "relaterade" uppgifterna ändras tillräckligt ofta för att det skulle vara opraktiskt att uppdatera inbäddade objekt i varje "förälder" där det "underordnade" objektet förekommer . Det enklaste fallet är att bara referera till "barnet" och göra ändringen en gång.
-
Det finns en tydlig åtskillnad mellan läsning och skrivning. I det fall du kanske inte alltid kommer att kräva den "relaterade" informationen när du läser "föräldern" eller på annat sätt för att inte alltid behöva ändra "föräldern" när du skriver till barnet, kan det finnas goda skäl att separera modellen som refererats. Dessutom om det finns en allmän önskan att uppdatera många "underdokument" samtidigt där dessa "underdokument" faktiskt är referenser till en annan samling, så är implementeringen ofta mer effektiv att göra när data finns i en separat samling.
Så det finns faktiskt en mycket bredare diskussion om "för- och nackdelarna" för båda ståndpunkterna i MongoDB-dokumentationen om datamodellering, som täcker olika användningsfall och sätt att närma sig antingen genom att använda inbäddningsmodeller eller refererade modeller som stöds av fyllmetoden.
Förhoppningsvis är "punktpunkterna" användbara, men den allmänna rekommendationen är att överväga dataanvändningsmönstren för din applikation och välja det som är bäst. Att ha "alternativet" att bädda in "borde" vara anledningen till att du har valt MongoDB, men det kommer faktiskt att vara hur din applikation "använder datan" som bestämmer vilken metod som passar vilken del av din datamodellering (eftersom det inte är det "allt eller inget") bäst.
- Observera att sedan detta ursprungligen skrevs introducerade MongoDB
$lookup
operatör som verkligen utför "joins" mellan samlingar på servern. För den allmänna diskussionen här, säg "bättre" i de flesta fall än "multiple query"-overheaden som uppstår avpopulate()
och "flera frågor" i allmänhet finns det fortfarande en "betydande overhead" uppstår med någon$lookup
operation.Kärndesignprincipen är "inbäddad" betyder "redan där" i motsats till "hämtning från någon annanstans". I huvudsak skillnaden mellan "i fickan" och "på hyllan", och i I/O-termer vanligtvis mer som "på hyllan i biblioteket i centrum" , och särskilt längre bort för nätverksbaserade förfrågningar.