我有以下模型:
type UserModel struct {
Id string `bson:"_id,omitempty"`
CreatedAt *time.Time `bson:"createdAt,omitempty"`
BasicInfo *UserBasicInfoModel `bson:"basicInfo,omitempty"`
}
// *Embedded document*
type UserBasicInfoModel struct {
FirstName *string `bson:"firstName,omitempty"`
LastName *string `bson:"lastName,omitempty"`
}
我正在使用指针,以便能够区分缺失值 (nil
) 和默认值(例如 empty 字符串、false 值等)。我还使用 omitempty
来进行部分更新。
当我创建一个用户时,我得到以下(正确的)响应:
"id": "aba19b45-5e84-55e0-84f8-90fad41712f6",
"createdAt": "2018-05-26T15:08:56.764453386+03:00",
"basicInfo": {
"firstName": "Initial first name",
"lastName": "Initial last name"
}
当我尝试更新文档时遇到问题。
我将更改作为新的 UserModel
发送,只更改嵌入文档中的 FirstName
字段,如下所示:
newFirstName := "New Value"
UserModel{
BasicInfo: &UserBasicInfoModel{
FirstName: &newFirstName,
},
}
我用来执行更新的代码如下:
UpdateId(id, bson.M{"$set": changes})
我得到的回复如下:
"id": "aba19b45-5e84-55e0-84f8-90fad41712f6",
"createdAt": "2018-05-26T12:08:56.764Z",
"basicInfo": {
"firstName": "New Value",
"lastName": null
}
createdAt
值不是null
(如我所料)但是lastName
值是null
(这不是我所期望的)
我本希望得到以下信息:
"id": "aba19b45-5e84-55e0-84f8-90fad41712f6",
"createdAt": "2018-05-26T12:08:56.764Z",
"basicInfo": {
"firstName": "New Value",
"lastName": "Initial last name"
}
如何使用 mgo 实现子文档的部分更新?
最佳答案
首先让我们快速解释一下您的 createdAt
字段。这是您保存的值:2018-05-26T15:08:56.764453386+03:00
。知道 MongoDB 以毫秒精度和 UTC 时区存储日期。因此,从 MongoDB 中保存和检索的这个日期变为 2018-05-26T12:08:56.764Z
,这是“相同”的时刻,只是在 UTC 时区中,精度被截断为毫秒。
现在更新嵌入文档:
简短而不幸的答案是我们不能直接使用 mgo
库和 Go 模型来做到这一点。
为什么?
当我们使用 ,omitempty
选项时,我们将一些指针字段保留为零值(即 nil
),就像我们使用其类型甚至没有这些字段的值。
所以在你的例子中,如果你只改变BasicInfo.FirstName
字段,并且你使用这个值来更新,就相当于使用这些结构:
type UserModel struct {
Id string `bson:"_id,omitempty"`
BasicInfo *UserBasicInfoModel `bson:"basicInfo,omitempty"`
}
type UserBasicInfoModel struct {
FirstName *string `bson:"firstName,omitempty"`
}
因此,您发出的 update
命令的效果如下:
db.users.update({_id: "aba19b45-5e84-55e0-84f8-90fad41712f6"},
{$set:{
"_id": "aba19b45-5e84-55e0-84f8-90fad41712f6",
"basicInfo": {
"firstName": "New Value"
}
}}
)
这是什么意思?将 _id
设置为相同的值(不会改变),并将 basicInfo
字段设置为只有一个 firstName< 的嵌入文档
属性。 这将删除嵌入的 basicInfo
文档的 lastName
字段。因此,当您在更新后将文档解码为 的值时UserModel
类型,LastName
字段将保持为 nil
(因为它不再存在于 MongoDB 中)。
我们能做什么?
展平嵌入文档
一个简单的解决方案是不使用嵌入式文档,而是将 UserBasicInfoModel
的字段添加到 UserModel
:
type UserModel struct {
Id string `bson:"_id,omitempty"`
CreatedAt *time.Time `bson:"createdAt,omitempty"`
FirstName *string `bson:"firstName,omitempty"`
LastName *string `bson:"lastName,omitempty"`
}
混合 ,inline
选项
此解决方案保留了单独的 Go 结构,但在 MongoDB 中它将不是嵌入式文档(BasicInfo
将像前面的示例一样被展平):
type UserModel struct {
Id string `bson:"_id,omitempty"`
CreatedAt *time.Time `bson:"createdAt,omitempty"`
BasicInfo UserBasicInfoModel `bson:"basicInfo,omitempty,inline"`
}
请注意,如果使用,inline
,BasicInfo
需要是一个非指针。这不是问题,因为如果不更改其字段,我们可以将其保留为空结构,因为它的字段是指针,因此将它们保留为 nil
不会更改它们。
进行“手动”更新
如果您确实需要使用嵌入式文档,mgo
库允许您更新嵌入式文档的特定字段,但是您必须“手动”构建更新文档,如本例所示:
c.UpdateId(Id, bson.M{"$set": bson.M{
"basicInfo.firstName": newFirstName,
}})
是的,这一点都不方便。如果您确实需要多次使用不同的类型,您可以创建一个使用反射的实用函数,递归地迭代字段,并从不是 nil
的字段组装更新文档。例如,您可以将动态生成的更新文档传递给 UpdateId()
。
关于mongodb - 使用 mgo 部分更新 mongoDB 中的嵌入式文档,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50542717/