mongodb - 使用 mgo 部分更新 mongoDB 中的嵌入式文档

标签 mongodb go 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) 和默认值(例如 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(如我所料)但是lastNamenull(这不是我所期望的)

我本希望得到以下信息:

"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"`
}

请注意,如果使用,inlineBasicInfo 需要是一个非指针。这不是问题,因为如果不更改其字段,我们可以将其保留为空结构,因为它的字段是指针,因此将它们保留为 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/

相关文章:

google-app-engine - Google App Engine 错误中的 LookupHost

go - 如何将当前年份添加到 Go 模板中?

php - 即使在正确的 CFLAGS 之后,也无法在 64 位 linux 机器(AWS EC2 实例)上创建 32 位 php 扩展

json - MongoDB 查询语言本身是否有 JSON 模式之类的东西?

mongodb - 过滤 MongoDB 中的嵌入文档

mongodb - 如何获取mongodb中所有具有对象id的记录

go - mgo $inc 更新不起作用

node.js - Mongoose 在哪里查询 "or"

go - 解码 XML : use different target type based on an attribute value

mongodb - mgo 有序排序聚合