köszönet Julian Motznak, hogy kedvesen segített a cikk szakértői értékelésében.
az egyik legnagyobb különbség az SQL és a NoSQL adatbázisok között a JOIN. A relációs adatbázisokban az SQL JOIN záradék lehetővé teszi két vagy több tábla sorainak kombinálását egy közös mező használatával. Ha például books
és publishers
táblákkal rendelkezik, akkor SQL parancsokat írhat, például:
SELECT book.title, publisher.nameFROM bookLEFT JOIN book.publisher_id ON publisher.id;
más szavakkal, a book
táblának van egy publisher_id
mezője, amely a id
mezőre hivatkozik a publisher
táblázatban.
ez praktikus, mivel egyetlen kiadó több ezer könyvet kínálhat. Ha valaha is frissítenünk kell egy kiadó adatait, egyetlen rekordot megváltoztathatunk. Az adatok redundanciája minimálisra csökken, mivel nem kell minden könyvnél megismételnünk a kiadói információkat. A technikát normalizációnak nevezik.
az SQL adatbázisok számos normalizálási és kényszer funkciót kínálnak a kapcsolatok fenntartásának biztosítására.
NoSQL = = nincs csatlakozás?
nem mindig …
a Dokumentumorientált adatbázisok, mint például a MongoDB, a denormalizált adatok tárolására szolgálnak. Ideális esetben nincs kapcsolat a gyűjtemények között. Ha ugyanazokra az adatokra van szükség két vagy több dokumentumban, meg kell ismételni.
ez frusztráló lehet, mivel kevés olyan helyzet van, ahol soha nincs szükség relációs adatokra. Szerencsére a MongoDB 3.2 bevezet egy új $lookup
operátort, amely két vagy több gyűjteményen képes bal-külső ILLESZTÉSSZERŰ műveletet végrehajtani. De van egy fogás …
MongoDB Összesítés
$lookup
csak összesítési műveletekben engedélyezett. Gondoljon ezekre úgy, mint operátorok csővezetékére, amelyek lekérdezik, szűrik és csoportosítják az eredményt. Az egyik operátor kimenetét a következő bemeneteként használják.
az összesítést nehezebb megérteni, mint az egyszerűbb find
lekérdezéseket, és általában lassabban futnak. Ezek azonban erőteljesek és felbecsülhetetlen lehetőségek a komplex Keresési műveletekhez.
az összesítést legjobban egy példával magyarázhatjuk. Tegyük fel, hogy létrehozunk egy közösségi média platformot egy user
gyűjteménygel. Minden felhasználó adatait külön dokumentumokban tárolja. Például:
{ "_id": ObjectID("45b83bda421238c76f5c1969"), "name": "User One", "email: "[email protected]", "country": "UK", "dob": ISODate("1999-09-13T00:00:00.000Z")}
annyi mezőt adhatunk hozzá, amennyire szükséges, de minden MongoDB dokumentumhoz _id
mező szükséges, amelynek egyedi értéke van. A _id
hasonló egy SQL elsődleges kulcshoz, és szükség esetén automatikusan beillesztésre kerül.
közösségi hálózatunknak most egy post
gyűjteményre van szüksége, amely számos, a felhasználóktól származó, áttekinthető frissítést tárol. A dokumentumok user_id
mezőben tárolják a szöveget, a dátumot, a minősítést és a hivatkozást arra a felhasználóra, aki írta:
{ "_id": ObjectID("17c9812acff9ac0bba018cc1"), "user_id": ObjectID("45b83bda421238c76f5c1969"), "date: ISODate("2016-09-05T03:05:00.123Z"), "text": "My life story so far", "rating": "important"}
most meg akarjuk mutatni az utolsó húsz hozzászólást az összes felhasználó “fontos” minősítésével fordított időrendi sorrendben. Minden visszaküldött dokumentumnak tartalmaznia kell a szöveget, a bejegyzés idejét, valamint a társított felhasználó nevét és országát.
a MongoDB aggregált lekérdezést egy sor csővezeték-operátor adja át, amelyek az egyes műveleteket sorrendben határozzák meg. Először ki kell vonnunk az összes dokumentumot a post
gyűjteményből, amelyek megfelelő minősítéssel rendelkeznek a $match
szűrő segítségével:
{ "$match": { "rating": "important" } }
most a $sort
operátor segítségével fordított dátum sorrendbe kell rendeznünk az egyező elemeket:
{ "$sort": { "date": -1 } }
mivel csak húsz hozzászólásra van szükségünk, alkalmazhatunk egy $limit
stádiumot, így a MongoDB-nek csak a kívánt adatokat kell feldolgoznia:
{ "$limit": 20 }
most a user
gyűjteményből származó adatokat az új $lookup
operátor segítségével csatlakoztathatjuk. Négy paraméterrel rendelkező objektumot igényel:
-
localField
: a beviteli dokumentum keresési mezője -
from
: a gyűjtemény csatlakozni -
foreignField
: afrom
gyűjteményben keresendő mező -
as
: a kimeneti mező neve.
üzemeltetőnk ezért:
{ "$lookup": { "localField": "user_id", "from": "user", "foreignField": "_id", "as": "userinfo"} }
ez egy új mezőt hoz létre a kimenetünkben userinfo
néven. Tartalmaz egy tömböt, ahol minden érték megegyezik a user
dokumentummal:
"userinfo":
a post.user_id
és user._id
között egy-egy kapcsolat van, mivel egy bejegyzésnek csak egy szerzője lehet. Ezért a userinfo
tömbünk csak egy elemet tartalmaz. Használhatjuk a $unwind
operátort, hogy egy aldokumentumba dekonstruáljuk:
{ "$unwind": "$userinfo" }
a kimenet most egy praktikusabb formátumra konvertálódik, amely további operátorokat alkalmazhat:
"userinfo": { "name": "User One", "email: "[email protected]", …}
végül visszaadhatjuk a szöveget, a bejegyzés idejét, a felhasználó nevét és az országot a folyamat $project
szakaszában:
{ "$project": { "text": 1, "date": 1, "userinfo.name": 1, "userinfo.country": 1} }
mindent összerakva
végső összesített lekérdezésünk megegyezik a bejegyzésekkel, rendezi a sorrendet, korlátozza a legutóbbi húsz elemet, összekapcsolja a felhasználói adatokat, ellapítja a felhasználói tömböt, és csak a szükséges mezőket adja vissza. A teljes parancs:
db.post.aggregate();
az eredmény legfeljebb húsz dokumentum gyűjteménye. Például:
nagyszerű! Végre át tudok váltani a NoSQL-re!
a MongoDB $lookup
hasznos és hatékony, de még ez az alapvető példa is komplex aggregált lekérdezést igényel. Ez nem helyettesíti az SQL-ben kínált erősebb csatlakozási záradékot. A MongoDB sem kínál korlátokat; ha egy user
dokumentumot törölnek, az orphan post
dokumentumok megmaradnak.
ideális esetben a $lookup
operátort ritkán kell megkövetelni. Ha nagyon szüksége van rá, akkor valószínűleg rossz adattárat használ…
ha rendelkezik relációs adatokkal, használjon relációs (SQL) adatbázist!
ennek ellenére a $lookup
örvendetes kiegészítés a MongoDB 3.2-hez. Meg tudja oldani néhány frusztráló problémát, ha kis mennyiségű relációs adatot használ egy NoSQL adatbázisban.