golang简化MongoDB数据库操作的插件库qmgo的使用

Golang简化MongoDB数据库操作的插件库qmgo的使用

Qmgo简介

Qmgo是一个Go语言的MongoDB驱动库,基于MongoDB官方驱动开发,但提供了更简单易用的API(如链式调用)。

  • Qmgo允许用户以更优雅的方式使用MongoDB的新功能
  • Qmgo是从mgo迁移到新MongoDB驱动的首选方案,只需最小代码改动

要求

  • Go 1.10及以上版本
  • MongoDB 2.6及以上版本

特性

  • 文档的CRUD操作,支持所有官方选项
  • 排序、限制、计数、选择、去重
  • 事务支持
  • 钩子函数
  • 自动默认和自定义字段
  • 预定义操作符
  • 聚合、索引操作、游标
  • 验证标签
  • 插件系统

安装

使用go mod自动安装依赖:

import "github.com/qiniu/qmgo"

或使用go get:

go get github.com/qiniu/qmgo

使用示例

1. 初始化连接

import (
    "context"
    "github.com/qiniu/qmgo"
)

ctx := context.Background()
// 方式1:创建客户端
client, err := qmgo.NewClient(ctx, &qmgo.Config{Uri: "mongodb://localhost:27017"})
db := client.Database("class")
coll := db.Collection("user")

// 方式2:直接连接到指定数据库和集合(推荐)
cli, err := qmgo.Open(ctx, &qmgo.Config{
    Uri: "mongodb://localhost:27017", 
    Database: "class", 
    Coll: "user",
})

// 确保关闭连接
defer func() {
    if err = cli.Close(ctx); err != nil {
        panic(err)
    }
}()

2. 定义数据结构

type UserInfo struct {
    Name   string `bson:"name"`
    Age    uint16 `bson:"age"`
    Weight uint32 `bson:"weight"`
}

var userInfo = UserInfo{
    Name: "xm",
    Age: 7,
    Weight: 40,
}

3. 创建索引

// 创建单个索引
cli.CreateOneIndex(context.Background(), options.IndexModel{Key: []string{"name"}})

// 创建多个索引
cli.CreateIndexes(context.Background(), []options.IndexModel{{Key: []string{"id2", "id3"}}})

4. 插入文档

// 插入单个文档
result, err := cli.InsertOne(ctx, userInfo)

// 插入多个文档
var userInfos = []UserInfo{
    {Name: "a1", Age: 6, Weight: 20},
    {Name: "b2", Age: 6, Weight: 25},
    {Name: "c3", Age: 6, Weight: 30},
    {Name: "d4", Age: 6, Weight: 35},
    {Name: "a1", Age: 7, Weight: 40},
    {Name: "a1", Age: 8, Weight: 45},
}
result, err = cli.InsertMany(ctx, userInfos)

5. 查询文档

// 查询单个文档
one := UserInfo{}
err = cli.Find(ctx, bson.M{"name": userInfo.Name}).One(&one)

// 查询所有文档,排序和限制
batch := []UserInfo{}
cli.Find(ctx, bson.M{"age": 6}).Sort("weight").Limit(7).All(&batch)

// 计数
count, err := cli.Find(ctx, bson.M{"age": 6}).Count()

6. 更新文档

// 更新单个文档
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}})

7. 删除文档

err = cli.Remove(ctx, bson.M{"age": 7})

8. 选择字段

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

9. 聚合操作

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)

10. 事务支持

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)

11. 钩子函数

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)

12. 自动字段

// 默认字段
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)
// 自动生成createAt、updateAt和_id字段

// 自定义字段
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)
// 自动生成CreateTimeAt、UpdateTimeAt和MyId字段

13. 验证标签

type User struct {
    FirstName string            `bson:"fname"`
    LastName  string            `bson:"lname"`
    Age       uint8             `bson:"age" validate:"gte=0,lte=130"`    // 年龄必须在[0,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"`       // 关系不能超过2个元素
}

14. 插件系统

func Do(ctx context.Context, doc interface{}, opType operator.OpType, opts ...interface{}) error{
  // 执行任何操作
}

// 注册插件
middleware.Register(Do)

Qmgo与其他驱动的比较

Qmgo vs go.mongodb.org/mongo-driver

Qmgo提供了更简洁的链式调用API,例如:

// qmgo方式
batch := []UserInfo{}
cli.Find(ctx, bson.M{"age": 6}).Sort("weight").Limit(7).All(&batch)

// mongo-driver方式
findOptions := options.Find()
findOptions.SetLimit(7)
var sorts D
sorts = 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 vs mgo

Qmgo与mgo的API风格相似,但基于新的MongoDB驱动开发,支持更多新特性。

贡献

Qmgo项目欢迎所有贡献者,我们感谢您的帮助!

交流


更多关于golang简化MongoDB数据库操作的插件库qmgo的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang简化MongoDB数据库操作的插件库qmgo的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


qmgo - 简化MongoDB操作的Golang库

qmgo是一个优秀的MongoDB ORM/ODM库,它基于官方MongoDB Go驱动(mongo-go-driver)进行了封装,提供了更简洁、更符合Golang风格的API。下面我将详细介绍qmgo的使用方法。

