Первые шаги с MongoDB
Разработчику
Аналитику
Тестировщику
Архитектору
Инженеру
Установка системы MongoDB
Процесс установки СУБД требует выполнения действий в зависимости от операционной системы пользователя. Для работы с базой данных необходим серверный компонент (mongod) и клиентские утилиты (mongosh).
Установка программ обычно выглядит так (на Windows — MSI мастера MongoDB):
Play ITЗагрузка интерактивного демо…
Play ITЗагрузка интерактивного демо…
Требования к системе
- Операционная система: Windows, Linux (Debian/Ubuntu/CentOS) или macOS.
- Права администратора на машине.
- Доступ к интернету для загрузки установочных пакетов.
- Минимум 2 ГБ оперативной памяти.
Алгоритм установки
Вариант А — Установка на Windows
- Перейдите на официальный сайт проекта MongoDB.
- Скачайте последний стабильный дистрибутив для Windows Community Server.
- Запустите файл установки.
- Следуйте инструкциям мастера установки:
- Выберите тип установки "Complete" для установки всех компонентов, включая инструменты командной строки и GUI (MongoDB Compass).
- Укажите путь к директории установки.
- Настройте параметры запуска службы. Рекомендуется выбрать "Run as a service", чтобы MongoDB запускалась автоматически при загрузке системы.
- Укажите порт подключения (стандартное значение — 27017).
- Завершите установку и откройте приложение 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)
- Откройте MongoDB Compass и подключитесь к локальному серверу.
- Нажмите кнопку "Create Database".
- Введите имя базы
company_db. - Укажите имя коллекции (например,
employees). - Нажмите кнопку "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.