必须用 insertMany() 获取所有生成的_id,它原子性插入并返回完整 insertedIds;ordered: false 时失败项对应位置为 null,需用 Array.from 保持索引对齐,手动指定_id 会导致类型混杂。

插入时想拿到所有生成的 _id,必须用 insertMany() 而不是循环 insertOne()
单条插入再收集 _id 不仅慢,还无法保证原子性——万一中间某条失败,前面成功的就回滚不了。而 insertMany() 一次发一个请求,返回结果里直接带全部文档的 _id(无论是否自动生成)。注意:它默认开启有序插入(ordered: true),遇到错误会停在那条,后续不执行。
ordered: false 不是“跳过错误”,而是让所有合法文档都插入成功
设你传了 10 条数据,第 3 条因唯一索引冲突报错,其余都合法。若用 ordered: true(默认),只插入前 2 条;设为 ordered: false,则第 1、2、4–10 条都会插入,第 3 条被忽略,并在返回的 result.writeErrors 里列出具体错误。但关键点来了:所有成功插入的文档 _id 依然完整保留在 result.insertedIds 中,顺序和输入数组一致(缺失位置为 null)。
常见误用:以为 ordered: false 会打乱 insertedIds 的顺序 —— 实际不会,MongoDB 严格按输入顺序映射 insertedIds 索引,哪怕中间有失败项。
- 示例代码片段:
const result = await collection.insertMany([ { name: "a" }, { name: "b" }, { name: "c", _id: "duplicate_id" }, // 假设 _id 已存在 { name: "d" } ], { ordered: false }); -
result.insertedIds是一个对象,键为输入数组下标:{ '0': ObjectId(...), '1': ObjectId(...), '3': ObjectId(...) } - 别直接用
Object.values(result.insertedIds)拿 ID 数组——会丢掉空位信息;需用Array.from({ length: docs.length }, (_, i) => result.insertedIds[i])保持对齐
手动指定 _id 会影响 insertedIds 的内容完整性
如果你在插入文档里自己写了 _id 字段(比如字符串或 ObjectId),MongoDB 不会覆盖它,也不会生成新值。此时 insertedIds 里对应位置的值就是你给的那个 _id,类型和值完全一致。但要注意:
- 如果传入的是非法
ObjectId字符串(如"abc"),插入会成功,但该_id就是字符串"abc",不是 ObjectId 类型 - 混合使用自动生成和手动指定
_id时,insertedIds仍保持一一对应,但类型可能混杂(ObjectId + string) - 某些驱动(如 Node.js 4.x+)对
insertedIds的键名做了标准化,始终是字符串数字("0","1"),不是整数
批量插入后立刻查新 ID,得留意写关注(write concern)和读一致性
即使 insertMany() 返回了 insertedIds,也不代表这些文档已对其他客户端可见。默认 w: 1 只写到主节点,若紧接着用另一个连接去查,可能因复制延迟没看到。更隐蔽的问题是:如果你在事务里插入,又想在同事务中查刚插的文档,必须用 session.startTransaction() 并确保查询也走同一 session。
真正需要“插入即可见”的场景(比如生成订单号后立刻查详情),建议:
- 显式设置
writeConcern: { w: "majority" }(牺牲一点吞吐换一致性) - 避免在插入后立即跨连接做依赖查询;优先用返回的
insertedIds做后续操作 - 不要依赖
insertedIds的顺序来推断数据库中物理存储顺序——MongoDB 不保证文档磁盘顺序与插入顺序一致
ordered: false 下对 insertedIds 索引对齐的处理,以及手动设 _id 后类型不一致导致后续 findOne({ _id: { $in: ids } }) 查询失败。


















