阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 文档的插入(insertOne、insertMany)

文档的插入(insertOne、insertMany)

作者:陈川 阅读数:43797人阅读 分类: MongoDB

文档的插入(insertOne、insertMany)

MongoDB提供了两种主要的文档插入方法:insertOneinsertMany。这两种方法分别用于插入单个文档和批量插入多个文档,是MongoDB中最基础也是最常用的操作之一。

insertOne方法

insertOne方法用于向集合中插入单个文档。它的基本语法如下:

db.collection.insertOne(
   <document>,
   {
      writeConcern: <document>
   }
)

其中,<document>是要插入的文档,writeConcern是可选的写入关注级别。

基本示例

// 插入一个简单的用户文档
db.users.insertOne({
  name: "张三",
  age: 28,
  email: "zhangsan@example.com",
  hobbies: ["阅读", "游泳"]
});

插入成功后,MongoDB会返回一个包含以下信息的对象:

{
  acknowledged: true,
  insertedId: ObjectId("5f8d8a7b9d8e7b6a9d8e7b6a")
}

自动生成_id字段

如果插入的文档没有指定_id字段,MongoDB会自动生成一个唯一的ObjectId作为_id

db.products.insertOne({
  name: "笔记本电脑",
  price: 5999,
  category: "电子产品"
});

也可以显式指定_id

db.products.insertOne({
  _id: "prod001",
  name: "智能手机",
  price: 3999
});

错误处理

当插入违反唯一索引约束的文档时,会抛出错误:

try {
  db.users.insertOne({
    _id: "user001",
    name: "李四"
  });
  
  // 再次插入相同_id的文档会报错
  db.users.insertOne({
    _id: "user001",
    name: "王五"
  });
} catch (e) {
  print("插入失败: " + e);
}

insertMany方法

insertMany方法用于批量插入多个文档,比多次调用insertOne更高效。基本语法如下:

db.collection.insertMany(
   [ <document1>, <document2>, ... ],
   {
      writeConcern: <document>,
      ordered: <boolean>
   }
)

基本示例

// 批量插入多个用户
db.users.insertMany([
  {
    name: "王五",
    age: 32,
    email: "wangwu@example.com"
  },
  {
    name: "赵六",
    age: 25,
    email: "zhaoliu@example.com"
  },
  {
    name: "钱七",
    age: 40,
    email: "qianqi@example.com"
  }
]);

成功插入后会返回类似以下的结果:

{
  acknowledged: true,
  insertedIds: [
    ObjectId("5f8d8a7b9d8e7b6a9d8e7b6b"),
    ObjectId("5f8d8a7b9d8e7b6a9d8e7b6c"),
    ObjectId("5f8d8a7b9d8e7b6a9d8e7b6d")
  ]
}

ordered参数

ordered参数默认为true,表示按顺序插入文档。如果某个文档插入失败,后续文档将不会继续插入:

try {
  db.users.insertMany([
    { _id: 1, name: "用户1" },
    { _id: 1, name: "用户1重复" }, // 这里会失败
    { _id: 2, name: "用户2" }
  ], { ordered: true });
} catch (e) {
  print("批量插入出错: " + e);
}

ordered设为false可以让MongoDB继续尝试插入后续文档:

db.users.insertMany([
  { _id: 3, name: "用户3" },
  { _id: 3, name: "用户3重复" }, // 这里会失败
  { _id: 4, name: "用户4" }
], { ordered: false });

批量插入的性能考虑

对于大量文档插入,insertMany比循环调用insertOne更高效。以下是一个性能对比示例:

// 不推荐的方式:循环插入1000个文档
const start1 = new Date();
for (let i = 0; i < 1000; i++) {
  db.test.insertOne({ value: i });
}
const end1 = new Date();
print("循环insertOne耗时: " + (end1 - start1) + "ms");

// 推荐的方式:批量插入
const start2 = new Date();
const docs = [];
for (let i = 0; i < 1000; i++) {
  docs.push({ value: i });
}
db.test.insertMany(docs);
const end2 = new Date();
print("insertMany耗时: " + (end2 - start2) + "ms");

写入关注(Write Concern)

写入关注指定了MongoDB确认写操作成功的级别。可以在插入操作中指定:

// 等待写入被复制到大多数副本集成员
db.products.insertOne(
  {
    name: "蓝牙耳机",
    price: 299
  },
  {
    writeConcern: { w: "majority", j: true, wtimeout: 5000 }
  }
);

