尝试使用MongoDB官方的Go语言驱动程序(1)准备和插入数据

由于MongoDB官方的Go语言驱动程序v1.0.0在三月份发布,所以我也试着使用它来学习Go语言。
链接:https://docs.mongodb.com/ecosystem/drivers/go/

执行环境

    • Windows 10

 

    • go.12 windows/amd64

 

    • MongoDB Community版 4.0.7

 

    • MongoDB Go Driver 1.0.0

 

    • Goパッケージ管理ツール dep

 

    エディタ VS Code & Go Extension

前提

    • MongoDBがインストールされている

 

    Go言語がインストールされている

准备好了

为了进行事务处理,将MongoDB作为仅有一个节点的副本集启动。

以MongoDB副本集的形式启动mongod。

mongod --dbpath db --replSet repl

顺便一提,如果将其设置为独立的副本集,既可以使用事务,又可以在oplog中保留更新历史记录,以便以后进行确认,非常方便。

初始化MongoDB副本集

mongo
> rs.initiate()
     メッセージが表示される
repl:SECONDARY>
     何回かenterキーを押すとSECONDARYがPRIMARYに変わる
repl:PRIMARY>

安装dep

go get -u github.com/golang/dep/cmd/dep

创建Go应用程序的文件夹

%GOPATH%\src\mongo1 的中文释义是什么?

mkdir mongo1
cd mongo1
dep init

创建一个导入了MongoDB Driver的源文件(main.go)。

package main

import  "go.mongodb.org/mongo-driver/mongo"

func main() {
}

安装Go语言驱动

我们先创建上述的main.go文件,然后进行安装。

dep ensure -add "go.mongodb.org/mongo-driver/mongo@~1.0.0"

请参考 https://github.com/mongodb/mongo-go-driver 的安装步骤。

创建插入文档的方法

我将介绍三种文档表达方法。

    • bson.D

 

    • bson.M

 

    構造体(struct) 自分で定義します

另外,在bson.D和bson.M中,若要使用数组,请使用bson.A。

使用上述的方法表达JSON如下所示

{"str1": "abc", "num1":1, "str2": "xyz", "num2": [2,3,4], "subdoc": {"str": "subdoc", "num": 987},"date": 現在の日付}

bson.D = BSON字典

    bsonD := bson.D{
        {"str1", "abc"},
        {"num1", 1},
        {"str2", "xyz"},
        {"num2", bson.A{2,3,4}},
        {"subdoc", bson.D{{"str", "subdoc"}, {"num", 987}}},
        {"date", time.Now()},
    }

bson.D是使用primitive.D表示的primitive.E的切片。

bson.M可以以原始的中国方式进行改写

    bsonM := bson.M{
        "str1": "abc",
        "num1": 1,
        "str2": "xyz",
        "num2": bson.A{2,3,4},
        "subdoc": bson.M{"str": "subdoc", "num": 987},
        "date": time.Now(),
    }

bson.M 是 primitive.M 的别名,它以 map[string]interface{} 的形式表示。

bson.D和bson.M的主要区别是什么?

bson.D保证了项目的顺序(str1,num1,str2,num2,subdoc,日期),但bson.M不能保证项目的顺序。我会在之后多次执行插入操作来确认。

当在使用bson.M定义子文档(subdoc)时,如果子文档内的项目顺序很重要,我们可以在bson.M内部使用bson.D。

    bsonM := bson.M{

        "subdoc": bson.D{{"str", "subdoc"}, {"num", 987}},

    }

構造体(struct)

type myType struct {
    Str1 string
    Num1 int
    Str2 string
    Num2 []int
    Subdoc struct {
        Str string
        Num int
    }
    Date time.Time
}
    doc := myType{
        "abc",
        1,
        "xyz",
        []int{2, 3, 4},
        struct {
            Str string
            Num int
        }{"subdoc", 987},
        time.Now(),
    }

MongoDBへのInsert処理

context.WithTimeoutは使っていません

ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)

Insertするデータは各3タイプごとに少し変えています

