mongo 中的 setOnUpdate 与 upsert 上的一个查询?

问题描述 投票:0回答:1

我一直在谷歌上搜索并阅读mongo文档,但觉得很奇怪没有人问这个,所以我以为我以错误的方式面对问题,但我坚持不懈,所以我要在这里问,我知道有一个

setOnInsert mongo 中的 
运算符在使用
upsert:true
时,
setOnInsert
上的字段将在插入时插入,但在更新时忽略,但我想要的是,我希望某些字段仅在更新时保存,并在插入(更新插入)时忽略,与在单个查询中
setOnInsert

所以这里是我有字段“createdDate”和“updatedDate”与

upsert:true
并使用
setOnInsert
我可以在
setOnInsert
上创建字段createdDate,但对于“updatedDate”如果我把它放在
$set
它将在插入(upsert)时保存我希望“updatedDate”仅在更新时保存,但在一个查询中的插入(upsert)时被忽略。我可以用 2 个查询和更简单的查询来完成此操作,但寻找单个查询解决方案,还发现奇怪的 mongo 还没有
setOnInsert
的相反内容,也许这种情况很不寻常?

我尝试使用

$switch
$cond
但它被插入,因为文档不起作用,我尝试使用 mongo 的 go 驱动程序,但也接受作为 mongo 查询/shell 的答案(然后我自己转换它们)

coll := mongoCon.Mongo.Collection("productModels")
productId, _ := primitive.ObjectIDFromHex("65b87bbc571dd2a8d301c9f2")
doc := bson.D{
        {Key: "$set", Value: bson.D{
            {Key: "modelName", Value: "orange 11"},
            {Key: "updatedDate", Value: bson.M{
                "$switch": bson.M{
                    "branches": bson.A{
                        bson.M{
                            "case": bson.M{
                                "$eq": bson.A{
                                    bson.M{"_id": bson.M{"$exists": false}}, false,
                                }},
                            "then": nil,
                        },
                    },
                },
            },
            },
        }},
        {Key: "$setOnInsert", Value: bson.D{{Key: "createdDate", Value: time.Now()}}},
    }
    res, err := coll.UpdateOne(context.Background(), bson.M{"product_id": productId}, doc, options.Update().SetUpsert(true))
    // throw res here now, might use it later for 2 query solution
    _ = res
    if err != nil {
        log.Fatal(err)
    }
mongodb go mongodb-query mongo-go
1个回答
0
投票

在我看来,最简单的解决方案是即使对于新文档也可以插入

updatedDate
。如果您必须判断文档是否已更新,您可以比较
updatedDate
createdDate
,或者维护单独的
modifiedCount
字段。

现在来解决您的需求。您第二次尝试使用

$switch
不起作用,因为这是一个聚合运算符,并且要将聚合管道与更新操作一起使用,您必须使用数组(或切片)文档作为更新文档,这就是触发的原因将其解释为聚合管道。有关详细信息,请参阅 Golang 和 MongoDB - 我尝试使用 golang 将切换布尔值更新为 mongodb,但得到了对象

这样做会引入一个新问题:你不能再使用

$setOnInsert
。并且您不能在聚合管道中使用
$exists
运算符:(

但是您想要的可以使用单个

$set
来实现:您可以使用
$ifNull
来检索字段的值(如果存在),或者提供后备值(如果不存在)。

因此,我们的想法是使用

createdDate
的当前值(如果存在),否则传递并设置当前时间。

类似地,如果

updatedDate
已经存在,我们只能更新
createdDate
,我们可以使用上面提到的
$ifNull
运算符进行检查(并使用
$cond
来判断结果是否为
null
)。

所以你可以这样做:

res, err := coll.UpdateOne(ctx,
    bson.M{"product_id": productId},
    []any{
        bson.M{
            "$set": bson.M{
                "modelName": "orange 11",
                "updatedDate": bson.M{"$cond": []any{
                    bson.M{"$eq": []any{bson.M{"$ifNull": []any{"$createdDate", nil}}, nil}},
                    nil, time.Now(),
                }},
                "createdDate": bson.M{"$ifNull": []any{"$createdDate", time.Now()}},
            },
        },
    },
    options.Update().SetUpsert(true),
)

但是,我随时都更喜欢下面的解决方案,而不是上面的“丑陋”:

res, err = c.UpdateOne(ctx,
    bson.M{"product_id": productId},
    bson.M{
        "$set": bson.M{
            "modelName":   "orange 11",
            "updatedDate": time.Now(),
        },
        "$setOnInsert": bson.M{"createdDate": time.Now()},
    },
    options.Update().SetUpsert(true),
)
© www.soinside.com 2019 - 2024. All rights reserved.