go-mongox icon indicating copy to clipboard operation
go-mongox copied to clipboard

Upsert 的问题

Open CatDrinkCoffee opened this issue 1 year ago • 23 comments

mongox 版本

v1.2.0

问题

在使用内置model,并开启EnableDefaultFieldHook的情况下,insert是可以正常更新created_at字段,而使用Upsert 的情况,如果数据不存在是会插入一条新数据的,但并没有更新created_at到现在的时间 第一条数据没有时间,使用 Updater().Filter(query.Eq("name", node.Name)).Replacement(node).Upsert(context.Background()) 第二条数据有时间,使用 Creator().InsertOne(context.Background(), &node)

image

CatDrinkCoffee avatar Sep 05 '24 06:09 CatDrinkCoffee

并且使用 Upsert 时,会覆盖掉原有的时间为空值,是使用上的问题吗,请指正

CatDrinkCoffee avatar Sep 05 '24 07:09 CatDrinkCoffee

正常是会更新 created_at 的值的,麻烦提供一下 node 结构体的代码哈,我看看。

chenmingyong0423 avatar Sep 05 '24 10:09 chenmingyong0423

image

正常是会更新 created_at 的值的,麻烦提供一下 node 结构体的代码哈,我看看。

CatDrinkCoffee avatar Sep 06 '24 09:09 CatDrinkCoffee

updated_at也是这个情况,Upsert 更新的时候并不会赋值,而是写入了一个 0001-01-01 00:00:00.000 的时间

CatDrinkCoffee avatar Sep 06 '24 10:09 CatDrinkCoffee

另外在使用 UpdateOne 更新数据时,发现并不会去更新 updated_at

CatDrinkCoffee avatar Sep 06 '24 10:09 CatDrinkCoffee

我在我这边测是没问题的,InsertOneUpsertcreated_at 的值都有被设置。你可以再检查检查你的代码,然后必要时需要你提供你那边的所有代码看看才知道是什么问题。

chenmingyong0423 avatar Sep 06 '24 10:09 chenmingyong0423

另外在使用 UpdateOne 更新数据时,发现并不会去更新 updated_at

你这个问题,可以看看这个讨论 #45 ,没有满足条件的话,现在没有侵入式的修改 updated_at。

chenmingyong0423 avatar Sep 06 '24 10:09 chenmingyong0423

updated_at也是这个情况,Upsert 更新的时候并不会赋值,而是写入了一个 0001-01-01 00:00:00.000 的时间

可以看看这个讨论 https://github.com/chenmingyong0423/go-mongox/issues/45 ,没有满足条件的话,现在没有侵入式的修改 updated_at。

chenmingyong0423 avatar Sep 06 '24 10:09 chenmingyong0423

我在我这边测是没问题的,InsertOneUpsertcreated_at 的值都有被设置。你可以再检查检查你的代码,然后必要时需要你提供你那边的所有代码看看才知道是什么问题。

代码如下实际测试,created_at 为 0001-01-01 00:00:00.000

	type Test struct {
		mongox.Model `bson:"inline"`
		Name         string
		Age          int
	}
	test := Test{
		Name: "test1",
		Age:  12,
	}
	_, err := database2.NewCollection[Test]("test2").Updater().
		Filter(
			query.NewBuilder().
				Eq("name", "test1").Build(),
		).Replacement(test).Upsert(context.Background())
	if err != nil {
		fmt.Println(err.Error())
		return
	}

CatDrinkCoffee avatar Sep 06 '24 10:09 CatDrinkCoffee

我在我这边测是没问题的,InsertOneUpsertcreated_at 的值都有被设置。你可以再检查检查你的代码,然后必要时需要你提供你那边的所有代码看看才知道是什么问题。

代码如下实际测试,created_at 为 0001-01-01 00:00:00.000 type Test struct { mongox.Modelbson:"inline" Name string Age int } test := Test{ Name: "test1", Age: 12, } _, err := database2.NewCollection[Test]("test2").Updater(). Filter( query.NewBuilder(). Eq("name", "test1").Build(), ).Replacement(test).Upsert(context.Background()) if err != nil { fmt.Println(err.Error()) return }

全部代码给我看看吧,,部分代码看不出问题,包括是否调用 mongox.InitPlugin 初始化。

chenmingyong0423 avatar Sep 06 '24 10:09 chenmingyong0423

我在我这边测是没问题的,InsertOneUpsertcreated_at 的值都有被设置。你可以再检查检查你的代码,然后必要时需要你提供你那边的所有代码看看才知道是什么问题。

代码如下实际测试,created_at 为 0001-01-01 00:00:00.000 type Test struct { mongox.Modelbson:"inline" Name string Age int } test := Test{ Name: "test1", Age: 12, } _, err := database2.NewCollection[Test]("test2").Updater(). Filter( query.NewBuilder(). Eq("name", "test1").Build(), ).Replacement(test).Upsert(context.Background()) if err != nil { fmt.Println(err.Error()) return }

