Qmgo Qmgo 是一款Go语言的MongoDB driver,它基于MongoDB 官方 driver 开发实现,同时使用更易用的接口设计,比如参考mgo (比如mgo的链式调用)。
要求
Go 1.10 及以上。
MongoDB 2.6 及以上。
功能
文档的增删改查, 均支持官方driver支持的所有options
Sort、limit、count、select、distinct
事务
Hooks
自动化更新的默认和定制fields
预定义操作符
聚合Aggregate、索引操作、cursor
validation tags 基于tag的字段验证
可自定义插件化编程
安装 推荐方式是使用go mod,通过在源码中import github.com/qiniu/qmgo 来自动安装依赖。
当然,通过下面方式同样可行:
1 go get github.com/qiniu/qmgo
Usage 开始
import并新建连接
1 2 3 4 5 6 7 8 9 10 11 import ( "context" "github.com/qiniu/qmgo" ) ctx := context.Background() client, err := qmgo.NewClient(ctx, &qmgo.Config{Uri: "mongodb://localhost:27017" }) db := client.Database("class" ) coll := db.Collection("user" )
如果你的连接是指向固定的 database 和 collection,我们推荐使用下面的更方便的方法初始化连接,后续操作都基于cli而不用再关心 database 和 collection
1 cli, err := qmgo.Open(ctx, &qmgo.Config{Uri: "mongodb://localhost:27017" , Database: "class" , Coll: "user" })
后面都会基于cli来举例,如果你使用第一种传统的方式进行初始化,根据上下文,将cli替换成client、db 或 coll即可
在初始化成功后,请defer来关闭连接
1 2 3 4 5 defer func () { if err = cli.Close(ctx); err != nil { panic (err) } }()
创建索引 做操作前,我们先初始化一些数据:
1 2 3 4 5 6 7 8 9 10 11 12 type UserInfo struct { Name string `bson:"name"` Age uint16 `bson:"age"` Weight uint32 `bson:"weight"` } var userInfo = UserInfo{ Name: "xm" , Age: 7 , Weight: 40 , }
创建索引
1 2 cli.CreateOneIndex(context.Background(), options.IndexModel{Key: []string {"name" }}) cli.CreateIndexes(context.Background(), []options.IndexModel{{Key: []string {"id2" , "id3" }}})
插入一个文档
1 2 result, err := cli.InsertOne(ctx, userInfo)
查询 查找一个文档 1 2 3 one := UserInfo{} err = cli.Find(ctx, bson.M{"name" : userInfo.Name}).One(&one)
Select只返回需要的字段 只返回age字段
1 err := cli.Find(ctx, bson.M{"age" : 10 }).Select(bson.M{"age" : 1 }).One(&one)
排序 使用sort方法。写上字段名即可,默认是升序,前面加上-就是降序了
1 2 3 4 5 6 7 8 9 // 升序 err = coll.Find(ctx, findOptions).Sort("weight").All(&users) // 降序 err = coll.Find(ctx, findOptions).Sort("-weight").All(&users) // 组合排序 err = coll.Find(ctx, findOptions).Sort("age","-weight").All(&users)
分页查找 1 2 batch := []UserInfo{} cli.Find(ctx, bson.M{"age" : 6 }).Sort("weight" ).Skip(10 ).Limit(10 ).All(&batch)
Count 1 count, err := cli.Find(ctx, bson.M{"age" : 6 }).Count()
删除文档
1 err = cli.Remove(ctx, bson.M{"age" : 7 })
插入多条数据
1 2 3 4 5 6 7 8 9 10 var userInfos = []UserInfo{ UserInfo{Name: "a1" , Age: 6 , Weight: 20 }, UserInfo{Name: "b2" , Age: 6 , Weight: 25 }, UserInfo{Name: "c3" , Age: 6 , Weight: 30 }, UserInfo{Name: "d4" , Age: 6 , Weight: 35 }, UserInfo{Name: "a1" , Age: 7 , Weight: 40 }, UserInfo{Name: "a1" , Age: 8 , Weight: 45 }, } result, err = cli.Collection.InsertMany(ctx, userInfos)
Update
1 2 3 4 5 err := cli.UpdateOne(ctx, bson.M{"name" : "d4" }, bson.M{"$set" : bson.M{"age" : 7 }}) result, err := cli.UpdateAll(ctx, bson.M{"age" : 6 }, bson.M{"$set" : bson.M{"age" : 10 }})
Aggregate
1 2 3 4 matchStage := bson.D{{"$match" , []bson.E{{"weight" , bson.D{{"$gt" , 30 }}}}}} groupStage := bson.D{{"$group" , bson.D{{"_id" , "$name" }, {"total" , bson.D{{"$sum" , "$age" }}}}}} var showsWithInfo []bson.Merr = cli.Aggregate(context.Background(), Pipeline{matchStage, groupStage}).All(&showsWithInfo)
建立连接时支持所有 mongoDB 的Options
1 2 3 4 5 6 7 8 9 10 11 12 13 14 poolMonitor := &event.PoolMonitor{ Event: func (evt *event.PoolEvent) { switch evt.Type { case event.GetSucceeded: fmt.Println("GetSucceeded" ) case event.ConnectionReturned: fmt.Println("ConnectionReturned" ) } }, } opt := options.Client().SetPoolMonitor(poolMonitor) cli, err := Open(ctx, &Config{Uri: URI, Database: DATABASE, Coll: COLL}, opt)
事务 有史以来最简单和强大的事务, 同时还有超时和重试等功能:
1 2 3 4 5 6 7 8 9 10 11 callback := func (sessCtx context.Context) (interface {}, error ) { if _, err := cli.InsertOne(sessCtx, bson.D{{"abc" , int32 (1 )}}); err != nil { return nil , err } if _, err := cli.InsertOne(sessCtx, bson.D{{"xyz" , int32 (999 )}}); err != nil { return nil , err } return nil , nil } result, err = cli.DoTransaction(ctx, callback)
关于事务的更多内容
预定义操作符
1 2 3 4 5 matchStage := bson.D{{operator.Match, []bson.E{{"weight" , bson.D{{operator.Gt, 30 }}}}}} groupStage := bson.D{{operator.Group, bson.D{{"_id" , "$name" }, {"total" , bson.D{{operator.Sum, "$age" }}}}}} var showsWithInfo []bson.Merr = cli.Aggregate(context.Background(), Pipeline{matchStage, groupStage}).All(&showsWithInfo)
Hooks
Qmgo 灵活的 hooks:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 type User struct { Name string `bson:"name"` Age int `bson:"age"` } func (u *User) BeforeInsert(ctx context.Context) error { fmt.Println("before insert called" ) return nil } func (u *User) AfterInsert(ctx context.Context) error { fmt.Println("after insert called" ) return nil } u := &User{Name: "Alice" , Age: 7 } _, err := cli.InsertOne(context.Background(), u)
Hooks 详情介绍
自动化更新fields
Qmgo支持2种方式来自动化更新特定的字段
在文档结构体里注入 field.DefaultField, Qmgo 会自动在更新和插入操作时更新 createAt、updateAt and _id field的值.
1 2 3 4 5 6 7 8 9 10 type User struct { field.DefaultField `bson:",inline"` Name string `bson:"name"` Age int `bson:"age"` } u := &User{Name: "Lucas" , Age: 7 } _, err := cli.InsertOne(context.Background(), u)
可以自定义field名, Qmgo 会自动在更新和插入操作时更新他们.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 type User struct { Name string `bson:"name"` Age int `bson:"age"` MyId string `bson:"myId"` CreateTimeAt time.Time `bson:"createTimeAt"` UpdateTimeAt int64 `bson:"updateTimeAt"` } func (u *User) CustomFields() field.CustomFieldsBuilder { return field.NewCustom().SetCreateAt("CreateTimeAt" ).SetUpdateAt("UpdateTimeAt" ).SetId("MyId" ) } u := &User{Name: "Lucas" , Age: 7 } _, err := cli.InsertOne(context.Background(), u) err = cli.ReplaceOne(context.Background(), bson.M{"_id" : Id}, &ui)
例子介绍
自动化 fields 详情介绍
Validation tags 基于tag的字段验证
功能基于go-playground/validator 实现。
所以Qmgo支持所有go-playground/validator 的struct验证规则 ,比如:
1 2 3 4 5 6 7 8 type User struct { FirstName string `bson:"fname"` LastName string `bson:"lname"` Age uint8 `bson:"age" validate:"gte=0,lte=130" ` Email string `bson:"e-mail" validate:"required,email"` CreateAt time.Time `bson:"createAt" validate:"lte"` Relations map [string ]string `bson:"relations" validate:"max=2"` }
本功能只对以下API有效:InsertOne、InsertyMany、Upsert、UpsertId、ReplaceOne
插件化编程
1 2 3 func Do (ctx context.Context, doc interface {}, opType operator.OpType, opts ...interface {}) error { }
调用middleware包的Register方法,注入Do Qmgo会在支持的操作 执行前后调用Do
Example
Qmgo的hook、自动更新field和validation tags都基于plugin的方式实现
qmgo vs go.mongodb.org/mongo-driver下面我们举一个多文件查找、sort和limit的例子, 说明qmgo和mgo的相似,以及对go.mongodb.org/mongo-driver的改进
官方Driver需要这样实现
1 2 3 4 5 6 7 8 9 10 11 findOptions := options.Find() findOptions.SetLimit(7 ) var sorts Dsorts = append (sorts, E{Key: "weight" , Value: 1 }) findOptions.SetSort(sorts) batch := []UserInfo{} cur, err := coll.Find(ctx, bson.M{"age" : 6 }, findOptions) cur.All(ctx, &batch)
Qmgo和mgo更简单,而且实现相似:
1 2 3 4 5 6 7 8 batch := []UserInfo{} cli.Find(ctx, bson.M{"age" : 6 }).Sort("weight" ).Limit(7 ).All(&batch) coll.Find(bson.M{"age" : 6 }).Sort("weight" ).Limit(7 ).All(&batch)
Qmgo vs mgoQmgo 和 Mgo 的差异