Skip to content

Latest commit

 

History

History
382 lines (279 loc) · 10.9 KB

README_ZH.md

File metadata and controls

382 lines (279 loc) · 10.9 KB

Qmgo

Qmgo 是一款Go语言的MongoDB driver,它基于MongoDB 官方 driver 开发实现,同时使用更易用的接口设计,比如参考mgo (比如mgo的链式调用)。

  • Qmgo让您以更优雅的姿势使用MongoDB的新特性。

  • Qmgo是从mgo迁移到新MongoDB driver的第一选择,对代码的改动影响最小。

要求

  • Go 1.10 及以上。
  • MongoDB 2.6 及以上。

功能

  • 文档的增删改查, 均支持官方driver支持的所有options
  • Sortlimitcountselectdistinct
  • 事务
  • Hooks
  • 自动化更新的默认和定制fields
  • 预定义操作符
  • 聚合Aggregate、索引操作、cursor
  • validation tags 基于tag的字段验证
  • 可自定义插件化编程

安装

推荐方式是使用go mod,通过在源码中import github.com/qiniu/qmgo 来自动安装依赖。

当然,通过下面方式同样可行:

go get github.com/qiniu/qmgo

Usage

  • 开始

    import并新建连接

    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

    cli, err := qmgo.Open(ctx, &qmgo.Config{Uri: "mongodb://localhost:27017", Database: "class", Coll: "user"})

    后面都会基于cli来举例,如果你使用第一种传统的方式进行初始化,根据上下文,将cli替换成clientdbcoll即可

    在初始化成功后,请defer来关闭连接

    defer func() {
        if err = cli.Close(ctx); err != nil {
            panic(err)
        }
    }()
  • 创建索引

    做操作前,我们先初始化一些数据:

    type UserInfo struct {
        Name   string `bson:"name"`
        Age    uint16 `bson:"age"`
        Weight uint32 `bson:"weight"`
    }
    
    var userInfo = UserInfo{
        Name:   "xm",
        Age:    7,
        Weight: 40,
    }

    创建索引

    cli.CreateOneIndex(context.Background(), options.IndexModel{Key: []string{"name"}})
    cli.CreateIndexes(context.Background(), []options.IndexModel{{Key: []string{"id2", "id3"}}})
  • 插入一个文档

    // insert one document
    result, err := cli.InsertOne(ctx, userInfo)
  • 查找一个文档

        // find one document
    one := UserInfo{}
    err = cli.Find(ctx, bson.M{"name": userInfo.Name}).One(&one)
  • 删除文档

    err = cli.Remove(ctx, bson.M{"age": 7})
  • 插入多条数据

    // multiple insert
    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)
  • 批量查找、SortLimit

    // find all 、sort and limit
    batch := []UserInfo{}
    cli.Find(ctx, bson.M{"age": 6}).Sort("weight").Limit(7).All(&batch)
  • Count

    count, err := cli.Find(ctx, bson.M{"age": 6}).Count()
  • Update

    // UpdateOne one
    err := cli.UpdateOne(ctx, bson.M{"name": "d4"}, bson.M{"$set": bson.M{"age": 7}})
    
    // UpdateAll
    result, err := cli.UpdateAll(ctx, bson.M{"age": 6}, bson.M{"$set": bson.M{"age": 10}})
  • Select

    err := cli.Find(ctx, bson.M{"age": 10}).Select(bson.M{"age": 1}).One(&one)
  • Aggregate

    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.M
    err = cli.Aggregate(context.Background(), Pipeline{matchStage, groupStage}).All(&showsWithInfo)
  • 建立连接时支持所有 mongoDB 的Options

    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)  // more options use the chain options.
    cli, err := Open(ctx, &Config{Uri: URI, Database: DATABASE, Coll: COLL}, opt)
  • 事务

    有史以来最简单和强大的事务, 同时还有超时和重试等功能:

    callback := func(sessCtx context.Context) (interface{}, error) {
        // 重要:确保事务中的每一个操作,都使用传入的sessCtx参数
        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)

    关于事务的更多内容

  • 预定义操作符

    // aggregate
    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.M
    err = cli.Aggregate(context.Background(), Pipeline{matchStage, groupStage}).All(&showsWithInfo)
  • Hooks

    Qmgo 灵活的 hooks:

    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种方式来自动化更新特定的字段

    • 默认 fields

    在文档结构体里注入 field.DefaultField, Qmgo 会自动在更新和插入操作时更新 createAtupdateAt and _id field的值.

    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)
    // tag为createAt、updateAt 和 _id 的字段会自动更新插入
    • Custom fields

    可以自定义field名, Qmgo 会自动在更新和插入操作时更新他们.

    type User struct {
        Name string `bson:"name"`
        Age  int    `bson:"age"`
    
        MyId         string    `bson:"myId"`
        CreateTimeAt time.Time `bson:"createTimeAt"`
        UpdateTimeAt int64     `bson:"updateTimeAt"`
    }
    // 指定自定义field的field名
    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)
    // CreateTimeAt、UpdateTimeAt and MyId 会自动更新并插入DB 
    
    // 假设Id和ui已经初始化
    err = cli.ReplaceOne(context.Background(), bson.M{"_id": Id}, &ui)
    // UpdateTimeAt 会被自动更新

    例子介绍

    自动化 fields 详情介绍

  • Validation tags 基于tag的字段验证

    功能基于go-playground/validator实现。

    所以Qmgo支持所有go-playground/validator 的struct验证规则,比如:

    type User struct {
        FirstName string            `bson:"fname"`
        LastName  string            `bson:"lname"`
        Age       uint8             `bson:"age" validate:"gte=0,lte=130" `    // Age must in [0,130]
        Email     string            `bson:"e-mail" validate:"required,email"` //  Email can't be empty string, and must has email format
        CreateAt  time.Time         `bson:"createAt" validate:"lte"`          // CreateAt must lte than current time
        Relations map[string]string `bson:"relations" validate:"max=2"`       // Relations can't has more than 2 elements
    }

    本功能只对以下API有效: InsertOne、InsertyMany、Upsert、UpsertId、ReplaceOne

  • 插件化编程

    • 实现以下方法
    func Do(ctx context.Context, doc interface{}, opType operator.OpType, opts ...interface{}) error{
      // do anything
    }
    • 调用middleware包的Register方法,注入Do Qmgo会在支持的操作执行前后调用Do
    middleware.Register(Do)

    Example

    Qmgo的hook、自动更新field和validation tags都基于plugin的方式实现

qmgo vs go.mongodb.org/mongo-driver

下面我们举一个多文件查找、sortlimit的例子, 说明qmgomgo的相似,以及对go.mongodb.org/mongo-driver的改进

官方Driver需要这样实现

// go.mongodb.org/mongo-driver
// find all 、sort and limit
findOptions := options.Find()
findOptions.SetLimit(7)  // set limit
var sorts D
sorts = append(sorts, E{Key: "weight", Value: 1})
findOptions.SetSort(sorts) // set sort

batch := []UserInfo{}
cur, err := coll.Find(ctx, bson.M{"age": 6}, findOptions)
cur.All(ctx, &batch)

Qmgomgo更简单,而且实现相似:

// qmgo
// find all 、sort and limit
batch := []UserInfo{}
cli.Find(ctx, bson.M{"age": 6}).Sort("weight").Limit(7).All(&batch)

// mgo
// find all 、sort and limit
coll.Find(bson.M{"age": 6}).Sort("weight").Limit(7).All(&batch)

Qmgo vs mgo

Qmgo 和 Mgo 的差异

Contributing

非常欢迎您对Qmgo的任何贡献,非常感谢您的帮助!

沟通交流: