Перейти к основному содержимому

Первые шаги с MongoDB

Разработчику Аналитику Тестировщику
Архитектору Инженеру


Установка системы MongoDB

Процесс установки СУБД требует выполнения действий в зависимости от операционной системы пользователя. Для работы с базой данных необходим серверный компонент (mongod) и клиентские утилиты (mongosh).

Установка программ обычно выглядит так (на Windows — MSI мастера MongoDB):

Play ITЗагрузка интерактивного демо…

Play ITЗагрузка интерактивного демо…


Требования к системе

  • Операционная система: Windows, Linux (Debian/Ubuntu/CentOS) или macOS.
  • Права администратора на машине.
  • Доступ к интернету для загрузки установочных пакетов.
  • Минимум 2 ГБ оперативной памяти.

Алгоритм установки

Вариант А — Установка на Windows

  1. Перейдите на официальный сайт проекта MongoDB.
  2. Скачайте последний стабильный дистрибутив для Windows Community Server.
  3. Запустите файл установки.
  4. Следуйте инструкциям мастера установки:
    • Выберите тип установки "Complete" для установки всех компонентов, включая инструменты командной строки и GUI (MongoDB Compass).
    • Укажите путь к директории установки.
    • Настройте параметры запуска службы. Рекомендуется выбрать "Run as a service", чтобы MongoDB запускалась автоматически при загрузке системы.
    • Укажите порт подключения (стандартное значение — 27017).
  5. Завершите установку и откройте приложение MongoDB Compass для визуального управления данными.

Вариант Б — Установка на Linux (на примере Ubuntu/Debian)

На Ubuntu/Debian (репозиторий и пакет):

# Ubuntu 22.04+ / Debian — ключ через keyring (без устаревшего apt-key)
curl -fsSL https://www.mongodb.org/static/pgp/server-7.0.asc | \
sudo gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg --dearmor
echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] \
https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | \
sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list
sudo apt-get update
sudo apt-get install -y mongodb-org

Для другой версии ОС или релиза MongoDB см. Install MongoDB Community.

Проверка службы:

sudo systemctl status mongod

Интерактивная оболочка:

mongosh

Вариант В — Установка на macOS (Homebrew)

brew tap mongodb/brew
brew install mongodb-community@7.0
brew services start mongodb-community@7.0
mongosh

Путь к данным по умолчанию — /opt/homebrew/var/mongodb (Apple Silicon) или /usr/local/var/mongodb (Intel). Остановка службы: brew services stop mongodb-community@7.0.

brew services stop mongodb-community@7.0

Вариант Г — Использование Docker (универсальный способ)

Создание контейнера с базой данных позволяет изолировать среду разработки:

docker run --name mongo \
-d \
-p 27017:27017 \
mongo:latest

Подключение к контейнеру:

docker exec -it mongo mongosh

Создание базы данных

База данных в MongoDB представляет собой логическое хранилище для коллекций документов. В отличие от реляционных систем, база данных создается неявно при первом сохранении документа.


Командный способ

В mongosh:

use company_db;

Если база company_db не существует, она будет создана автоматически после первого вставки документа.


Графический способ (MongoDB Compass)

  1. Откройте MongoDB Compass и подключитесь к локальному серверу.
  2. Нажмите кнопку "Create Database".
  3. Введите имя базы company_db.
  4. Укажите имя коллекции (например, employees).
  5. Нажмите кнопку "Create Database".

Структура данных задаётся на уровне коллекции (и опционально — валидатором JSON Schema), а не "схемой всей базы". Одна БД может содержать коллекции с разной формой документов.


Создание коллекции и вставка документов

Коллекция — это аналог таблицы в реляционных базах данных, но она хранит документы в формате BSON (бинарный JSON). Структура документа определяется пользователем.


Порядок — валидатор, затем данные

Сначала создайте коллекцию с правилами (или убедитесь, что учебная коллекция пуста). Если вы уже вставляли документы в employees без валидатора, для чистого сценария выполните db.employees.drop() и повторите шаги ниже.

Код ITЗагрузка примера кода…

Если коллекция уже существует, валидатор добавляют через collMod:

db.runCommand({
collMod: "employees",
validator: { $jsonSchema: { bsonType: "object", required: ["first_name", "last_name"] } },
validationLevel: "moderate"
});

Синтаксис вставки документа

Используйте метод insertOne для добавления одного документа или insertMany для нескольких.

db.employees.insertOne({
// _id можно не указывать — MongoDB сгенерирует ObjectId()
first_name: "Ivan",
last_name: "Ivanov",
email: "ivanov@example.com",
hire_date: ISODate("2024-01-15"),
salary: 75000.00,
department_id: 1,
skills: ["JavaScript", "Python"],
is_active: true
});