bson.Mのデータにつていは同じデータを10回insertしています。Insertされた項目の順序が保証されないことが確かめられると思います
トランザクション処理はしていません

package main

import (
    "context"
    "log"
    "time"

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

func insertBsonD(col *mongo.Collection) error {
    bsonD := bson.D{
        {"str1", "abc"},
        {"num1", 1},
        {"str2", "xyz"},
        {"num2", bson.A{2, 3, 4}},
        {"subdoc", bson.D{{"str", "subdoc"}, {"num", 987}}},
        {"date", time.Now()},
    }
    _, err := col.InsertOne(context.Background(), bsonD)
    return err
}

func insertBsonM(col *mongo.Collection) error {
    bsonM := bson.M{
        "str1": "efg",
        "num1": 11,
        "str2": "opq",
        "num2": bson.A{12, 13, 14},
        "subdoc": bson.M{"str": "subdoc", "num": 987},
        "date": time.Now(),
    }
    for i := 0; i < 10; i++ {
        _, err := col.InsertOne(context.Background(), bsonM)
        if err != nil {
            return err
        }
    }
    return nil
}

type myType struct {
    Str1 string
    Num1 int
    Str2 string
    Num2 []int
    Subdoc struct {
        Str string
        Num int
    }
    Date time.Time
}

func insertStruct(col *mongo.Collection) error {
    doc := myType{
        "hij",
        21,
        "rst",
        []int{22, 23, 24},
        struct {
            Str string
            Num int
        }{"subdoc", 987},
        time.Now(),
    }
    _, err := col.InsertOne(context.Background(), doc)
    return err
}

func mainMain() error {
    client, err := mongo.NewClient(options.Client().ApplyURI("mongodb://localhost:27017"))
    if err != nil {
        return err
    }
    if err = client.Connect(context.Background()); err != nil {
        return err
    }
    defer client.Disconnect(context.Background())

    col := client.Database("test").Collection("col")
    if err = insertBsonD(col); err != nil {
        return err
    }
    if err = insertBsonM(col); err != nil {
        return err
    }
    if err = insertStruct(col); err != nil {
        return err
    }
    return nil
}

func main() {
    if err := mainMain(); err != nil {
        log.Fatal(err)
    }
    log.Println("normal end.")
}

当不想要插入结构体中定义的某些项目时

例えばstr2をInsertから除くとき、bson.D,bson.Mのときはデータを追加しなければInsertされませんので、必要に応じてstr2の項目をInsertしたりしなかったりできます。構造体の場合はstr2を含むものと含まないものを2つ用意すれば可能ですが、1つの構造体で行う方法を紹介します。

Str2 *string ",omitempty" 
または
Str2 *string `bson:",omitempty"` 

str2をstringのポインタにしてomitemptyタグを追加します。
ポインタの値がnilであればInsert項目から除かれます。もしomitemptyタグが無くnilのときは”str2″:nullとしてInsertされます。

構造体のタグについては次のドキュメントを参照してください。
https://godoc.org/go.mongodb.org/mongo-driver/bson/bsoncodec#StructTagParserFunc

type myType struct {
    Str1 string
    Num1 int
    Str2 *string ",omitempty"
    Num2 []int
    Subdoc struct {
        Str string
        Num int
    }
    Date time.Time
}

func insertStruct(col *mongo.Collection) error {
    doc := myType{
        "hij",
        21,
        nil,
        []int{22, 23, 24},
        struct {
            Str string
            Num int
        }{"subdoc", 987},
        time.Now(),
    }
    //str2はinsertされない
    _, err := col.InsertOne(context.Background(), doc)
    if err != nil {
        return err
    }
    doc = myType{
        "hij",
        21,
        new(string),
        []int{22, 23, 24},
        struct {
            Str string
            Num int
        }{"subdoc", 987},
        time.Now(
    }
    *doc.Str2 = "rst"
    //str2は "rst"がinsertされる
    _, err = col.InsertOne(context.Background(), doc)
    return err
}

_idの扱いと同じ項目に複数タイプのデータを入れたいとき

当_id字段不存在时,MongoDB会自动创建一个ObjectId并进行注册。当在结构体中定义_id时,可以使用标签将字段命名为ID等,并以_id作为标识。ObjectId的类型是primitive.ObjectID,但可以使用任何喜欢的数据类型,如string、int等。以下示例是使用primitive.ObjectID的范例。

例えばstr2にstring, intなど複数タイプのデータを入れたいときはinterface{}として定義します。

type myType struct {
    ID   primitive.ObjectID "_id"
    Str1 string
    Num1 int
    Str2 interface{}  ",omitempty"
    Num2 []int
    Subdoc struct {
        Str string
        Num int
    }
    Date time.Time
}
doc := myType{
        primitive.NewObjectID(),
        "hij",
        21,
        "rst", //string
        []int{22, 23, 24},
        struct {
            Str string
            Num int
        }{"subdoc", 987},
        time.Now(),
    }
doc2 := myType{
        primitive.NewObjectID(),
        "hij",
        21,
        567, //int
        []int{22, 23, 24},
        struct {
            Str string
            Num int
        }{"subdoc", 987},
        time.Now(),
    }
doc3 := myType{
        primitive.NewObjectID(),
        "hij",
        21,
        nil, //str2は",omitempty"があるのでinsertされない
        []int{22, 23, 24},
        struct {
            Str string
            Num int
        }{"subdoc", 987},
        time.Now(),
    }

插入多个

InsertManyの引数は[]interface{}なので、いろいろな構造のデータを一度にInsertとできます。次の例はbson.D,bson.M, structとの3タイプで定義されたドキュメントを一度にInsertする例です.

package main

import (
    "context"
    "log"
    "time"

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

func makeBsonD() interface{} {
    return bson.D{
        {"str1", "abc"},
        {"num1", 1},
        {"str2", "xyz"},
        {"num2", bson.A{2, 3, 4}},
        {"subdoc", bson.D{{"str", "subdoc"}, {"num", 987}}},
        {"date", time.Now()},
    }
}

func makeBsonM() interface{} {
    return bson.M{
        "str1": "efg",
        "num1": 11,
        "str2": "opq",
        "num2": bson.A{12, 13, 14},
        "subdoc": bson.M{"str": "subdoc", "num": 987},
        "date": time.Now(),
    }
}

type myType struct {
    Str1 string
    Num1 int
    Str2 string
    Num2 []int
    Subdoc struct {
        Str string
        Num int
    }
    Date time.Time
}

func makeStruct() interface{} {
    return myType{
        "hij",
        21,
        "rst",
        []int{22, 23, 24},
        struct {
            Str string
            Num int
        }{"subdoc", 987},
        time.Now(),
    }
}

func mainMain() error {
    client, err := mongo.NewClient(options.Client().ApplyURI("mongodb://localhost:27017"))
    if err != nil {
        return err
    }
    if err = client.Connect(context.Background()); err != nil {
        return err
    }
    defer client.Disconnect(context.Background())

    col := client.Database("test").Collection("col")
    insertDocuments := []interface{}{
        makeBsonD(),
        makeBsonM(),
        makeStruct(),
    }
    _, err = col.InsertMany(context.Background(), insertDocuments)

    return err
}

func main() {
    if err := mainMain(); err != nil {
        log.Fatal(err)
    }
    log.Println("normal end.")
}

トランザクション処理(Insertのみの例)

トランザクション処理を行うにはclient.UseSession()を使ってクロージャ内でStartTransaction()でトランザクション処理を開始します。 終了はCommitTransaction()またはAbortTransaction()を使ってInsertを確定するか取り消すかをします。またInsertOneのcontext.ContextにはUseSessionで渡されたmongo.SessionContextを使います。
ctx.CommitTransaction(ctx)をコメントにして、代わりにコメントになっているctx.AbortTransaction(ctx)を有効にしてみてください。1件もInsertされないはずです。

注意:通常,如果在没有集合存在的情况下进行插入操作,将会创建一个集合。但是,在事务处理中无法创建集合,所以请事先创建好集合。因此,当多次重复测试事务处理时,请不要使用db.col.drop()来删除集合,而应该使用db.col.remove({})。

package main

import (
    "context"
    "log"
    "time"

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

func insertBsonD(ctx mongo.SessionContext, col *mongo.Collection) error {
    bsonD := bson.D{
        {"str1", "abc"},
        {"num1", 1},
        {"str2", "xyz"},
        {"num2", bson.A{2, 3, 4}},
        {"subdoc", bson.D{{"str", "subdoc"}, {"num", 987}}},
        {"date", time.Now()},
    }
    _, err := col.InsertOne(ctx, bsonD)
    return err
}

func insertBsonM(ctx mongo.SessionContext, col *mongo.Collection) error {
    bsonM := bson.M{
        "str1": "efg",
        "num1": 11,
        "str2": "opq",
        "num2": bson.A{12, 13, 14},
        "subdoc": bson.M{"str": "subdoc", "num": 987},
        "date": time.Now(),
    }
    for i := 0; i < 10; i++ {
        _, err := col.InsertOne(ctx, bsonM)
        if err != nil {
            return err
        }
    }
    return nil
}

type myType struct {
    Str1 string
    Num1 int
    Str2 string
    Num2 []int
    Subdoc struct {
        Str string
        Num int
    }
    Date time.Time
}

func insertStruct(ctx mongo.SessionContext, col *mongo.Collection) error {
    doc := myType{
        "hij",
        21,
        "rst",
        []int{22, 23, 24},
        struct {
            Str string
            Num int
        }{"subdoc", 987},
        time.Now(),
    }
    _, err := col.InsertOne(ctx, doc)
    return err
}

func mainMain() error {
    client, err := mongo.NewClient(options.Client().ApplyURI("mongodb://localhost:27017"))
    if err != nil {
        return err
    }
    if err = client.Connect(context.Background()); err != nil {
        return err
    }
    defer client.Disconnect(context.Background())

    col := client.Database("test").Collection("col")
    err = client.UseSession(
        context.Background(),
        func(ctx mongo.SessionContext) error {
            var err2 error
            //トランザクション処理の開始
            if err2 = ctx.StartTransaction(); err2 != nil {
                return err2
            } else if err2 = insertBsonD(ctx, col); err2 != nil {
            } else if err2 = insertBsonM(ctx, col); err2 != nil {
            } else if err2 = insertStruct(ctx, col); err2 != nil {
            }
            if err2 == nil {
                ctx.CommitTransaction(ctx)
                //ctx.AbortTransaction(ctx)
            } else {
                ctx.AbortTransaction(ctx)
            }
            return err2
        })
    return err
}

func main() {
    if err := mainMain(); err != nil {
        log.Fatal(err)
    }
    log.Println("normal end.")
}

使用mongo.WithSession进行事务操作

只写下mainMain()。

func mainMain() error {
    client, err := mongo.NewClient(options.Client().ApplyURI("mongodb://localhost:27017"))
    if err != nil {
        return err
    }
    if err = client.Connect(context.Background()); err != nil {
        return err
    }
    defer client.Disconnect(context.Background())

    col := client.Database("test").Collection("col")
    sess, err := client.StartSession()
    if err != nil {
        return err
    }

    err = mongo.WithSession(
        context.Background(),
        sess,
        func(ctx mongo.SessionContext) error {
            var err2 error
            //トランザクション処理の開始
            if err2 = ctx.StartTransaction(); err2 != nil {
                return err2
            } else if err2 = insertBsonD(ctx, col); err2 != nil {
            } else if err2 = insertBsonM(ctx, col); err2 != nil {
            } else if err2 = insertStruct(ctx, col); err2 != nil {
            }
            if err2 == nil {
                ctx.CommitTransaction(ctx)
                //ctx.AbortTransaction(ctx)
            } else {
                ctx.AbortTransaction(ctx)
            }
            return err2
        })
    return err
}

我计划在下一篇文章中进行Find处理。

广告
将在 10 秒后关闭
bannerAds