insertOne и insertMany
Вы вставите отдельные документы и документы пакетами, а также изучите автоматически созданное поле _id, возвращаемое каждой операцией.
«insertOne и insertMany» — бесплатный урок MongoDB Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения MongoDB Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс MongoDB Academy содержит 4 уроков всего.
The Two Insert Methods
MongoDB предоставляет два метода для добавления документов в коллекцию:
- insertOne(doc) — вставляет один документ и возвращает объект с полями
acknowledgedиinsertedId - insertMany(docs) — вставляет массив документов и возвращает
acknowledgedиinsertedIds(объект, сопоставляющий индекс массива со сгенерированным _id)
Оба метода являются синхронными в mongosh и возвращают промисы в драйвере Node.js. Всегда используйте await при вызове этих методов в асинхронных функциях Node.js для корректной обработки ошибок.
// insertOne
const res1 = await db.collection('users').insertOne({ name: 'Alice', age: 28 });
console.log(res1.insertedId); // ObjectId('...')
// insertMany
const res2 = await db.collection('users').insertMany([
{ name: 'Bob', age: 32 },
{ name: 'Carol', age: 25 }
]);
console.log(res2.insertedIds);
// { '0': ObjectId('...'), '1': ObjectId('...') }Auto-Generated ObjectId
При вставке документа без поля _id MongoDB автоматически генерирует ObjectId. ObjectId — это 12-байтовый тип данных BSON, структура которого обеспечивает глобальную уникальность в распределенных узлах без использования центрального счетчика:
- Байты 0-3: метка времени Unix (в секундах) — позволяет выполнять приблизительную сортировку по времени вставки
- Байты 4-8: случайное значение (генерируется один раз для процесса)
- Байты 9-11: инкрементный счетчик (инициализируется случайным значением)
Такая архитектура означает, что два разных сервера, вставляющих документы в одну и ту же миллисекунду, все равно создадут разные ObjectId, что делает распределенную вставку свободной от коллизий.
// Auto-generated ObjectId on insert
db.items.insertOne({ name: 'Widget' });
// { acknowledged: true, insertedId: ObjectId('64a2f3b1c9e7e12345678901') }
// Extract timestamp from an ObjectId
const id = ObjectId('64a2f3b1c9e7e12345678901');
print(id.getTimestamp());
// ISODate('2023-07-03T10:15:29.000Z')
// Sorting by _id gives approximate insertion order
db.items.find({}).sort({ _id: 1 });Supplying Your Own _id
Вы можете переопределить автоматически сгенерированный ObjectId, предоставив собственное значение _id. Подходит любой тип BSON: строка, число, UUID или даже вложенный объект. Единственное требование — значение должно быть уникальным в рамках коллекции.
Пользовательские _id полезны, если у вас уже есть естественный уникальный ключ — артикул товара (SKU), слаг имени пользователя или ID из внешней системы. Однако избегайте использования монотонно возрастающих целых чисел в качестве пользовательских _id в шардированных кластерах — они создают «горячие точки» записи на шарде, который содержит диапазон наибольших ключей.
// String _id (natural key)
db.products.insertOne({
_id: 'SKU-WH-BLK-42',
name: 'Wireless Headphones Black',
price: 79.99
});
// Number _id
db.categories.insertOne({ _id: 100, name: 'Electronics' });
// Duplicate _id throws E11000 duplicate key error:
db.categories.insertOne({ _id: 100, name: 'Gadgets' });
// MongoServerError: E11000 duplicate key errorinsertMany: Bulk Inserts
Метод insertMany принимает массив документов. Он значительно эффективнее, чем вызов insertOne в цикле, поскольку отправляет все документы за один сетевой запрос, а MongoDB может выполнять пакетную запись внутри системы.
По умолчанию insertMany вставляет документы в упорядоченном режиме (ordered mode): если один из документов не проходит проверку или имеет дубликат _id, операция останавливается и не вставляет оставшиеся документы. Установите { ordered: false } в качестве второго аргумента, чтобы переключиться в неупорядоченный режим, при котором MongoDB продолжит вставку остальных документов, даже если некоторые из них будут отклонены.
// Ordered insertMany (default) - stops on first error
db.users.insertMany([
{ name: 'Alice' },
{ name: 'Bob' },
{ name: 'Carol' }
]);
// Unordered insertMany - continues after errors
db.users.insertMany(
[
{ _id: 1, name: 'Alice' },
{ _id: 1, name: 'Duplicate' }, // Will fail (dup key)
{ _id: 2, name: 'Bob' } // Still inserted in unordered mode
],
{ ordered: false }
);Inspecting the insertedIds Object
Объект результата метода insertMany содержит insertedIds в виде объекта (а не массива), где ключами являются строковые представления индексов массива, а значениями — сгенерированные идентификаторы ObjectId. Это позволяет сопоставить каждый исходный документ с его результирующим _id.
В Node.js часто требуется соотнести вставленные документы с их идентификаторами для дальнейшей обработки — например, для создания связанных документов в другой коллекции, которые будут ссылаться на эти новые _id.
const docs = [
{ name: 'Widget A', price: 10 },
{ name: 'Widget B', price: 20 },
{ name: 'Widget C', price: 30 }
];
const result = await db.collection('products').insertMany(docs);
console.log(result.insertedCount); // 3
console.log(result.insertedIds);
// {
// '0': ObjectId('...'),
// '1': ObjectId('...'),
// '2': ObjectId('...')
// }
// Attach _ids back to original docs
docs.forEach((doc, i) => {
doc._id = result.insertedIds[i];
});WriteConcern: Controlling Durability
По умолчанию insertOne и insertMany используют стандартный для соединения параметр write concern — обычно w: 1, что означает, что первичный узел подтверждает запись. Вы можете переопределить это для каждой операции отдельно:
{ w: 0 }— «отправил и забыл», без подтверждения (самый быстрый, небезопасный вариант){ w: 1 }— подтверждение первичным узлом (по умолчанию){ w: 'majority' }— подтверждение большинством участников набора реплик (самый безопасный вариант){ j: true }— дополнительно требует сброса записи в журнал на диске
// Safe insert: confirmed by majority of replica set + journaled
await db.collection('orders').insertOne(
{ orderId: 'ORD-001', total: 99.99, status: 'pending' },
{ writeConcern: { w: 'majority', j: true } }
);
// Slower than w:1 but guaranteed durable even if primary crashesHandling Duplicate Key Errors
MongoDB выдает ошибку дублирования ключа (E11000), когда операция вставки нарушает уникальный индекс — чаще всего это требование уникальности поля _id, но также и любой пользовательский уникальный индекс. В Node.js это вызывает ошибку MongoServerError с кодом 11000.
Всегда оборачивайте операции вставки в блоки try/catch, когда ожидаются ошибки дублирования ключей (например, при апсерте пользователей по email). Проверяйте код ошибки, чтобы отличать дубликаты от других ошибок записи, и реагируйте соответствующим образом — возвращайте статус 409 Conflict из API или обновляйте существующий документ.
try {
await db.collection('users').insertOne({
email: 'alice@example.com',
name: 'Alice'
});
} catch (err) {
if (err.code === 11000) {
// Duplicate key - email already exists
console.log('User with this email already exists');
// Respond with HTTP 409 Conflict in an API
} else {
throw err; // Re-throw unexpected errors
}
}Seeding Test Data With Loops
Распространенной задачей при разработке является наполнение коллекции реалистичными тестовыми данными. Вы можете сделать это напрямую в mongosh с помощью цикла JavaScript или написать скрипт на Node.js. Для больших наборов данных используйте insertMany пакетами по 500-1000 документов вместо одного огромного массива — это позволит избежать проблем с памятью и даст возможность отслеживать прогресс.
Для генерации реалистичных фиктивных данных в Node.js широко используется библиотека @faker-js/faker. Она создает реалистичные имена, адреса электронной почты, адреса, даты и многие другие типы полей, делая тестовые данные неотличимыми от реальных.
// Seed 100 test users in mongosh
const users = [];
for (let i = 1; i <= 100; i++) {
users.push({
name: 'User ' + i,
email: 'user' + i + '@example.com',
age: 18 + (i % 50),
active: i % 3 !== 0,
createdAt: new Date(Date.now() - i * 86400000)
});
}
db.users.insertMany(users);
// { acknowledged: true, insertedCount: 100 }Checking Inserted Documents
После выполнения операции вставки всегда проверяйте, что данные были успешно записаны. Полезные команды для проверки:
db.collection.countDocuments({})— общее количество документов;db.collection.findOne({ _id: insertedId })— проверка конкретного документа по возвращенному значению _id;db.collection.find({}).sort({ _id: -1 }).limit(5)— просмотр 5 последних добавленных документов.
В автоматизированных тестах проверяйте не только то, что вставка прошла успешно (отсутствие ошибок), но и то, что сохраненный документ содержит ожидаемые значения полей — неявное приведение типов или промежуточное ПО (middleware) могут изменять поля в процессе вставки.
// Insert and immediately verify
const { insertedId } = await db.collection('orders').insertOne({
customerId: ObjectId('...'),
items: [{ sku: 'A1', qty: 2 }],
total: 39.98,
status: 'pending'
});
const stored = await db.collection('orders').findOne({ _id: insertedId });
console.log(stored.status); // 'pending'
console.log(stored.total); // 39.98Transactions and Multiple Inserts
Когда вам необходимо вставить документы в несколько коллекций атомарно — то есть либо все операции должны завершиться успешно, либо ни одна из них — используйте многодокументную транзакцию. Без транзакции, если вторая операция вставки завершится с ошибкой после успешного выполнения первой, ваши данные окажутся в несогласованном состоянии.
Транзакции увеличивают задержку, поэтому их следует использовать только в тех случаях, когда атомарность действительно необходима. Для большинства случаев массовой вставки в одну коллекцию метод insertMany в упорядоченном режиме (ordered) обеспечивает достаточную атомарность на уровне коллекции (либо все документы до момента сбоя будут вставлены, либо, если потребуется откат, ни один из них).
// Atomic insert across two collections using a transaction
const session = client.startSession();
try {
await session.withTransaction(async () => {
await db.collection('orders').insertOne(
{ total: 99.99, customerId: custId }, { session }
);
await db.collection('orderEvents').insertOne(
{ type: 'created', orderId: ordId }, { session }
);
});
} finally {
await session.endSession();
}Performance Tip: Bulk Write API
Для смешанных операций (вставки, обновления, удаления) в рамках одного пакета используйте db.collection.bulkWrite(operations). Это эффективнее, чем отдельные вызовы insertMany + updateMany, поскольку все операции выполняются за один сетевой запрос.
Каждая операция в массиве представляет собой объект с ключом, таким как insertOne, updateOne, deleteOne и т. д. Результирующий объект содержит сводную информацию о количестве вставленных, измененных и удаленных документов. bulkWrite также поддерживает упорядоченный (ordered) и неупорядоченный (unordered) режимы, аналогично insertMany.
await db.collection('products').bulkWrite([
{ insertOne: { document: { name: 'New Product', price: 25 } } },
{ updateOne: {
filter: { name: 'Old Product' },
update: { $set: { price: 15 } }
}},
{ deleteOne: { filter: { name: 'Discontinued' } } }
]);
// { insertedCount: 1, modifiedCount: 1, deletedCount: 1 }Quick Check
Проверьте свое понимание концепций MongoDB и баз данных NoSQL из этого урока.
Lesson Recap
В этом уроке вы узнали: insertOne и insertMany — это два метода вставки; insertMany более эффективен для массовой обработки данных и поддерживает упорядоченную/неупорядоченную обработку ошибок; ObjectId генерируется автоматически, если вы не указали _id, при этом первые 4 байта содержат временную метку вставки для приблизительной сортировки; write concern контролирует надежность записи — от режима «отправил и забыл» (w:0) до подтверждения большинством реплик (w:'majority'). Далее мы подробно рассмотрим курсор find() и узнаем, как выполнять запросы к вложенным полям и массивам с помощью точечной нотации.
Часто задаваемые вопросы
Урок «insertOne и insertMany» бесплатный?
Да — полный текст урока «insertOne и insertMany» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс MongoDB Academy, подпишись на CoddyKit PRO. Курс MongoDB Academy содержит 4 уроков всего.
Чему я научусь в уроке «insertOne и insertMany»?
Вы вставите отдельные документы и документы пакетами, а также изучите автоматически созданное поле _id, возвращаемое каждой операцией. Ты практикуешь MongoDB Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать MongoDB Academy?
Предыдущий опыт не требуется. MongoDB Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «insertOne и insertMany»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке MongoDB Academy?
Да. Каждый урок MongoDB Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- insertOne и insertMany
- findOne и find: объяснение курсоров
- Запросы к вложенным полям и массивам
- Чтение документов с драйвером Node.js