【问题标题】:Converting string from MongoDB into int using MongoDB decode使用 MongoDB 解码将字符串从 MongoDB 转换为 int
【发布时间】:2020-02-15 08:59:26
【问题描述】:

当我尝试将我的集合中的字符串类型的属性解码为我的结构中的 int 类型的属性时,我在使用 Go 的 mongodb 驱动程序时遇到了一些问题。我有一个集合,该集合的属性应该是 int 类型,该属性被编辑单个文档的人转换为 string 类型。由于所有文档都具有该属性作为 int 除了那个,我收到错误“无法将字符串解码为整数类型”。我想出了一种方法来做到这一点,但这不是“应该”做的事情。我在 Go 的 mongodb 驱动程序中修改了 default_value_decoders.go 中的 IntDecodeValue 函数。以下是我添加的内容。

    case bsontype.String:
    s, err := vr.ReadString()
    if err != nil {
        return err
    }
    i32, err := strconv.Atoi(s)
    if err != nil {
        return err
    }
    i64 = int64(i32)

我知道当我更新驱动程序时这将被覆盖,但我一直在努力想弄清楚如何处理这种情况。我知道最好的解决方案是不允许直接编辑文档,但要考虑这种情况。

【问题讨论】:

  • 唯一可行的解​​决方案是删除任何愚蠢到手动编辑数据库中文档的人的访问权限。您无法解释用户在手动修改数据时可以使用多少种方式来破坏数据。如果他们稍后将字符串更改为 int 怎么办?还是一个数组到一个字符串?您不能只是不断地向 Mongo 驱动程序添加 hack 来解释那些显然不应该首先拥有 DB 访问权限的人的不当行为。
  • 我完全同意这个评估。我只是希望有一种方法可以扩展该功能,以便能够优雅地处理所述重整,而不会导致应用程序出现问题或排除已被重整的文档。
  • 是的,有点。您需要为他们可能会破坏您希望能够优雅处理的数据的每一种方式添加另一个 hack。这些会堆积起来,使更新安全补丁变得更加困难。也就是说......你的问题实际上并没有问题。你的问题到底是什么?
  • 有没有办法扩展该功能,所以我可以添加至少一些它可能会被破坏的方式。我只学了几个星期,而且 afaik 没有像其他语言那样的扩展功能。
  • 我决定这可能不是我想像阿德里安所说的那样走下去的道路,太多的可能会被破坏。我将在创建集合时设置一些验证规则,这样如果有人手动编辑文档,他们就不能违反集合上的数据类型

标签: mongodb go


【解决方案1】:

您可以通过自定义 BSON 解码器执行此操作。文档中的一个示例是 https://pkg.go.dev/go.mongodb.org/mongo-driver/bson/bsoncodec?tab=doc#example-Registry-CustomDecoder。对于您的特定用例,以下应该有效:

func intDecodeValue(dc bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) {

// Same logic as DefaultValueDecoders.IntDecodeValue
// In the switch statement for vr.Type, you can add a case for bsontype.String

}

要注册此解码器以便在执行 CRUD 操作时使用它,您可以使用 SetRegistry 客户端选项:

decoder := bsoncodec.ValueDecoderFunc(intDecodeValue)
registry := bson.NewRegistryBuilder().RegisterDefaultDecoder(reflect.Int, decoder).Build()

clientOpts := options.Client().SetRegistry(registry)
client, err := mongo.Connect(context.TODO(), clientOpts)

请注意,由于 Go 区分不同的整数类型(例如 int8/int16/int32/int64),因此您需要调用 RegisterDefaultDecoder 为您可能在结构中看到的每种整数类型注册自定义解码器,类似default_value_decoders.go 中的 RegisterDefaultDecoders 函数所做的事情。

【讨论】:

  • 是的,这就是我最终所做的,直到我决定我什至不打算检查以确保每个文档具有相同的数据结构。我在集合本身上使用验证规则来完全阻止数据类型的更改。感谢大家的帮助。
【解决方案2】:

这是在处理字符串和 int 字段的结构中将该字符串字段解码为 int 的可能解决方案。

我在这个例子中使用了以下结构:

type Obj struct {
    Field1   string `bson:"field1"`
    IntField int    `bson:"intField"`
}

并插入以下文档(注意第二个文档的字符串字段为"intField": "2"):

db.s2i.insertOne({"field1": "value", "intField": 3})
db.s2i.insertOne({"field1": "value2", "intField": "2"})

使用聚合框架中的$toInt 运算符,如下所示:

pipeline := []bson.M{bson.M{"$match": bson.M{}}, bson.M{"$project": bson.M{"field1": 1, "intField": bson.M{"$toInt": "$intField"}}}}

cur, err := client.Database("stack").Collection("s2i").Aggregate(context.TODO(), pipeline)

for cur.Next(context.TODO()) {
    var res *Obj
    err = cur.Decode(&res)

    fmt.Println(res, err)
    fmt.Println("Value of res.IntField:")
    fmt.Println(res.IntField)
    fmt.Println("Type of res.IntField:")
    fmt.Println(reflect.TypeOf(res.IntField))
}

返回以下文档,其中“2”解码为 int:

&{value 3} <nil>
Value of res.IntField:
3
Type of res.IntField:
int
&{value2 2} <nil>
Value of res.IntField:
2
Type of res.IntField:
int

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-10-30
    • 1970-01-01
    • 2011-08-21
    • 1970-01-01
    • 2021-11-09
    • 2015-10-06
    • 1970-01-01
    相关资源
    最近更新 更多