web-dev-qa-db-ja.com

mongodb goドライバーを使用して、ドキュメントをカスタムタイプフィールドを持つ構造体にデコードする

私はgoとmongodbの両方の初心者です。 bsonタグを使用してDocumentResultを構造体にデコードしようとしましたが、文字列をラップするカスタムタイプでは機能しません。フィールドのタイプを文字列に変更せずに実行できますか?

    import (
    "context"
    "github.com/mongodb/mongo-go-driver/mongo"
)

type MyDoc struct {
    SomeInt int `bson:"some_int"`
    SomeString string `bson:"some_string,omitempty"`
    CustomType MyType `bson:"custom_type,omitempty"`
}

type MyType string

const myType MyType = "ABCD"

func main() {

    //Connect to db
    client, _ := mongo.Connect(context.Background(), "mongodb://localhost:27017", nil)
    db := client.Database("example_db")
    collection := db.Collection("col")

    //Insert document
    docToInsert := MyDoc{42, "The Answer", myType}
    collection.InsertOne(nil, docToInsert)

    //Retrieve document
    filterDoc := MyDoc{SomeInt: 42}
    resultDoc := &MyDoc{}
    result := collection.FindOne(nil, filterDoc)
    result.Decode(resultDoc)

    println(resultDoc.SomeInt, resultDoc.SomeString, resultDoc.CustomType)

印刷結果: "42 The Answer" // "ABCD"がありません

前もって感謝します

5
amz

残念ながらあなたは運が悪い。公式のmongo goドライバーの現在の状態は、BSONからstringを基本型として持つカスタム型であるGo値へのstring値の非整列化をサポートしていません。これは将来変更される可能性がありますが、現時点ではサポートされていません。

構造体フィールドへのデコードの処理方法は、 bson/decode.go、現在行#387

case 0x2:
    str := v.StringValue()
    switch containerType {
    case tString, tEmpty:
        val = reflect.ValueOf(str)
    case tJSONNumber:
        _, err := strconv.ParseFloat(str, 64)
        if err != nil {
            return val, err
        }
        val = reflect.ValueOf(str).Convert(tJSONNumber)

    case tURL:
        u, err := url.Parse(str)
        if err != nil {
            return val, err
        }
        val = reflect.ValueOf(u).Elem()
    default:
        return val, nil
    }

0x02はBSON文字列タイプです。構造体フィールドのタイプが次のいずれかである場合にのみ、構造体フィールドへのデコードが試行されます:stringinterface{}json.Number または url.URL (またはこれらへのポインタ)。

残念ながら bson.Unmarshaler は、構造体自体がそれを実装している場合にのみ、構造体フィールドの場合はチェックされないため、カスタムタイプでも役に立ちません。ただし、構造体自体に実装する場合は、上記のサポートされている型のいずれかであるフィールドを持つ構造体を複製する必要があります(またはマップまたは bson.Document type)。

これはライブラリの一部に対する重大な制限であり、非常に簡単に解決できるため、近い将来にサポートが追加されることを願っています。

0
icza

Bsonタグを使用してDocumentResultを構造体にデコードしようとしましたが、文字列をラップするカスタムタイプでは機能しません

現在のMyTypeを使用すると、MongoDBに保存されるドキュメントは次のようになります。

{
  "_id": ObjectId("..."),
  "some_int": NumberLong("42"),
  "some_string": "The Answer",
  "custom_type": "ABCD"
}

基になる型はstringですが、型の折り返しのため、現在のバージョンの mongo-go-driver (v0.0.12)でデコードするのは難しい場合があります。

ただし、カスタムタイプをそのまま使用する場合は、代わりに構造体を 埋め込みフィールド に変更できます。例えば:

type MyDoc struct {
    SomeInt    int    `bson:"some_int"`
    SomeString string `bson:"some_string,omitempty"`
    CustomType MyType `bson:"custom_type,omitempty"`
}

type MyType struct {
    Value string `bson:"value,omitempty"`
}

var myType = MyType{Value: "ABCD"}

docToInsert := MyDoc{42, "The Answer", "ABCD"}

insertResult, err := collection.InsertOne(nil, docToInsert)

resultDoc := collection.FindOne(context.Background(), nil)
if err != nil {
    log.Fatal(err)
}
elem := &MyDoc{}
err = resultDoc.Decode(elem)
if err != nil {
    log.Fatal(err)
}
fmt.Println(elem.SomeInt, elem.SomeString, elem.CustomType.Value)
// 42 The Answer ABCD

ドキュメントは次のようにMongoDBに保存されます。

{
  "_id": ObjectId("..."),
  "some_int": NumberLong("42"),
  "some_string": "The Answer",
  "custom_type": {
    "value": "ABCD"
  }
}

そうでない場合は、stringタイプを直接使用します。データベース内の結果のドキュメントは、タイプラッピングバージョンと同じになるからです。

type MyDoc struct {
    SomeInt    int    `bson:"some_int"`
    SomeString string `bson:"some_string,omitempty"`
    CustomType string `bson:"custom_type,omitempty"`
} 

また、 MongoDB Data Modeling が役立つリファレンスを見つけるかもしれません。

1
Wan Bachtiar