MongoDB单Upsert查询中实现仅更新生效字段(setOnInsert反向)
单查询实现Upsert时仅更新场景设置字段
核心思路
MongoDB确实没有直接对应$setOnInsert的反向操作符,但可以通过聚合管道式更新(MongoDB 4.2+支持)结合条件判断实现需求:
createdDate依然用$setOnInsert在插入时设置updatedDate通过$cond判断:仅当文档已存在(更新场景)时才设置,插入时忽略该字段
Mongo Shell 实现方案
db.productModels.updateOne( { product_id: ObjectId("65b87bbc571dd2a8d301c9f2") }, [ { $set: { modelName: "orange 11", updatedDate: { $cond: { if: { $exists: ["$_id", true] }, // 文档存在则为更新场景 then: new Date(), else: "$$REMOVE" // 插入场景下移除该字段,即不设置 } } } }, { $setOnInsert: { createdDate: new Date() } } ], { upsert: true } )
Go 驱动实现方案
coll := mongoCon.Mongo.Collection("productModels") productId, _ := primitive.ObjectIDFromHex("65b87bbc571dd2a8d301c9f2") // 使用聚合管道作为更新文档 updatePipeline := bson.A{ bson.D{ {Key: "$set", Value: bson.D{ {Key: "modelName", Value: "orange 11"}, {Key: "updatedDate", Value: bson.M{ "$cond": bson.M{ "if": bson.M{"$exists": []interface{}{"$_id", true}}, "then": time.Now(), "else": "$$REMOVE", // 插入时移除该字段,不设置 }, }}, }}, }, bson.D{ {Key: "$setOnInsert", Value: bson.D{ {Key: "createdDate", Value: time.Now()}, }}, }, } res, err := coll.UpdateOne( context.Background(), bson.M{"product_id": productId}, updatePipeline, options.Update().SetUpsert(true), ) if err != nil { log.Fatal(err) }
关键说明
- 使用聚合管道形式的更新,才能在更新逻辑中访问当前文档的字段状态(
$_id是否存在) $$REMOVE是MongoDB的特殊变量,用于在聚合操作中移除指定字段,插入场景下使用它就能让updatedDate不被写入新文档- 该方案仅支持MongoDB 4.2及以上版本,这是聚合管道式更新的最低要求
内容的提问来源于stack exchange,提问作者Fath-Likki
相关产品推荐
相关产品推荐