全部代码给我看看吧,,部分代码看不出问题,包括是否调用 mongox.InitPlugin 初始化。

完整代码如下

package main

import (
	"context"
	"fmt"
	"github.com/chenmingyong0423/go-mongox"
	"github.com/chenmingyong0423/go-mongox/builder/query"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"log"
)

func main() {
	type Test struct {
		mongox.Model `bson:"inline"`
		Name         string
		Age          int
	}
	test := Test{
		Name: "test1",
		Age:  12,
	}
	client, err := mongo.Connect(
		context.Background(),
		options.Client().
			ApplyURI("mongodb://192.168.100.101:27017").
			SetAuth(options.Credential{
				Username: "xxx",
				Password: "xxx",
			}))
	if err != nil {
		log.Fatal(err)
		return
	}
	collection := client.Database("xxx").Collection("test2")

	_, err = mongox.NewCollection[Test](collection).Updater().
		Filter(
			query.NewBuilder().
				Eq("name", "test1").Build(),
		).Replacement(test).Upsert(context.Background())
	if err != nil {
		fmt.Println(err.Error())
		return
	}
}

CatDrinkCoffee avatar Sep 06 '24 10:09 CatDrinkCoffee

我在我这边测是没问题的,InsertOneUpsertcreated_at 的值都有被设置。你可以再检查检查你的代码,然后必要时需要你提供你那边的所有代码看看才知道是什么问题。

代码如下实际测试,created_at 为 0001-01-01 00:00:00.000 type Test struct { mongox.Modelbson:"inline" Name string Age int } test := Test{ Name: "test1", Age: 12, } _, err := database2.NewCollection[Test]("test2").Updater(). Filter( query.NewBuilder(). Eq("name", "test1").Build(), ).Replacement(test).Upsert(context.Background()) if err != nil { fmt.Println(err.Error()) return }

全部代码给我看看吧,,部分代码看不出问题,包括是否调用 mongox.InitPlugin 初始化。

完整代码如下

package main

import (
	"context"
	"fmt"
	"github.com/chenmingyong0423/go-mongox"
	"github.com/chenmingyong0423/go-mongox/builder/query"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"log"
)

func main() {
	type Test struct {
		mongox.Model `bson:"inline"`
		Name         string
		Age          int
	}
	test := Test{
		Name: "test1",
		Age:  12,
	}
	client, err := mongo.Connect(
		context.Background(),
		options.Client().
			ApplyURI("mongodb://192.168.100.101:27017").
			SetAuth(options.Credential{
				Username: "xxx",
				Password: "xxx",
			}))
	if err != nil {
		log.Fatal(err)
		return
	}
	collection := client.Database("xxx").Collection("test2")

	_, err = mongox.NewCollection[Test](collection).Updater().
		Filter(
			query.NewBuilder().
				Eq("name", "test1").Build(),
		).Replacement(test).Upsert(context.Background())
	if err != nil {
		fmt.Println(err.Error())
		return
	}
}

没有调用 mongox.InitPlugin 打开配置

chenmingyong0423 avatar Sep 06 '24 10:09 chenmingyong0423

我在我这边测是没问题的,InsertOneUpsertcreated_at 的值都有被设置。你可以再检查检查你的代码,然后必要时需要你提供你那边的所有代码看看才知道是什么问题。

代码如下实际测试,created_at 为 0001-01-01 00:00:00.000 type Test struct { mongox.Modelbson:"inline" Name string Age int } test := Test{ Name: "test1", Age: 12, } _, err := database2.NewCollection[Test]("test2").Updater(). Filter( query.NewBuilder(). Eq("name", "test1").Build(), ).Replacement(test).Upsert(context.Background()) if err != nil { fmt.Println(err.Error()) return }

全部代码给我看看吧,,部分代码看不出问题,包括是否调用 mongox.InitPlugin 初始化。

完整代码如下

package main

import (
	"context"
	"fmt"
	"github.com/chenmingyong0423/go-mongox"
	"github.com/chenmingyong0423/go-mongox/builder/query"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"log"
)

func main() {
	type Test struct {
		mongox.Model `bson:"inline"`
		Name         string
		Age          int
	}
	test := Test{
		Name: "test1",
		Age:  12,
	}
	client, err := mongo.Connect(
		context.Background(),
		options.Client().
			ApplyURI("mongodb://192.168.100.101:27017").
			SetAuth(options.Credential{
				Username: "xxx",
				Password: "xxx",
			}))
	if err != nil {
		log.Fatal(err)
		return
	}
	collection := client.Database("xxx").Collection("test2")

	_, err = mongox.NewCollection[Test](collection).Updater().
		Filter(
			query.NewBuilder().
				Eq("name", "test1").Build(),
		).Replacement(test).Upsert(context.Background())
	if err != nil {
		fmt.Println(err.Error())
		return
	}
}

test 传指针

传指针结果也是一样的,还是不会设置created_at

CatDrinkCoffee avatar Sep 06 '24 10:09 CatDrinkCoffee

