尝试使用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处理。