met dank aan Julian Motz voor zijn hulp bij peer review van dit artikel.
een van de grootste verschillen tussen SQL-en NoSQL-databases is JOIN. In relationele databases kunt u met de SQL JOIN-clausule rijen van twee of meer tabellen combineren met een gemeenschappelijk veld tussen deze tabellen. Als u bijvoorbeeld tabellen met books
en publishers
hebt, kunt u SQL-opdrachten schrijven zoals:
SELECT book.title, publisher.nameFROM bookLEFT JOIN book.publisher_id ON publisher.id;
met andere woorden, de tabel book
heeft een veld publisher_id
dat verwijst naar het veld id
in de tabel publisher
.
dit is praktisch, aangezien één uitgever duizenden boeken kan aanbieden. Als we ooit de gegevens van een uitgever moeten bijwerken, kunnen we één record wijzigen. Gegevensredundantie wordt geminimaliseerd, omdat we niet de uitgeversinformatie voor elk boek hoeven te herhalen. De techniek staat bekend als normalisatie.
SQL-databases bieden een reeks normalisatie-en beperkingsfuncties om ervoor te zorgen dat relaties worden onderhouden.
NoSQL = = No JOIN?
niet altijd …
Documentgeoriënteerde databases zoals MongoDB zijn ontworpen om gedenormaliseerde gegevens op te slaan. Idealiter zou er geen relatie tussen collecties moeten zijn. Indien dezelfde gegevens in twee of meer documenten worden verlangd, moeten deze worden herhaald.
dit kan frustrerend zijn, omdat er weinig situaties zijn waarin je nooit relationele gegevens nodig hebt. Gelukkig introduceert MongoDB 3.2 een nieuwe $lookup
operator die een links-buitenste-JOIN-achtige bewerking kan uitvoeren op twee of meer collecties. Maar er is een vangst …
MongoDB Aggregatie
$lookup
is alleen toegestaan bij aggregatie. Zie deze als een pijplijn van operators die een resultaat opvragen, filteren en groeperen. De output van de ene operator wordt gebruikt als input voor de volgende.
aggregatie is moeilijker te begrijpen dan eenvoudigere find
queries en zal over het algemeen langzamer lopen. Echter, ze zijn krachtig en een onschatbare optie voor complexe zoekoperaties.
Aggregatie kan het best met een voorbeeld worden uitgelegd. Stel dat we een social media platform maken met een user
collectie. Het slaat de gegevens van elke gebruiker op in afzonderlijke documenten. Bijvoorbeeld::
{ "_id": ObjectID("45b83bda421238c76f5c1969"), "name": "User One", "email: "[email protected]", "country": "UK", "dob": ISODate("1999-09-13T00:00:00.000Z")}
We kunnen zoveel velden toevoegen als nodig is, maar alle MongoDB-documenten vereisen een _id
veld dat een unieke waarde heeft. De _id
is vergelijkbaar met een SQL primaire sleutel, en zal automatisch worden ingevoegd indien nodig.
ons sociale netwerk heeft nu een post
verzameling nodig, die talrijke inzichtelijke updates van gebruikers opslaat. De documenten slaan de tekst, de datum, een waardering en een verwijzing naar de gebruiker op die het in een user_id
– veld heeft geschreven.:
{ "_id": ObjectID("17c9812acff9ac0bba018cc1"), "user_id": ObjectID("45b83bda421238c76f5c1969"), "date: ISODate("2016-09-05T03:05:00.123Z"), "text": "My life story so far", "rating": "important"}
we willen nu de laatste twintig berichten tonen met een” belangrijke ” beoordeling van alle gebruikers in omgekeerde chronologische volgorde. Elk geretourneerd document moet de tekst, het tijdstip van de post en de naam en het land van de geassocieerde gebruiker bevatten.
de MongoDB aggregate query wordt doorgegeven aan een reeks pijpleidingoperators die elke operatie in volgorde definiëren. Eerst moeten we alle documenten uit de post
collectie extraheren die de juiste waardering hebben met behulp van het filter $match
:
{ "$match": { "rating": "important" } }
we moeten nu de overeenkomende items sorteren in omgekeerde datumvolgorde met behulp van de $sort
operator:
{ "$sort": { "date": -1 } }
aangezien we slechts twintig berichten nodig hebben, kunnen we een $limit
fase toepassen, zodat MongoDB alleen gegevens hoeft te verwerken die we willen:
{ "$limit": 20 }
we kunnen nu gegevens samenvoegen uit de user
collectie met behulp van de nieuwe $lookup
operator. Het vereist een object met vier parameters:
-
localField
: het opzoekveld in het invoerdocument -
from
: de collectie om mee te doen -
foreignField
: het op te zoeken veld in de verzamelingfrom
-
as
: de naam van het uitvoerveld.
onze operator is daarom:
{ "$lookup": { "localField": "user_id", "from": "user", "foreignField": "_id", "as": "userinfo"} }
dit zal een nieuw veld aanmaken in onze uitvoer met de naam userinfo
. Het bevat een array waarin elke waarde overeenkomt met het user
document:
"userinfo":
we hebben een één-op-één relatie tussen de post.user_id
en user._id
, omdat een bericht slechts één auteur kan hebben. Daarom zal onze userinfo
array maar één item bevatten. We kunnen de $unwind
operator gebruiken om het te deconstrueren tot een sub-document:
{ "$unwind": "$userinfo" }
De output zal nu worden omgezet naar een meer praktische indeling die andere operatoren toegepast:
"userinfo": { "name": "User One", "email: "[email protected]", …}
tot slot kunnen we de terugkeer van de tekst, de tijd van de post, de naam van de gebruiker en het land met behulp van een $project
fase in de pijplijn:
{ "$project": { "text": 1, "date": 1, "userinfo.name": 1, "userinfo.country": 1} }
Putting Het Allemaal Samen
Onze uiteindelijke totale query overeenkomt met berichten, gesorteerd in volgorde, grenzen aan de laatste twintig items, joins gegevens van de gebruiker, vlakt de gebruiker matrix en geeft benodigde velden alleen. Het volledige commando:
db.post.aggregate();
het resultaat is een verzameling van maximaal twintig documenten. Bijvoorbeeld:
geweldig! Ik kan eindelijk overschakelen naar NoSQL!
MongoDB $lookup
is nuttig en krachtig, maar zelfs dit basisvoorbeeld vereist een complexe geaggregeerde query. Het is geen vervanging voor de meer krachtige JOIN clausule aangeboden in SQL. MongoDB biedt ook geen beperkingen; als een user
document wordt verwijderd, blijven er verweesde post
documenten over.
idealiter zou de $lookup
operator zelden nodig moeten zijn. Als je het nodig hebt veel, je bent mogelijk met behulp van de verkeerde data store …
als je relationele gegevens hebt, gebruik dan een relationele (SQL) database!
dat gezegd hebbende, is $lookup
een welkome aanvulling op MongoDB 3.2. Het kan een aantal van de meer frustrerende problemen te overwinnen bij het gebruik van kleine hoeveelheden relationele gegevens in een NoSQL-database.