Преобразование строки из MongoDB в int с использованием декодирования MongoDB - PullRequest
0 голосов
/ 13 февраля 2020

У меня возникли некоторые проблемы с драйвером mongodb для Go при попытке декодировать свойство в моей коллекции, имеющее тип string, в свойство в моей структуре типа int. У меня есть коллекция, у которой есть свойство, которое должно иметь тип int, который был преобразован в строку типа кем-то, кто редактировал один документ. Поскольку все документы имеют это свойство как int, кроме этого, я получаю сообщение об ошибке «не удается декодировать строку в целочисленный тип». Я нашел способ сделать это, но это не то, что «должно» быть сделано. Я изменил веселье IntDecodeValue c в default_value_decoders. go в драйвере mongodb для Go. Ниже приведено то, что я добавил.

    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)

Я знаю, что это будет перезаписано, когда я обновлю драйвер, но буду биться головой о стену, пытаясь понять, как справиться с таким случаем. Я знаю, что лучшее решение - не допускать непосредственного редактирования документов, но хочу учитывать этот случай.

Ответы [ 2 ]

1 голос
/ 15 февраля 2020

Вы можете сделать это с помощью специального декодера BSON. Примером этого в документации является https://pkg.go.dev/go.mongodb.org/mongo-driver/bson/bsoncodec?tab=doc#example -Registry-CustomDecoder . Для вашего конкретного c варианта использования должно работать следующее:

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, чтобы зарегистрировать пользовательский декодер для каждого целочисленного типа, который вы можете увидеть в вашем структуры, аналогичные тому, что сделано в функции RegisterDefaultDecoders в default_value_decoders.go.

1 голос
/ 13 февраля 2020

Вот возможное решение для декодирования этого строкового поля как int в вашей структуре, обрабатывающего как строковые, так и int поля.

Я использовал следующую структуру для этого примера:

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

И вставил следующие документы (обратите внимание, что второй do c со строковым полем как "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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...