sql >> Database teknologi >  >> NoSQL >> MongoDB

Mongoose befolker vs objektnesting

Den første ting at forstå om mongoose population er, at det ikke er magi, men blot en bekvemmelighedsmetode, der giver dig mulighed for at hente relateret information uden at gøre det hele selv.

Konceptet er i det væsentlige til brug, hvor du beslutter, at du skal placere data i en separat samling i stedet for at indlejre disse data, og dine hovedovervejelser bør typisk være på dokumentstørrelsen, eller hvor den relaterede information er genstand for hyppige opdateringer, der ville gøre vedligeholde indlejrede data uhåndterligt.

Den "ikke magiske" del er, at det, der i bund og grund sker under coveret, er, at når du "henviser" til en anden kilde, laver populate-funktionen en yderligere forespørgsel/forespørgsler til den "relaterede" samling for at "flette" disse resultater fra forælderen. objekt, som du har hentet. Du kunne gøre dette selv, men metoden er der for nemheds skyld for at forenkle opgaven. Den åbenlyse "performance"-overvejelse er, at der ikke er en eneste rundrejse til databasen (MongoDB-instans) for at hente al information. Der er altid mere end én.

Som et eksempel, tag to samlinger:

{ 
    "_id": ObjectId("5392fea00ff066b7d533a765"),
    "customerName": "Bill",
    "items": [
        ObjectId("5392fee10ff066b7d533a766"),
        ObjectId("5392fefe0ff066b7d533a767")
    ]
}

Og tingene:

{ "_id": ObjectId("5392fee10ff066b7d533a766"), "prod": "ABC", "qty": 1 }
{ "_id": ObjectId("5392fefe0ff066b7d533a767"), "prod": "XYZ", "qty": 2 }

Det "bedste", der kan gøres med en "refereret" model eller brugen af ​​populate (under motorhjelmen), er dette:

var order = db.orders.findOne({ "_id": ObjectId("5392fea00ff066b7d533a765") });
order.items = db.items.find({ "_id": { "$in": order.items } ).toArray();

Så der er klart "mindst" to forespørgsler og operationer for at "join" disse data.

Indlejringskonceptet er i bund og grund MongoDB-svaret på, hvordan man håndterer ikke at understøtte "joins". Så i stedet for at opdele data i normaliserede samlinger, prøver du at indlejre de "relaterede" data direkte i det dokument, der bruger dem. Fordelene her er, at der er en enkelt "læse"-operation til at hente den "relaterede" information, og også et enkelt punkt med "skrive"-operationer til både at opdatere "forældre" og "barn"-poster, selvom det ofte ikke er muligt at skrive til "mange" børn på én gang uden at behandle "lister" på klienten eller på anden måde acceptere "flere" skriveoperationer, og helst i "batch"-behandling.

Data ser så snarere sådan ud (sammenlignet med eksemplet ovenfor):

{ 
    "_id": ObjectId("5392fea00ff066b7d533a765"),
    "customerName": "Bill",
    "items": [
        { "_id": ObjectId("5392fee10ff066b7d533a766"), "prod": "ABC", "qty": 1 },
        { "_id": ObjectId("5392fefe0ff066b7d533a767"), "prod": "XYZ", "qty": 2 }
    ]
}

Derfor er faktisk hentning af data blot et spørgsmål om:

db.orders.findOne({ "_id": ObjectId("5392fea00ff066b7d533a765") });

Fordele og ulemper ved begge vil altid i høj grad afhænge af brugsmønsteret for din applikation. Men med et blik:

Indlejring

  • Samlet dokumentstørrelse med indlejrede data vil typisk ikke overstige 16 MB lagerplads (BSON-grænsen) eller på anden måde (som en retningslinje) have arrays, der indeholder 500 eller flere poster.

  • Data, der er indlejret, kræver generelt ikke hyppige ændringer. Så du kan leve med "duplikering", der kommer fra denormaliseringen, der ikke resulterer i behovet for at opdatere disse "duplikater" med den samme information på tværs af mange overordnede dokumenter, bare for at påkalde en ændring.

  • Relaterede data bruges ofte i samarbejde med forælderen. Hvilket betyder, at hvis dine "læse/skrive" sager stort set altid skal "læse/skrive" til både forælder og barn, så giver det mening at indlejre dataene til atomoperationer.

Reference

  • De relaterede data vil altid overskride grænsen på 16 MB BSON. Du kan altid overveje en hybrid tilgang til "bucketing", men den generelle hårde grænse for hoveddokumentet kan ikke overtrædes. Almindelige tilfælde er "indlæg" og "kommentarer", hvor "kommentar"-aktiviteten forventes at være meget stor.

  • Relaterede data skal løbende opdateres. Eller i det væsentlige tilfældet, hvor du "normaliserer", fordi disse data "deles" mellem mange forældre, og de "relaterede" data ændres hyppigt nok til, at det ville være upraktisk at opdatere indlejrede elementer i alle "forældre", hvor det "underordnede" element forekommer . Det nemmeste er bare at henvise til "barnet" og foretage ændringen én gang.

  • Der er en klar adskillelse mellem læsning og skrivning. I det tilfælde, hvor du måske ikke altid vil kræve den "relaterede" information, når du læser "forælderen" eller på anden måde ikke altid behøver at ændre "forælderen", når du skriver til barnet, kan der være god grund til at adskille modellen som refereret. Hvis der desuden er et generelt ønske om at opdatere mange "underdokumenter" på én gang, hvor disse "underdokumenter" faktisk er referencer til en anden samling, så er implementeringen ofte mere effektiv at udføre, når dataene er i en separat samling.

Så der er faktisk en meget bredere diskussion af "fordele/ulemper" for begge holdninger til MongoDB-dokumentationen om datamodellering, som dækker forskellige use cases og måder at nærme sig enten ved hjælp af indlejring eller refereret model, som understøttes af populate-metoden.

Forhåbentlig er "punktpunkterne" nyttige, men den generelle anbefaling er at overveje dataforbrugsmønstrene for din applikation og vælge det, der er bedst. At have "muligheden" for at indlejre "burde" være grunden til, at du har valgt MongoDB, men det vil faktisk være, hvordan din applikation "bruger dataene", der træffer beslutningen om, hvilken metode der passer til hvilken del af din datamodellering (da det ikke er "alt eller intet") den bedste.

  1. Bemærk, at siden dette oprindeligt blev skrevet introducerede MongoDB $lookup operatør, som faktisk udfører "joins" mellem samlinger på serveren. Med henblik på den generelle diskussion her, hvis "bedre" i de fleste tilfælde, at den "multiple query"-overhead påløber af populate() og "flere forespørgsler" generelt, er der stadig en "betydelig overhead" opstået med enhver $lookup operation.

Kernedesignprincippet er "indlejret" betyder "allerede der" i modsætning til "hentning et andet sted fra". Grundlæggende er forskellen mellem "i lommen" og "på hylden", og i I/O-termer normalt mere som "på hylden i biblioteket i centrum" , og især længere væk for netværksbaserede anmodninger.




  1. mongoDB:Oprettelse af et objekt-id for hvert nyt barn, der føjes til array-feltet

  2. Sådan implementeres Open edX MongoDB-databasen for høj tilgængelighed

  3. Mongo Change Streams, der kører flere gange (en slags):Node-app, der kører flere forekomster

  4. Indstilling af cache_store i en initializer