使用 mgo 部分更新 mongoDB 中的嵌入文档
Partial update of embedded document in mongoDB using mgo
我有以下型号:
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 实现子文档的部分更新?
首先让我们快速解释一下您的 createdAt
字段。这是您保存的值:2018-05-26T15:08:56.764453386+03:00
。知道 MongoDB 以毫秒精度和 UTC 时区存储日期。因此,从 MongoDB 保存和检索的日期变为 2018-05-26T12:08:56.764Z
,这是 "same" 时间点,仅在 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
不会更改它们。
正在进行 "manual" 更新
如果您确实需要使用嵌入文档,mgo
库允许您更新嵌入文档的特定字段,但是您必须 "manually" 构建更新文档,如本例所示:
c.UpdateId(Id, bson.M{"$set": bson.M{
"basicInfo.firstName": newFirstName,
}})
是的,这一点都不方便。如果您确实需要多次使用不同的类型,您可以创建一个使用反射的实用函数,递归地迭代字段,并 assemble 从不是 nil
的字段更新文档。然后您可以将动态生成的更新文档传递给 UpdateId()
例如。
我有以下型号:
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 实现子文档的部分更新?
首先让我们快速解释一下您的 createdAt
字段。这是您保存的值:2018-05-26T15:08:56.764453386+03:00
。知道 MongoDB 以毫秒精度和 UTC 时区存储日期。因此,从 MongoDB 保存和检索的日期变为 2018-05-26T12:08:56.764Z
,这是 "same" 时间点,仅在 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
不会更改它们。
正在进行 "manual" 更新
如果您确实需要使用嵌入文档,mgo
库允许您更新嵌入文档的特定字段,但是您必须 "manually" 构建更新文档,如本例所示:
c.UpdateId(Id, bson.M{"$set": bson.M{
"basicInfo.firstName": newFirstName,
}})
是的,这一点都不方便。如果您确实需要多次使用不同的类型,您可以创建一个使用反射的实用函数,递归地迭代字段,并 assemble 从不是 nil
的字段更新文档。然后您可以将动态生成的更新文档传递给 UpdateId()
例如。