文档的插入(insertOne、insertMany)
文档的插入(insertOne、insertMany)
MongoDB提供了两种主要的文档插入方法:insertOne
和insertMany
。这两种方法分别用于插入单个文档和批量插入多个文档,是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}`);
}
插入操作的性能优化
- 批量插入:总是优先考虑使用
insertMany
而不是循环调用insertOne
- 适当调整ordered:对于非关键性数据,可以设置
ordered: false
以提高吞吐量 - 禁用索引:对于一次性大量数据导入,可以先禁用索引,导入后再重建
- 使用批量写入:对于混合操作(插入+更新),可以考虑使用
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("文档过大,需要拆分");
}
插入速度慢
当插入速度不符合预期时,可以考虑:
- 检查是否有过多索引
- 调整写入关注级别
- 使用更大的批量插入
- 考虑分片集群以分散写入负载
// 测试不同批量大小的插入性能
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
上一篇:集合的创建与删除