我有以下模型:
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"`
}
Run Code Online (Sandbox Code Playgroud)
我正在使用指针,以便能够区分缺失值 ( nil
) 和默认值(例如空字符串、假值等)。我也曾经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"
}
Run Code Online (Sandbox Code Playgroud)
当我尝试更新文档时,虽然我遇到了问题。我将更改作为 new 发送UserModel
,只更改FirstName
嵌入文档中的字段,如下所示:
newFirstName := "New Value"
UserModel{
BasicInfo: &UserBasicInfoModel{
FirstName: &newFirstName,
},
}
Run Code Online (Sandbox Code Playgroud)
我用来进行更新的代码如下:
UpdateId(id, bson.M{"$set": changes})
Run Code Online (Sandbox Code Playgroud)
我得到的回复如下:
"id": "aba19b45-5e84-55e0-84f8-90fad41712f6",
"createdAt": "2018-05-26T12:08:56.764Z",
"basicInfo": {
"firstName": "New Value",
"lastName": null
}
Run Code Online (Sandbox Code Playgroud)
该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"
}
Run Code Online (Sandbox Code Playgroud)
如何使用 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"`
}
Run Code Online (Sandbox Code Playgroud)
因此,您发出的update
命令的效果如下:
db.users.update({_id: "aba19b45-5e84-55e0-84f8-90fad41712f6"},
{$set:{
"_id": "aba19b45-5e84-55e0-84f8-90fad41712f6",
"basicInfo": {
"firstName": "New Value"
}
}}
)
Run Code Online (Sandbox Code Playgroud)
这是什么意思?将 设置_id
为相同的值(它不会改变),并将basicInfo
字段设置为只有一个firstName
属性的嵌入文档。这将擦除lastName
嵌入basicInfo
文档的字段。因此,当您将更新后的文档解组为您UserModel
类型的值时,该LastName
字段将保留nil
(因为它不再存在于 MongoDB 中)。
我们可以做什么?
一个简单的解决方案是不使用嵌入式文档,而是添加UserBasicInfoModel
to字段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"`
}
Run Code Online (Sandbox Code Playgroud)
,inline
选项的混合这个解决方案保留了单独的 Go 结构,但在 MongoDB 中它不会是一个嵌入的文档(BasicInfo
会像前面的例子一样被扁平化):
type UserModel struct {
Id string `bson:"_id,omitempty"`
CreatedAt *time.Time `bson:"createdAt,omitempty"`
BasicInfo UserBasicInfoModel `bson:"basicInfo,omitempty,inline"`
}
Run Code Online (Sandbox Code Playgroud)
请注意,BasicInfo
如果,inline
使用,则需要是非指针。这不是问题,因为如果不更改其字段,我们可以将其保留为空结构,因为它的字段是指针,因此保留它们nil
不会更改它们。
如果您确实需要使用嵌入式文档,该mgo
库允许您更新嵌入式文档的特定字段,但您必须“手动”构建更新文档,如下例所示:
c.UpdateId(Id, bson.M{"$set": bson.M{
"basicInfo.firstName": newFirstName,
}})
Run Code Online (Sandbox Code Playgroud)
是的,这根本不方便。如果您确实需要多次使用不同的类型,您可以创建一个使用反射的实用函数,递归遍历字段,并从不是 的字段组装更新文档nil
。然后您可以将动态生成的更新文档传递UpdateId()
给例如。
归档时间: |
|
查看次数: |
1449 次 |
最近记录: |