原子地查找并更新单个文档,返回更新前或更新后的文档。这是一个原子操作,适合需要读取旧值同时更新的场景。
collection(collectionName).findOneAndUpdate(filter, update, options)筛选条件。
更新操作,必须使用更新操作符。
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
projection |
Object | - | 字段投影 |
sort |
Object | - | 排序条件 |
upsert |
Boolean | false | 不存在时是否插入 |
returnDocument |
String | "before" | "before" 或 "after" |
maxTimeMS |
Number | - | 最大执行时间 |
comment |
String | - | 操作注释 |
collation |
Object | - | 排序规则 |
arrayFilters |
Array | - | 数组过滤器 |
hint |
String/Object | - | 索引提示 |
includeResultMetadata |
Boolean | false | 是否包含完整元数据 |
默认返回文档对象或 null(未找到)。
如果 includeResultMetadata: true,返回:
{
value: <文档或null>,
ok: 1,
lastErrorObject: {
updatedExisting: true,
n: 1,
upserted: <id> // 仅 upsert 时
}
}
⚠️ 重要提示 - MongoDB 驱动 6.x 兼容性monSQLize 使用 MongoDB Node.js 驱动 6.x,该版本对
findOneAndUpdate的返回值格式进行了重要变更:驱动 6.x (当前版本):
- 默认直接返回文档对象
- 需要显式设置
includeResultMetadata: true才返回完整元数据驱动 5.x 及更早版本:
- 默认返回完整元数据
{ value, ok, lastErrorObject }✅ monSQLize 的处理:
- 已在内部自动处理此差异,用户无需关心驱动版本
- API 行为保持一致,向后兼容
- 详见技术分析报告:
analysis-reports/2025-11-17-mongodb-driver-6x-compatibility.md
// ✅ 原子操作 - 查找和更新在同一事务中
const oldDoc = await collection("counters").findOneAndUpdate(
{ counterName: "orderNumber" },
{ $inc: { value: 1 } }
);
// ❌ 非原子 - 有竞态条件风险
const doc = await collection("counters").findOne({ counterName: "orderNumber" });
await collection("counters").updateOne(
{ counterName: "orderNumber" },
{ $inc: { value: 1 } }
);// 返回更新前的文档(默认)
const oldDoc = await collection("users").findOneAndUpdate(
{ userId: "user123" },
{ $inc: { loginCount: 1 } }
);
console.log("Old count:", oldDoc.loginCount); // 5
// 返回更新后的文档
const newDoc = await collection("users").findOneAndUpdate(
{ userId: "user123" },
{ $inc: { loginCount: 1 } },
{ returnDocument: "after" }
);
console.log("New count:", newDoc.loginCount); // 6// 原子递增并获取新值
const counter = await collection("counters").findOneAndUpdate(
{ counterName: "orderNumber" },
{ $inc: { value: 1 } },
{ returnDocument: "after" }
);
const newOrderNumber = counter.value; // 1001
console.log(`New order number: ${newOrderNumber}`);// 使用版本号防止并发冲突
const doc = await collection("documents").findOneAndUpdate(
{
docId: "doc1",
version: 5 // 仅当版本号匹配时更新
},
{
$set: { content: "Updated content" },
$inc: { version: 1 }
},
{ returnDocument: "after" }
);
if (!doc) {
console.log("更新失败:版本冲突");
} else {
console.log("更新成功,新版本:", doc.version);
}// 原子地获取并标记任务
const task = await collection("tasks").findOneAndUpdate(
{ status: "pending" },
{
$set: {
status: "processing",
workerId: "worker-1",
startedAt: new Date()
}
},
{
sort: { priority: -1 }, // 优先级最高的
returnDocument: "after"
}
);
if (task) {
console.log("Processing task:", task.taskId);
// 处理任务...
} else {
console.log("No pending tasks");
}// 获取锁
const lock = await collection("locks").findOneAndUpdate(
{
lockKey: "resource-lock",
locked: false
},
{
$set: {
locked: true,
ownerId: "worker-1",
acquiredAt: new Date()
}
},
{ returnDocument: "after" }
);
if (lock) {
try {
// 执行需要锁保护的操作...
console.log("Lock acquired");
} finally {
// 释放锁
await collection("locks").updateOne(
{ lockKey: "resource-lock" },
{ $set: { locked: false } }
);
}
} else {
console.log("Lock already held");
}// 更新活动时间并返回用户信息
const user = await collection("users").findOneAndUpdate(
{ userId: "user123" },
{
$set: { lastActiveAt: new Date() },
$inc: { pageViews: 1 }
},
{
projection: { name: 1, email: 1, lastActiveAt: 1 },
returnDocument: "after"
}
);
console.log(`Welcome back, ${user.name}!`);const oldDoc = await collection("users").findOneAndUpdate(
{ userId: "user123" },
{ $set: { status: "active" } }
);
if (oldDoc) {
console.log("Old status:", oldDoc.status);
} else {
console.log("User not found");
}// 找到分数最高的用户并更新
const topUser = await collection("users").findOneAndUpdate(
{ status: "active" },
{ $set: { winner: true } },
{
sort: { score: -1 },
returnDocument: "after"
}
);// 只返回需要的字段
const user = await collection("users").findOneAndUpdate(
{ userId: "user123" },
{ $inc: { loginCount: 1 } },
{
projection: { name: 1, loginCount: 1 },
returnDocument: "after"
}
);
// user 只包含 _id, name, loginCount// 基本用法:计数器示例
const counter = await collection("counters").findOneAndUpdate(
{ counterName: "pageViews" },
{ $inc: { value: 1 } },
{
upsert: true,
returnDocument: "after"
}
);
// 如果不存在会创建新文档upsert = update + insert 的组合:
- ✅ 存在:执行更新操作
- ✅ 不存在:插入新文档
使用场景:
// 场景 1:用户配置(不存在则创建默认配置)
const userConfig = await collection("user_configs").findOneAndUpdate(
{ userId: "user123" },
{
$set: {
theme: "dark",
language: "zh-CN",
updatedAt: new Date()
},
$setOnInsert: {
// 仅在插入时设置
createdAt: new Date(),
defaultSettings: true
}
},
{
upsert: true,
returnDocument: "after"
}
);
// 场景 2:统计数据(自动初始化)
const stats = await collection("daily_stats").findOneAndUpdate(
{
date: "2026-01-28",
userId: "user123"
},
{
$inc: { pageViews: 1, loginCount: 1 }
},
{
upsert: true,
returnDocument: "after"
}
);
// 不存在时会创建:{ date: "2026-01-28", userId: "user123", pageViews: 1, loginCount: 1 }
// 场景 3:缓存更新(不存在则缓存新数据)
const cache = await collection("cache").findOneAndUpdate(
{ key: "user:profile:123" },
{
$set: {
value: profileData,
expireAt: new Date(Date.now() + 3600000) // 1小时后过期
}
},
{
upsert: true,
returnDocument: "after"
}
);
// 场景 4:商品库存(自动创建库存记录)
const inventory = await collection("inventory").findOneAndUpdate(
{ productId: "prod-456" },
{
$inc: { quantity: -1 }, // 减少库存
$set: { lastUpdated: new Date() }
},
{
upsert: true,
returnDocument: "after"
}
);// ❌ 错误:使用 $setOnInsert 但忘记 upsert
const doc = await collection("users").findOneAndUpdate(
{ userId: "user123" },
{ $setOnInsert: { createdAt: new Date() } }
// 缺少 upsert: true,$setOnInsert 不会生效
);
// ✅ 正确:同时使用 $set 和 $setOnInsert
const doc = await collection("users").findOneAndUpdate(
{ userId: "user123" },
{
$set: { lastLogin: new Date() }, // 每次都更新
$setOnInsert: { createdAt: new Date() } // 仅插入时设置
},
{ upsert: true }
);
// ✅ 正确:获取 upsert 的 _id
const result = await collection("users").findOneAndUpdate(
{ email: "new@example.com" },
{ $set: { name: "New User" } },
{
upsert: true,
returnDocument: "after",
includeResultMetadata: true
}
);
if (result.lastErrorObject.upserted) {
console.log("创建了新文档,_id:", result.lastErrorObject.upserted);
} else {
console.log("更新了现有文档");
}const result = await collection("users").findOneAndUpdate(
{ userId: "user123" },
{ $set: { status: "active" } },
{ includeResultMetadata: true }
);
console.log("Document:", result.value);
console.log("Updated existing:", result.lastErrorObject.updatedExisting);
console.log("Operation ok:", result.ok);// ✅ 推荐 - 在筛选字段上建立索引
await collection("counters").findOneAndUpdate(
{ counterName: "orderNumber" }, // counterName 应有索引
{ $inc: { value: 1 } }
);// ✅ 推荐 - 只返回需要的字段
const user = await collection("users").findOneAndUpdate(
{ userId: "user123" },
{ $inc: { score: 10 } },
{
projection: { _id: 0, score: 1 },
returnDocument: "after"
}
);const task = await collection("tasks").findOneAndUpdate(
{ status: "pending" },
{ $set: { status: "processing" } },
{
sort: { priority: -1, createdAt: 1 },
hint: "status_priority_createdAt_idx", // 使用复合索引
returnDocument: "after"
}
);try {
const doc = await collection("users").findOneAndUpdate(
{ userId: "user123" },
{ $inc: { score: 10 } }
);
if (!doc) {
console.log("Document not found");
}
} catch (err) {
if (err.code === "INVALID_ARGUMENT") {
console.error("参数错误:", err.message);
} else if (err.code === "DUPLICATE_KEY") {
console.error("唯一性约束冲突:", err.message);
} else {
console.error("操作失败:", err);
}
}| 方法 | 原子性 | 返回值 | 场景 |
|---|---|---|---|
updateOne |
❌ | 计数 | 普通更新 |
findOneAndUpdate |
✅ | 文档 | 需要旧值/原子操作 |
findOne + updateOne |
❌ | 文档+计数 |
// ✅ 安全 - 原子操作
for (let i = 0; i < 10; i++) {
await collection("counters").findOneAndUpdate(
{ name: "total" },
{ $inc: { value: 1 } }
);
}
// 最终 value = 10(正确)
// ❌ 不安全 - 非原子操作
for (let i = 0; i < 10; i++) {
const doc = await collection("counters").findOne({ name: "total" });
await collection("counters").updateOne(
{ name: "total" },
{ $set: { value: doc.value + 1 } }
);
}
// 最终 value 可能 < 10(错误,有竞态条件)// 需要旧值时
const oldValue = await collection("counters").findOneAndUpdate(
{ name: "counter" },
{ $inc: { value: 1 } }
// returnDocument: "before" 是默认值
);
// 需要新值时
const newValue = await collection("counters").findOneAndUpdate(
{ name: "counter" },
{ $inc: { value: 1 } },
{ returnDocument: "after" }
);async function updateWithRetry(docId, newContent, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const doc = await collection("documents").findOne({ docId });
const result = await collection("documents").findOneAndUpdate(
{ docId, version: doc.version },
{
$set: { content: newContent },
$inc: { version: 1 }
},
{ returnDocument: "after" }
);
if (result) return result;
console.log(`Retry ${i + 1}: version conflict`);
}
throw new Error("Update failed after retries");
}// ✅ 推荐
const user = await collection("users").findOneAndUpdate(
{ userId: "user123" },
{ $inc: { score: 10 } },
{
projection: { score: 1 },
returnDocument: "after"
}
);findOneAndReplace()- 原子地查找并替换updateOne()- 更新单个文档findOne()- 查找单个文档