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项目欢迎所有贡献者,我们感谢您的帮助!
交流
- 加入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
}
}
性能优化建议
- 复用client和session,避免频繁创建和销毁
- 合理使用批量操作代替单条操作
- 使用Projection减少返回字段
- 为常用查询创建索引
总结
qmgo通过简洁的API封装了MongoDB的复杂操作,提供了:
- 更符合Golang习惯的链式调用
- 强大的查询构建器
- 完善的钩子机制
- 事务支持
- 良好的性能
相比官方驱动,qmgo更适合快速开发MongoDB应用,同时保持了足够的灵活性。