0Pricing
MongoDB Academy · Урок

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 error

insertMany: 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 crashes

Handling 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.98

Transactions 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 — локальная установка не требуется.

Все уроки этого курса

  1. insertOne и insertMany
  2. findOne и find: объяснение курсоров
  3. Запросы к вложенным полям и массивам
  4. Чтение документов с драйвером Node.js
← Назад к MongoDB Academy