Модуль 18

Pipeline та оптимізація запитів

Ось готовий урок, створений за твоїм майстер-промптом.


🎓 Тема уроку: Pipeline та оптимізація запитів

(Або: Як змусити базу даних працювати замість вас)


1. 🔥 Вступ: проблема та мотивація

Уявіть, що ви — власник величезного складу, як у Amazon. У вас мільйони товарів.

Приходить менеджер і каже: "Мені потрібен список усіх червоних футболок розміру M, які ми продали минулого вівторка, відсортований за ціною від найдорожчої".

У вас є два шляхи це зробити:

  1. Шлях новачка (The Application Side): Ви кажете вантажникам: "Принесіть мені на стіл ВСІ товари, які ми продали у вівторок". Вони тягнуть тисячі коробок. Ви починаєте їх відкривати, дивитися: "Це футболка? Ні. Це червона? Ні. Це М-ка? Так. Відкладаємо". Потім ви вручну сортуєте цю купу на столі.
  2. Шлях інженера (The Database Side): Ви даєте вантажникам чітку інструкцію: "Ідіть у сектор 'Одяг', візьміть тільки червоні футболки 'М', і поки несете їх до мене — розкладіть по ціні".

Питання до вас: Який варіант швидший? Очевидно, другий. У першому варіанті ви "забили" коридори (мережу), перевантажили свій стіл (оперативну пам'ять сервера) і згаяли час.

Чому без цього не обійтись? Коли у вас 100 записів у базі — різниці немає. Коли у вас 10 мільйонів записів (Big Data) — перший метод "покладе" ваш сервер за секунду. Сьогодні ми навчимося будувати Pipeline (Конвеєр) — спосіб передати роботу туди, де лежать дані, щоб отримати готовий, чистий результат.


2. 🧠 Теоретична база (без сухої академічності)

Отже, що таке Pipeline?

Уявіть собі заводський конвеєр. Дані заходять з одного боку, проходять через низку станцій (Stages), і виходять з іншого боку вже оброблені.

У світі баз даних (особливо NoSQL, як MongoDB, або складних SQL-запитів) це працює так:

  1. Input: Сирі дані (колекція документів).
  2. Stages (Етапи):
    • $match (Фільтр) — це як сито. Пропускає тільки те, що нам треба.
    • $project (Форматування) — це як скальпель. Відрізає зайві поля, залишає тільки потрібні.
    • $group (Агрегація) — це як кошик. Збирає схожі дані разом (наприклад, сумує продажі по містах).
    • $sort (Сортування) — розставляє по порядку.
    • $limit / $skip — бере тільки частину.

⚙️ Як це працює "під капотом"?

Кожен етап передає результат наступному. Вихід етапу 1 стає входом етапу 2.

❗ Що запам'ятати залізно (Золоте правило оптимізації):

Чим раніше ви відфільтруєте дані, тим краще. Якщо ви спочатку посортуєте мільйон записів, а потім відфільтруєте 10 потрібних — ви змарнували ресурси процесора. Спочатку $match (фільтр), потім все інше! Це називається Early Filtering.


3. 🧪 Приклади (від простого до реального)

Уявімо, що у нас є база даних Orders (Замовлення) для мережі кав'ярень. Структура документа: { item: "Latte", price: 50, city: "Lviv", status: "completed" }

Приклад 1: Мінімум (Hello World)

Завдання: Знайти всі завершені замовлення у Львові.

// Pipeline:
[
  { $match: { city: "Lviv", status: "completed" } }
]

Тут все просто. Ми кажемо базі: "Дай мені тільки це".


Приклад 2: Реальний кейс (Агрегація)

Завдання: Менеджер питає: "Скільки грошей ми заробили у кожному місті на продажі Латте?"

Спробуйте здогадатися, які кроки нам треба зробити? 1. Відібрати тільки "Latte". 2. Згрупувати по містах. 3. Порахувати суму.

// Pipeline:
[
  // Етап 1: Фільтруємо (Залишаємо тільки Латте)
  { $match: { item: "Latte" } },

  // Етап 2: Групуємо (Ключ - місто, Результат - сума цін)
  {
    $group: {
      _id: "$city",
      totalRevenue: { $sum: "$price" }
    }
  }
]

Результат:

[
  { "_id": "Lviv", "totalRevenue": 15000 },
  { "_id": "Kyiv", "totalRevenue": 24000 }
]

Бачите? Ми не тягнули всі чеки на сервер. База сама порахувала і віддала дві стрічки.


Приклад 3: Складний (Оптимізація порядку)

Завдання: Знайти ТОП-3 найдорожчих замовлень у Києві.

Як НЕ треба робити (Погана оптимізація): 1. Сортуємо ВСІ замовлення в базі за ціною. 2. Фільтруємо Київ. 3. Беремо 3.

Як ТРЕБА робити (Правильний Pipeline):

[
  // 1. Спочатку відкидаємо все, що не Київ! (Найважливіший крок)
  { $match: { city: "Kyiv" } },

  // 2. Тепер сортуємо значно меншу кількість даних
  { $sort: { price: -1 } }, // -1 означає "від більшого до меншого"

  // 3. Відрізаємо лише 3
  { $limit: 3 }
]

Чому так? Тому що сортування — це найдорожча операція для комп'ютера. Сортувати 1000 записів із Києва у тисячу разів швидше, ніж сортувати 1 000 000 записів зі всього світу.


4. 🛠 Практична частина

Час "забруднити руки". Уявіть, що ви працюєте з базою даних Netflix. Документ: { title: "Movie Name", genre: "Action", views: 1000, rating: 4.5, year: 2023 }

Завдання 1 (Розігрів): Напишіть пайплайн, який знайде всі фільми жанру "Comedy", випущені у 2023 році.

Завдання 2 (Аналітика): Порахуйте середній рейтинг ($avg) фільмів жанру "Horror". (Підказка: спочатку $match, потім $group).

Завдання 3 (Bug Fixing): Джуніор написав такий запит для пошуку найпопулярніших старих фільмів: 1. $sort by views 2. $match year < 2000 3. $limit 5 Що тут не так? Перепишіть порядок етапів правильно.

Завдання 4 (Творче): Нам треба отримати список назв фільмів (title), які мають рейтинг вище 4.0, але нам не треба показувати користувачеві скільки там переглядів чи який рік. Тільки назва. (Підказка: використовуйте етап $project або схожий механізм вибірки полів).


5. 💡 Мислення як у розробника

Як відрізнити новачка від сеньйора, дивлячись на їхній код?

1. Синдром "Все моє ношу з собою" Новачок робить SELECT * FROM table (або db.find({})), забирає всі дані в Python/JavaScript, і там пише цикл for, щоб порахувати суму. Сеньйор думає: "База даних написана на C++ або C, вона оптимізована геніями протягом 30 років. Я не переплюну її своїм циклом на Python. Нехай база рахує".

2. Мережа — це вузьке місце Передача даних по кабелю/Wi-Fi — це найповільніша частина. Ваше завдання — зробити так, щоб по кабелю пройшов мінімальний обсяг даних. Тільки фінальна відповідь.

3. Індекси (Тизер) Ви написали ідеальний Pipeline. Але він все одно повільний. Чому? Бо база "сканує" кожен документ, щоб перевірити умову $match. Сеньйор знає: без Індексу (змісту, як у книзі) навіть найкращий пайплайн буде гальмувати на великих обсягах.


6. 🧩 Підсумок

Що ми сьогодні зрозуміли:

  1. Pipeline — це конвеєр. Дані течуть через етапи трансформації.
  2. Order matters. Порядок має значення. Спочатку відріж зайве ($match), потім обробляй.
  3. Делегування. Не робіть роботу за базу даних. Вона розумніша і швидша в обробці масивів даних.

Тепер ви вмієте: писати запити, які не "вбивають" продакшн-сервер.

🔜 У наступній серії: Ми згадали слово "Індекс". Це магія, яка перетворює пошук з 10 секунд на 10 мілісекунд. Як правильно створювати індекси і чому забагато індексів — це теж погано? Про це — на наступному уроці!

Це був CS50... тобто, урок про пайплайни. 😉