使用Golang的MongoDriver和reflect包来获取数据的方法是什么?

package main

import (
    "context"
    "fmt"
    "log"
    "reflect"
    "time"

    "go.mongodb.org/mongo-driver/bson"
    "go.mongodb.org/mongo-driver/bson/primitive"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
    "go.mongodb.org/mongo-driver/mongo/readpref"
)

type Foo struct {
    ID   *primitive.ObjectID `json:"_id" bson:"_id,omitempty"`
    Name string              `json:"name" bson:"name"`
}

func main() {
    ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
    client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://localhost:27017"))
    ctx, _ = context.WithTimeout(context.Background(), 2*time.Second)
    if err = client.Ping(ctx, readpref.Primary()); err != nil {
        log.Fatalf("%+v\n", err)
    }
    collection := client.Database("testing").Collection("foo")

    // ここまでサンプルどおり ---------------------------------

    fooType := reflect.TypeOf(Foo{})
    fooSlice := createSlice(reflect.SliceOf(fooType))

    // この行がメイン
    d := find(collection, fooSlice, fooType).([]Foo)
    fmt.Println(d)
}

// 黒魔術感ある
// 単純にMakeSliceだけだと到達可能なアドレスを返せないので、makedSlice.CanSet() == false
// なので、新しくreflect.valueをNewして、作ったmakedSliceをセットする形を取っている。
func createSlice(sliceType reflect.Type) reflect.Value {
    makedSlice := reflect.MakeSlice(sliceType, 0, 16)
    reflectionValue := reflect.New(makedSlice.Type())
    reflectionValue.Elem().Set(makedSlice)
    slicePtr := reflect.ValueOf(reflectionValue.Interface())
    return slicePtr.Elem()
}

func find(col *mongo.Collection, reflectSlice reflect.Value, reflectType reflect.Type) interface{} {
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel()
    cur, err := col.Find(ctx, bson.M{})
    if err != nil {
        log.Fatalf("%+v\n", err)
    }
    defer cur.Close(ctx)
    for cur.Next(ctx) {
        v := reflect.New(reflectType)
        if err := cur.Decode(v.Interface()); err != nil {
            log.Fatalf("%+v\n", err)
        }
        reflectSlice.Set(reflect.Append(reflectSlice, v.Elem()))
    }
    if err := cur.Err(); err != nil {
        log.Fatalf("%+v\n", err)
    }
    return reflectSlice.Interface()
}

如果使用Mongo数据库或者进行数据准备,虽然需要一些步骤,但如果使用Golang 1.12以上版本,只需复制黏贴即可运行。

不过话说回来,Golang的反射真的很难理解和使用。

听说有引入范型的计划,如果实现了,就不用写这种代码了,感觉真不错呢。

在性能方面,-试了使用1万条数据进行查找的情况下,

    型指定:55ms
    リフレクション:58ms

因为差别只有大约3毫秒左右,所以应该可以使用,不是吗?

此外,关于createSlice函数的具体内容,为什么必须这样做才能使其正常工作?我还没有完全理解。。。

※2019年06月17日追記:我写了一篇文章,旨在深入理解黑魔法代码。
https://qiita.com/daijinload/items/f822e4a208d17e432599

bannerAds