3.06. Справочник по MongoDB
Разработчику
Аналитику
Тестировщику
Архитектору
Инженеру
Справочник по MongoDB
1. Структурные единицы данных
document — документ
Основная единица хранения данных в MongoDB. Представляет собой упорядоченный набор пар ключ–значение. Документы хранятся в BSON, сериализуются в JSON-подобный вид.
Ограничения:
- Максимальный размер документа — 16 МБ (включая служебную информацию).
- Документ обязательно должен содержать поле
_id. - Ключи не могут содержать символы
$,., и не могут начинаться с$. Вложенные ключи не должны содержать.в именах полей (это ограничение BSON, не MongoDB как таковой, но нарушение приведёт к ошибке при вставке). - Ключ
_idуникален в пределах коллекции.
_id — идентификатор документа
- Тип: обычно
ObjectId, но может быть любого типа, кроме массива иundefined. - Значение: генерируется автоматически, если не указано явно.
- Формат
ObjectId(12 байт):- 4 байта — временная метка (Unix timestamp)
- 5 байт — случайный идентификатор машины и процесса
- 3 байта — счётчик
- Можно генерировать вручную:
new ObjectId(),ObjectId("…"), или передавать строку, число, UUID и т. д.
collection — коллекция
- Аналог таблицы в реляционных СУБД, но не требует фиксированной схемы.
- Документы в одной коллекции могут иметь разную структуру.
- Имена коллекций не должны:
- быть пустыми
- содержать символ
\0(null byte) - начинаться с
system.(зарезервировано для служебных коллекций) - содержать
$(в MongoDB 3.6+ разрешено, но не рекомендуется)
- Коллекции создаются неявно при первой вставке, либо явно через
db.createCollection().
database — база данных
- Группа коллекций.
- Имена баз данных:
- не пустые
- не содержат
\0 - не содержат
/,\,.,"и*в MongoDB 4.4+ - не могут называться
admin,local,config(зарезервированы)
- Ограничение на число баз данных — физическое (место на диске, дескрипторы ОС), логического лимита нет.
2. Формат BSON и его отличия от JSON
Что такое BSON
BSON (Binary JSON) — двоичное представление структурированных данных, расширяющее JSON типами, необходимыми для эффективной работы с данными в памяти и на диске.
Поддерживаемые типы данных (BSON Types, по спецификации):
| Код типа | Название | Пример в MongoDB Shell | Замечания |
|---|---|---|---|
1 | Double | 3.14, Number(3.14) | IEEE 754 double-precision |
2 | String | "текст" | UTF-8, обязательны кавычки |
3 | Object | {a: 1} | вложенный документ |
4 | Array | [1, "a", {b: 2}] | упорядоченный список |
5 | Binary data | BinData(0, "aGVsbG8=") | 6 подтипов (generic, function, UUID и др.) |
6 | Undefined (устар.) | undefined | не сохраняется в новых версиях; отбрасывается |
7 | ObjectId | ObjectId("507f191e810c19729de860ea") | 12-байтный хеш, генерируется клиентом или сервером |
8 | Boolean | true, false | — |
9 | Date | new Date("2025-11-21"), ISODate("2025-11-21T00:00:00Z") | хранится как 64-битное целое (мс с Unix epoch) |
10 | Null | null | — |
11 | Regular Expression | /abc/i | сохраняется с флагами; не совместим с JS напрямую в индексах |
12 | DBPointer (устар.) | — | не рекомендуется; эквивалентно { $ref: "coll", $id: ObjectId(...) } |
13 | JavaScript | Code("function() { return 1; }") | без области видимости (scope) |
14 | Symbol (устар.) | Symbol("x") | считается устаревшим, эквивалентно строке |
15 | JavaScript with Scope | Code("x + y", { x: 1, y: 2 }) | код + контекст; редко используется |
16 | 32-bit integer | NumberInt(42) | — |
17 | Timestamp | Timestamp(1700000000, 1) | не Date! используется для внутренних операций (репликация); 4 байта — время, 4 — инкремент |
18 | 64-bit integer | NumberLong("9223372036854775807") | строка обязательна при превышении int32 |
19 | Decimal128 | NumberDecimal("3.14159265358979323846264338327950288419716939937510") | точная арифметика с плавающей точкой; до 34 цифр |
127 | Min key | MinKey() | используется в сортировке/индексах как «минимум всех» |
255 | Max key | MaxKey() | используется в сортировке/индексах как «максимум всех» |
Важно:
- При передаче чисел без указания типа (например,
42,3.14) MongoDB интерпретирует их какDouble, если есть дробная часть, иначе — какInt32, если укладывается в 32-битный диапазон (-2^31 … 2^31-1), иначе —Int64.new Date()в shell создаётDate, а неTimestamp.- При сериализации в JSON (например,
mongoexport)ObjectId,Date,BinDataпреобразуются в объекты с полями$oid,$date,$binary.
3. CRUD-операции: методы и их параметры
insertOne(document, options?)
Параметры:
document: вставляемый документ (обязательно).options:writeConcern: объект{ w: <value>, j: <boolean>, wtimeout: <ms> }w: 0 — без подтверждения; 1 — подтверждение от primary;"majority"— от большинства реплик;<tag>— по тегу replica set.j:true— ждать записи в журнал (journal);false— не ждать (по умолчанию).wtimeout: таймаут подтверждения в мс (по умолчанию — неограничен).
bypassDocumentValidation:true/false— пропустить валидацию схемы (если включена).comment: строка — произвольный комментарий (для логов и профилирования).
Возвращает: { acknowledged: true, insertedId: ObjectId(...) }
Ошибки:
E11000 duplicate key— при конфликте по_id.DocumentTooLarge— при превышении 16 МБ.
insertMany(documents, options?)
Параметры:
documents: массив документов (обязательно; не пустой).options: как уinsertOne, плюс:ordered:true(по умолчанию) — вставка прекращается при первой ошибке;false— продолжать, накапливать ошибки.bypassDocumentValidation,writeConcern,comment.
Возвращает:
{
acknowledged: true,
insertedIds: [ ObjectId(...), ObjectId(...), ... ]
}
Если ordered: false и были ошибки — свойство insertedIds содержит _id только успешно вставленных документов; ошибки — в writeErrors.
findOne(filter?, options?)
Параметры:
filter: объект запроса (по умолчанию{}— любой документ).options:projection:{ поле: 1 | 0 | { $elemMatch: ... } }— какие поля возвращать (1— включить,0— исключить; нельзя смешивать, кроме_id).sort:{ поле: 1 | -1 }— сортировка перед выбором.collation:{ locale: "ru", strength: 2, caseLevel: true, ... }— правила сравнения строк.hint: название индекса или документ-спецификация индекса — подсказка оптимизатору.comment: строка — для аудита/профилирования.
Возвращает: документ или null.
find(filter?, options?) → курсор
Параметры — такие же, как у findOne, но результат — курсор, а не документ.
Методы курсора (цепочка вызовов):
.sort({ поле: 1 | -1 })— сортировка..limit(n)— максимумnдокументов..skip(n)— пропустить первыеn..project({ ... })— проекция..hint({ ... })— указание индекса..collation({ ... })— правила сравнения..batchSize(n)— размер пакета (по умолчанию ~101 приlimit, иначе динамически)..maxTimeMS(ms)— лимит времени выполнения на сервере..explain("queryPlanner" | "executionStats" | "allPlansExecution")— анализ плана выполнения..toArray()— получить массив (материализовать)..forEach(fn)— обход (синхронный в shell)..map(fn)— трансформация..hasNext()/.next()— итерация вручную.
Важно: курсоры ленивые. Запрос выполняется при первом обращении к данным (например,
.toArray()).
updateOne(filter, update, options?)
updateMany(filter, update, options?)
Параметры:
filter: условие поиска.update: операторы обновления ($set,$inc,$pushи т.д.) или замена документа (если без$-операторов — только дляupdateOne, и только еслиfilterсодержит_id).options:upsert:true/false— создать документ, если не найден.writeConcern,bypassDocumentValidation,comment,collation,hint,arrayFilters(для$[<identifier>]).
Операторы обновления (update operators):
| Группа | Операторы | Краткое описание |
|---|---|---|
| Поле | $currentDate, $inc, $min, $max, $mul, $rename, $set, $setOnInsert, $unset | Модификация скалярных полей |
| Массив | $, $[], $[<identifier>], $addToSet, $pop, $pull, $pullAll, $push, $pushAll (устар.), $each, $position, $slice, $sort (в $push) | Работа с массивами |
| Битовые | $bit (and, or, xor) | Битовые операции над целыми |
| Корневой документ | $set, $unset, $rename на верхнем уровне | — |
Пример:
db.users.updateOne(
{ _id: ObjectId("...") },
{
$set: { "profile.lastLogin": new Date() },
$inc: { "stats.logins": 1 },
$push: {
"history": {
$each: [{ action: "login", ts: new Date() }],
$position: 0,
$slice: 10
}
}
},
{ upsert: false }
)
deleteOne(filter, options?)
deleteMany(filter, options?)
Параметры:
filter— обязательный (даже{}дляdeleteMany).options:writeConcern,collation,hint,comment.
Возвращает:
{ acknowledged: true, deletedCount: N }
4. Операторы запросов (Query Operators)
MongoDB поддерживает богатый набор операторов для построения фильтров (filter). Все операторы начинаются с $. Их можно комбинировать в одном условии (кроме исключений, оговорённых ниже).
4.1. Операторы сравнения ($eq, $ne, $gt, $gte, $lt, $lte, $in, $nin)
| Оператор | Синтаксис | Описание | Примечания |
|---|---|---|---|
$eq | { поле: { $eq: значение } } | равно | Эквивалентно { поле: значение }, но позволяет избежать конфликтов с зарезервированными именами ($eq, $ne и т.п.) |
$ne | { поле: { $ne: значение } } | не равно | Возвращает документы, где поле отсутствует, не равно значение, или значение null. Не использует индекс для поиска null, если индекс не покрывает null. |
$gt | { поле: { $gt: значение } } | больше | Работает с числами, датами, строками, ObjectId, BinData (лексикографически). |
$gte | { поле: { $gte: значение } } | больше или равно | — |
$lt | { поле: { $lt: значение } } | меньше | — |
$lte | { поле: { $lte: значение } } | меньше или равно | — |
$in | { поле: { $in: [знач1, знач2, …] } } | в множестве | До 32 767 элементов (MongoDB 6.0+). Поддерживает регулярные выражения в массиве. |
$nin | { поле: { $nin: [знач1, знач2, …] } } | не в множестве | Возвращает документы, где поле отсутствует или значение не в списке. Не использует индекс эффективно. |
Важно:
$inи$ninне выполняют поискnullпо умолчанию. Чтобы включитьnull, добавьте его явно:{ status: { $in: ["active", "pending", null] } }.- При сравнении значений разных типов MongoDB использует порядок типов:
MinKey < null < numbers < Symbol, String < Object < Array < BinData < ObjectId < Boolean < Date < Timestamp < Regular Expression < MaxKey
(см. документацию).
4.2. Логические операторы ($and, $or, $not, $nor)
| Оператор | Синтаксис | Описание |
|---|---|---|
$and | { $and: [ {усл1}, {усл2}, … ] } | логическое И |
$or | { $or: [ {усл1}, {усл2}, … ] } | логическое ИЛИ |
$not | { поле: { $not: { $gt: 5 } } } | отрицание |
$nor | { $nor: [ {усл1}, {усл2}, … ] } | ни одно из условий не выполняется |
4.3. Операторы элементов ($exists, $type, $expr, $jsonSchema, $mod, $regex, $text, $where)
| Оператор | Синтаксис | Значения параметров | Примечания |
|---|---|---|---|
$exists | { поле: { $exists: true|false } } | true — поле присутствует (в т.ч. null); false — отсутствует | Не проверяет тип. Индекс может быть использован. |
$type | { поле: { $type: код|строка } } | Возможные значения: – 1 / "double" – 2 / "string" – 3 / "object" – 4 / "array" – 6 / "undefined" (устар.) – 7 / "objectId" – 8 / "bool" – 9 / "date" – 10 / "null" – 16 / "int" – 18 / "long" – 19 / "decimal" – "number" — int, long, double, decimal – "binData" – "regex" – "javascript" – "symbol" – "timestamp" – "minKey", "maxKey" | Можно перечислять массивом: { $type: ["string", "null"] }. |
$expr | { $expr: { <выражение агрегации> } } | Любое выражение, допустимое в $project и $addFields (например, { $gt: ["$price", { $multiply: ["$qty", 10] }] }) | Позволяет сравнивать поля внутри документа. Может быть медленным, т.к. не может напрямую использовать индексы (кроме случаев с $eq и покрывающими индексами). |
$jsonSchema | { $jsonSchema: { … } } | Валидатор по спецификации JSON Schema Draft 4 (частично поддерживаемые ключи: bsonType, required, properties, additionalProperties, pattern, minimum, maximum, minLength, maxLength, enum, dependencies, allOf, anyOf, oneOf, not) | Используется в валидации коллекций (db.createCollection(..., { validator: { $jsonSchema: … } })), но также применим в запросах. Не поддерживает $ref, format, default. |
$mod | { поле: { $mod: [делитель, остаток] } } | делитель > 0, оба целые | Работает только с целыми (int, long). Не поддерживает double/decimal. |
$regex | { поле: { $regex: "паттерн", $options: "i" } } | паттерн — строка или объект RegExp; $options: i (регистр), m (многострочный), x (игнор пробелов), s (dotall) | Может использовать индекс только при anchored-паттернах (^abc). Регэкспы не могут использовать индекс для $not: { $regex: … }. |
$text | { $text: { $search: "ключ", $language: "ru", $caseSensitive: false, $diacriticSensitive: false } } | ключ — строка; $language: "danish", "dutch", "english", "finnish", "french", "german", "hungarian", "italian", "norwegian", "portuguese", "romanian", "russian", "spanish", "swedish", "turkish" | Требует text-индекса. Игнорирует стоп-слова, стемминг. Возвращает score в поле score, если добавить {$meta: "textScore"} в проекцию. |
$where | { $where: "this.price > this.cost" } или функция | JavaScript-код (строка или function() { … }) | Не рекомендуется: не масштабируется, не использует индексы, подвержен инъекциям, требует security.javascriptEnabled: true. Альтернатива — $expr. |
4.4. Операторы массивов ($all, $elemMatch, $size)
| Оператор | Синтаксис | Описание |
|---|---|---|
$all | { поле: { $all: [знач1, знач2] } } | поле-массив содержит все указанные значения (порядок не важен). Поддерживает $elemMatch внутри: { tags: { $all: [ { $elemMatch: { category: "a", rating: { $gt: 5 } } } ] } }. |
$elemMatch | { поле: { $elemMatch: { подусловие } } } | хотя бы один элемент массива удовлетворяет всем условиям в подусловие. Если нужно применить разные условия к разным элементам — используйте $and с несколькими $elemMatch. |
$size | { поле: { $size: 3 } } | длина массива равна N. Не использует индексы. Альтернатива — хранить длину отдельно (tagsCount) или использовать агрегацию ($size). |
Ограничение:
$sizeне поддерживает переменные или выражения. Только константа.
4.5. Геопространственные операторы ($geoWithin, $geoIntersects, $near, $nearSphere, $maxDistance, $minDistance, $geometry, $box, $polygon, $center, $centerSphere)
Для всех гео-операторов требуется геоиндекс:
2dsphere(для WGS84 — долгота/широта) или2d(для плоских координат 0…360).
| Оператор | Использование | Требования |
|---|---|---|
$geoWithin | { loc: { $geoWithin: { $geometry: … } } } | loc — Point, LineString, Polygon в GeoJSON. Поддерживает $box, $polygon, $center, $centerSphere (устар., только для 2d). |
$geoIntersects | { loc: { $geoIntersects: { $geometry: … } } } | Только с 2dsphere, loc и geometry — GeoJSON. |
$near / $nearSphere | { loc: { $near: { $geometry: point, $maxDistance: 1000 } } } | Возвращает отсортированные по расстоянию документы. $nearSphere учитывает сферичность Земли. Требует одного гео-индекса в коллекции. Не совместим с sort(). |
$maxDistance | в $near/$nearSphere | в метрах (для 2dsphere) или радианах (для 2d). |
$minDistance | аналогично | минимальное расстояние (фильтр ближайших). |
$geometry | { type: "Point", coordinates: [lon, lat] } | Формат GeoJSON. Обязательно: coordinates в порядке [долгота, широта], а не наоборот. |
$box | { $box: [[minLon, minLat], [maxLon, maxLat]] } | Только для 2d, устаревшее. |
$polygon | { $polygon: [[x1,y1], [x2,y2], …] } | Только для 2d. |
$center / $centerSphere | { $center: [[centerLon, centerLat], radius] } | radius — в градусах (для $center) или радианах (для $centerSphere). Только 2d. |
5. Проекция (projection)
Указывает, какие поля включать/исключать в результатах.
Синтаксис:
{ <поле1>: <0|1|выражение>, <поле2>: … }
Правила:
- Нельзя смешивать
0и1, кроме исключения:_id: 0при общем1. _idвключается по умолчанию. Чтобы исключить — указать_id: 0.1— включить поле;0— исключить.- Можно использовать
$slice,$elemMatch,$(позиционный оператор) на уровне массивов. - В
$find()проекция не поддерживает агрегационные выражения ($add,$concatи т.д.). Для этого —$projectв агрегации.
Примеры:
// Только name и email, без _id
{ name: 1, email: 1, _id: 0 }
// Все поля, кроме password и tokens
{ password: 0, tokens: 0 }
// Первые 3 элемента массива history
{ history: { $slice: 3 } }
// Последние 2 элемента
{ history: { $slice: -2 } }
// Элементы 5–10 (пропустить 5, взять 5)
{ history: { $slice: [5, 5] } }
// Только первый элемент массива, удовлетворяющий условию
{ "comments": { $elemMatch: { rating: { $gte: 4 } } } }
// Позиционный оператор: вернуть только тот элемент comments, который matched в запросе
db.posts.find(
{ "comments.author": "timur" },
{ "comments.$": 1 } // → вернёт только первый подходящий комментарий
)
Важно:
$elemMatchв проекции возвращает только первый подходящий элемент.$работает только если в фильтре используется условие на элемент массива (например,"comments.author"), и возвращает первый matched элемент.
6. Сортировка (sort)
Синтаксис:
{ поле1: 1 | -1, поле2: 1 | -1, … }
1— по возрастанию (A→Z, 0→∞, старые → новые даты)-1— по убыванию
Поведение:
- Сортирует после фильтрации.
- Поддерживает составные сортировки.
- Может использовать составной индекс, если порядок полей и направления совпадают.
null,undefined, отсутствующие поля считаются равными и идут в конце при1, в начале при-1.MinKeyвсегда первым при1,MaxKey— последним.- При сортировке строк применяется collation (если указана), иначе — бинарное сравнение (UTF-8 byte order).
Ограничения:
- Без индекса сортирует в памяти. Лимит —
32 MB(иначе ошибкаSort exceeded memory limit). - Чтобы обойти — использовать
allowDiskUse: trueв агрегации, либо добавить индекс. - Нельзя сортировать по полю, не входящему в состав индекса, если индекс — multikey (массив), и сортируемое поле не последнее в индексе.
Collation в сортировке:
db.coll.find().sort({ name: 1 }).collation({
locale: "ru",
strength: 2, // 1 — только базовые символы, 2 — регистронезависимо, 3 — чувствительно к регистру
caseLevel: false, // учитывать регистр отдельно (для strength=1 и 2)
numericOrdering: true, // "2" < "10" (вместо лексикографического "10" < "2")
caseFirst: "upper", // при strength=3: upper/lower/off
backwards: false // для некоторых языков (французский) — акценты в обратном порядке
})
7. Индексы (Indexes)
Типы индексов:
| Тип | Создание | Описание |
|---|---|---|
| Single Field | db.coll.createIndex({ поле: 1 }) | По одному полю. Порядок (1/-1) важен для сортировки и составных индексов, но не для поиска равенства. |
| Compound | db.coll.createIndex({ a: 1, b: -1, c: 1 }) | По нескольким полям. Правило EON: Equality → Sort → Range. Пример: { status: 1, createdAt: -1, score: 1 } эффективен для status = "active", sort(createdAt), score > 0.5. |
| Multikey | автоматически | При индексации поля-массива. Создаёт отдельную запись в индексе для каждого элемента массива. Ограничения: не более одного multikey-поля в составном индексе; нельзя индексировать массив объектов, если запрос использует $elemMatch с несколькими условиями на разные подполя (требуется partialFilterExpression). |
| Text | db.coll.createIndex({ title: "text", body: "text" }, { default_language: "russian" }) | Полноценный текстовый поиск. Поддерживает веса: { title: "text", body: "text" }, { weights: { title: 10, body: 1 } }. Один text-индекс на коллекцию (можно объединять поля). |
| Wildcard | db.coll.createIndex({ "$**": 1 }) или { "user.$**": 1 } | Индексирует все вложенные поля. Полезен для неизвестной/динамической схемы. Поддерживает исключения: { "user.$**": 1 }, { "user.password": 0 }. Не поддерживает TTL, уникальность, partial. |
| 2dsphere | db.coll.createIndex({ loc: "2dsphere" }) | Для геоданных в формате GeoJSON (Point, LineString, Polygon). Поддерживает $near, $geoWithin, $geoIntersects. |
| 2d | db.coll.createIndex({ loc: "2d" }) | Для плоских координат [x, y], x,y ∈ [−180, 180]. Устаревший. |
| Hashed | db.coll.createIndex({ _id: "hashed" }) | Хэш-индекс по одному полю. Используется только для шардирования по хэшу. Не поддерживает диапазонные запросы ($gt, sort). |
| TTL | db.coll.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 }) | Автоматическое удаление документов по времени жизни. Поле должно быть Date. Работает только на верхнеуровневом поле. Удаление — фоновым демоном (каждые 60 сек). |
Опции при создании индекса:
| Опция | Значение по умолчанию | Описание |
|---|---|---|
unique | false | Запрещает дубликаты значений (включая null). Если поле отсутствует — считается как null. |
partialFilterExpression | null | Условие ({ status: "active" }), по которому документы попадают в индекс. Экономит место и повышает производительность. |
sparse | false | Устаревшая альтернатива partialFilterExpression. Индексирует только документы, где поле присутствует (но не null). Не рекомендуется. |
background | false | Создаёт индекс в фоне без блокировки записи. Влияет только на создание, не на использование. В MongoDB 4.2+ все индексы фоновые по умолчанию в replica set. |
collation | локаль по умолчанию | Правила сравнения строк. Индекс с collation может использоваться только в запросах с той же collation. |
name | автогенерируемое | Имя индекса (для удаления: db.coll.dropIndex("имя")). |
hidden | false (MongoDB 4.4+) | Скрывает индекс от планировщика запросов. Полезно для экспериментов. |
expireAfterSeconds | — | Только для TTL-индексов. |
weights | {} | Только для text-индексов. |
default_language | "english" | Для text-индексов. Поддерживает "russian". |
language_override | "language" | Поле в документе, переопределяющее язык для text-индекса. |
Ограничения индексов:
- Максимум 64 индекса на коллекцию (включая
_id). - Общий размер всех индексов — ограничен памятью/диском, но рекомендуется не превышать 50% RAM.
- Составной индекс: максимум 32 поля.
- Размер ключа индекса — не более 1024 байт (иначе
KeyTooLarge). _id-индекс всегда уникален и не может быть удалён.
Управление индексами:
// Создать
db.coll.createIndex({ a: 1 }, { name: "idx_a", unique: true })
// Посмотреть все
db.coll.getIndexes()
// Удалить по имени
db.coll.dropIndex("idx_a")
// Удалить по спецификации
db.coll.dropIndex({ a: 1 })
// Удалить все (кроме _id)
db.coll.dropIndexes()
// Статистика по использованию (требует serverStatus или профилирование)
db.coll.aggregate([{ $indexStats: {} }])
8. Агрегация (Aggregation Pipeline)
Агрегация — мощный механизм трансформации, фильтрации, группировки и анализа данных. Pipeline состоит из стадий (stages), каждая из которых преобразует поток документов и передаёт результат следующей.
Основные характеристики:
- Выполняется последовательно, слева направо.
- Каждая стадия получает поток документов (не массив), обрабатывает их по одному (за исключением
$group,$sort,$bucketи т.п.). - Промежуточные результаты могут материализовываться на диске (если
allowDiskUse: trueи объём > 100 МБ). - Поддерживает оптимизации: свёртку
$project/$addFields, перенос$matchв начало, слияние$sort+$limit, pushdown в шарды. - Все стадии начинаются с
$.
8.1. Стадии Pipeline (в алфавитном порядке)
$addFields
Добавляет новые поля или перезаписывает существующие (аналог $set в новых версиях).
Синтаксис:
{ $addFields: { <новоеПоле>: <выражение>, … } }
Особенности:
- Не удаляет исходные поля.
- Можно ссылаться на уже добавленные поля внутри одного объекта только в MongoDB 5.0+ (до этого — ошибка циклической зависимости).
- Пример:
{ $addFields: {
total: { $add: ["$price", "$tax"] },
isExpensive: { $gt: ["$price", 1000] },
tagsCount: { $size: "$tags" }
} }
$bucket
Группирует входные документы по диапазонам («ведрам») на основе значения числового или строкового поля.
Синтаксис:
{
$bucket: {
groupBy: <выражение>,
boundaries: [<min>, <val1>, <val2>, …, <max>],
default: <значение>,
output: { <поле>: { <аккум>: <выражение> }, … }
}
}
Требования:
boundaries— массив возрастающих значений (числа, даты, строки).- Каждый документ попадает в ведро
[boundaries[i], boundaries[i+1]), т.е. левая граница включена, правая — нет. default— значение для документов вне всех границ (например,"other"). Обязательно, если возможны такие документы.
Пример:
{
$bucket: {
groupBy: "$score",
boundaries: [0, 20, 40, 60, 80, 100],
default: "invalid",
output: {
count: { $sum: 1 },
avgScore: { $avg: "$score" }
}
}
}
// → { _id: 0, count: 12, avgScore: 15.2 }, { _id: 20, … }, { _id: "invalid", … }
$bucketAuto
Автоматически определяет границы вёдер для равномерного распределения документов.
Синтаксис:
{
$bucketAuto: {
groupBy: <выражение>,
buckets: <N>,
output: { … },
granularity: "R5" | "R10" | "E6" | "E12" | "POWERSOF2" | "1-2-5" | "E192" | "E48" | "E24" | "E3"
}
}
Параметры:
buckets: целое число ≥ 1.granularity: округление границ (стандарты IEC/ISO для электроники и инженерии). Например,"1-2-5"даёт границы вида10, 20, 50, 100, 200….
Примечание: не гарантирует точное равенство числа документов в ведре — зависит от распределения данных.
$collStats
Возвращает статистику по коллекции (размер, индексы, операции).
Синтаксис:
{ $collStats: {
latencyStats: { histograms: true },
storageStats: { scale: 1 },
count: { },
queryExecStats: { }
} }
Возможные подразделы:
latencyStats— задержки операций (чтение, запись, команды).storageStats— размер данных, индексов, фрагментация (аналогdb.coll.stats()).count— общее число документов.queryExecStats— статистика по планам выполнения запросов.
Используется для мониторинга и диагностики. Результат — один документ.
$count
Считает общее число документов на входе и выводит один документ с полем.
Синтаксис:
{ $count: "fieldName" }
Результат: { fieldName: 42 }
Эквивалент:
{ $group: { _id: null, fieldName: { $sum: 1 } } },
{ $project: { _id: 0, fieldName: 1 } }
$densify
(Начиная с MongoDB 5.1)
Добавляет «пропущенные» документы в последовательность на основе временной или числовой оси.
Синтаксис:
{
$densify: {
field: "timestamp",
range: {
step: 3600, // шаг в секундах (для Date), или число
unit: "hour", // "millisecond", "second", "minute", "hour", "day", "week", "month", "quarter", "year"
bounds: [start, end] | "full" // "full" — от min до max по данным
}
}
}
Пример:
// Заполнить пропуски в почасовых метриках
{ $densify: { field: "ts", range: { step: 1, unit: "hour", bounds: "full" } } }
Требует, чтобы
fieldбылDateили числом. Часто комбинируется с$fill.
$documents
(Внутренняя стадия, начиная с MongoDB 5.1)
Позволяет вставить документы непосредственно в pipeline, без обращения к коллекции.
Синтаксис:
{ $documents: [ { a: 1 }, { b: 2 } ] }
Использование:
- В
$facet,$lookup(sub-pipeline). - Для тестирования, генерации данных, join с «виртуальной» таблицей.
Аналог
VALUESв SQL.
$facet
Параллельное выполнение нескольких подконвейеров над одним и тем же набором документов.
Синтаксис:
{
$facet: {
output1: [ <стадии> ],
output2: [ <стадии> ],
…
}
}
Результат: один документ с полями output1, output2, … — каждый содержит массив результатов соответствующего подконвейера.
Пример: мульти-агрегация для дашборда:
{
$facet: {
byStatus: [
{ $group: { _id: "$status", count: { $sum: 1 } } }
],
topUsers: [
{ $group: { _id: "$user", total: { $sum: "$amount" } } },
{ $sort: { total: -1 } },
{ $limit: 5 }
],
stats: [
{ $group: {
_id: null,
avg: { $avg: "$amount" },
max: { $max: "$amount" },
min: { $min: "$amount" }
} }
]
}
}
Ограничения:
- Каждый подконвейер должен умещаться в 16 МБ (результат массива).
- Нельзя использовать
$out,$merge,$search,$geoNearвнутри$facet.
$fill
(Начиная с MongoDB 5.3)
Заполняет null/отсутствующие значения в полях на основе соседних документов (аналог fillna в pandas).
Синтаксис:
{
$fill: {
partitionBy: "$userId",
sortBy: { ts: 1 },
output: {
temperature: { value: 0 },
status: { method: "locf" }, // locf — last observation carried forward
location: { method: "linear" } // только для чисел/дат
}
}
}
Методы:
value: подставить константу.locf: взять значение из предыдущего документа.linear: линейная интерполяция (только числа/даты; требует равномерного шага вsortBy).
Часто используется после
$densify.
$geoNear
Возвращает документы, отсортированные по расстоянию до заданной точки.
Синтаксис:
{
$geoNear: {
near: { type: "Point", coordinates: [lon, lat] },
distanceField: "dist", // обязательное поле для записи расстояния
distanceMultiplier: 1, // коэффициент (например, для км: 0.001 при метрах)
maxDistance: 5000, // в метрах (для 2dsphere)
minDistance: 0,
query: { status: "active" }, // дополнительный фильтр
spherical: true, // для 2dsphere — true
key: "loc", // имя поля с геоданными (если не "_id")
includeLocs: "coords" // сохранить координаты найденной точки
}
}
Особенности:
- Должна быть первой стадией в pipeline.
- Требует одного геоиндекса (
2dsphereили2d). - Возвращает отсортированные документы —
$sortпосле$geoNearизбыточен.
$graphLookup
Рекурсивный поиск по графу (например, иерархия подразделений, цепочка рефералов).
Синтаксис:
{
$graphLookup: {
from: "employees",
startWith: "$_id",
connectFromField: "_id",
connectToField: "managerId",
as: "subordinates",
maxDepth: 5,
depthField: "level",
restrictSearchWithMatch: { status: "active" }
}
}
Параметры:
from— имя коллекции (только локальной БД).startWith— начальное значение (массив или скаляр).connectFromField/connectToField— поля для связи («откуда» → «куда»).as— имя выходного массива.maxDepth— глубина рекурсии (по умолчанию — бесконечно, но ограничена сервером).depthField— добавить поле с уровнем вложенности в каждый найденный документ.restrictSearchWithMatch— фильтр на каждый шаг рекурсии.
Важно: неэффективен на больших графах; лучше использовать Neo4j для сложных графовых задач.
$group
Группирует документы по выражению и вычисляет агрегаты.
Синтаксис:
{
$group: {
_id: <выражение>, // null — одна группа; "$field" — по полю; { a: "$a", b: "$b" } — составной ключ
поле1: { <аккум>: <выражение> },
…
}
}
Аккумуляторы (см. раздел 8.2 ниже):
$sum, $avg, $min, $max, $first, $last, $push, $addToSet, $stdDevPop, $stdDevSamp, $mergeObjects, $accumulator (пользовательский), $function (JS), $top, $bottom, $topN, $bottomN (MongoDB 5.2+).
Пример:
{
$group: {
_id: { year: { $year: "$createdAt" }, month: { $month: "$createdAt" } },
totalSales: { $sum: "$amount" },
uniqueUsers: { $addToSet: "$userId" },
firstOrder: { $first: "$$ROOT" }
}
}
Оптимизация: если
_id: null, MongoDB может использовать оптимизацию scalar group (без перегруппировки).
$indexStats
Возвращает статистику по использованию индексов.
Синтаксис:
{ $indexStats: {} }
Результат — документы вида:
{
name: "status_1_createdAt_-1",
key: { status: 1, createdAt: -1 },
host: "host:port",
accesses: { ops: 1250, since: ISODate("…") }
}
Поле
ops— число операций с моментаsince. Сбрасывается при рестарте сервера.
$limit
Ограничивает количество документов на выходе.
Синтаксис:
{ $limit: 10 }
Оптимизации:
- Если перед
$limitесть$sort, MongoDB может использовать Top-K Sort (не сортировать всю коллекцию, а держать только K лучших). - Может «проталкиваться» в шарды.
$listLocalSessions, $listSessions
(Для внутреннего использования)
Возвращают активные сессии на локальном узле или в кластере.
$lookup
Выполняет left outer join с другой коллекцией.
3 формы:
-
Обычная (равенство полей):
{
$lookup: {
from: "orders",
localField: "_id",
foreignField: "userId",
as: "orders"
}
} -
Sub-pipeline (гибкий join):
{
$lookup: {
from: "orders",
let: { userId: "$_id", status: "$premium" },
pipeline: [
{ $match: {
$expr: {
$and: [
{ $eq: ["$userId", "$$userId"] },
{ $gte: ["$amount", { $cond: ["$$status", 1000, 500] }] }
]
}
}
},
{ $project: { _id: 0, date: 1, amount: 1 } }
],
as: "bigOrders"
}
} -
Uncorrelated subquery (MongoDB 5.1+) — без
let, статический pipeline.
Ограничения:
from— только локальная БД, не шардированная (если сама коллекция не шардирована).- Результат — массив (даже если 0 или 1 документ).
- Для join с шардами требуется, чтобы
localFieldбыл shard key.
$match
Фильтрует документы по условию (аналог find()).
Синтаксис:
{ $match: { status: "active", score: { $gt: 10 } } }
Оптимизация:
- MongoDB «перемещает»
$matchкак можно ближе к началу pipeline. - Если стоит сразу после
$lookup, может использоваться для pushdown вfrom-коллекцию.
$merge
Записывает результат pipeline в коллекцию с объединением (в отличие от $out, который заменяет коллекцию).
Синтаксис:
{
$merge: {
into: "report_daily",
on: "_id", // ключ объединения (массив полей)
let: { ts: "$timestamp" },
whenMatched: [ // pipeline для обновления существующих
{ $addFields: { updatedAt: "$$ts" } },
{ $mergeObjects: ["$$new", "$$ROOT"] }
],
whenNotMatched: "insert", // "insert", "discard", "fail"
unique: true // требует уникального индекса по `on`
}
}
Режимы whenMatched:
"replace"— полная замена (по умолчанию)."keepExisting"— оставить старый документ."merge"— объединить поля (новые перезаписывают старые)."fail"— ошибка при конфликте.[ pipeline ]— кастомная логика (можно использовать$$new,$$ROOT,$$let).
Поддерживает шардированные коллекции (начиная с 4.2). Идеально для ETL, материализованных представлений.
$out
Записывает результат pipeline в коллекцию, заменяя её содержимое.
Синтаксис:
{ $out: "new_collection_name" }
// или
{ $out: { db: "otherdb", coll: "target" } } // 4.4+
Ограничения:
- Коллекция создаётся, если не существует.
- Всегда атомарна (старая коллекция удаляется только после успешного завершения).
- Не поддерживает шардирование.
- Нельзя использовать после
$facet,$geoNear,$collStats.
$planCacheStats
Возвращает статистику по кэшу планов запросов.
Результат — документы с queryHash, planCacheKey, works, fails, createdFromQuery, createdFromHint.
$project
Выбирает/переименовывает/вычисляет поля (устаревшая форма $addFields + проекция).
Синтаксис:
{ $project: {
_id: 0,
name: 1,
email: "$contact.email",
year: { $year: "$createdAt" },
tagsCount: { $size: "$tags" }
} }
В новых версиях предпочтительно использовать
$addFields+$unset, т.к.$projectне позволяет одновременно включать и вычислять поля без дублирования.
$redact
(Устаревший, начиная с 4.4)
Рекурсивно фильтрует документы и поддокументы на основе выражения.
Замена: $project + $cond + $filter.
$replaceRoot, $replaceWith
Поднимает вложенный документ на верхний уровень.
Синтаксис:
{ $replaceRoot: { newRoot: "$embedded.doc" } }
// или
{ $replaceWith: "$embedded.doc" } // MongoDB 4.2+
Пример:
// Было: { _id: 1, data: { name: "Timur", age: 30 } }
// После $replaceWith: "$data" → { name: "Timur", age: 30 }
$sample
Случайная выборка документов.
Синтаксис:
{ $sample: { size: 100 } }
Особенности:
- Не гарантирует равномерность при
size > 5%коллекции. - При
size ≥ collection size— возвращает все документы в случайном порядке. - Не использует индексы.
$search (MongoDB Atlas)
Полнотекстовый, семантический, векторный поиск в Atlas Search.
Пример:
{
$search: {
index: "default",
text: {
query: "mongodb",
path: ["title", "body"]
}
}
}
Требует Atlas. Не доступен в Community/Enterprise Edition без Atlas.
$set
Псевдоним для $addFields (начиная с MongoDB 4.2). Предпочтителен для читаемости.
$setWindowFields
(Начиная с MongoDB 5.0)
Аналог оконных функций в SQL (OVER, PARTITION BY, ORDER BY, ROWS/RANGE).
Синтаксис:
{
$setWindowFields: {
partitionBy: "$department",
sortBy: { salary: -1 },
output: {
rank: { $rank: {} },
movingAvg: {
$derivative: { // или $avg, $sum, $min, $max, $stdDevPop, $stdDevSamp
input: "$salary",
unit: "month"
},
window: { documents: [-2, 0] } // текущий + 2 предыдущих
},
cumulativeCount: {
$documentNumber: {},
window: { documents: ["unbounded", "current"] }
}
}
}
}
Встроенные функции:
$rank,$denseRank,$documentNumber$shift— сдвиг значений$derivative,$integral— для временных рядов$avg,$sum,$min,$max,$stdDevPop,$stdDevSamp— с окном
Требует сортировки (
sortBy). Мощный инструмент для аналитики временных рядов и ранжирования.
$skip
Пропускает N документов.
Синтаксис:
{ $skip: 20 }
Неэффективен без
$sort+ индекса. Лучше использовать ключевое постраничное разбиение (_id > lastSeenId).
$sort
Сортирует документы.
Синтаксис:
{ $sort: { name: 1, createdAt: -1 } }
Оптимизации:
- Может использовать индекс.
$sort+$limit→ Top-K.- В MongoDB 6.0+ поддерживает сортировку по выражению:
{ $sort: { fullName: { $concat: ["$firstName", " ", "$lastName"] } } }.
$sortByCount
Группирует по полю и сортирует по убыванию количества.
Синтаксис:
{ $sortByCount: "$status" }
Эквивалент:
{ $group: { _id: "$status", count: { $sum: 1 } } },
{ $sort: { count: -1 } }
$unionWith
Объединяет результаты двух pipeline’ов (аналог UNION ALL в SQL).
Синтаксис:
{ $unionWith: "other_collection" }
// или
{ $unionWith: {
coll: "logs_old",
pipeline: [
{ $match: { ts: { $lt: ISODate("2025-01-01") } } },
{ $addFields: { source: "archive" } }
]
} }
Особенности:
- Порядок не гарантируется — добавьте
$sortпри необходимости. - Коллекции должны быть в одной БД.
$unset
Удаляет указанные поля.
Синтаксис:
{ $unset: ["password", "tokens", "internal.flag"] }
Эквивалентно
$project: { password: 0, tokens: 0, … }, но короче и безопаснее (игнорирует отсутствующие поля).
$unwind
Разворачивает массив в отдельные документы.
Синтаксис:
{ $unwind: "$tags" }
// или
{ $unwind: {
path: "$comments",
includeArrayIndex: "idx",
preserveNullAndEmptyArrays: true
} }
Параметры:
includeArrayIndex— добавить поле с индексом элемента.preserveNullAndEmptyArrays— еслиtrue, документы сnull/[]не удаляются.
После
$unwindдокументы дублируются — учитывайте при агрегации ($group→$sum: 1даст число элементов, а не документов!).
8.2. Аккумуляторы (в $group, $setWindowFields, $bucket)
| Аккумулятор | Описание | Пример |
|---|---|---|
$sum | сумма/счётчик | { $sum: 1 }, { $sum: "$amount" } |
$avg | среднее | { $avg: "$score" } |
$min / $max | минимум/максимум | { $min: "$price" } |
$first / $last | первый/последний в группе | { $first: "$ts" } |
$push | собирает все значения в массив | { $push: "$tag" } |
$addToSet | собирает уникальные значения | { $addToSet: "$userId" } |
$stdDevPop / $stdDevSamp | стандартное отклонение (генеральная/выборочная) | { $stdDevSamp: "$value" } |
$mergeObjects | объединяет объекты (последний перезаписывает) | { $mergeObjects: "$config" } |
$accumulator | пользовательская агрегатная функция (на JS/C++) | — |
$function | пользовательская скалярная функция (на JS) | — |
$top / $bottom | возвращает документ с экстремальным значением | { $top: { output: "$$ROOT", sortBy: { score: -1 } } } |
$topN / $bottomN | возвращает N документов | { $topN: { n: 3, output: "$$ROOT", sortBy: { ts: -1 } } } |
$top/$bottom— MongoDB 5.2+, заменяют конструкции с$push+$slice+$arrayElemAt.
9. Транзакции (Transactions)
Начиная с MongoDB 4.0 (реплика-сеты) и 4.2 (шардированные кластеры), MongoDB поддерживает много-документные, много-коллекционные, много-БД транзакции с уровнем изоляции snapshot (MVCC).
Основные характеристики:
- Атомарность, согласованность, изолированность (snapshot), но не долговечность по умолчанию (см.
writeConcern). - Максимальная длительность: 60 секунд (по умолчанию), можно увеличить до 120 секунд через
transactionLifetimeLimitSeconds. - Максимум 1 000 изменённых документов за одну транзакцию (ограничение на лог операций).
- Все операции внутри транзакции должны использовать один сессион (session).
- Поддерживаются:
insert,update,delete,findAndModify,aggregate(без$out,$merge,$lookupна шардированные коллекции),distinct.
Работа с транзакциями (в shell / драйверах):
// 1. Создать сессию
const session = db.getMongo().startSession();
// 2. Начать транзакцию
session.startTransaction({
readConcern: { level: "snapshot" },
writeConcern: { w: "majority", j: true },
readPreference: "primary" // обязательно для транзакций
});
try {
// 3. Выполнять операции в сессии
db.users.updateOne(
{ _id: ObjectId("..."), balance: { $gte: 100 } },
{ $inc: { balance: -100 } },
{ session: session }
);
db.orders.insertOne(
{ userId: ObjectId("..."), amount: 100, ts: new Date() },
{ session: session }
);
// 4. Зафиксировать
session.commitTransaction();
print("Транзакция успешна");
} catch (error) {
// 5. Откат при ошибке
session.abortTransaction();
throw error;
} finally {
// 6. Закрыть сессию (важно!)
session.endSession();
}
Параметры startTransaction():
| Параметр | Возможные значения | Описание |
|---|---|---|
readConcern | { level: "local" | "majority" | "snapshot" } | snapshot — единственный допустимый уровень в транзакциях (гарантирует согласованное чтение). |
writeConcern | { w: <n | "majority" | tag>, j: true|false, wtimeout: ms } | Должен быть одинаковым для всех операций в транзакции. Если не указан — берётся из настроек коллекции (но лучше указать явно). |
maxCommitTimeMS | число мс | Таймаут на фазу коммита (по умолчанию — 10 мин). |
Ограничения и особенности:
- Нельзя использовать:
$out,$merge,$search,$geoNear,$indexStats,$collStats,$listSessions,$currentOp,$planCacheStats.- Операции DDL:
createCollection,drop,createIndex,dropIndex,renameCollection.
- Нельзя читать из одной коллекции и писать в неё же в рамках одной транзакции (write skew — разрешено с MongoDB 4.2+, но с оговорками).
- При перегрузке оперативной памяти транзакция может быть прервана с ошибкой
TransientTransactionError. - Транзакции не видны в
mongostat,serverStatus— только вcurrentOp.
Обработка ошибок:
TransientTransactionError— повторить всю транзакцию (например, при failover).UnknownTransactionCommitResult— неизвестен результат коммита (повторитьcommit, но не всю транзакцию).WriteConflict— конфликт записи (автоматически перезапускается до 120 сек; если не удалось — ошибка).
Рекомендуется оборачивать транзакции в retry-логику (например, с
retryWrites: true+withTransaction()в драйверах).
10. Репликация (Replica Sets)
Группа серверов (обычно 3+), обеспечивающая высокую доступность и отказоустойчивость.
Архитектура:
- Primary — один, принимает все записи и большинство чтений.
- Secondary — 0+, реплицируют данные, обслуживают чтение (если
readPreferenceразрешает). - Arbiter — «арбитр», без данных, участвует только в выборах (не рекомендуется в продакшене).
- Hidden, Delayed, Priority 0 — специальные роли вторичных узлов.
Конфигурация replica set (в shell):
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "node1:27017" },
{ _id: 1, host: "node2:27017", priority: 0.5 },
{ _id: 2, host: "node3:27017", hidden: true, votes: 0 }
]
})
Важные параметры узла:
| Параметр | Тип | Значение по умолчанию | Описание |
|---|---|---|---|
priority | число | 1.0 | Приоритет при выборах. 0 — не может стать primary. |
votes | 0 | 1 | 1 | Голосует ли узел. Максимум 7 голосующих узлов. |
hidden | bool | false | Не виден клиентам (только для бэкапов, аналитики). |
slaveDelay | сек | 0 | Задержка репликации (только для hidden: true). Для защиты от drop. |
tags | объект | {} | Произвольные метки (например, { dc: "ufa", rack: "a1" }) — используются в readPreference и writeConcern. |
buildIndexes | bool | true | Строить ли индексы (если false — нельзя сделать primary). |
Write Concern в replica set:
{ w: 1 } // подтверждение от primary
{ w: 2 } // от primary + 1 secondary
{ w: "majority" } // от большинства голосующих узлов
{ w: "tag:dc_ufa" } // от узлов с тегом `dc: "ufa"`
{ j: true } // ждать записи в journal
{ wtimeout: 5000 } // таймаут в мс
Read Preference:
| Режим | Описание |
|---|---|
primary | только primary (по умолчанию) |
primaryPreferred | primary, при недоступности — secondary |
secondary | только secondary |
secondaryPreferred | secondary, при недоступности — primary |
nearest | ближайший по latency (не по гео!) |
Можно комбинировать с тегами:
db.collection.find().readPref("secondary", [ { dc: "ufa" } ])
Операции управления:
rs.status() // состояние replica set
rs.conf() // конфигурация
rs.reconfig(conf) // изменить конфиг (без downtime при force: false)
rs.stepDown(60) // primary уступает за 60 сек
rs.freeze(120) // запретить участвовать в выборах 120 сек
rs.syncFrom("host") // принудительно реплицировать с узла (только для secondary)
Ограничения:
- Минимум 3 узла для отказоустойчивости (2 + arbiter не рекомендуется — потеря данных при split-brain).
- Все узлы должны иметь одинаковую версию MongoDB (мажорная и минорная).
- Максимум 50 узлов в replica set, из них 7 голосующих.
11. Шардирование (Sharding)
Горизонтальное масштабирование: данные распределяются по нескольким replica sets (шардам).
Компоненты кластера:
- Config Server — replica set, хранит метаданные (маршруты, диапазоны шардов).
- mongos — роутер (stateless), принимает клиентские запросы, перенаправляет в шарды.
- Shard — replica set, хранит данные (обычно 2+ шарда + 1 arbiter на каждый).
Типы шардирования:
- Hashed Shard Key
- Ключ:
{ userId: "hashed" } - Равномерное распределение, но не поддерживает диапазонные запросы.
- Ключ:
- Range-based Shard Key
- Ключ:
{ region: 1, createdAt: -1 } - Поддерживает диапазоны и сортировку, но риск «горячего» шарда при монотонном ключе (например,
ObjectId).
- Ключ:
- Zone Sharding (до 5.0 — Tag-Aware Sharding)
- Привязка диапазонов к зонам:
{ region: "EU" } → shard01,{ region: "RU" } → shard02.
- Привязка диапазонов к зонам:
- Custom Shard Key (MongoDB 4.4+)
- Композитный ключ с хэшированием части:
{ region: 1, userId: "hashed" }.
- Композитный ключ с хэшированием части:
Включение шардирования:
// 1. Включить шардирование для БД
sh.enableSharding("universe")
// 2. Определить shard key и включить для коллекции
sh.shardCollection("universe.posts", { "authorId": 1, "_id": -1 }, false, { unique: true })
Операции:
sh.status() // состояние кластера
sh.splitAt("universe.posts", { authorId: "user123" })
sh.splitFind("universe.posts", { authorId: { $gt: "user500" } })
sh.moveChunk("universe.posts", { authorId: "user123" }, "shard02")
sh.disableBalancing("universe.posts")
sh.enableBalancing("universe.posts")
Важные параметры:
chunkSize— размер чанка (по умолчанию 64 МБ). Можно изменить глобально:db.settings.updateOne({ _id: "chunksize" }, { $set: { value: 128 } }).balancer— фоновый процесс перераспределения чанков. Управляется черезsh.setBalancerState(true/false).zone— логическая группа шардов. Пример:sh.addShardToZone("shard01", "RU")
sh.addShardToZone("shard02", "EU")
sh.updateZoneKeyRange("universe.users", { region: "RU" }, { region: "RU", userId: MaxKey }, "RU")
Ограничения:
- Shard key не может быть изменён после шардирования коллекции.
- Коллекция должна иметь уникальный индекс, покрывающий shard key (если
unique: true). _id— всегда индексирован, но не обязательно является частью shard key._idдолжен быть уникальным глобально (все шарды).
12. Безопасность
Аутентификация
- SCRAM-SHA-1 (устар.), SCRAM-SHA-256 (по умолчанию), x.509, LDAP, Kerberos (Enterprise).
- Пользователи создаются в БД
adminили локальной БД.
use admin
db.createUser({
user: "timur",
pwd: passwordPrompt(), // безопасный ввод
roles: [
{ role: "readWrite", db: "universe" },
{ role: "dbAdmin", db: "universe" },
{ role: "clusterMonitor", db: "admin" }
],
mechanisms: ["SCRAM-SHA-256"]
})
Роли (встроенные):
| Категория | Роли |
|---|---|
| Database User | read, readWrite |
| Database Admin | dbAdmin, dbOwner, userAdmin |
| Cluster Admin | clusterAdmin, clusterManager, clusterMonitor, hostManager |
| Backup/Restore | backup, restore |
| Superuser | root |
Авторизация (RBAC):
- Разрешения = роль + ресурс (
db,collection,cluster). - Можно создавать кастомные роли:
db.createRole({
role: "devOps",
privileges: [
{ resource: { db: "", collection: "" }, actions: ["listDatabases"] },
{ resource: { cluster: true }, actions: ["serverStatus"] }
],
roles: []
})
Шифрование:
- TLS/SSL — для трафика (настраивается в
mongod.conf:net.ssl.mode: requireSSL). - Encryption at Rest (Enterprise) — прозрачное шифрование данных на диске (WiredTiger).
- Client-Side Field Level Encryption (CSFLE) — шифрование полей на клиенте (библиотеки:
mongocryptd,key vault).
Поддерживаемые алгоритмы:AEAD_AES_256_CBC_HMAC_SHA_512-Random,AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic.
Аудит (Enterprise):
- Логирование операций:
createUser,dropDatabase,authCheck,update,remove. - Конфигурация в
mongod.conf:auditLog:
destination: file
format: JSON
path: /var/log/mongodb/audit.log
filter: '{ atype: { $in: ["createUser", "dropDatabase"] } }'
13. Настройка сервера (mongod.conf)
Основные секции конфигурационного файла (YAML):
storage:
dbPath: /var/lib/mongodb
journal:
enabled: true
wiredTiger:
engineConfig:
cacheSizeGB: 8.0 # ~50% RAM
collectionConfig:
blockCompressor: zlib # snappy (default), zlib, zstd
indexConfig:
prefixCompression: true
systemLog:
destination: file
path: /var/log/mongodb/mongod.log
logAppend: true
verbosity: 0 # 0-5, 1 — debug
net:
port: 27017
bindIp: 127.0.0.1,192.168.1.10
tls:
mode: requireTLS
certificateKeyFile: /etc/ssl/mongo.pem
CAFile: /etc/ssl/ca.pem
security:
authorization: enabled
keyFile: /etc/mongodb-keyfile # для replica set
replication:
replSetName: rs0
oplogSizeMB: 2048 # по умолчанию ~5% диска, минимум 990 МБ
sharding:
clusterRole: shardsvr # или configsvr, mongos
Важные параметры:
wiredTiger.cacheSizeGB— кэш данных/индексов (не должен превышать 50% RAM).oplogSizeMB— размер oplog (журнала репликации); должен покрывать окно репликации (например, 24 ч при высокой нагрузке).setParameter— динамические настройки:setParameter:
transactionLifetimeLimitSeconds: 120
wiredTigerConcurrentReadTransactions: 256
wiredTigerConcurrentWriteTransactions: 128
14. Инструменты администрирования
| Инструмент | Назначение | Команды/Особенности |
|---|---|---|
| mongosh | Shell (заменяет mongo) | Поддержка await, TypeScript-подобный синтаксис, автодополнение. |
| mongodump / mongorestore | Резервное копирование | mongodump --uri="mongodb://host/db" --gzip --archive=backup.gz |
| mongoexport / mongoimport | Экспорт/импорт JSON/CSV | Не для полных бэкапов (не сохраняет индексы, типы BSON). |
| mongostat | Мониторинг в реальном времени | insert, query, update, delete, getmore, command, flushes, mapped, vsize, res. |
| mongotop | Топ операций по времени | Показывает read, write время на коллекцию. |
| db.currentOp() | Текущие операции | Фильтрация по secs_running, op, ns. |
| db.killOp(opid) | Прерывание операции | Осторожно: может нарушить согласованность. |
| Profiler | Лог медленных запросов | db.setProfilingLevel(1, { slowms: 100 }), db.system.profile.find() |
| explain() | Анализ плана запроса | { "queryPlanner", "executionStats", "allPlansExecution" } |
Профилирование:
// Включить профилирование уровня 1 (только медленные)
db.setProfilingLevel(1, { slowms: 50 })
// Посмотреть лог
db.system.profile.find().sort({ ts: -1 }).limit(5)
// Сбросить
db.system.profile.drop()
Explain (пример):
db.users.find({ status: "active" }).sort({ createdAt: -1 }).explain("executionStats")
Ключевые поля в выводе:
executionStages.stage—COLLSCAN,IXSCAN,FETCH,SORT.executionStats.totalDocsExamined— сколько документов прочитано.executionStats.totalKeysExamined— сколько записей индекса прочитано.executionStats.executionTimeMillis— время выполнения.
Идеальный запрос:
totalKeysExamined ≈ totalDocsExamined ≈ nReturned,
stage—IXSCAN, безSORTилиFETCHпри покрывающем индексе.
15. Резервное копирование и восстановление
15.1. Типы бэкапов
| Тип | Инструмент | Преимущества | Недостатки |
|---|---|---|---|
| Logical (BSON/JSON) | mongodump / mongorestore | Портативность, выбор БД/коллекций, совместимость версий | Медленно при больших данных, не атомарно, не сохраняет индексы в точности (пересоздаются) |
| Physical (файловый) | cp / rsync + fsyncLock | Очень быстро, атомарно (если lock), сохраняет всё состояние | Требует остановки записи, зависит от storage engine, не переносим между версиями/ОС |
| Cloud (Atlas, Ops Manager) | Atlas Backup / Ops Manager | Автоматизация, point-in-time recovery, инкрементальные бэкапы | Требует подписки, vendor lock-in |
| Oplog-based | mongodump --oplog + mongorestore --oplogReplay | Возможность восстановления на конкретный момент (в пределах oplog) | Только для replica set, требует достаточного размера oplog |
15.2. mongodump — параметры и практики
mongodump \
--uri="mongodb://user:pass@host:27017/admin?authSource=admin" \
--db=universe \
--collection=posts \
--query='{"createdAt": {"$gte": {"$date": "2025-01-01T00:00:00Z"}}}' \
--gzip \
--archive=/backup/posts_2025.gz \
--oplog # только для replica set primary
Важные флаги:
--gzip— сжатие (экономит место и трафик).--archive— один файл вместо директории (удобно для передачи).--oplog— захватывает oplog в момент дампа (для point-in-time recovery).--numParallelCollections=N— параллельная выгрузка коллекций (ускоряет).--readPreference=secondary— снимать с secondary (разгрузка primary).
⚠️ Ограничения:
mongodumpне сохраняет:
- пользователей и роли (только данные),
- функции
system.js,- индексы с опцией
partialFilterExpression(восстанавливаются без фильтра в старых версиях),- TTL-индексы (пересоздаются, но
expireAfterSecondsсохраняется).- Для пользователей: бэкапить отдельно:
mongodump --db=admin --collection=system.users.
15.3. mongorestore — восстановление
mongorestore \
--uri="mongodb://localhost:27017" \
--gzip \
--archive=/backup/posts_2025.gz \
--oplogReplay \ # применить oplog (если был --oplog)
--oplogLimit=1700000000:123 \ # timestamp:term
--drop \ # удалить коллекции перед восстановлением
--nsFrom="old.*" --nsTo="new.*" # переименование пространств имён
Рекомендации:
- Всегда проверяйте целостность через
--dryRun(но он не гарантирует полную проверку). - При восстановлении в production — делайте в maintenance window, отключите приложение.
- После восстановления — пересоздайте индексы (
db.coll.createIndex(...)) и проверьтеdb.coll.stats().
15.4. Файловые бэкапы (WiredTiger)
Только для standalone или secondary (с остановкой):
# На secondary (предварительно сделать hidden + отключить от репликации)
db.fsyncLock() # блокирует все записи
rs.freeze(10000) # заморозить на 10 000 сек (защита от выборов)
# В другом терминале:
sudo rsync -av /var/lib/mongodb/ /backup/mongodb_$(date +%F)/
# Вернуть в строй:
db.fsyncUnlock()
rs.stepDown()
⚠️ Не применяется к шардированным кластерам — только через
mongodumpили Ops Manager.
15.5. Point-in-Time Recovery (PITR)
Требования:
- Replica set.
- Достаточный размер
oplog(должен покрывать окно бэкапа + время восстановления). - Бэкап через
mongodump --oplog.
Процедура:
- Сделать
mongodump --oplog. - Записать
optimeиз oplog-файла:bsondump dump/oplog.bson | tail -1 # → "ts": { "$timestamp": { "t": 1700000000, "i": 123 } } - Восстановить дамп:
mongorestore --oplogReplay --oplogLimit=1700000000:123.
16. Обновление MongoDB
Стратегии:
- Rolling Upgrade — для replica set: secondary → secondary → primary (stepDown).
- Major Version Upgrade — только последовательно: 5.0 → 6.0 → 7.0 (пропуск версий запрещён).
- Feature Compatibility Version (FCV) — контролирует доступность новых функций.
Процедура (replica set, 6.0 → 7.0):
-
Проверить совместимость:
db.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 })
// → { "version": "6.0" } -
Обновить все secondary (по одному):
- Остановить
mongod. - Заменить бинарники на 7.0.
- Запустить.
- Дождаться синхронизации (
rs.status()→stateStr: "SECONDARY").
- Остановить
-
Обновить primary:
rs.stepDown(60) // уступить
// затем обновить binary, как secondary -
Обновить FCV:
db.adminCommand({ setFeatureCompatibilityVersion: "7.0" })
⚠️ Перед обновлением:
- Протестировать на staging.
- Проверить deprecated features (например,
aggregateбез курсора в 5.0+).- Убедиться, что драйверы совместимы (например, Node.js driver 5.x+ для MongoDB 6.0+).
17. Мониторинг и диагностика
17.1. Встроенные метрики
| Источник | Команда | Описание |
|---|---|---|
| serverStatus | db.runCommand({ serverStatus: 1 }) | Общее состояние: connections, mem, ops, asserts, extra_info (page faults), wiredTiger stats. |
| replSetGetStatus | rs.status() | Состояние replica set: lag, state, optime. |
| currentOp | db.currentOp({ secs_running: { $gt: 5 } }) | Долгие операции. |
| connections | db.serverStatus().connections | current, available, totalCreated. |
| opcounters | db.serverStatus().opcounters | insert, query, update, delete, getmore, command. |
| wiredTiger cache | db.serverStatus().wiredTiger.cache | bytes currently in the cache, tracked dirty bytes in the cache, maximum bytes configured. |
17.2. Ключевые метрики для алертинга (Prometheus + mongodb_exporter)
| Метрика | Порог (пример) | Описание |
|---|---|---|
mongodb_connections_current | > 80% от connections.available | Исчерпание соединений |
mongodb_replset_member_optime_lag_seconds | > 30 с | Отставание secondary |
mongodb_wt_cache_bytes_dirty | > 5% от cache.max_bytes | Грязные страницы (риск flush) |
mongodb_op_counters_repl_commands | резкий рост | Аномальная активность |
mongodb_asserts_regular | > 0 | Ошибки приложения (например, duplicate key) |
mongodb_extra_info_page_faults | рост на 100+/с | Нехватка RAM, активный swap |
17.3. Профилирование и explain
Профиль медленных запросов:
// Включить
db.setProfilingLevel(1, { slowms: 50 })
// Посмотреть топ-10 медленных
db.system.profile.aggregate([
{ $match: { op: "query", millis: { $gt: 50 } } },
{ $sort: { millis: -1 } },
{ $limit: 10 },
{ $project: { ns: 1, millis: 1, query: 1, ts: 1 } }
])
Explain для агрегации:
db.orders.aggregate([
{ $match: { status: "shipped" } },
{ $group: { _id: "$region", total: { $sum: "$amount" } } }
]).explain("executionStats")
Ключевые индикаторы проблем:
executionStats.nReturned << executionStats.totalDocsExamined— неэффективный фильтр.executionStages.stage: "SORT"послеIXSCAN— отсутствует составной индекс.executionStages.inputStage.stage: "COLLSCAN"— полное сканирование.
18. Миграция данных
18.1. Из другой СУБД (PostgreSQL → MongoDB)
Этапы:
- Анализ схемы: нормализованные таблицы → денормализованные документы.
- Проектирование документной модели (вложенные объекты vs. ссылки).
- ETL-скрипт (Python + PyMongo + psycopg2):
from pymongo import MongoClient
import psycopg2
pg = psycopg2.connect("...")
mongo = MongoClient("...")
with pg.cursor() as cur:
cur.execute("SELECT id, name, email FROM users")
for row in cur:
user = {
"_id": row[0],
"name": row[1],
"email": row[2],
"orders": [] # будет заполнено далее
}
mongo.universe.users.insert_one(user)
# Затем orders + join в памяти или bulk_write
- Валидация: подсчёт строк, контрольные суммы.
18.2. Миграция внутри MongoDB (изменение структуры)
Пример: вынос address в отдельный документ
// 1. Добавить новое поле (без downtime)
db.users.updateMany(
{ address: { $exists: true } },
[
{ $set: { addressId: { $toString: "$_id" } } }
],
{ multi: true }
)
// 2. Создать коллекцию addresses
db.users.aggregate([
{ $match: { address: { $exists: true } } },
{ $project: {
_id: "$addressId",
userId: "$_id",
street: "$address.street",
city: "$address.city"
} },
{ $out: "addresses" }
])
// 3. Удалить address из users (постепенно, пачками)
db.users.updateMany(
{ address: { $exists: true } },
{ $unset: { address: "" } },
{ limit: 1000 }
)
⚠️ Для production: использовать
bulkWriteс паузами, логировать прогресс.
19. Best Practices (проверенные в production)
19.1. Схема и моделирование
- Денормализация — норма, но не злоупотреблять (избыточность → согласованность).
- Максимальный размер документа — 16 МБ; если близко — пересмотреть модель (разделить на поддокументы/коллекции).
- Избегать массивов неограниченного роста (комментарии, логи) → выносить в отдельную коллекцию.
- Использовать
_idразумно:ObjectId— по умолчанию,UUID— при интеграции с внешними системами,- составные
_id— для уникальности по нескольким полям (например,{ userId: ..., ts: ... }).
19.2. Индексы
- Правило EON для составных индексов: Equality → Sort → Range.
- Покрывающие индексы (
indexOnly: trueв explain) — минимум обращений к документам. - Partial indexes для фильтров по статусу:
db.orders.createIndex(
{ userId: 1, createdAt: -1 },
{ partialFilterExpression: { status: "active" } }
) - Избегать индексов на высококардинальных полях без фильтрации (например,
timestampбезstatus).
19.3. Производительность
- Использовать
projection— не выгружать 10 КБ документа ради 2 полей. - Постраничное разбиение через ключ, а не
skip:// Плохо:
db.users.find().sort({ _id: 1 }).skip(10000).limit(10)
// Хорошо:
db.users.find({ _id: { $gt: lastSeenId } }).sort({ _id: 1 }).limit(10) - Bulk-операции вместо поштучных:
db.users.bulkWrite([
{ updateOne: { filter: { _id: 1 }, update: { $inc: { views: 1 } } } },
{ updateOne: { filter: { _id: 2 }, update: { $inc: { views: 1 } } } }
])
19.4. Надёжность
- Replica set из 3+ узлов в разных зонах доступности.
- Oplog size ≥ 24 часа писковой нагрузки.
- Backups ежедневно + PITR для критичных БД.
- Тестирование восстановления — ежеквартально.
19.5. Безопасность
- TLS everywhere — между клиентом, mongos, шардами, config servers.
- RBAC — минимальные привилегии, отдельные учётные записи для приложений.
- CSFLE — для PII (персональных данных):
email,phone,passport. - Аудит — логировать
dropDatabase,createUser,grantRoles.