Анализ компонентов определения

  • _id: Уникальный идентификатор документа. Если поле не указано явно, MongoDB генерирует его автоматически в виде объекта ObjectId.
  • first_name, last_name: Текстовые поля произвольной длины.
  • email: Поле для адреса электронной почты.
  • hire_date: Тип данных Date, хранящий временную метку.
  • salary: Числовое поле типа NumberDecimal или Double.
  • department_id: Целочисленное поле для связи с другими данными.
  • skills: Массив строк, позволяющий хранить список значений в одном документе.
  • is_active: Булево поле (true или false).

Вставка нескольких документов

Код ITЗагрузка примера кода…


Добавление ограничений и индексов

Ограничения целостности в MongoDB реализуются через валидацию схем (Schema Validation), а индексы ускоряют поиск информации.


Проверка валидации

При попытке вставить документ без обязательных полей или с отрицательной зарплатой сервер вернёт ошибку Document failed validation:

db.employees.insertOne({
first_name: "Test",
last_name: "User",
salary: -5000
});

Уникальность email обеспечивается уникальным индексом (см. ниже), а не валидатором — при дубликате будет E11000 duplicate key error.


Создание индекса

Индекс — это структура данных, которая ускоряет операции выборки. Индексируются часто используемые поля поиска или сортировки.

Создание индекса по фамилии сотрудников:

db.employees.createIndex({ last_name: 1 });

Создание составного индекса для ускорения поиска по отделу и зарплате:

db.employees.createIndex({ department_id: 1, salary: -1 });

Создание уникального индекса для поля email:

db.employees.createIndex({ email: 1 }, { unique: true });

Использование индексов снижает время выполнения запросов, особенно на больших объемах данных.


Связи между коллекциями

В реляционных СУБД связь "сотрудник → отдел" задаётся внешним ключом. В MongoDB хранят идентификатор связанного документа и при необходимости подтягивают данные вторым запросом или $lookup.

use company_db;

db.departments.insertOne({ _id: 1, name: "Engineering", floor: 3 });
db.departments.insertOne({ _id: 2, name: "Sales", floor: 1 });

// department_id уже есть в employees — привязка к справочнику
db.employees.updateOne(
{ email: "ivanov@example.com" },
{ $set: { department_id: 1 } }
);

const emp = db.employees.findOne({ email: "ivanov@example.com" });
db.departments.findOne({ _id: emp.department_id });

Для отчётов "сотрудник + название отдела одним запросом":

Код ITЗагрузка примера кода…

Устаревший формат DBRef ($ref, $id) не используйте — достаточно department_id и индекса { department_id: 1 }. Подробнее: проектирование схемы.


Отсутствие поля, null и типы в запросах

В одной коллекции документы могут иметь разный набор полей. Отсутствие ключа — не то же самое, что null:

db.employees.insertOne({ first_name: "NoEmail", last_name: "Test", salary: 50000 });
db.employees.insertOne({
first_name: "WithNull",
last_name: "Test",
email: null,
salary: 51000
});

// Только документы БЕЗ поля email
db.employees.countDocuments({ email: { $exists: false } });

// Только документы, где email явно null
db.employees.countDocuments({ email: null });

Фильтр по возрасту не найдёт строку, если возраст записан строкой:

db.employees.insertOne({ first_name: "StrAge", last_name: "X", age: "30", salary: 1 });
db.employees.insertOne({ first_name: "IntAge", last_name: "Y", age: NumberInt(30), salary: 1 });

db.employees.countDocuments({ age: 30 }); // 1 — только NumberInt
db.employees.countDocuments({ age: "30" }); // 1 — только строка

GridFS и файлы крупнее 16 МБ

Один документ BSON не может превышать 16 МБ. Для вложений (PDF, архивы, крупные изображения) внутри той же БД используют GridFS: файл режется на чанки в коллекциях fs.files и fs.chunks (или с пользовательским именем bucket).

Через утилиту mongofiles (входит в MongoDB Database Tools, рядом с mongodump):

mongofiles --db=company_db put ./local/report.pdf
mongofiles --db=company_db list
mongofiles --db=company_db get report.pdf

Через mongosh:

use company_db;
const bucket = new GridFSBucket(db);

const data = Buffer.from("Учебное содержимое файла");
const stream = bucket.openUploadStream("lesson-notes.txt", {
metadata: { course: "nosql", author: "ivanov" }
});
stream.end(data);

db.fs.files.find({}, { filename: 1, length: 1, metadata: 1 });

