MongoDB — документоориентированная СУБД: данные хранятся не в таблицах, а в коллекциях JSON-подобных документов (BSON). Гибкая схема, богатый язык запросов и встроенная репликация.
Установка#
Официальный репозиторий для Ubuntu 22.04 (MongoDB 7.0):
# Ключ подписи репозитория (apt-key устарел — кладём ключ в keyring)
sudo apt install gnupg curl
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 update
sudo apt install -y mongodb-org
sudo systemctl enable --now mongod
Подключение к шеллу (в современных версиях это mongosh, legacy-клиент
mongo удалён):
mongosh
Всё ниже — команды шелла (JavaScript). Демонстрируем на коллекции
customers с документами вида { name, email, city, age, status }.
Вставка#
// Один документ
db.customers.insertOne({
name: "Olga Smirnova",
email: "olga@example.com",
city: "Minsk",
age: 29,
status: "active"
})
// Массив документов — вставка пачкой
db.customers.insertMany([
{ name: "Ivan Petrov", email: "ivan@example.com", city: "Gomel", age: 34, status: "inactive" },
{ name: "Anna Koval", email: "anna@example.com", city: "Minsk", age: 41, status: "active" },
{ name: "Maksim Lee", email: "maks@example.com", city: "Brest", age: 23, status: "active" }
])
Коллекция создаётся автоматически при первой вставке; у документа без явного
_id MongoDB сгенерирует его сама.
Выборки#
Простые#
// Все документы коллекции
db.customers.find()
// Один документ, совпавший первым
db.customers.findOne({ name: "Anna Koval" })
// По точному значению поля
db.customers.find({ city: "Minsk" })
Операторы сравнения#
// Возраст больше 25
db.customers.find({ age: { $gt: 25 } })
// Комбинированный диапазон: 25 <= age <= 40
db.customers.find({ age: { $gte: 25, $lte: 40 } })
// Вхождение в набор значений
db.customers.find({ city: { $in: ["Minsk", "Brest"] } })
// Поле существует / отсутствует
db.customers.find({ phone: { $exists: true } })
// Подстрока в начале поля (регулярное выражение)
db.customers.find({ name: { $regex: "^A" } })
Основные операторы сравнения: $eq, $ne, $gt, $gte, $lt, $lte,
$in, $nin, $exists, $regex.
Логические операторы#
// И: оба условия одновременно
db.customers.find({
age: { $gte: 25 },
status: "active"
})
// Явная форма с $and (нужна, когда условие на одно поле сложное)
db.customers.find({
$and: [
{ age: { $gte: 25 } },
{ age: { $lte: 40 } }
]
})
// ИЛИ: хотя бы одно из условий
db.customers.find({
$or: [
{ city: "Minsk" },
{ age: { $gte: 35 } }
]
})
// Отрицание набора условий
db.customers.find({ $nor: [ { status: "inactive" }, { age: { $lt: 25 } } ] })
Проекции, сортировка и пагинация#
// Только name и email (без _id) у активных клиентов
db.customers.find(
{ status: "active" },
{ name: 1, email: 1, _id: 0 }
)
// Свежие первыми, 10 документов, третья страница
db.customers.find()
.sort({ age: -1 })
.limit(10)
.skip(20)
Агрегации#
Конвейер документов: каждый этап ($match, $group, $sort, …) обрабатывает
поток и передаёт результат следующему.
// Группировка по городу: количество и средний возраст
db.customers.aggregate([
{ $match: { status: "active" } },
{ $group: { _id: "$city", count: { $sum: 1 }, avgAge: { $avg: "$age" } } },
{ $sort: { count: -1 } },
{ $limit: 5 }
])
Обновления#
// Обновить один документ: заменить поля через $set
db.customers.updateOne(
{ name: "Ivan Petrov" },
{ $set: { status: "active" } }
)
// Обновить все документы под фильтр
db.customers.updateMany(
{ status: "inactive" },
{ $set: { city: "unknown" } }
)
// Инкремент счётчика
db.customers.updateOne(
{ name: "Anna Koval" },
{ $inc: { logins: 1 } }
)
// Добавить элемент в массив
db.customers.updateOne(
{ name: "Anna Koval" },
{ $push: { tags: "vip" } }
)
// Удалить поле целиком
db.customers.updateOne(
{ name: "Anna Koval" },
{ $unset: { phone: "" } }
)
Без
$setи других операторовupdateOne({ … }, { age: 30 })заменит весь документ на новый. Всегда указывайте оператор.
Довольно частый паттерн — апsert: обновить, а если документа нет, вставить (пригоден для инкрементальных синхронизаций):
db.customers.updateOne(
{ email: "new@example.com" },
{ $set: { name: "New User", status: "active" } },
{ upsert: true }
)
Удаления#
// Один документ (первый совпавший)
db.customers.deleteOne({ email: "maks@example.com" })
// Все документы под фильтр
db.customers.deleteMany({ status: "inactive" })
// Удалить коллекцию вместе с индексами
db.customers.drop()
deleteMany({})чистит документы,drop()— коллекцию целиком (включая индексы). Для «зачистки таблицы» быстрее именноdrop().
Реплика-сет#
Репликация держит копии данных на нескольких узлах: записи принимает один PRIMARY, остальные синхронизируются с ним по oplog. При падении PRIMARY участники выбирают нового — кластер продолжает работать.
Конфигурация узлов#
Одинаковый replSetName в /etc/mongod.conf на каждом узле. Обратите
внимание: bindIp и port живут в секции net, а не в replication:
# /etc/mongod.conf
net:
bindIp: 0.0.0.0
port: 27017
replication:
replSetName: rs0
Затем на каждом узле: sudo systemctl restart mongod. Узлам нужен доступ друг
к другу и клиентам по порту 27017:
# Точечно: только из доверенной подсети
sudo ufw allow from 10.0.0.0/24 to any port 27017
Инициализация#
Подключаемся к одному узлу и инициализируем набор — остальные перечислены
в members:
// Подключение (реплика-сет указывается в строке подключения)
mongosh "mongodb://mongo1.example.com:27017/?replicaSet=rs0"
// Выполняется один раз, на любом перечисленном узле
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "mongo1.example.com:27017" },
{ _id: 1, host: "mongo2.example.com:27017" },
{ _id: 2, host: "mongo3.example.com:27017" }
]
})
Статус и конфигурация#
// Состояние участников: health, state (PRIMARY/SECONDARY), lag
rs.status()
// Текущая конфигурация набора
rs.conf()
// Передать роль PRIMARY другому узлу (например, перед обслуживанием)
rs.stepDown()
Добавление и удаление участников#
// Подключить новый узел (сначала — с тем же replSetName в mongod.conf)
rs.add("mongo4.example.com:27017")
// Убрать узел из набора
rs.remove("mongo3.example.com:27017")
Приоритет участника задаётся в конфиге набора:
rs.conf().members— полеpriority; узел с наибольшим приоритетом выигрывает выборы PRIMARY. Для реплика-сета нужны минимум три узла с данным (или арбитры) — кворум должен быть надёжным.