常用的写入关注选项包括:

  • w: 写入传播到的节点数
  • j: 是否等待日志写入
  • wtimeout: 超时时间(毫秒)

实际应用示例

电商系统中的商品导入

// 从外部数据源获取商品数据
const externalProducts = [
  {
    sku: "SKU1001",
    name: "无线鼠标",
    price: 129,
    stock: 50,
    categories: ["电子产品", "电脑配件"]
  },
  {
    sku: "SKU1002",
    name: "机械键盘",
    price: 399,
    stock: 30,
    categories: ["电子产品", "电脑配件"]
  },
  // 更多商品...
];

// 批量导入商品
try {
  const result = db.products.insertMany(externalProducts, {
    ordered: false // 即使某些商品导入失败也继续
  });
  print(`成功导入 ${result.insertedCount} 个商品`);
} catch (e) {
  print(`导入过程中出现错误: ${e}`);
}

用户注册系统

function registerUser(userData) {
  // 验证用户数据
  if (!userData.username || !userData.password) {
    throw new Error("用户名和密码不能为空");
  }
  
  // 检查用户名是否已存在
  const existingUser = db.users.findOne({ username: userData.username });
  if (existingUser) {
    throw new Error("用户名已存在");
  }
  
  // 添加注册时间戳
  userData.createdAt = new Date();
  userData.updatedAt = new Date();
  
  // 插入新用户
  const result = db.users.insertOne(userData);
  
  // 返回新创建的用户ID
  return result.insertedId;
}

// 使用示例
try {
  const newUserId = registerUser({
    username: "newuser",
    password: "securepassword123",
    email: "newuser@example.com"
  });
  print(`用户注册成功,ID: ${newUserId}`);
} catch (e) {
  print(`注册失败: ${e.message}`);
}

插入操作的性能优化

  1. 批量插入:总是优先考虑使用insertMany而不是循环调用insertOne
  2. 适当调整ordered:对于非关键性数据,可以设置ordered: false以提高吞吐量
  3. 禁用索引:对于一次性大量数据导入,可以先禁用索引,导入后再重建
  4. 使用批量写入:对于混合操作(插入+更新),可以考虑使用bulkWrite
// 批量写入示例
const bulkOps = [
  { insertOne: { document: { name: "商品A", price: 100 } } },
  { insertOne: { document: { name: "商品B", price: 200 } } },
  { updateOne: { filter: { name: "商品A" }, update: { $set: { stock: 50 } } } }
];

db.products.bulkWrite(bulkOps);

常见问题与解决方案

重复键错误

当插入的文档_id或唯一索引字段与已有文档冲突时,会抛出重复键错误。解决方案:

try {
  db.users.insertOne({
    _id: "user123",
    name: "测试用户"
  });
} catch (e) {
  if (e.code === 11000) {
    // 处理重复键错误
    print("用户ID已存在");
    // 可以选择生成新的ID重试
    db.users.insertOne({
      _id: ObjectId(), // 自动生成新ID
      name: "测试用户"
    });
  } else {
    throw e;
  }
}

文档大小限制

MongoDB单个文档大小限制为16MB。对于可能超过此限制的数据:

// 检查文档大小
function getDocumentSize(doc) {
  return Object.bsonsize(doc);
}

const largeDoc = { /* 非常大的文档 */ };
if (getDocumentSize(largeDoc) > 16 * 1024 * 1024) {
  // 需要拆分文档或使用GridFS
  print("文档过大,需要拆分");
}

插入速度慢

当插入速度不符合预期时,可以考虑:

  1. 检查是否有过多索引
  2. 调整写入关注级别
  3. 使用更大的批量插入
  4. 考虑分片集群以分散写入负载
// 测试不同批量大小的插入性能
function testInsertPerformance(batchSize, totalDocs) {
  const docs = [];
  for (let i = 0; i < totalDocs; i++) {
    docs.push({ value: i, data: "x".repeat(100) });
    if (docs.length >= batchSize) {
      db.perftest.insertMany(docs);
      docs.length = 0; // 清空数组
    }
  }
  if (docs.length > 0) {
    db.perftest.insertMany(docs);
  }
}

// 测试不同批量大小
testInsertPerformance(100, 10000);  // 每次插入100个文档
testInsertPerformance(500, 10000);  // 每次插入500个文档
testInsertPerformance(1000, 10000); // 每次插入1000个文档

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