MongoDB je oblíbená NoSQL databáze, která organizuje data do sbírek. Tyto sbírky sestávají z jednotlivých dokumentů, jež obsahují data ve formátu JSON. Dokumenty jsou obdobou řádků v tradičních relačních SQL databázích, zatímco kolekce se podobají tabulkám.
Jednou z klíčových funkcí databází je schopnost provádět dotazy na uložená data. Dotazování umožňuje vyhledávání specifických informací, provádění datové analýzy, vytváření reportů a integraci dat do různých systémů.
Pro efektivní dotazování je důležité umět spojit data z více tabulek v SQL databázích, nebo z více kolekcí v NoSQL databázích, do jedné výsledné sady.
V MongoDB, operátor $lookup
umožňuje kombinovat informace z dvou různých kolekcí při dotazování. Tato operace provádí ekvivalent levého vnějšího spojení, jak je známé z SQL databází.
Využití a účel operátoru $lookup
Klíčovou funkcí databází je schopnost zpracovávat surová data a transformovat je na užitečné informace.
Představte si například, že vlastníte restauraci. Potřebujete analyzovat data o tržbách, abyste zjistili, kolik vyděláte denně, které pokrmy jsou populární o víkendech, nebo třeba kolik šálků kávy prodáte za hodinu.
Pro tyto potřeby nepostačí jednoduché databázové dotazy. Musíte provádět sofistikovanější dotazy na data, která máte uložená. MongoDB proto nabízí nástroj zvaný agregační kanál.
Agregační kanál představuje systém operací, neboli fází, které lze skládat za sebou a provádět komplexní datové transformace za účelem získání finálního souhrnu. Mezi příklady fází agregačního kanálu patří $sort
, $match
, $group
, $merge
, $count
a právě $lookup
.
Tyto fáze se dají skládat v libovolném pořadí v rámci agregačního kanálu. V každé fázi jsou data, která kanálem procházejí, upravena specifickým způsobem.
Operátor $lookup
je tedy jednou z fází agregačního kanálu v MongoDB. Slouží k provedení levého vnějšího spojení mezi dvěma kolekcemi v databázi. Levé vnější spojení kombinuje všechny dokumenty z „levé“ kolekce s odpovídajícími dokumenty z „pravé“ kolekce.
Podívejme se na následující dvě kolekce, které jsou pro přehlednost zobrazeny v tabulkové formě:
objednávky_výběr
:
order_id |
customer_id |
order_date |
total_amount |
1 | 100 | 2022-05-01 | 50.00 |
2 | 101 | 2022-05-02 | 75.00 |
3 | 102 | 2022-05-03 | 100.00 |
Zákazníci_kolekce
:
customer_num |
customer_name |
customer_email |
customer_phone |
100 | John | [email protected] | [email protected] |
Pokud provedeme levé vnější spojení těchto dvou kolekcí pomocí pole customer_id
z kolekce order_collection
(která je „levou“ kolekcí) a pole customer_num
z kolekce customers_collection
(která je „pravou“ kolekcí), výsledkem bude sada obsahující všechny dokumenty z kolekce objednávek a ty dokumenty z kolekce zákazníků, které mají customer_num
shodující se s customer_id
některého záznamu v kolekci objednávek.
Finální výsledek operace levého vnějšího spojení na kolekcích objednávek a zákazníků vypadá takto:
order_id |
customer_id |
order_date |
total_amount |
customer_num |
customer_name |
customer_email |
customer_phone |
1 | 100 | 2022-05-01 | 50.00 | 100 | John | [email protected] | [email protected] |
2 | 101 | 2022-05-02 | 75.00 | null | null | null | null |
3 | 102 | 2022-05-03 | 100.00 | null | null | null | null |
Všimněte si, že pro zákazníka s customer_id
101 z kolekce objednávek, který neměl odpovídající customer_num
v kolekci zákazníků, jsou chybějící hodnoty z tabulky zákazníků nahrazeny hodnotou null
.
Operátor $lookup
provádí porovnání na základě rovnosti mezi zadanými poli a načítá celý dokument, který se shoduje, nikoliv jen pole, která se shodují.
Syntaxe operátoru $lookup
Syntaxe operátoru $lookup
je následující:
{ $lookup: { from: <kolekce ke spojení>, localField: <pole z vstupních dokumentů>, foreignField: <pole z dokumentů kolekce "from">, as: <výstupní pole pole> } }
Operátor $lookup
má čtyři parametry:
from
– určuje kolekci, ze které chceme vyhledávat dokumenty. V našem předchozím příkladu s kolekcemiorders_collection
acustomers_collection
bychom jako hodnotufrom
zadalicustomers_collection
.localField
– je pole v aktuální, tedy „primární“ kolekci, které používáme k porovnávání s poli v „cizí“ kolekci (v našem případěcustomer_collection
). V uvedeném příkladu bylocalField
bylocustomer_id
z kolekceorders_collection
.foreignField
– je pole v „cizí“ kolekci, se kterým chceme provést porovnání. V našem příkladu by to bylo polecustomer_num
z kolekcecustomer_collection
, kterou jsme specifikovali v parametrufrom
.as
– je nový název pole, které se objeví ve výstupním dokumentu a bude obsahovat pole dokumentů odpovídajících shodám mezilocalField
aforeignField
. Pokud nejsou žádné shody, bude toto pole obsahovat prázdné pole.
Na základě našich dvou předchozích kolekcí bychom použili následující kód pro provedení operace $lookup
, přičemž orders_collection
je naše pracovní nebo primární kolekce:
{ $lookup: { from: "customers_collection", localField: "customer_id", foreignField: "customer_num", as: "customer_info" } }
Název pole as
může být libovolná řetězcová hodnota. Nicméně, pokud zvolíte název, který již v pracovním dokumentu existuje, bude toto pole přepsáno.
Spojování dat z více kolekcí
V MongoDB je $lookup
velmi užitečná fáze agregačního kanálu. Ačkoli použití $lookup
není v agregačním kanálu nutné, je tato fáze klíčová pro provádění složitějších dotazů, které vyžadují spojování dat z několika kolekcí.
Fáze $lookup
provádí levé vnější spojení dvou kolekcí, což má za následek vytvoření nového pole (nebo přepsání stávajícího) obsahujícího pole dokumentů z jiné kolekce.
Tyto dokumenty jsou vybírány na základě shody hodnot mezi poli, která se porovnávají. Výsledkem je pole obsahující pole dokumentů v případě nalezení shod, nebo prázdné pole v případě, že shody nalezeny nejsou.
Představte si kolekce zaměstnanců a projektů:
Pro spojení těchto dvou kolekcí můžeme použít následující kód:
db.projects.aggregate([ { $lookup: { from: "employees", localField: "employees", foreignField: "_id", as: "assigned_employees" } } ])
Výsledkem této operace je kombinace obou kolekcí. Ke každému projektu jsou přidruženi všichni zaměstnanci, kteří jsou mu přiřazeni. Tito zaměstnanci jsou uvedeni v poli.
Fáze kanálu, které lze použít společně s $lookup
Jak již bylo zmíněno, $lookup
je fází agregačního kanálu v MongoDB a lze ji používat společně s dalšími fázemi. Abychom ukázali, jak lze tyto fáze kombinovat, použijeme pro ilustraci následující dvě kolekce.
V MongoDB jsou uloženy ve formátu JSON. Takto vypadají výše uvedené kolekce v MongoDB:
Mezi příklady fází agregačního kanálu, které lze použít společně s $lookup
patří:
$match
$match
je fáze agregačního kanálu, která se používá k filtrování dokumentů tak, aby do další fáze kanálu mohly postoupit pouze dokumenty, které splňují danou podmínku. Nejvhodnější je používat tuto fázi na začátku procesu, aby se vyloučily dokumenty, které nebudou potřeba, a tím se optimalizoval agregační kanál.
S pomocí dvou předchozích kolekcí můžeme kombinovat $match
a $lookup
následujícím způsobem:
db.users.aggregate([ { $match: { country: "USA" } }, { $lookup: { from: "orders", localField: "_id", foreignField: "user_id", as: "orders" } } ])
$match
se používá k filtrování uživatelů z USA. Výsledek z $match
se pak kombinuje s $lookup
pro získání podrobností o objednávkách těchto uživatelů. Výsledek této operace je uveden níže:
$project
$project
je fáze používaná k transformaci dokumentů určením, která pole se mají zahrnout, vyloučit nebo přidat do dokumentů. Například, pokud pracujete s dokumenty, které obsahují deset polí, ale pouze čtyři pole obsahují relevantní data, můžete pomocí $project
odfiltrovat nepotřebná pole.
To vám umožní odesílat do další fáze kanálu pouze potřebná data.
Můžeme kombinovat $lookup
a $project
následujícím způsobem:
db.users.aggregate([ { $lookup: { from: "orders", localField: "_id", foreignField: "user_id", as: "orders" } }, { $project: { name: 1, _id: 0, total_spent: { $sum: "$orders.price" } } } ])
Výše uvedený kód kombinuje kolekce uživatelů a objednávek pomocí $lookup
, následně se pomocí $project
zobrazí pouze jméno každého uživatele a částka, kterou utratil. $project
se také používá k odstranění pole _id
z výsledku. Výsledek této operace je uveden níže:
$unwind
$unwind
je agregační fáze sloužící k dekonstrukci neboli „rozbalení“ pole, čímž vytvoří nové dokumenty pro každý prvek v daném poli. To je užitečné, když chcete provádět agregace s hodnotami prvků v poli.
Například, v případě že chcete provádět agregaci na poli zájmů (hobbies), to není možné, protože se jedná o pole. Nicméně, můžete jej rozbalit pomocí $unwind
a poté provádět agregace na výsledných dokumentech.
S použitím kolekcí uživatelů a objednávek můžeme kombinovat $lookup
a $unwind
následujícím způsobem:
db.users.aggregate([ { $lookup: { from: "orders", localField: "_id", foreignField: "user_id", as: "orders" } }, { $unwind: "$orders" } ])
Ve výše uvedeném kódu vrací $lookup
pole s názvem „orders“. $unwind
pak slouží k rozbalení tohoto pole. Výsledkem této operace je následující: Všimněte si, že Alice se objeví dvakrát, protože má dvě objednávky.
Příklady případů použití operátoru $lookup
Při zpracování dat je operátor $lookup
velmi užitečný nástroj. Můžete mít například dvě kolekce, které chcete spojit na základě polí s podobnými daty. K tomu můžete použít jednoduchou fázi $lookup
a přidat nové pole do primárních kolekcí, které budou obsahovat dokumenty z jiné kolekce.
Vezměme v úvahu kolekce uživatelů a objednávek:
Tyto dvě kolekce je možné zkombinovat s pomocí $lookup
a získat výsledek uvedený níže:
$lookup
lze také použít k provádění složitějších spojení. $lookup
se neomezuje pouze na spojování dvou kolekcí. Můžete implementovat více fází $lookup
pro provedení spojení na více než dvou kolekcích. Představte si tři následující kolekce:
Následující kód nám umožňuje provést složitější spojení napříč třemi kolekcemi a získat tak všechny objednávky a podrobnosti o objednaných produktech:
db.orders.aggregate([ { $lookup: { from: "order_items", localField: "_id", foreignField: "order_id", as: "order_items" } }, { $unwind: "$order_items" }, { $lookup: { from: "products", localField: "order_items.product_id", foreignField: "_id", as: "product_details" } }, { $group: { _id: "$_id", customer: { $first: "$customer" }, total: { $sum: "$order_items.price" }, products: { $push: "$product_details" } } } ])
Výsledek této operace je uveden níže:
Závěr
Při zpracování dat zahrnujících více kolekcí je $lookup
užitečný, protože umožňuje spojovat data a vyvozovat závěry na základě dat uložených ve více kolekcích. Zpracování dat zřídkakdy závisí pouze na jedné kolekci.
Pro vyvození smysluplných závěrů z dat je klíčovým krokem spojení dat z více kolekcí. Zvažte proto použití fáze $lookup
ve vašem agregačním kanálu v MongoDB. Umožní vám lépe zpracovávat vaše data a získávat smysluplné informace ze surových dat uložených napříč různými kolekcemi.
Můžete také prozkoumat některé další příkazy a dotazy v MongoDB.