В коллекции employees (или attachments_meta) можно хранить ссылку на файл:

const meta = db.fs.files.findOne({ filename: "lesson-notes.txt" });
db.employees.updateOne(
{ email: "ivanov@example.com" },
{ $set: { notesFileId: meta._id, notesFileName: meta.filename } }
);

Для публичных медиа и очень больших архивов чаще выбирают object storage; в MongoDB оставляют URL. Теория: GridFS, справочник: раздел 19.


Выполнение CRUD запросов

CRUD (Create, Read, Update, Delete) — набор операций для управления данными.


Создание записей (Create)

Вставка новых сотрудников уже рассмотрена выше. Для "вставить или обновить по ключу" используйте updateOne с upsert: true или replaceOne — устаревший метод save в современном API коллекций отсутствует.

db.employees.updateOne(
{ email: "ivanov@example.com" },
{ $set: { salary: 76000 } },
{ upsert: true }
);

Чтение данных (Read)

Выборка всех записей:

db.employees.find();

Выборка конкретных полей с фильтрацией:

db.employees.find(
{ salary: { $gt: 70000 } }, // Где зарплата больше 70000
{ first_name: 1, last_name: 1, salary: 1, _id: 0 } // Только эти поля
);

Сортировка результатов:

db.employees.find().sort({ salary: -1 }); // Сортировка по убыванию зарплаты

Поиск по массиву:

db.employees.find({ skills: "Python" }); // Найдутся сотрудники со skill "Python"

Пагинация без глубокого skip

skip(N) заставляет сервер пройти первые N документов в порядке сортировки — при N = 10 000 это дорого. Для ленты и API используйте курсор по индексируемому полю (ключ следующей страницы):

// Страница 1
const page1 = db.employees.find({}).sort({ _id: 1 }).limit(20).toArray();
const lastId = page1[page1.length - 1]._id;

// Страница 2 — только документы «правее» последнего _id
db.employees.find({ _id: { $gt: lastId } }).sort({ _id: 1 }).limit(20);

Для сортировки по createdAt сохраняйте пару (createdAt, _id) — tie-breaker на случай одинаковых дат. Tailable cursor на capped collection — отдельный сценарий (хвост лога). Подробнее — антипаттерны в курсе.


Обновление данных (Update)

Изменение зарплаты сотрудника:

db.employees.updateOne(
{ _id: ObjectId("...") }, // Фильтр
{ $set: { salary: 80000.00 } } // Операция обновления
);

Обновление нескольких полей одновременно:

db.employees.updateMany(
{ department_id: 1 },
{ $inc: { salary: 5000 } } // Увеличить зарплату на 5000 всем в отделе 1
);

Добавление элемента в массив:

db.employees.updateOne(
{ first_name: "Ivan" },
{ $push: { skills: "Docker" } }
);

Удаление данных (Delete)

Удаление конкретного сотрудника:

db.employees.deleteOne({ _id: ObjectId("...") });

Удаление всех записей из коллекции:

db.employees.deleteMany({});

Удаление всей коллекции:

db.employees.drop();

Создание представлений (Views)

Представление в MongoDB — это виртуальная таблица, созданная на основе пайплайна агрегации. Представления не хранят данные физически, а вычисляют их при обращении.


Простое представление

Сначала заполните справочник отделов — в employees поле department_id ссылается на _id в departments:

db.departments.insertMany([
{ _id: 1, name: "Разработка" },
{ _id: 2, name: "Аналитика" }
]);

Создание представления со списком сотрудников и их зарплатами:

Код ITЗагрузка примера кода…

Использование представления:

db.employee_salary_view.find({ salary: { $gt: 75000 } });

Преимущества использования View

  • Упрощение сложных запросов.
  • Сокрытие деталей реализации физических коллекций.
  • Контроль доступа к определенным полям.
  • Стандартизация логики выборки данных.

Агрегация, скрипты в shell и реакция на изменения

В MongoDB нет хранимых процедур и триггеров BEFORE INSERT, как в SQL. Отчёты и join-подобная логика — это aggregation pipeline ($lookup, $group). Одноразовые скрипты в mongosh — обычный JavaScript, но в production бизнес-логику размещают в приложении (драйвер PyMongo, Node.js и т.д.).

Реакция на изменения в реальном времени — Change Streams (db.collection.watch()), обработчик в сервисе; в Atlas — триггеры App Services.


Функция с агрегатными функциями

Общая база: функции в коде — вызов, параметры, возврат. Примеры ниже на JavaScript в mongosh; в production ту же логику обычно выносят в приложение.

