golang基于官方驱动的MongoDB ODM插件库mgm的使用
Golang基于官方驱动的MongoDB ODM插件库mgm的使用
Mongo Go Models (mgm)是Go语言的MongoDB ODM库,基于官方MongoDB Go驱动开发。
特性
- 定义模型并执行CRUD操作,每个操作前后都有钩子
- 简化MongoDB搜索和聚合操作
- 一次性配置后可在任何地方获取集合
- 预定义了所有Mongo操作符和键,无需硬编码
- 封装了官方MongoDB Go驱动
要求
- Go 1.17或更高版本
- MongoDB 3.6或更高版本
安装
go get github.com/kamva/mgm/v3
使用
基本配置
import (
"github.com/kamva/mgm/v3"
"go.mongodb.org/mongo-driver/mongo/options"
)
func init() {
// 设置mgm默认配置
err := mgm.SetDefaultConfig(nil, "mgm_lab", options.Client().ApplyURI("mongodb://root:12345@localhost:27017"))
}
定义模型
type Book struct {
// DefaultModel添加_id, created_at和updated_at字段
mgm.DefaultModel `bson:",inline"`
Name string `json:"name" bson:"name"`
Pages int `json:"pages" bson:"pages"`
}
func NewBook(name string, pages int) *Book {
return &Book{
Name: name,
Pages: pages,
}
}
CRUD操作示例
插入文档
book := NewBook("Pride and Prejudice", 345)
// 确保传递模型的引用(以便mgm更新模型的"updated_at"、"created_at"和"id"字段)
err := mgm.Coll(book).Create(book)
查找文档
// 获取文档的集合
book := &Book{}
coll := mgm.Coll(book)
// 查找并将文档解码到book模型
_ = coll.FindByID("5e0518aa8f1a52b0b9410ee3", book)
// 获取集合的第一个文档
_ = coll.First(bson.M{}, book)
// 使用过滤器获取集合的第一个文档
_ = coll.First(bson.M{"pages":400}, book)
更新文档
// 找到你的书
book := findMyFavoriteBook()
// 并更新它
book.Name = "Moulin Rouge!"
err := mgm.Coll(book).Update(book)
删除文档
// 只需找到并删除你的文档
err := mgm.Coll(book).Delete(book)
查找并解码结果
result := []Book{}
err := mgm.Coll(&Book{}).SimpleFind(&result, bson.M{"pages": bson.M{operator.Gt: 24}})
模型的默认字段
每个模型默认(通过使用DefaultModel结构体)有以下字段:
_id
: 文档IDcreated_at
: 文档创建日期。保存新文档时,由Creating
钩子自动填充updated_at
: 文档最后更新日期。保存文档时,由Saving
钩子自动填充
你可以实现自己的默认模型来自定义其字段。
模型的钩子
每个模型有以下钩子:
Creating
: 创建新模型时调用Created
: 新模型创建后调用Updating
: 更新模型时调用Updated
: 模型更新后调用Saving
: 创建或更新模型时调用Saved
: 模型创建或更新后调用Deleting
: 删除模型时调用Deleted
: 模型删除后调用
关于钩子的注意事项:
- 每个模型默认使用
Creating
和Saving
钩子,如果你想自己定义这些钩子,记得从你自己的钩子中调用DefaultModel
的钩子 - 调用这些钩子的集合方法:
Create
&CreateWithCtx
Update
&UpdateWithCtx
Delete
&DeleteWithCtx
示例:
func (model *Book) Creating(ctx context.Context) error {
// 调用DefaultModel的Creating钩子
if err := model.DefaultModel.Creating(ctx); err!=nil {
return err
}
// 我们可以验证模型的字段并返回错误以防止文档插入
if model.Pages < 1 {
return errors.New("book must have at least one page")
}
return nil
}
配置
mgm
默认配置有一个上下文超时:
func init() {
_ = mgm.SetDefaultConfig(&mgm.Config{CtxTimeout:12 * time.Second}, "mgm_lab", options.Client().ApplyURI("mongodb://root:12345@localhost:27017"))
}
// 获取上下文,只需调用Ctx()方法,将其赋值给变量
ctx := mgm.Ctx()
// 并使用它
coll := mgm.Coll(&Book{})
coll.FindOne(ctx, bson.M{})
// 或者直接调用Ctx()并使用
coll.FindOne(mgm.Ctx(), bson.M{})
集合
获取模型的集合:
coll := mgm.Coll(&Book{})
// 对集合进行操作
mgm
自动检测模型集合的名称:
book := Book{}
// 打印模型的集合名称
collName := mgm.CollName(&book)
fmt.Println(collName) // 输出: books
你也可以通过实现CollectionNameGetter
接口为模型设置自定义集合名称:
func (model *Book) CollectionName() string {
return "my_books"
}
// mgm返回"my_books"集合
coll := mgm.Coll(&Book{})
或者通过名称获取集合(无需为其定义模型):
coll := mgm.CollectionByName("my_coll")
// 对集合进行聚合等操作
聚合
虽然我们可以使用Mongo Go驱动的聚合功能,但mgm
也提供了更简单的方法来执行聚合:
运行聚合并解码结果:
authorCollName := mgm.Coll(&Author{}).Name()
result := []Book{}
// 只需一行代码即可完成查找
_ := mgm.Coll(&Book{}).SimpleAggregate(&result, builder.Lookup(authorCollName, "auth_id", "_id", "author"))
// 多阶段(混合mgm构建器和原始阶段)
_ := mgm.Coll(&Book{}).SimpleAggregate(&result,
builder.Lookup(authorCollName, "auth_id", "_id", "author"),
M{operator.Project: M{"pages": 0}},
)
使用mongo的Aggregate方法进行聚合:
import (
"github.com/kamva/mgm/v3"
"github.com/kamva/mgm/v3/builder"
"github.com/kamva/mgm/v3/field"
. "go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// Author模型集合
authorColl := mgm.Coll(&Author{})
cur, err := mgm.Coll(&Book{}).Aggregate(mgm.Ctx(), A{
// S函数接受操作符并返回bson.M类型
builder.S(builder.Lookup(authorColl.Name(), "author_id", field.Id, "author")),
})
更复杂的示例,与mongo原始管道混合:
import (
"github.com/kamva/mgm/v3"
"github.com/kamva/mgm/v3/builder"
"github.com/kamva/mgm/v3/field"
"github.com/kamva/mgm/v3/operator"
. "go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// Author模型集合
authorColl := mgm.Coll(&Author{})
_, err := mgm.Coll(&Book{}).Aggregate(mgm.Ctx(), A{
// S函数获取操作符并返回bson.M类型
builder.S(builder.Lookup(authorColl.Name(), "author_id", field.Id, "author")),
builder.S(builder.Group("pages", M{"books": M{operator.Push: M{"name": "$name", "author": "$author"}}})),
M{operator.Unwind: "$books"},
})
if err != nil {
panic(err)
}
事务
- 要在默认连接上运行事务,使用
mgm.Transaction()
函数,例如:
d := &Doc{Name: "Mehran", Age: 10}
err := mgm.Transaction(func(session mongo.Session, sc mongo.SessionContext) error {
// 不要忘记将session的上下文传递给集合方法
err := mgm.Coll(d).CreateWithCtx(sc, d)
if err != nil {
return err
}
return session.CommitTransaction(sc)
})
- 要使用自己的上下文运行事务,使用
mgm.TransactionWithCtx()
方法 - 要在另一个连接上运行事务,使用
mgm.TransactionWithClient()
方法
其他Mongo Go Models包
我们实现了这些包来简化mongo中的查询和聚合
builder
: 简化mongo查询和聚合
operator
: 包含作为预定义变量的mongo操作符
(例如 Eq = "$eq"
, Gt = "$gt"
)
field
: 包含聚合等中使用的mongo字段作为预定义变量
(例如 LocalField = "localField"
, ForeignField = "foreignField"
)
示例:
import (
"github.com/kamva/mgm/v3"
f "github.com/kamva/mgm/v3/field"
o "github.com/kamva/mgm/v3/operator"
"go.mongodb.org/mongo-driver/bson"
)
// 代替硬编码mongo操作符和字段
_, _ = mgm.Coll(&Book{}).Aggregate(mgm.Ctx(), bson.A{
bson.M{"$count": ""},
bson.M{"$project": bson.M{"_id": 0}},
})
// 使用预定义的操作符和管道字段
_, _ = mgm.Coll(&Book{}).Aggregate(mgm.Ctx(), bson.A{
bson.M{o.Count: ""},
bson.M{o.Project: bson.M{f.Id: 0}},
})
更多关于golang基于官方驱动的MongoDB ODM插件库mgm的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang基于官方驱动的MongoDB ODM插件库mgm的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang中使用mgm进行MongoDB对象文档映射(ODM)
mgm是一个基于官方MongoDB Go驱动(mongo-go-driver)的轻量级ODM库,提供了类似Mongoose的API风格,简化了MongoDB操作。下面我将详细介绍mgm的使用方法。
安装
首先安装mgm和官方驱动:
go get -u github.com/kamva/mgm/v3
go get go.mongodb.org/mongo-driver/mongo
基本配置
import (
"context"
"log"
"time"
"github.com/kamva/mgm/v3"
"go.mongodb.org/mongo-driver/mongo/options"
)
func init() {
// 设置MongoDB连接
err := mgm.SetDefaultConfig(nil, "mgm_lab", options.Client().ApplyURI("mongodb://localhost:27017"))
if err != nil {
log.Fatal(err)
}
}
定义模型
type Book struct {
mgm.DefaultModel `bson:",inline"`
Name string `json:"name" bson:"name"`
Pages int `json:"pages" bson:"pages"`
}
// 自定义集合名称
func (model *Book) CollectionName() string {
return "books"
}
CRUD操作示例
创建文档
func CreateBook() {
book := &Book{
Name: "Golang编程",
Pages: 300,
}
// 插入文档
err := mgm.Coll(book).Create(book)
if err != nil {
log.Fatal(err)
}
fmt.Printf("创建成功,ID: %v\n", book.ID)
}
查询文档
func FindBooks() {
var books []Book
// 查询所有文档
err := mgm.Coll(&Book{}).SimpleFind(&books, bson.M{})
if err != nil {
log.Fatal(err)
}
// 带条件查询
var filteredBooks []Book
err = mgm.Coll(&Book{}).SimpleFind(&filteredBooks, bson.M{"pages": bson.M{"$gt": 200}})
if err != nil {
log.Fatal(err)
}
// 查询单个文档
book := &Book{}
err = mgm.Coll(book).FindByID("5f8d8a7f9d6b2b2b2b2b2b2b", book)
if err != nil {
log.Fatal(err)
}
}
更新文档
func UpdateBook() {
book := &Book{}
err := mgm.Coll(book).FindByID("5f8d8a7f9d6b2b2b2b2b2b2b", book)
if err != nil {
log.Fatal(err)
}
book.Name = "Go语言高级编程"
book.Pages = 400
err = mgm.Coll(book).Update(book)
if err != nil {
log.Fatal(err)
}
}
删除文档
func DeleteBook() {
book := &Book{}
err := mgm.Coll(book).FindByID("5f8d8a7f9d6b2b2b2b2b2b2b", book)
if err != nil {
log.Fatal(err)
}
err = mgm.Coll(book).Delete(book)
if err != nil {
log.Fatal(err)
}
}
高级功能
事务支持
func TransactionExample() {
err := mgm.Transaction(func(session mongo.Session, sc mongo.SessionContext) error {
book1 := &Book{Name: "Book1", Pages: 100}
if err := mgm.Coll(book1).CreateWithCtx(sc, book1); err != nil {
return err
}
book2 := &Book{Name: "Book2", Pages: 200}
if err := mgm.Coll(book2).CreateWithCtx(sc, book2); err != nil {
return err
}
return nil
})
if err != nil {
log.Fatal(err)
}
}
钩子函数
mgm支持多种钩子函数:
type User struct {
mgm.DefaultModel `bson:",inline"`
Name string `json:"name" bson:"name"`
Email string `json:"email" bson:"email"`
}
// 创建前钩子
func (u *User) Creating() error {
u.Email = strings.ToLower(u.Email)
return nil
}
// 更新前钩子
func (u *User) Updating() error {
if u.Name == "" {
return errors.New("name cannot be empty")
}
return nil
}
// 保存后钩子
func (u *User) Saved() error {
log.Printf("User %s saved", u.ID.Hex())
return nil
}
分页查询
func PaginationExample() {
var books []Book
page := 1
perPage := 10
findOptions := options.Find()
findOptions.SetSkip(int64((page - 1) * perPage))
findOptions.SetLimit(int64(perPage))
findOptions.SetSort(bson.M{"pages": -1}) // 按页数降序
cursor, err := mgm.Coll(&Book{}).Find(context.Background(), bson.M{}, findOptions)
if err != nil {
log.Fatal(err)
}
if err = cursor.All(context.Background(), &books); err != nil {
log.Fatal(err)
}
fmt.Printf("第%d页数据: %v\n", page, books)
}
性能优化建议
- 合理使用索引
- 批量操作时使用BulkWrite
- 对于大量数据查询使用游标
- 合理设置连接池参数
总结
mgm提供了简洁的API来操作MongoDB,同时保留了官方驱动的灵活性。它特别适合需要简单ODM功能但不想引入复杂框架的项目。通过模型定义、钩子函数和事务支持,可以构建结构良好的MongoDB应用程序。
注意:在实际项目中,建议将数据库操作封装在repository层,并使用依赖注入来管理数据库连接。