@@ -286,9 +286,10 @@ const user = await collection("users").findOneAndUpdate(
286286// user 只包含 _id, name, loginCount
287287```
288288
289- ### 使用 upsert
289+ ### 使用 upsert(不存在就插入,存在则更新)⭐
290290
291291``` javascript
292+ // 基本用法:计数器示例
292293const counter = await collection (" counters" ).findOneAndUpdate (
293294 { counterName: " pageViews" },
294295 { $inc: { value: 1 } },
@@ -300,6 +301,119 @@ const counter = await collection("counters").findOneAndUpdate(
300301// 如果不存在会创建新文档
301302```
302303
304+ #### Upsert 详细说明
305+
306+ ** upsert = update + insert** 的组合:
307+ - ✅ ** 存在** :执行更新操作
308+ - ✅ ** 不存在** :插入新文档
309+
310+ ** 使用场景** :
311+
312+ ``` javascript
313+ // 场景 1:用户配置(不存在则创建默认配置)
314+ const userConfig = await collection (" user_configs" ).findOneAndUpdate (
315+ { userId: " user123" },
316+ {
317+ $set: {
318+ theme: " dark" ,
319+ language: " zh-CN" ,
320+ updatedAt: new Date ()
321+ },
322+ $setOnInsert: {
323+ // 仅在插入时设置
324+ createdAt: new Date (),
325+ defaultSettings: true
326+ }
327+ },
328+ {
329+ upsert: true ,
330+ returnDocument: " after"
331+ }
332+ );
333+
334+ // 场景 2:统计数据(自动初始化)
335+ const stats = await collection (" daily_stats" ).findOneAndUpdate (
336+ {
337+ date: " 2026-01-28" ,
338+ userId: " user123"
339+ },
340+ {
341+ $inc: { pageViews: 1 , loginCount: 1 }
342+ },
343+ {
344+ upsert: true ,
345+ returnDocument: " after"
346+ }
347+ );
348+ // 不存在时会创建:{ date: "2026-01-28", userId: "user123", pageViews: 1, loginCount: 1 }
349+
350+ // 场景 3:缓存更新(不存在则缓存新数据)
351+ const cache = await collection (" cache" ).findOneAndUpdate (
352+ { key: " user:profile:123" },
353+ {
354+ $set: {
355+ value: profileData,
356+ expireAt: new Date (Date .now () + 3600000 ) // 1小时后过期
357+ }
358+ },
359+ {
360+ upsert: true ,
361+ returnDocument: " after"
362+ }
363+ );
364+
365+ // 场景 4:商品库存(自动创建库存记录)
366+ const inventory = await collection (" inventory" ).findOneAndUpdate (
367+ { productId: " prod-456" },
368+ {
369+ $inc: { quantity: - 1 }, // 减少库存
370+ $set: { lastUpdated: new Date () }
371+ },
372+ {
373+ upsert: true ,
374+ returnDocument: " after"
375+ }
376+ );
377+ ```
378+
379+ ** ⚠️ Upsert 注意事项** :
380+
381+ ``` javascript
382+ // ❌ 错误:使用 $setOnInsert 但忘记 upsert
383+ const doc = await collection (" users" ).findOneAndUpdate (
384+ { userId: " user123" },
385+ { $setOnInsert: { createdAt: new Date () } }
386+ // 缺少 upsert: true,$setOnInsert 不会生效
387+ );
388+
389+ // ✅ 正确:同时使用 $set 和 $setOnInsert
390+ const doc = await collection (" users" ).findOneAndUpdate (
391+ { userId: " user123" },
392+ {
393+ $set: { lastLogin: new Date () }, // 每次都更新
394+ $setOnInsert: { createdAt: new Date () } // 仅插入时设置
395+ },
396+ { upsert: true }
397+ );
398+
399+ // ✅ 正确:获取 upsert 的 _id
400+ const result = await collection (" users" ).findOneAndUpdate (
401+ { email: " new@example.com" },
402+ { $set: { name: " New User" } },
403+ {
404+ upsert: true ,
405+ returnDocument: " after" ,
406+ includeResultMetadata: true
407+ }
408+ );
409+
410+ if (result .lastErrorObject .upserted ) {
411+ console .log (" 创建了新文档,_id:" , result .lastErrorObject .upserted );
412+ } else {
413+ console .log (" 更新了现有文档" );
414+ }
415+ ```
416+
303417### 获取完整元数据
304418
305419``` javascript
0 commit comments