tack till Julian Motz för att du vänligt hjälpte till att granska den här artikeln.
en av de största skillnaderna mellan SQL-och NoSQL-databaser är JOIN. I relationsdatabaser låter SQL JOIN-klausulen dig kombinera rader från två eller flera tabeller med ett gemensamt fält mellan dem. Om du till exempel har tabeller med books
och publishers
kan du skriva SQL-kommandon som:
SELECT book.title, publisher.nameFROM bookLEFT JOIN book.publisher_id ON publisher.id;
med andra ord har tabellen book
ett fält publisher_id
som refererar till fältet id
i tabellen publisher
.
detta är praktiskt, eftersom en enda utgivare kan erbjuda tusentals böcker. Om vi någonsin behöver uppdatera en utgivares detaljer kan vi ändra en enda post. Dataredundans minimeras, eftersom vi inte behöver upprepa utgivarinformationen för varje bok. Tekniken är känd som normalisering.
SQL-databaser erbjuder en rad normaliserings-och begränsningsfunktioner för att säkerställa att relationer upprätthålls.
NoSQL = = ingen anslutning?
inte alltid …
dokumentorienterade databaser som MongoDB är utformade för att lagra denormaliserad data. Helst bör det inte finnas något samband mellan samlingar. Om samma data krävs i två eller flera dokument måste den upprepas.
detta kan vara frustrerande, eftersom det finns få situationer där du aldrig behöver relationsdata. Lyckligtvis introducerar MongoDB 3.2 en ny $lookup
-operatör som kan utföra en vänster-yttre-KOPPLINGSLIKNANDE operation på två eller flera samlingar. Men det finns en fångst …
MongoDB Aggregation
$lookup
är endast tillåtet i aggregeringsoperationer. Tänk på dessa som en pipeline av operatörer som frågar, filtrerar och grupperar ett resultat. Utgången från en operatör används som ingång för nästa.
aggregering är svårare att förstå än enklare find
frågor och kommer i allmänhet att köra långsammare. De är dock kraftfulla och ett ovärderligt alternativ för komplexa sökoperationer.
aggregering förklaras bäst med ett exempel. Antag att vi skapar en social medieplattform med en user
samling. Den lagrar varje användares detaljer i separata dokument. Till exempel:
{ "_id": ObjectID("45b83bda421238c76f5c1969"), "name": "User One", "email: "[email protected]", "country": "UK", "dob": ISODate("1999-09-13T00:00:00.000Z")}
vi kan lägga till så många fält som behövs, men alla MongoDB-dokument kräver ett _id
– fält som har ett unikt värde. _id
liknar en SQL-primärnyckel och infogas automatiskt om det behövs.
vårt sociala nätverk kräver nu en post
samling, som lagrar många insiktsfulla uppdateringar från användare. Dokumenten lagrar text, datum, ett betyg och en referens till användaren som skrev det i ett user_id
– fält:
{ "_id": ObjectID("17c9812acff9ac0bba018cc1"), "user_id": ObjectID("45b83bda421238c76f5c1969"), "date: ISODate("2016-09-05T03:05:00.123Z"), "text": "My life story so far", "rating": "important"}
vi vill nu visa de senaste tjugo inläggen med ett ”viktigt” betyg från alla användare i omvänd kronologisk ordning. Varje returnerat dokument ska innehålla texten, tidpunkten för inlägget och tillhörande användares namn och land.
MongoDB-aggregatfrågan skickas en rad pipelineoperatörer som definierar varje operation i ordning. Först måste vi extrahera alla dokument från samlingen post
som har rätt betyg med hjälp av filtret $match
:
{ "$match": { "rating": "important" } }
vi måste nu sortera de matchade objekten i omvänd datumordning med operatören $sort
:
{ "$sort": { "date": -1 } }
eftersom vi bara behöver tjugo inlägg kan vi tillämpa ett $limit
– steg så MongoDB behöver bara bearbeta data vi vill ha:
{ "$limit": 20 }
vi kan nu ansluta data från user
– samlingen med den nya $lookup
– operatören. Det kräver ett objekt med fyra parametrar:
-
localField
: sökfältet i inmatningsdokumentet -
from
: samlingen att gå med -
foreignField
: fältet att slå upp ifrom
samlingen -
as
: namnet på utmatningsfältet.
vår operatör är därför:
{ "$lookup": { "localField": "user_id", "from": "user", "foreignField": "_id", "as": "userinfo"} }
detta skapar ett nytt fält i vår produktion med namnet userinfo
. Den innehåller en array där varje värde matchar dokumentet user
:
"userinfo":
vi har ett en-till-en-förhållande mellan post.user_id
och user._id
, eftersom ett inlägg bara kan ha en författare. Därför kommer vår userinfo
array bara att innehålla ett objekt. Vi kan använda operatören $unwind
för att dekonstruera den till ett underdokument:
{ "$unwind": "$userinfo" }
utgången kommer nu att konverteras till ett mer praktiskt format som kan få ytterligare operatörer tillämpade:
"userinfo": { "name": "User One", "email: "[email protected]", …}
slutligen kan vi returnera texten, tidpunkten för inlägget, användarens namn och land med ett $project
– steg i rörledningen:
{ "$project": { "text": 1, "date": 1, "userinfo.name": 1, "userinfo.country": 1} }
att sätta ihop allt
vår slutliga aggregerade fråga matchar inlägg, sorterar i ordning, begränsar till de senaste tjugo objekten, går med i användardata, plattar användarmatrisen och returnerar endast nödvändiga fält. Det fullständiga kommandot:
db.post.aggregate();
resultatet är en samling av upp till tjugo dokument. Exempelvis:
bra! Jag kan äntligen byta till NoSQL!
MongoDB $lookup
är användbar och kraftfull, men även detta grundläggande exempel kräver en komplex aggregerad fråga. Det är inte en ersättning för den mer kraftfulla JOIN-klausulen som erbjuds i SQL. MongoDB erbjuder inte heller begränsningar; om ett user
– dokument raderas skulle föräldralösa post
– dokument förbli.
helst bör $lookup
– operatören krävas sällan. Om du behöver det mycket använder du eventuellt fel datalager …
om du har relationsdata, använd en relationsdatabas (SQL)!
som sagt, $lookup
är ett välkommet tillskott till MongoDB 3.2. Det kan övervinna några av de mer frustrerande problemen när man använder små mängder relationsdata i en NoSQL-databas.