MongoDB є популярною базою даних NoSQL, яка організовує інформацію в колекціях. Кожна колекція MongoDB містить один або декілька документів, де зберігаються фактичні дані у форматі JSON. Документи можна розглядати як аналоги рядків у традиційних реляційних базах даних SQL, а колекції – як таблиці.
Однією з ключових функцій будь-якої бази даних є можливість запитувати збережені дані. Запити дозволяють витягувати конкретну інформацію, аналізувати дані, створювати звіти та інтегрувати дані з різних джерел.
Для ефективного використання бази даних важливо мати можливість об’єднувати дані з різних таблиць (у випадку SQL) або колекцій (у випадку NoSQL) в один результуючий набір.
У MongoDB оператор $lookup
дозволяє користувачам об’єднувати інформацію з двох колекцій під час виконання запиту. Він виконує функцію, аналогічну лівому зовнішньому з’єднанню в базі даних SQL.
Застосування та мета $lookup
Важлива функція баз даних – це обробка даних для вилучення корисної інформації. Наприклад, якщо ви керуєте рестораном, ви можете аналізувати дані, щоб з’ясувати, скільки страв готується щодня, які страви є популярними у вихідні, або навіть скільки кави продається протягом кожної години дня.
Для таких цілей простих запитів до бази даних недостатньо. Вам потрібно виконувати розширені запити. Для задоволення таких потреб MongoDB надає функцію конвеєра агрегації.
Конвеєр агрегації – це система, що складається з операцій, які називаються етапами. Ці етапи використовуються для обробки даних з метою отримання кінцевого результату. Серед прикладів етапів конвеєра агрегації можна назвати $sort
, $match
, $group
, $merge
, $count
і $lookup
.
Ці етапи можна застосовувати в будь-якому порядку. Кожен етап конвеєра виконує свою операцію над даними, які проходять через конвеєр.
Таким чином, $lookup
є етапом конвеєра агрегації MongoDB. Він використовується для виконання лівого зовнішнього з’єднання між двома колекціями в базі даних MongoDB. Ліве зовнішнє з’єднання поєднує всі документи з лівої колекції з відповідними документами з правої колекції.
Розглянемо дві колекції, представлені у форматі таблиці для кращого розуміння:
збір_замовлень
:
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 |
customer_collection
:
customer_num | customer_name | customer_email | customer_phone |
100 | John | [email protected] | [email protected] |
Якщо ми виконаємо ліве зовнішнє з’єднання цих колекцій за полем customer_id
в order_collection
(ліва колекція) та customer_collection
(права колекція), то результат включатиме всі документи з order_collection
і документи з customer_collection
, які мають customer_num
, що відповідає customer_id
будь-якого з записів у order_collection
.
Ось як виглядатиме результат операції лівого зовнішнього з’єднання, якщо його подати у табличному форматі:
Зверніть увагу, що для клієнта з customer_id
101 у колекції замовлень, який не мав відповідного значення customer_num
в колекції клієнтів, відповідні значення з таблиці клієнтів заповнені нулями.
Оператор $lookup
виконує точне порівняння на рівність між полями та вилучає весь документ, а не лише поля, які збігаються.
Синтаксис $lookup
Синтаксис $lookup
виглядає так:
{ $lookup: { from: <колекція для з'єднання>, localField: <поле з вхідних документів>, foreignField: <поле з документів колекції "from">, as: <поле вихідного масиву> } }
$lookup
приймає чотири параметри:
from
– представляє колекцію, з якої ми хочемо отримувати документи. У попередньому прикладі, де використовувалисяorders_collection
таcustomers_collection
, це будеcustomer_collection
.localField
– це поле в робочій або первинній колекції, яке ми використовуємо для порівняння з полями в колекціїfrom
(customers_collection
в нашому випадку). У прикладіlocalField
будеcustomer_id
зorders_collection
.foreignField
– це поле, з яким ми хочемо порівняти в колекціїfrom
. У нашому випадку це будеcustomer_num
зcustomer_collection
.as
– це нове ім’я поля, яке ми задаємо для представлення поля, яке буде відображатися в нашому документі. Це поле містить масив документів, отриманих в результаті збігів міжlocalField
таforeignField
. Якщо збігів немає, це поле буде містити порожній масив.
З нашими двома попередніми колекціями ми використали б наступний код для виконання операції $lookup
між двома колекціями, де orders_collection
є нашою робочою або основною колекцією:
{ $lookup: { from: "customers_collection", localField: "customer_id", foreignField: "customer_num", as: "customer_info" }
Зауважте, що поле as
може мати будь-яке рядкове значення. Однак якщо ви дасте йому ім’я, яке вже існує в робочому документі, це поле буде перезаписане.
Об’єднання даних з кількох колекцій
Оператор $lookup
в MongoDB є важливим етапом у конвеєрі агрегації. Хоча конвеєр агрегації не обов’язково повинен містити етап $lookup
, цей етап є вирішальним для виконання складних запитів, які потребують об’єднання даних з різних колекцій.
Етап $lookup
виконує ліве зовнішнє з’єднання двох колекцій, створюючи нове поле або перезаписуючи значення існуючого поля масивом, що містить документи з іншої колекції.
Ці документи вибираються на основі того, чи мають вони значення, які відповідають значенням поля, з яким вони порівнюються. Кінцевим результатом є поле, що містить масив документів (якщо збіги знайдені), або порожній масив (якщо збігів не знайдено).
Розглянемо колекції співробітників та проектів, показані нижче:
Ми можемо використати наступний код для об’єднання цих двох колекцій:
db.projects.aggregate([ { $lookup: { from: "employees", localField: "employees", foreignField: "_id", as: "assigned_employees" } } ])
Результатом цієї операції є об’єднання двох колекцій. Результатом є проекти та всі співробітники, призначені для кожного проекту. Співробітники представлені у вигляді масиву.
Етапи конвеєра, які можна використовувати разом з $lookup
Як вже згадувалося, $lookup
є етапом конвеєра агрегації MongoDB, і його можна використовувати разом з іншими етапами. Щоб продемонструвати, як ці етапи можна використовувати разом із $lookup
, ми використаємо наступні дві колекції:
У MongoDB вони зберігаються у форматі JSON. Ось так виглядають наведені вище колекції в MongoDB:
Деякі приклади етапів конвеєра агрегації, які можна використовувати разом із $lookup
, включають:
$match
$match
– це етап конвеєра агрегації, який використовується для фільтрації потоку документів. Він дозволяє лише тим документам, які відповідають заданій умові, перейти до наступного етапу конвеєра. Цей етап найкраще використовувати на початку конвеєра, щоб видалити непотрібні документи і оптимізувати конвеєр агрегації.
Використовуючи попередні дві колекції, можна поєднати $match
та $lookup
так:
db.users.aggregate([ { $match: { country: "USA" } }, { $lookup: { from: "orders", localField: "_id", foreignField: "user_id", as: "orders" } } ])
$match
використовується для фільтрації користувачів із США. Результат $match
потім об’єднується з $lookup
для отримання деталей замовлень користувачів із США. Результат цієї операції показаний нижче:
$project
$project
– це етап, що використовується для зміни структури документів. Він дозволяє визначити, які поля слід включити, виключити або додати до документів. Наприклад, якщо ви обробляєте документи з десятьма полями, але лише чотири поля містять дані, необхідні для обробки, ви можете використати $project
, щоб відфільтрувати непотрібні поля.
Це дозволяє уникнути надсилання непотрібних даних на наступний етап конвеєра агрегації.
Ми можемо об’єднати $lookup
і $project
таким чином:
db.users.aggregate([ { $lookup: { from: "orders", localField: "_id", foreignField: "user_id", as: "orders" } }, { $project: { name: 1, _id: 0, total_spent: { $sum: "$orders.price" } } } ])
Наведений вище код об’єднує колекції користувачів та замовлень за допомогою $lookup
. Потім $project
використовується для відображення імені кожного користувача та загальної суми, витраченої кожним користувачем. $project
також використовується для видалення поля _id
з результатів. Результат цієї операції показаний нижче:
$unwind
$unwind
– це етап агрегації, який використовується для деконструкції або розгортання масиву, створюючи нові документи для кожного елемента в масиві. Це корисно, якщо ви хочете виконати агрегацію значень полів масиву.
Наприклад, у прикладі нижче, якщо ви хочете запустити агрегацію в полі “hobbies”, ви не можете цього зробити, оскільки це масив. Однак ви можете використати $unwind
для розгортання масиву, а потім виконати агрегацію отриманих документів.
Використовуючи колекції користувачів та замовлень, ми можемо використати $lookup
та $unwind
разом таким чином:
db.users.aggregate([ { $lookup: { from: "orders", localField: "_id", foreignField: "user_id", as: "orders" } }, { $unwind: "$orders" } ])
У наведеному вище коді $lookup
повертає масив під назвою “orders”. Потім $unwind
використовується для розгортання цього масиву. Результат цієї операції показаний нижче: Зверніть увагу, що Аліса з’являється двічі, тому що вона має два замовлення.
Приклади використання $lookup
$lookup
є корисним інструментом під час обробки даних. Наприклад, у вас можуть бути дві колекції, які ви хочете об’єднати на основі полів, що містять подібні дані. Для цього можна використати $lookup
, додаючи нове поле до основних колекцій, яке міститиме документи з іншої колекції.
Розглянемо колекції користувачів та замовлень, показані нижче:
Дві колекції можна об’єднати за допомогою $lookup
для отримання результату, показаного нижче:
$lookup
також можна використовувати для виконання складніших об’єднань. Він не обмежується лише двома колекціями. Ви можете використовувати кілька етапів $lookup
для об’єднання більш ніж двох колекцій. Розглянемо три колекції:
Ми можемо використати наступний код для виконання складнішого об’єднання трьох колекцій, щоб отримати всі зроблені замовлення, а також деталі замовлених продуктів:
Наступний код дозволяє нам це зробити:
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" } } } ])
Результат цієї операції показано нижче:
Висновок
Під час обробки даних із залученням кількох колекцій, $lookup
є корисним інструментом. Він дозволяє об’єднувати дані та робити висновки на основі інформації, що зберігається в різних колекціях. Обробка даних рідко обмежується однією колекцією.
Об’єднання даних з кількох колекцій є важливим кроком для вилучення значущої інформації. Тому, подумайте про використання етапу $lookup
у конвеєрі агрегації MongoDB, щоб краще обробляти дані та отримувати корисну інформацію з необроблених даних, що зберігаються в колекціях.
Ви також можете дослідити інші команди та запити MongoDB.