How can I assign a null value to date field using mongo go driver instead of Date(-62135596800000)

huangapple go评论122阅读模式
英文:

How can I assign a null value to date field using mongo go driver instead of Date(-62135596800000)

问题

我有两个MongoDB服务器。我使用mongo go驱动程序从其中一个服务器接收数据。接收到的数据有一个日期字段,该字段始终为null。然后在我的代码中,我可能会将其更改为其他日期,也可能保持为null并将接收到的数据放入另一个服务器。

问题是,当我提交数据时,时间字段变为Date(-62135596800000),而不是null

我尝试过分配time.Time{},下面的代码也没有解决问题。

t, err := time.Parse("2006-01-02T15:04:05Z", "0001-01-01T00:00:01Z")
if err != nil {
    fmt.Println(err)
}
retrieved[i].SessionEndedDateUTC = t

每次我都会得到Date(-62135596800000),即使我检索数据并在不修改的情况下进行插入更新。

英文:

I have two MongoDB servers. From one I receive data using mongo go driver. The received data has one date field which is always null. Then in my code I may or may not change it to some other date or leave it as null and put the received data to the other server.

The problem is that when I post the data, the time field turns to

> Date(-62135596800000) instead of null.

I have tried to assign time.Time{} and the code below didn't solve the problem as well.

t, err := time.Parse("2006-01-02T15:04:05Z", "0001-01-01T00:00:01Z")
			if err != nil {
			    fmt.Println(err)
			}
			retrieved[i].SessionEndedDateUTC = t

Everytime I get Date(-62135596800000) instead of null, even if I retrieve the data and upsert it without modification.

答案1

得分: 5

在Go语言中,time.Time是一个结构体,不能为nil值。它有一个零值(即所有结构字段都具有它们的零值),但是这个零值对应于MongoDB的ISODate("0001-01-01T00:00:00Z"),而不是MongoDB的null值。

如果你的字段是time.Time类型,你不能将任何值设置给它以得到MongoDB的null值。

最简单的解决方案是改用指向time.Time的指针类型,即*time.Time。如果你将该字段保留为Go的nil值,它将在MongoDB中变为null

如果你不能或不想使用*time.Time,仍然有一个"变通"方法:声明两个字段,一个是你的"常规"的time.Time类型,使用结构标签将其排除在MongoDB之外。然后添加另一个类型为*time.Time的字段,并将其映射到MongoDB。编写自定义的编组/解组逻辑,当编组时,根据原始字段更新这个额外的字段,或者在解组时,根据额外的字段设置原始字段。

以下是一个示例:

type User struct {
    CreatedAt  time.Time  `bson:"-"`
    PCreatedAt *time.Time `bson:"created"`
}

func (u *User) GetBSON() (interface{}, error) {
    if u.CreatedAt.IsZero() {
        u.PCreatedAt = nil
    } else {
        u.PCreatedAt = &u.CreatedAt
    }
    return u, nil
}

func (u *User) SetBSON(raw bson.Raw) (err error) {
    if err = raw.Unmarshal(u); err != nil {
        return
    }
    if u.PCreatedAt == nil {
        u.CreatedAt = time.Time{}
    } else {
        u.CreatedAt = *u.PCreatedAt
    }
    return
}

解释:

User.CreatedAt保存了你可以使用(读取/写入)的time.Time值。这个字段在MongoDB中被排除。

有一个指针字段User.PCreatedAt,它被映射到MongoDB中的created属性。

当一个User对象被编组(保存到MongoDB)时,会调用GetBSON()方法。如果CreatedAt是零值,则将PCreatedAt设置为nil,这将在MongoDB中变为null。否则,使用非零的时间戳。

当一个User对象被解组(从MongoDB加载)时,会调用SetBSON()方法。它检查PCreatedAt是否为nil(对应于MongoDB的null),如果是,则将CreatedAt设置为零值。否则,使用从MongoDB中检索到的时间戳。

相关/类似的问题:

https://stackoverflow.com/questions/41907619/set-default-date-when-inserting-document-with-time-time-field/42344267#42344267

https://stackoverflow.com/questions/42342943/accesing-mongodb-from-go/42343810#42343810

英文:

In Go time.Time is a struct which cannot have a nil value. It has a zero value (which is all struct fields having their zero values), but this zero value corresponds to MongoDB ISODate("0001-01-01T00:00:00Z") and not to the MongoDB null value.

If your field is of type time.Time, you cannot set any value to it to end up MongoDB null value.

The simplest solution is to use a field of type pointer to time.Time instead, that is *time.Time. If you leave or set this field to Go nil, it will end up as null in MongoDB.

If you can't or don't want to use *time.Time, there is still a "workaround": declare 2 fields, one is of your "regular" time.Time type, and use a struct tag to exclude this from MongoDB. And add another field of type *time.Time, and make this mapped to MongoDB. And write a custom marshaling / unmarshaling logic, which when marshaling would update this extra field based on the original, or would set the original based on the extra when unmarshaling.

This is an example how it could look like:

type User struct {
	CreatedAt  time.Time  `bson:"-"`
	PCreatedAt *time.Time `bson:"created"`
}

func (u *User) GetBSON() (interface{}, error) {
	if u.CreatedAt.IsZero() {
		u.PCreatedAt = nil
	} else {
		u.PCreatedAt = &u.CreatedAt
	}
	return u, nil
}

func (u *User) SetBSON(raw bson.Raw) (err error) {
	if err = raw.Unmarshal(u); err != nil {
		return
	}
	if u.PCreatedAt == nil {
		u.CreatedAt = time.Time{}
	} else {
		u.CreatedAt = *u.PCreatedAt
	}
	return
}

Explanation:

User.CreatedAt holds the time.Time value which you can work with (read / write). This field is excluded from MongoDB.

There is a pointer User.PCreatedAt field which is mapped to the created property in MongoDB.

When a User is marshaled (saved to MongoDB), GetBSON() is called. If CreatedAt is the zero value, sets PCreatedAt to nil which will end up as null in MongoDB. Else sets / uses the non-zero timestamp.

When a User is unmarshaled (loaded from MongoDB), SetBSON() is called. This checks if PCreatedAt is nil (which corresponds to MongoDB null), and if so, sets CreatedAt to its zero value. Else uses the timestamp retrieved from MongoDB.

Related / similar questions:

https://stackoverflow.com/questions/41907619/set-default-date-when-inserting-document-with-time-time-field/42344267#42344267

https://stackoverflow.com/questions/42342943/accesing-mongodb-from-go/42343810#42343810

答案2

得分: 0

行为

在内部,Date对象以一个64位整数的形式存储,表示自Unix纪元(1970年1月1日)以来的毫秒数,这导致了一个可表示的日期范围,大约可以追溯到过去和未来的290亿年。

请参阅https://docs.mongodb.com/manual/reference/method/Date/

英文:

mongodb doc

Behavior

Internally, Date objects are stored as a 64 bit integer representing the number of milliseconds since the Unix epoch (Jan 1, 1970), which results in a representable date range of about 290 millions years into the past and future

see https://docs.mongodb.com/manual/reference/method/Date/

huangapple
  • 本文由 发表于 2017年4月27日 17:09:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/43653402.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定