De auteur heeft de Open Internet/Free Speech Fund gekozen om een donatie te ontvangen als onderdeel van het Write for Donationsprogramma.
Inleiding
Als je veel ervaring hebt met relatieve databasen, kan het moeilijk zijn om over de principes van de relatieve model te gaan, zoals op basis van tabelen en relaties denken. Databases zoals MongoDB maken het mogelijk om uit de rigiditeit en beperkingen van het relatieve model te breken. Echter, de flexibiliteit en vrijheid die komt met de mogelijkheid om documenten te storpen in de database kunnen leiden tot andere pitfallen en problemen.
Dit conceptueel artikel brengt vijf gebruiken voor schema-ontwerp bij document-georiënteerde databases aan het licht en houdt diverse overwegingen aan over het modelleren van relaties tussen gegevens. Het wil ook meerdere strategieën aanbrengen waarop je moet nadenken wanneer deze strategieën het meest appropriaat zijn om te gebruiken.
Richtlijn 1 — Samen bewaren wat samen benaderd moet worden
In een typische relationele database worden gegevens opgeslagen in tabellen, en elke tabel is opgebouwd met een vaste lijst van kolommen die verschillende attributen vertegenwoordigen die een entiteit, object of gebeurtenis vormen. Bijvoorbeeld, in een tabel die studenten aan een universiteit weergeeft, zou je kolommen kunnen vinden die de voornaam, achternaam, geboortedatum en een uniek identificatienummer van elke student bevatten.
Meestal vertegenwoordigt elke tabel een enkel onderwerp. Als je informatie over de huidige studies, beurzen of voorgaande opleiding van een student wilt opslaan, kan het zinvol zijn om die gegevens in een afzonderlijke tabel te bewaren van die met hun persoonlijke informatie. Je zou deze tabellen dan kunnen verbinden om aan te geven dat er een relatie bestaat tussen de gegevens in elk van hen, wat aangeeft dat de informatie die ze bevatten een betekenisvolle verbinding heeft.
Bijvoorbeeld, een tabel die de beursstatus van elke student beschrijft, kan verwijzen naar studenten via hun student-ID nummer, maar slaat de naam of het adres van de student niet direct op, zodat gegevensduplicatie wordt vermeden. In zo’n geval, om informatie over een willekeurige student te verkrijgen met alle informatie over de sociale media accounts, vorige opleidingen en beurzen van de student, zou een query meer dan één tabel tegelijk moeten benaderen en vervolgens de resultaten van verschillende tabellen samenvoegen tot één.
Deze methode om relaties te beschrijven door middel van verwijzingen staat bekend als een genormaliseerd gegevensmodel. Het opslaan van gegevens op deze manier — met meerdere afzonderlijke, beknopte objecten die aan elkaar gerelateerd zijn — is ook mogelijk in document-georiënteerde databases. De flexibiliteit van het documentmodel en de vrijheid die het biedt om ingebedde documenten en arrays binnen één document op te slaan, betekent echter dat je gegevens anders kunt modelleren dan je misschien in een relationele database zou doen.
Het onderliggende concept voor het modelleren van gegevens in een document-georiënteerde database is om “samen te bewaren wat samen zal worden geraadpleegd.”” Als we dieper ingaan op het studentenvoorbeeld, laten we zeggen dat de meeste studenten op deze school meer dan één e-mailadres hebben. Vanwege dit feit wil de universiteit de mogelijkheid hebben om meerdere e-mailadressen op te slaan bij de contactgegevens van elke student.
In een situatie als deze zou een voorbeelddocument een structuur kunnen hebben zoals de volgende:
Merk op dat dit voorbeelddocument een ingebedde lijst van e-mailadressen bevat.
Een ongenormaliseerde datamodel wordt gekenmerkt door het vertegenwoordigen van meer dan één onderwerp binnen een enkel document. Het maakt het mogelijk voor toepassingen om alle relevante gegevens voor een bepaald object ( hier: een student) te ontgrendelen en te bewerken in één keer, zonder nodig om meerdere afzonderlijke objecten en collecties te raadplegen. Door zo te handelen wordt ook de atoomkerkheid van bewerkingen op zo’n document gegarandeerd zonder gebruik te maken van multi-documenttransacties om integriteit te waarborgen.
Het samenstellen van gegevens die samen moeten worden aangevraagd met behulp van ingebedde documenten is vaak de optimale manier om gegevens te representeren in een documentgeoriënteerde database. In de volgende richtlijnen zult u leren hoe verschillende relaties tussen objecten, zoals een-naar-één of een-naar-veel relaties, het beste kunnen worden gemodelleerd in een documentgeoriënteerde database.
Richtlijn 2 — Modelleren van Een-naar-één Relaties met Ingebedde Documenten
Een een-naar-één relatie stelt een associatie tussen twee verschillende objecten voor waar één object is verbonden met exact één ander type object.
Verdergaand met het studentenvoorbeeld uit de vorige sectie, heeft elke student op elk moment maar één geldige studentenkaart. Eén kaart behoort nooit aan meerdere studenten toe, en geen enkele student kan meerdere identificatiekaarten hebben. Als je al deze gegevens zou opslaan in een relationele database, zou het waarschijnlijk verstandig zijn om de relatie tussen studenten en hun ID-kaarten te modelleren door de studentenregistraties en de ID-kaartregistraties in afzonderlijke tabellen op te slaan die aan elkaar gekoppeld zijn via referenties.
Een veelgebruikte methode om dergelijke relaties in een documentdatabase weer te geven, is door ingebedde documenten te gebruiken. Als voorbeeld beschrijft het volgende document een student genaamd Sammy en hun studentenkaart:
Merk op dat in plaats van een enkele waarde, het veld id_card
van dit voorbeelddocument een ingebed document bevat dat de studentenidentificatiekaart beschrijft, gekenmerkt door een ID-nummer, de uitgiftedatum van de kaart en de vervaldatum van de kaart. De identiteitskaart wordt in wezen onderdeel van het document dat de student Sammy beschrijft, ook al is het een afzonderlijk object in het echte leven. Meestal is het een goede keuze om het documentenschema op deze manier in te delen, zodat je alle gerelateerde informatie via één query kunt ophalen.
Zaken worden minder eenvoudig als je relaties tegenkomt die één object van een bepaald type verbinden met veel objecten van een ander type, zoals de e-mailadressen van een student, de cursussen die ze volgen of de berichten die ze plaatsen op het berichtenbord van de studentenraad. In de volgende richtlijnen gebruik je deze gegevensvoorbeelden om verschillende benaderingen te leren voor het werken met één-op-veel en veel-op-veel relaties.
Richtlijn 3 — Een-op-enige relaties modelleren met ingebedde documenten
Wanneer een object van het ene type gerelateerd is aan meerdere objecten van een ander type, kan dit worden omschreven als een een-op-veel relatie. Een student kan meerdere e-mailadressen hebben, een auto kan vele onderdelen hebben of een bestelling in een winkel kan uit meerdere artikelen bestaan. Elk van deze voorbeelden vertegenwoordigt een een-op-veel relatie.
Hoewel de meest gebruikelijke manier om een een-op-een relatie in een documentdatabase weer te geven via een ingebed document is, zijn er verschillende manieren om een-op-veel relaties in een documentschema te modelleren. Bij het overwegen van uw opties voor de beste manier om deze te modelleren, moet u echter drie eigenschappen van de gegeven relatie in overweging nemen:
- Kardinaliteit: Kardinaliteit is de maatstaf voor het aantal individuele elementen in een bepaalde verzameling. Als een klas bijvoorbeeld 30 studenten heeft, zou je kunnen zeggen dat die klas een kardinaliteit van 30 heeft. In een een-op-veel relatie kan de kardinaliteit in elk geval verschillen. Een student kan één e-mailadres hebben of meerdere. Ze kunnen ingeschreven zijn voor slechts enkele klassen of ze kunnen een volledig schema hebben. In een een-op-veel relatie zal de omvang van “veel” beïnvloeden hoe u de gegevens mogelijk modelleert.
- Onafhankelijke toegang: Sommige gerelateerde gegevens worden zelden, zo niet nooit, afzonderlijk van het hoofdobject benaderd. Het is bijvoorbeeld ongebruikelijk om het e-mailadres van één student op te vragen zonder andere studentgegevens. Aan de andere kant kunnen de cursussen van een universiteit afzonderlijk moeten worden benaderd en bijgewerkt, ongeacht de student of studenten die er zijn ingeschreven. Of je ooit een gerelateerd document alleen zult benaderen, zal ook beïnvloeden hoe je de gegevens kunt modelleren.
- Of de relatie tussen gegevens strikt een één-op-veel-relatie is: Overweeg de cursussen die een voorbeeldstudent aan een universiteit volgt. Vanuit het perspectief van de student kunnen ze aan meerdere cursussen deelnemen. Aan de oppervlakte lijkt dit misschien een één-op-veel-relatie. Echter, universitaire cursussen worden zelden door één student gevolgd; meestal zullen meerdere studenten dezelfde cursus volgen. In dergelijke gevallen is de betreffende relatie niet echt een één-op-veel-relatie, maar een veel-op-veel-relatie, en daarom zou je een andere aanpak nemen om deze relatie te modelleren dan bij een één-op-veel-relatie.
Stel je voor dat je besluit hoe je student-e-mailadressen wilt opslaan. Elke student kan meerdere e-mailadressen hebben, zoals een voor werk, een voor privégebruik en een geleverd door de universiteit. Een document dat één e-mailadres voorstelt, kan er zo uitzien:
Wat betreft kardinaliteit, zullen er maar enkele e-mailadressen zijn voor elke student, aangezien het onwaarschijnlijk is dat een student tientallen — laat staan honderden — e-mailadressen heeft. Daarom kan deze relatie worden gekenmerkt als een één-op-enkele relatie, wat een overtuigend argument is om e-mailadressen direct in het studentendocument in te sluiten en samen op te slaan. Je loopt geen risico dat de lijst met e-mailadressen oneindig zal groeien, wat het document groot en inefficiënt zou maken voor gebruik.
Opmerking: Wees ervan bewust dat er bepaalde valkuilen zijn verbonden aan het opslaan van gegevens in arrays. Bijvoorbeeld, een enkel MongoDB-document kan niet groter zijn dan 16MB. Hoewel het mogelijk en gebruikelijk is om meerdere documenten in te sluiten met arrayvelden, als de lijst met objecten onbeheersbaar groeit, zou het document snel deze groottelimiet kunnen bereiken. Bovendien heeft het opslaan van een grote hoeveelheid gegevens in ingebedde arrays een groot effect op de queryprestaties.
Het insluiten van meerdere documenten in een arrayveld zal waarschijnlijk geschikt zijn in veel situaties, maar weet dat het ook niet altijd de beste oplossing hoeft te zijn.
Met betrekking tot onafhankelijk toegang, zullen e-mailadressen waarschijnlijk niet afzonderlijk worden benaderd van de student. Daarom is er geen duidelijke reden om ze op te slaan als afzonderlijke documenten in een afzonderlijke collectie. Dit is nog een overtuigend argument om ze in te sluiten in het document van de student.
Het laatste dat je moet overwegen is of deze relatie echt een een-naar-meerdere relatie is in plaats van een meerdere-naar-meerdere relatie. Omdat een e-mailadres toebehoort aan één enkele persoon, lijkt het redelijk om deze relatie te beschrijven als een een-naar-meerdere relatie (of misschien nog nauwkeuriger, een een-naar-few relatie) in plaats van een meerdere-naar-meerdere relatie.
Deze drie aannames suggereren dat het embedden van de verschillende e-mailadressen van studenten binnen dezelfde documenten die de studenten zelf beschrijven een goede keuze zou zijn voor het opslaan van dit soort gegevens. Een voorbeelddocument van een student met ingebedde e-mailadressen zou er zo uit kunnen zien:
Door deze structuur te gebruiken, haal je elk keer dat je een document van een student ophaalt, ook de ingebedde e-mailadressen op in dezelfde leesoperatie.
Als je een relatie van het een-naar-few type modelleren, waarbij de gerelateerde documenten niet apart moeten worden bereikt, is het direct embedden van documenten zoals dit meestal wenselijk, aangezien dit de complexiteit van het schema kan verminderen.
Zoals eerder genoemd, is het embedden van documenten op deze manier echter niet altijd de optimale oplossing. De volgende sectie biedt meer details over waarom dit in sommige scenario’s mogelijk het geval is, en schetst hoe je kindverwijzingen kunt gebruiken als een alternatieve manier om relaties in een documentdatabase weer te geven.
Guideline 4 — Modeling Een-naar-veel en veel-naar-veel relaties met kindreferenties
De natuur van de relatie tussen studenten en hun e-mailadressen informeerde hoe die relatie het beste kunnen worden gemodeld in een documentdatabase. Er zijn wat betekent bij deze relatie tussen studenten en de cursussen waar ze aan deelnemen, dus de manier waarop je de relaties tussen studenten en hun cursussen moet modelleren zal ook verschillend zijn.
Een document dat beschrijft dat een student aan een enkele cursus deelt kan volgens de structuur zijn:
Say that you decided from the outset to use embedded documents to store information about each student’s courses, as in this example:
Dit zou een perfecte valide MongoDB document zijn en zou goed dienen voor de doeleinden, maar raak aan de drie relatie eigenschappen die je leerde in de vorige richtlijn.
De eerste is cardinaliteit. Een student zal waarschijnlijk slechts enkele e-mailadressen hebben, maar ze kunnen meer dan een cursus aanbrengen gedurende hun studie. Na meer dan vijf jaar aanwezigheid kunnen er dozens van cursussen zijn waar de student deelneemt. Daarnaast zullen ze de cursussen aanbrengen met veel andere studenten die hun eigen set cursussen aanbrengen over hun jaren aanwezigheid.
Als je ervoor had gekozen om elke cursus als in het vorige voorbeeld in te bedden, zou het document van de student snel onhandelbaar worden. Bij een hogere kardinaliteit wordt de benadering van ingebedde documenten minder aantrekkelijk.
De tweede overweging is onafhankelijke toegang. In tegenstelling tot e-mailadressen, is het verstandig aan te nemen dat er gevallen zouden zijn waarin informatie over universitaire cursussen afzonderlijk moet worden opgehaald. Stel bijvoorbeeld dat iemand informatie nodig heeft over beschikbare cursussen om een marketingfolder voor te bereiden. Bovendien zullen cursussen waarschijnlijk na verloop van tijd moeten worden bijgewerkt: de professor die de cursus geeft kan veranderen, het schema kan fluctueren of de voorwaarden moeten worden bijgewerkt.
Als je de cursussen zou opslaan als documenten die zijn ingebed in studentendocumenten, zou het ophalen van de lijst met alle cursussen die door de universiteit worden aangeboden problematisch worden. Ook zou elke keer dat een cursus een update nodig heeft, je door alle studentendossiers heen moeten gaan en de cursusinformatie overal moeten bijwerken. Beide zijn goede redenen om cursussen afzonderlijk op te slaan en niet volledig in te bedden.
Het derde te overwegen punt is of de relatie tussen een student en een universitaire cursus eigenlijk een-op-veel of veel-op-veel is. In dit geval is het de laatste, aangezien meer dan één student elke cursus kan volgen. De kardinaliteit en aspecten van onafhankelijke toegang van deze relatie pleiten tegen het inbedden van elk cursushandleiding, voornamelijk om praktische redenen zoals gemak van toegang en update. Gezien de veel-op-veel aard van de relatie tussen cursussen en studenten, kan het zinvol zijn om cursushandleidingen op te slaan in een afzonderlijke collectie met eigen unieke identificaties.
De documenten die klassen vertegenwoordigen in deze aparte collectie kunnen een structuur hebben zoals deze voorbeelden:
Als je besluit cursusinformatie op deze manier op te slaan, moet je een manier vinden om studenten met deze cursussen te verbinden, zodat je weet welke studenten welke cursussen volgen. In gevallen zoals dit, waarbij het aantal gerelateerde objecten niet buitensporig groot is, vooral met veel-op-veel-relaties, is een veelgebruikte methode hiervoor het gebruik van kindreferenties.
Met kindreferenties verwijst een studentdocument naar de objectidentifiers van de cursussen die de student volgt in een ingebedde array, zoals in dit voorbeeld:
Merk op dat dit voorbeelddocument nog steeds een courses
veld heeft dat ook een array is, maar in plaats van volledige cursussen documenten in te bedden zoals in het eerdere voorbeeld, worden alleen de identifiers die verwijzen naar de cursussen documenten in de aparte collectie ingebed. Nu, wanneer een studentdocument wordt opgehaald, zijn de cursussen niet onmiddellijk beschikbaar en moeten ze apart worden opgevraagd. Aan de andere kant, is het onmiddellijk bekend welke cursussen moeten worden opgehaald. Ook, als de details van een cursus moeten worden bijgewerkt, hoeft alleen het cursussen document zelf te worden aangepast. Alle referenties tussen studenten en hun cursussen blijven geldig.
Opmerking: Er is geen vaste regel voor wanneer de kardinaliteit van een relatie te groot is om kindreferenties op deze manier in te bedden. Je kiest misschien een andere aanpak bij een lagere of hogere kardinaliteit als dat het beste past bij de toepassing in kwestie. Uiteindelijk wil je altijd je gegevens structureren om te passen bij de manier waarop je toepassing ze opvraagt en bijwerkt.
Als je een een-op-veel relatie modelleert waarbij het aantal gerelateerde documenten binnen redelijke grenzen ligt en de gerelateerde documenten onafhankelijk moeten worden benaderd, bevoordelig het opslaan van de gerelateerde documenten apart en gebruik kindreferenties om er verbinding mee te maken.
Nu je hebt geleerd hoe je kindreferenties kunt gebruiken om relaties tussen verschillende soorten data aan te geven, zal deze gids een omgekeerd concept bespreken: ouderreferenties.
Richtlijn 5 — Modellering van Onbeperkte Een-op-veel Relaties met Ouderreferenties
Het gebruik van kindreferenties werkt goed wanneer er te veel gerelateerde objecten zijn om ze direct in het ouderdocument in te sluiten, maar het aantal ligt nog steeds binnen bekende grenzen. Er zijn echter gevallen waarin het aantal bijbehorende documenten onbeperkt kan zijn en in de loop van de tijd zal blijven groeien.
Als voorbeeld, stel je voor dat de studentenraad van de universiteit een berichtenbord heeft waar elke student wat voor berichten dan ook kan plaatsen, inclusief vragen over cursussen, reistales, vacatures, studiemateriaal of gewoon een vrijblijvend gesprek. Een voorbeeldbericht in dit geval bestaat uit een onderwerp en een berichttekst:
Je zou beide eerder besproken methoden — insluiten en kindreferenties — kunnen gebruiken om deze relatie te modelleren. Als je zou kiezen voor insluiten, zou het document van de student er misschien zo uit kunnen zien:
Echter, als een student veel berichten schrijft, zal hun document snel ontzettend lang worden en kan gemakkelijk de limiet van 16MB overschrijden, dus de kardinaliteit van deze relatie suggereert tegen embedden. Bovendien kunnen de berichten afzonderlijk van de student moeten worden benaderd, zoals het geval zou kunnen zijn als de berichtenpagina is ontworpen om de laatste berichten weer te geven die door studenten zijn geplaatst. Dit suggereert ook dat embedden niet de beste keuze is voor dit scenario.
Opmerking: U moet ook overwegen of de berichten op het berichtenbord vaak worden benaderd bij het ophalen van het document van de student. Zo niet, dan zou het ze allemaal inbedden in dat document een prestatiebeperking opleveren bij het ophalen en manipuleren van dit document, zelfs wanneer de lijst met berichten niet vaak zou worden gebruikt. Minder frequente toegang tot gerelateerde gegevens is vaak een andere aanwijzing dat u documenten niet zou moeten embedden.
Bekijk nu het gebruik van kindreferenties in plaats van volledige documenten in te bedden zoals in het vorige voorbeeld. De afzonderlijke berichten zouden in een afzonderlijke collectie worden opgeslagen en het document van de student zou dan de volgende structuur kunnen hebben:
In dit voorbeeld slaat het veld message_board_messages
nu de kindreferenties op naar alle berichten die door Sammy zijn geschreven. Echter, het wijzigen van de aanpak lost slechts één van de eerder genoemde problemen op, omdat het nu mogelijk is om de berichten onafhankelijk te benaderen. Hoewel het document van de student langzamer zou groeien met behulp van de kindreferenties, zou de verzameling objectidentificatoren ook onhandelbaar kunnen worden gezien de onbeperkte kardinaliteit van deze relatie. Een student kan immers gemakkelijk duizenden berichten schrijven tijdens hun vierjarige studie.
In dergelijke scenario’s is een veelgebruikte manier om één object met een ander te verbinden via ouderreferenties. In tegenstelling tot de eerder beschreven kindreferenties, verwijst het nu niet langer het studentendocument naar individuele berichten, maar een referentie in het berichtdocument die naar de student wijst die het bericht schreef.
Om ouderreferenties te gebruiken, moet u het berichtdocumentschema wijzigen om een referentie te bevatten naar de student die het bericht heeft geschreven:
Merk de nieuwe posted_by
veld op dat de objectidentificatie van het studentendocument bevat. Nu zal het studentendocument geen informatie bevatten over de berichten die ze hebben geplaatst:
Om de lijst met berichten op te halen die door een student zijn geschreven, zou u een query uitvoeren op de berichtenverzameling en filteren op basis van het posted_by
veld. Het in een aparte verzameling hebben, maakt het veilig om de lijst met berichten te laten groeien zonder dat dit invloed heeft op de documenten van de student.
Opmerking: Bij het gebruik van ouderreferenties kan het aanmaken van een index op het veld dat naar het ouderdocument verwijst de queryprestaties aanzienlijk verbeteren telkens wanneer u filtert op basis van de identificatie van het ouderdocument.
Als u een een-op-veel-relatie modelleert waarbij het aantal gerelateerde documenten onbeperkt is, ongeacht of de documenten onafhankelijk moeten worden benaderd, wordt over het algemeen aangeraden gerelateerde documenten afzonderlijk op te slaan en ouderreferenties te gebruiken om ze te verbinden met het ouderdocument.
Conclusie
Dankzij de flexibiliteit van document-georiënteerde databases is het bepalen van de beste manier om relaties in een documentdatabase te modelleren minder een strikte wetenschap dan in een relationele database. Door dit artikel te lezen, bent u bekend geraakt met het insluiten van documenten en het gebruik van kind- en ouderreferenties om gerelateerde gegevens op te slaan. U hebt geleerd over het overwegen van de kardinaliteit van de relatie en het vermijden van onbeperkte arrays, evenals rekening te houden met of het document afzonderlijk of frequent wordt benaderd.
Dit zijn slechts enkele richtlijnen die u kunnen helpen met het modelleren van typische relaties in MongoDB, maar het modelleren van databasschema’s is niet iets dat op elke situatie van toepassing is. Neem altijd in overweging uw applicatie en hoe deze de gegevens gebruikt en bijwerkt bij het ontwerpen van het schema.
Om meer te weten te komen over schemadesign en veelvoorkomende patronen voor het opslaan van verschillende soorten gegevens in MongoDB, raden we je aan de officiële MongoDB documentatie over dat onderwerp te bekijken.
Source:
https://www.digitalocean.com/community/tutorials/how-to-design-a-document-schema-in-mongodb