安装

go get github.com/qiniu/qmgo

基本用法

1. 连接MongoDB

import (
    "context"
    "github.com/qiniu/qmgo"
)

func main() {
    ctx := context.Background()
    
    // 连接MongoDB
    client, err := qmgo.NewClient(ctx, &qmgo.Config{
        Uri: "mongodb://localhost:27017",
    })
    if err != nil {
        panic(err)
    }
    defer client.Close(ctx)
    
    // 选择数据库和集合
    db := client.Database("testdb")
    coll := db.Collection("users")
}

2. 插入文档

type User struct {
    Name string `bson:"name"`
    Age  int    `bson:"age"`
    Email string `bson:"email"`
}

// 插入单个文档
user := User{Name: "张三", Age: 25, Email: "zhangsan@example.com"}
result, err := coll.InsertOne(ctx, user)
if err != nil {
    panic(err)
}
fmt.Printf("插入ID: %v\n", result.InsertedID)

// 批量插入
users := []interface{}{
    User{Name: "李四", Age: 30, Email: "lisi@example.com"},
    User{Name: "王五", Age: 28, Email: "wangwu@example.com"},
}
results, err := coll.InsertMany(ctx, users)
if err != nil {
    panic(err)
}
fmt.Printf("批量插入IDs: %v\n", results.InsertedIDs)

3. 查询文档

// 查询单个文档
var foundUser User
err = coll.Find(ctx, qmgo.M{"name": "张三"}).One(&foundUser)
if err != nil {
    panic(err)
}
fmt.Printf("找到用户: %+v\n", foundUser)

// 查询多个文档
var users []User
err = coll.Find(ctx, qmgo.M{"age": qmgo.M{"$gt": 25}}).All(&users)
if err != nil {
    panic(err)
}
fmt.Printf("年龄大于25的用户: %+v\n", users)

// 分页查询
err = coll.Find(ctx, qmgo.M{}).
    Sort("age").
    Skip(1).
    Limit(2).
    All(&users)
if err != nil {
    panic(err)
}

4. 更新文档

// 更新单个文档
err = coll.UpdateOne(ctx, 
    qmgo.M{"name": "张三"},
    qmgo.M{"$set": qmgo.M{"age": 26}})
if err != nil {
    panic(err)
}

// 更新多个文档
result, err := coll.UpdateAll(ctx,
    qmgo.M{"age": qmgo.M{"$gt": 25}},
    qmgo.M{"$inc": qmgo.M{"age": 1}})
if err != nil {
    panic(err)
}
fmt.Printf("更新了%d个文档\n", result.ModifiedCount)

5. 删除文档

// 删除单个文档
err = coll.Remove(ctx, qmgo.M{"name": "王五"})
if err != nil {
    panic(err)
}

// 删除多个文档
result, err = coll.RemoveAll(ctx, qmgo.M{"age": qmgo.M{"$lt": 25}})
if err != nil {
    panic(err)
}
fmt.Printf("删除了%d个文档\n", result.DeletedCount)

高级功能

1. 事务支持

session, err := client.StartSession()
if err != nil {
    panic(err)
}
defer session.EndSession(ctx)

err = session.StartTransaction()
if err != nil {
    panic(err)
}

// 在事务中执行操作
err = coll.WithContext(ctx).InsertOne(&User{Name: "事务用户", Age: 30})
if err != nil {
    session.AbortTransaction(ctx)
    panic(err)
}

err = session.CommitTransaction(ctx)
if err != nil {
    panic(err)
}

2. 聚合查询

pipeline := []qmgo.M{
    {"$match": qmgo.M{"age": qmgo.M{"$gt": 25}}},
    {"$group": qmgo.M{
        "_id": "$name",
        "total": qmgo.M{"$sum": 1},
    }},
}

var results []bson.M
err = coll.Aggregate(ctx, pipeline).All(&results)
if err != nil {
    panic(err)
}

3. 钩子函数

qmgo支持在操作前后执行钩子函数:

type UserWithHook struct {
    Name string `bson:"name"`
    Age  int    `bson:"age"`
    
    // 实现BeforeInsert接口
    func (u *UserWithHook) BeforeInsert(ctx context.Context) error {
        fmt.Println("即将插入用户:", u.Name)
        return nil
    }
    
    // 实现AfterInsert接口
    func (u *UserWithHook) AfterInsert(ctx context.Context) error {
        fmt.Println("已插入用户:", u.Name)
        return nil
    }
}

性能优化建议

  1. 复用client和session,避免频繁创建和销毁
  2. 合理使用批量操作代替单条操作
  3. 使用Projection减少返回字段
  4. 为常用查询创建索引

总结

qmgo通过简洁的API封装了MongoDB的复杂操作,提供了:

  • 更符合Golang习惯的链式调用
  • 强大的查询构建器
  • 完善的钩子机制
  • 事务支持
  • 良好的性能

相比官方驱动,qmgo更适合快速开发MongoDB应用,同时保持了足够的灵活性。

回到顶部