Функция рассчитывает среднюю зарплату в отделе.

function get_avg_salary_by_department(deptId) {
return db.employees.aggregate([
{ $match: { department_id: deptId } },
{ $group: { _id: null, avgSalary: { $avg: "$salary" } } }
]).toArray()[0].avgSalary;
}

// Вызов функции
get_avg_salary_by_department(1);

Процедура с использованием пайплайна агрегации

Процедура обновляет статистику отдела при добавлении нового сотрудника. В данном случае мы создадим скрипт, который выполняет группировку и подсчет.

Код ITЗагрузка примера кода…


Валидация и триггеры

Валидатор коллекции отклоняет некорректную форму документа (типы, обязательные поля, minimum: 0 для зарплаты). Это не замена триггеру: он не "подставит" значение по умолчанию и не вызовет внешний API — только вернёт ошибку. Сложные правила (лимиты, антифрод) реализуют в коде приложения или в Change Streams.


Сквозной практикум (15 минут)

Цель: пройти полный цикл CRUD + индекс + агрегация в mongosh. Подойдёт локальный mongod, Docker из раздела выше или Atlas с бесплатным кластером.

Подготовка

mongosh
# или — docker exec -it mongo mongosh

Шаг 1. База и вставка

use learning_nosql;

db.products.insertMany([
{ sku: "BOOK-01", title: "NoSQL вводный", price: 1200, tags: ["db", "nosql"] },
{ sku: "BOOK-02", title: "Redis patterns", price: 990, tags: ["redis", "cache"] },
{ sku: "BOOK-03", title: "Cassandra modelling", price: 1500, tags: ["cassandra", "db"] }
]);

Шаг 2. Чтение и фильтр

db.products.find({ tags: "db" }, { title: 1, price: 1, _id: 0 });
db.products.find({ price: { $gte: 1000 } }).sort({ price: -1 });

Шаг 3. Обновление

db.products.updateOne(
{ sku: "BOOK-01" },
{ $set: { price: 1090 }, $push: { tags: "sale" } }
);

Шаг 4. Индекс и план запроса

db.products.createIndex({ sku: 1 }, { unique: true });

const plan = db.products.find({ sku: "BOOK-02" }).explain("executionStats");
// До индекса: часто COLLSCAN и totalDocsExamined ≈ размер коллекции
// После: IXSCAN и totalDocsExamined ≈ nReturned (1)
plan.executionStats.executionStages.stage; // "IXSCAN" или вложенный IXSCAN
plan.executionStats.totalDocsExamined;
plan.executionStats.nReturned;

Повторная вставка с тем же sku после unique-индекса должна вернуть E11000 duplicate key error.

Шаг 5. Агрегация

db.products.aggregate([
{ $unwind: "$tags" },
{ $group: { _id: "$tags", count: { $sum: 1 }, avgPrice: { $avg: "$price" } } },
{ $sort: { count: -1 } }
]);

Шаг 6. TTL (опционально — логи с автоудалением)

db.event_log.insertOne({ msg: "test", createdAt: new Date() });
db.event_log.createIndex({ createdAt: 1 }, { expireAfterSeconds: 86400 });

Шаг 7. Связь с другой коллекцией ($lookup)

db.vendors.insertOne({ _id: "pub", name: "IT Publisher" });
db.products.updateOne({ sku: "BOOK-01" }, { $set: { vendorId: "pub" } });

db.products.aggregate([
{ $match: { sku: "BOOK-01" } },
{ $lookup: { from: "vendors", localField: "vendorId", foreignField: "_id", as: "vendor" } },
{ $unwind: "$vendor" },
{ $project: { title: 1, price: 1, vendorName: "$vendor.name" } }
]);

Шаг 8. GridFS (опционально)

const bucket = new GridFSBucket(db);
const upload = bucket.openUploadStream("catalog-cover.bin", { metadata: { sku: "BOOK-01" } });
upload.end(Buffer.from("binary-placeholder"));
db.fs.files.findOne({ filename: "catalog-cover.bin" }, { length: 1, metadata: 1 });

Шаг 9. Очистка учебной базы

db.dropDatabase(); // только learning_nosql

Теория — MongoDB (схема, GridFS, антипаттерны) · Справочник · Compass — раздел выше в основной статье.


В подборках

Статья входит в тематические подборки и блок "С чего начать?" на главной. Соседние шаги того же маршрута:

Первые шаги (маршрут подборки) — Первые шаги с SQL, Первые шаги с Redis, Первая программа на Expo, Первые шаги с Cassandra, Первая программа на React Native, Первые шаги с Memcached.


Содержание