看看

https://go-mongox.dev/model.html

chenmingyong0423 avatar Sep 06 '24 10:09 chenmingyong0423

没有调用 mongox.InitPlugin 打开开关

chenmingyong0423 avatar Sep 06 '24 10:09 chenmingyong0423

没有调用 mongox.InitPlugin 打开开关

很抱歉,临时写了个例子遗漏了,我想我找到问题在哪里了

另外,当文档已存在时,再运行该例子,会出现以下问题, write exception: write errors: [After applying the update, the (immutable) field '_id' was found to have been altered to _id: ObjectId('66dade7959ed37a26cd57087')]

实际找到是会调用 mongox.Model 中的DefaultId导致的,对于Upsert 来说,当文档已存在的时候是否可以优化调用DefaultId的逻辑 func (m *Model) DefaultId() { if m.ID.IsZero() { m.ID = primitive.NewObjectID() } }

CatDrinkCoffee avatar Sep 06 '24 10:09 CatDrinkCoffee

没有调用 mongox.InitPlugin 打开开关

很抱歉,临时写了个例子遗漏了,我想我找到问题在哪里了

另外,当文档已存在时,再运行该例子,会出现以下问题, write exception: write errors: [After applying the update, the (immutable) field '_id' was found to have been altered to _id: ObjectId('66dade7959ed37a26cd57087')]

实际找到是会调用 mongox.Model 中的DefaultId导致的,对于Upsert 来说,当文档已存在的时候是否可以优化调用DefaultId的逻辑 func (m *Model) DefaultId() { if m.ID.IsZero() { m.ID = primitive.NewObjectID() } }

其实没什么好的优化方法,因为在执行 upsert 操作之前,是没办法知道文档是否已存在的,DefautId 方法也是在 upsert 方法执行之前调用的。

要么你就自己自定义一个结构体,不要包含 ID 字段,实现更新 createdat 和 updatedat 的对应接口,mongox 就会去调用方法更新这两个字段,就不会有这个问题了。

你有好的想法也可以讨论讨论。

chenmingyong0423 avatar Sep 06 '24 11:09 chenmingyong0423

我考虑一下修改设计,upsert 的时候不要调用 DefaultId 方法,由 mongodb 自己去生成,当时设计的时候有遇到你这个问题,忘了当时的想法是咋样的了。

chenmingyong0423 avatar Sep 06 '24 13:09 chenmingyong0423

我考虑一下修改设计,upsert 的时候不要调用 DefaultId 方法,由 mongodb 自己去生成,当时设计的时候有遇到你这个问题,忘了当时的想法是咋样的了。

好的,非常感谢

CatDrinkCoffee avatar Sep 09 '24 02:09 CatDrinkCoffee

我考虑一下修改设计,upsert 的时候不要调用 DefaultId 方法,由 mongodb 自己去生成,当时设计的时候有遇到你这个问题,忘了当时的想法是咋样的了。

发现实际上upsert是调用了三个函数,但created_at和updated_at也都一起调用了,我在想created_at是不是可以无需调用,可以利用SetOnInsert来写入created_at的值,这样就会使是插入的时候来写入created_at,而不会导致在更新的时候同样将created_at覆盖为了最新时间 image

另外,之前说的DefaultId 的问题,我目前使用的方式自定义了一个model,对_id使用 omitempty 标签,并且我删除了DefaultId 的内容,这样在使用upsert的时候就不会再出现更新时提示无法修改_id的问题,不知道是不是可以作为一个参考的方法 image

CatDrinkCoffee avatar Sep 09 '24 10:09 CatDrinkCoffee

目前的设计对于 insert 操作没问题,但是对于更新,upsert 操作就有问题,所以我要修改一下设计,对于更新操作,我要侵入式的修改用户传来的 updates 文档,加上 updatedat 字段的更新,考虑到不一定是这个命名,所以我还会以另一种方式获取到字段名,对于 uosert 操作也是一样,侵入式修改 updates 文档,然后通过 SetOnInsert 决定要不要设置 id 和 created 字段。

你觉得怎么样?

chenmingyong0423 avatar Sep 09 '24 10:09 chenmingyong0423

目前的设计对于 insert 操作没问题,但是对于更新,upsert 操作就有问题,所以我要修改一下设计,对于更新操作,我要侵入式的修改用户传来的 updates 文档,加上 updatedat 字段的更新,考虑到不一定是这个命名,所以我还会以另一种方式获取到字段名,对于 uosert 操作也是一样,侵入式修改 updates 文档,然后通过 SetOnInsert 决定要不要设置 id 和 created 字段。

你觉得怎么样?

我觉得没问题,可以很好的解决现在的问题

CatDrinkCoffee avatar Sep 09 '24 10:09 CatDrinkCoffee

@CatDrinkCoffee 已经重构了设计,最新版本已经解决了我们所讨论的问题。

最新文档可参考 内置 Model

chenmingyong0423 avatar Sep 10 '24 15:09 chenmingyong0423