我有以下型号:
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
)和默认值(例如空字符串, 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在子文档中实现部分更新?
答案 0 :(得分:2)
首先让我们快速解释您的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
属性的嵌入文档。 这将删除嵌入的lastName
文档的basicInfo
字段。因此,当您在更新后将文档解组为UserModel
类型的值时,{ {1}}字段将保留LastName
(因为它不再出现在MongoDB中)。
我们能做什么?
一个简单的解决方案是不使用嵌入式文档,而是将nil
字段添加到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"`
}
选项混合此解决方案保留了单独的Go结构,但在MongoDB中它不会是嵌入式文档(,inline
将被平铺,就像前面的示例一样):
BasicInfo
请注意,如果使用type UserModel struct {
Id string `bson:"_id,omitempty"`
CreatedAt *time.Time `bson:"createdAt,omitempty"`
BasicInfo UserBasicInfoModel `bson:"basicInfo,omitempty,inline"`
}
,BasicInfo
必须是非指针。这不是问题,因为如果不改变它的字段,我们可以将它保留为空结构,因为它的字段是指针,所以让它们,inline
不会改变它们。
如果您确实需要使用嵌入式文档,nil
库允许您更新嵌入文档的特定字段,但是您必须手动"构建更新文档,如下例所示:
mgo
是的,这根本不方便。如果您确实需要多次使用不同类型,可以创建一个使用反射的实用程序函数,递归迭代字段,并从非c.UpdateId(Id, bson.M{"$set": bson.M{
"basicInfo.firstName": newFirstName,
}})
的字段组装更新文档。然后,您可以将动态生成的更新文档传递给nil
,例如。