# 前言
在 `Go` 语言里使用 `MongoDB` 官方提供的 `mongo-go-driver` 库进行集合操作时,你是否感到结构体与 `MongoDB` 集合之间的映射,以及构建 `BSON` 数据这些操作极其繁琐?特别是在构建 `BSON` 数据时,各种字段、逗号和括号的排列组合是否让你觉得仿佛在进行一场复杂的拼图游戏?
如果你有同感,那么你并不孤单,我也是。因此我在想,有没有一个能让我丝滑,高效操作 `MongoDB` 的第三方库呢,遗憾的是,并没有找到符合我预期的库,索性我就自己动手开发了一个,这就是 `go mongox` 库的由来。
如果你也有类似我的这种感受,相信 `go mongox` 库能给你带来不一样的体验。
# Go Mongox
`go mongox` 是一个基于泛型的库,扩展了 `MongoDB` 的官方库。通过泛型技术,它实现了结构体与 `MongoDB` 集合的绑定,旨在提供类型安全和简化的数据操作。
`go mongox` 还引入链式调用,让文档操作更流畅,并且提供了丰富的 `BSON` 构建器和内置函数,简化了 `BSON` 数据的构建。
此外,它还支持插件化编程和内置多种钩子函数,为数据库操作前后的自定义逻辑提供灵活性,增强了应用的可扩展性和可维护性。
> 仓库地址:https://github.com/chenmingyong0423/go-mongox
>
> 官网文档:https://go-mongox.dev
>
> 欢迎体验 `go mongox` 库,也期待您的贡献。如果您觉得这个库对您有帮助,请给它一个 Star 支持!
## 功能特性
- 泛型的 `MongoDB` 集合
- 支持 `BSON` 数据的构建
- 文档的 `CRUD` 操作
- 聚合操作
- 内置基本的 `Model` 结构体,自动化更新默认的 `field` 字段
- 支持结构体 `tag` 校验
- `Hooks`
- 支持插件化编程
## 安装
> go get github.com/chenmingyong0423/go-mongox@latest
# Collection 集合操作
## 基于泛型的 Collection 形态初始化
```go
package main
import (
"context"
"fmt"
"github.com/chenmingyong0423/go-mongox"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
)
type User struct {
mongox.Model `bson:"inline"`
Name string `bson:"name"`
Age int `bson:"age"`
}
func main() {
// 你需要预先创建一个 *mongo.Collection 对象
mongoColl := newCollection()
// 使用 User 结构体作为泛型参数创建一个 collection
userColl := mongox.NewCollection[User](mongoColl)
fmt.Println(userColl != nil)
}
// 示例代码,不是最佳的创建方式
func newCollection() *mongo.Collection {
client, err := mongo.Connect(context.Background(), options.Client().ApplyURI("mongodb://localhost:27017").SetAuth(options.Credential{
Username: "test",
Password: "test",
AuthSource: "db-test",
}))
if err != nil {
panic(err)
}
err = client.Ping(context.Background(), readpref.Primary())
if err != nil {
panic(err)
}
collection := client.Database("db-test").Collection("test_post")
return collection
}
```
通过 `mongox.NewCollection` 函数,我们可以指定泛型参数并创建一个泛型的 `Collection` 对象。这样我们就可以使用 `userColl` 对象来操作 `User` 类型的文档了。
**后面的操作将基于 `userColl` 对象进行举例。**
## Creator 创造器
通过 `Creator()` 方法获取一个新的泛型的创建器对象,即 `Creator[T]`,通过 `Creator[T]` 的方法,我们能够执行相关的创建操作。
### 示例
```go
// 插入单个文档
insertOneResult, err := userColl.Creator().InsertOne(context.Background(), &User{Name: "陈明勇", Age: 18})
users := []*User{
{Name: "Mingyong Chen", Age: 18},
{Name: "cmy", Age: 18},
}
// 插入多个文档
insertManyResult, err := userColl.Creator().InsertMany(context.Background(), users)
```
`InsertOne` 和 `InsertMany` 方法与官方的 `API` 同名,前者用于插入单个文档,后者用于插入多个文档。如果我们需要设置 `options` 参数,可以将其作为这两个方法的第三个参数传递。
更多用法可前往官网文档 [Creator 创造器 | go mongox](https://go-mongox.dev/operator/creator.html) 进行查看。
## Finder 查询器
通过 `Finder()` 方法获取一个新的泛型的查询器对象,即 `Finder[T]`。通过 `Finder[T]` 的方法,我们能够执行相关的查询操作。
### 示例
```go
// 查询单个文档
post, err := userColl.Finder().Filter(query.Id(primitive.NewObjectID())).FindOne(context.Background())
// - 复杂条件查询
// -- 使用 query 包构建复杂的 bson
// -- bson.D{bson.E{Key:"name", Value:bson.D{bson.E{Key:"$eq", Value:"陈明勇"}}}, bson.E{Key:"age", Value:bson.D{bson.E{Key:"$eq", Value:18}}}}
post, err = userColl.Finder().
Filter(query.NewBuilder().Eq("name", "陈明勇").Eq("age", 18).Build()).
FindOne(context.Background())
// 查询多个文档
posts, err := userColl.Finder().
Filter(query.In("name", []string{"陈明勇", "Mingyong Chen"}...)).
Find(context.Background())
```
`FindOne` 和 `Find` 方法与官方的 `API` 同名,前者用于查询单个文档,后者用于查询多个文档。我们可以通过 `Filter` 方法设置 **查询条件**,如果我们需要设置 `options` 参数,可以将其作为这两个方法的第二个参数传递。
对于简单的查询条件,我们可以使用 `query` 包提供的函数进行构建,例如 `query.(id)`;对于复杂的查询条件,我们可以使用 `query` 包提供的 `Builder` 构建器进行构建。`query` 包的用法会在下面的内容进行详细地介绍。
更多用法可前往官网文档 [Finder 查询器 | go mongox](https://go-mongox.dev/operator/finder.html) 进行查看。
## Updater 更新器
通过 `Updater()` 方法获取一个新的泛型的更新器对象,即 `Updater[T]`。通过 `Updater[T]` 的方法,我们能够执行相关的更新操作。
### 示例
```go
// 更新单个文档
// 通过 update 包构建 bson 更新语句
updateResult, err := userColl.Updater().
Filter(query.Id(primitive.NewObjectID())).
Updates(update.Set("age", 19)).
UpdateOne(context.Background())
// 更新多个文档
updateResult, err = userColl.Updater().
Filter(query.In("name", []string{"陈明勇", "Mingyong Chen"}...)).
Updates(update.Set("age", "19")).
UpdateMany(context.Background())
```
`UpdateOne` 和 `UpdateMany` 方法与官方的 `API` 同名,前者用于更新单个文档,后者用于更新多个文档。我们可以通过 `Filter` 方法设置 **文档匹配的条件**,如果我们需要设置 `options` 参数,可以将其作为这两个方法的第二个参数传递。
对于更新参数,我们可以使用 `Updates` 方法进行设置。该方法接收 `bson` 或 `map` 等合法的更新操作语句。上面的例子使用了 `update` 包里的 `Set` 对更新操作语句进行构建。
更多用法可前往官网文档 [Updater 更新器 | go mongox](https://go-mongox.dev/operator/updater.html) 进行查看。
## Deleter 删除器
通过 `Deleter()` 方法获取一个新的泛型的删除器对象,即 `Deleter[T]`。通过 `Deleter[T]` 的方法,我们能够执行相关的删除操作。
### 示例
```go
// 删除单个文档
deleteResult, err := userColl.Deleter().Filter(query.Id(primitive.NewObjectID())).DeleteOne(context.Background())
// 删除多个文档
deleteResult, err = userColl.Deleter().
Filter(query.In("name", "Mingyong Chen", "cmy")).
DeleteMany(context.Background())
```
`DeleteOne` 和 `DeleteMany` 方法与官方的 `API` 同名,前者用于删除单个文档,后者用于删除多个文档。我们可以通过 `Filter` 方法设置 **文档匹配的条件**。如果我们需要设置 `options` 参数,可以将其作为这两个方法的第二个参数传递。
更多用法可前往官网文档 [Deleter 删除器 | go mongox](https://go-mongox.dev/operator/deleter.html) 进行查看。
## Aggregator 聚合器
通过 `Aggregator()` 获取一个新的泛型的聚合器对象,即 `Aggregator[T any]`,通过 `Aggregator[T any]` 的方法,我们能够执行相关的聚合操作。
聚合器实现了三个方法:
- `Pipeline()` 用于设置聚合管道。
- `Aggregate()` 用于执行聚合操作,返回的查询结果类型与 `T` 一致。
- `AggregateWithParse()` 也是用于执行聚合操作,但使用场景不一样。当聚合结果的类型与 `T` 不一致时,使用 `AggregateWithParse()` 方法可以将结果解析到指定的对象里。
```go
// 忽略年龄字段,只查询名字
users, err := userColl.Aggregator().
Pipeline(aggregation.NewStageBuilder().Project(bsonx.M("age", 0)).Build()).
Aggregate(context.Background())
// 字段重命名,聚合查询并解析结果
type RealUser struct {
mongox.Model `bson:"inline"`
RealName string `bson:"real_name"`
Age int `bson:"age"`
}
var results []*RealUser
err = userColl.Aggregator().
Pipeline(aggregation.NewStageBuilder().Project(
bsonx.NewD().Add("real_name", "$name").Add("age", 1).Build(),
).Build()).
AggregateWithParse(context.Background(), &results)
```
更多用法可前往官网文档 [Aggregator 聚合器 | go mongox](https://go-mongox.dev/operator/aggregator.html) 进行查看。
# BSON 数据构建
`Go mongox` 设计了多种不同类型的 `BSON` 构建器和函数,为我们开发者在不同场景下构建 `BSON` 数据提供了强大的支持。无论是数据查询、更新,还是执行复杂的聚合操作,开发者都可以在 `bsonx`、`query` 和 `update` 以及 `aggregation` 专门的包中找到合适的构建器或函数。这些 `BSON` 构建器和函数不仅优化了代码的编写过程,还显著提高了开发效率,使得处理复杂的 BSON 数据变得既简单又高效。
## bsonx 包
`bsonx` 提供提供了一系列便捷的函数和构建器去构建 `BSON` 数据,旨在简化 `BSON` 数据的构建过程。
```go
d := bsonx.NewD().Add("name", "陈明勇").Add("age", "18").Build()
m := bsonx.M("name", "陈明勇")
e := bsonx.E("name", "陈明勇")
d = bsonx.D("name", "陈明勇")
a := bsonx.A("Mingyong Chen", "陈明勇")
```
更多用法可前往官网文档 [bsonx 包 | go mongox](https://go-mongox.dev/build/bsonx.html) 进行查看。
## query 查询构建
`query` 包为构建 `MongoDB` 查询条件提供了便捷方法。它包括一系列函数和构建器,旨在简化查询条件的构建。对于复杂查询条件,构建器通过链式调用方式,提供了灵活的构建手段;对于简单需求,则可直接利用函数来快速构建查询条件。
```go
// bson.D{bson.E{Key:"_id", Value:"12345678"}}
/*
{
"_id": "12345678"
}
*/
query.Id("12345678")
// bson.D{bson.E{Key:"name", Value:bson.D{bson.E{Key:"$regex", Value:".*cmy.*"}, bson.E{Key:"$options", Value:"i"}}}}
/*
{
"name": {
"$regex": ".*cmy.*",
"$options": "i"
}
}
*/
query.RegexOptions("name", ".*cmy.*", "i")
// bson.D{bson.E{Key:"age", Value:bson.D{bson.E{Key:"$gte", Value:18}, bson.E{Key:"$lte", Value:25}}}, bson.E{Key:"name", Value:bson.D{bson.E{Key:"$in", Value:[]interface {}{"陈明勇", "chenmingyong"}}}}}
/*
{
"age": {
"$gte": 18,
"$lte": 25
},
"name": {
"$in": ["陈明勇", "Mingyong Chen"]
}
}
*/
query.NewBuilder().Gte("age", 18).Lte("age", 25).In("name", "陈明勇", "Mingyong Chen").Build()
// bson.D{bson.E{Key:"lastLogin", Value:bson.D{bson.E{Key:"$gte", Value:time.Date(2024, time.March, 0, 0, 0, 0, 189625000, time.Local)}}}, bson.E{Key:"$or", Value:[]interface {}{bson.D{bson.E{Key:"status", Value:bson.D{bson.E{Key:"$eq", Value:"active"}}}}, bson.D{bson.E{Key:"loginAttempts", Value:bson.D{bson.E{Key:"$gte", Value:5}}}}}}}
/*
{
"lastLogin": {
"$gte": "2024-03-08T00:00:00.189Z"
},
"$or": [
{
"status": {
"$eq": "active"
}
},
{
"loginAttempts": {
"$gte": 5
}
}
]
}
*/
query.NewBuilder().Gte("lastLogin", time.Now().Add(-30*24*time.Hour)).Or(
query.Eq("status", "active"),
query.Gte("loginAttempts", 5),
).Build()
// bson.D{bson.E{Key:"name", Value:bson.D{bson.E{Key:"$eq", Value:"陈明勇"}}}, bson.E{Key:"hobbies", Value:bson.D{bson.E{Key:"$elemMatch", Value:primie.D{bson.E{Key:"name", Value:bson.D{bson.E{Key:"$eq", Value:"coding"}}}, bson.E{Key:"level", Value:bson.D{bson.E{Key:"$gte", Value:5}}}}}}}}
/*
{
"name": {
"$eq": "陈明勇"
},
"hobbies": {
"$elemMatch": {
"name": {
"$eq": "coding"
},
"level": {
"$gte": 5
}
}
}
}
*/
query.NewBuilder().
Eq("name", "陈明勇").
ElemMatch("hobbies", query.NewBuilder().Eq("name", "coding").Gte("level", 5).Build()).
Build()
```
更多用法可前往官网文档 [query 包 | go mongox](https://go-mongox.dev/build/query/introduction.html) 进行查看。
## update 更新构建器
`update` 包为构建 `MongoDB` 更新文档提供了便捷方法。它包括一系列函数和构建器,旨在简化更新文档的构建。对于复杂的更新文档,构建器通过链式调用方式,提供了灵活的构建手段;对于简单需求,则可直接利用函数来快速构建更新文档。
```go
// bson.D{bson.E{Key:"$set", Value:bson.D{bson.E{Key:"name", Value:"陈明勇"}}}}
/*
{
"$set": {
"name": "陈明勇"
}
}
*/
update.Set("name", "陈明勇")
// bson.D{bson.E{Key:"$inc", Value:bson.D{bson.E{Key:"money", Value:"100000"}}}}
/*
{
"$inc": {
"money": 100000
}
}
*/
update.Inc("money", "100000")
// bson.D{bson.E{Key:"$push", Value:bson.D{bson.E{Key:"tags", Value:"golang"}}}}
/*
{
"$push": {
"tags": "golang"
}
}
*/
update.Push("tags", "golang")
// bson.D{bson.E{Key:"$set", Value:bson.D{bson.E{Key:"name", Value:"陈明勇"}, bson.E{Key:"age", Value:18}}}}
/*{
"$set": {
"name": "陈明勇",
"age": 18
}
}
*/
update.NewBuilder().Set("name", "陈明勇").Set("age", 18).Build()
// bson.D{bson.E{Key:"$set", Value:bson.D{bson.E{Key:"update_at", Value:time.Date(2024, time.April, 7, 3, 13, 23, 958924000, time.Local)}}}, bson.E{Key:"$inc", Value:bson.D{bson.E{Key:"view", Value:1}}}}
/*{
"$set": {
"update_at": "2024-04-07T00:00:00.958Z"
},
"$inc": {
"view": 1
}
}
*/
update.NewBuilder().Set("update_at", time.Now()).Inc("view", 1).Build()
// bson.D{bson.E{Key:"$push", Value:bson.D{bson.E{Key:"comments", Value:"新评论"}}}, bson.E{Key:"$inc", Value:bson.D{bson.E{Key:"commentCount", Value:}}}
/*
{
"$push": {
"comments": "新评论"
},
"$inc": {
"commentCount": 1
}
}
*/
update.NewBuilder().Push("comments", "新评论").Inc("commentCount", 11).Build()
```
更多用法可前往官网文档 [Update 包 | go mongox](https://go-mongox.dev/build/update/introduction.html) 进行查看。
## aggregation 聚合构建器
`aggregation` 包提供了方便的方法来构建`MongoDB`聚合管道(**pipeline**)结构。它包括多个函数和构建器,简化了管道构建过程。对于复杂场景,构建器支持链式调用,使得构建过程更加灵活;而对于基础需求,可以直接使用函数快速完成构建。
`aggregation` 包提供了两种构建器:
- `aggregation.StageBuilder`:用于轻松构建聚合管道的各个阶段(`Pipeline Stages`),如`$group`、`$match`等。通过 `aggregation.StageBsonBuilder()` 创建一个新的构建器实例,然后调用相应的方法来构建阶段。
- `aggregation.Builder`:用于构建管道阶段内部使用的复杂表达式(`Pipeline Expressions`),例如条件逻辑、数学运算等。通过 `aggregation.BsonBuilder()` 创建一个新的构建器实例,然后调用相应的方法来构建表达式。
### 聚合管道阶段
聚合阶段构建器用于轻松构建聚合管道的各个阶段(`Pipeline Stages`),如 `$group`、`$match` 等。
通过 `aggregation.NewStageBuilder()` 创建一个新的构建器实例,然后调用相应的方法来构建阶段。
```go
// mongo.Pipeline{bson.D{bson.E{Key:"$group", Value:bson.D{bson.E{Key:"_id", Value:"$age"}, bson.E{Key:"count", Value:bson.D{bson.E{Key:"$sum", Value:1}}}, bson.E{Key:"names", Value:bson.D{bson.E{Key:"$push", Value:"$name"}}}}}}}
/*[
{
"$group": {
"_id": "$age",
"count": { "$sum": 1 },
"names": { "$push": "$name" }
}
}
]
*/
aggregation.NewStageBuilder().Group("$age",
aggregation.NewBuilder().Sum("count", 1).Push("names", "$name").Build()...,
).Build()
// mongo.Pipeline{bson.D{bson.E{Key:"$addFields", Value:bson.D{bson.E{Key:"isAdult", Value:bson.D{bson.E{Key:"$gte", Value:[]interface {}{"$age", 18}}}}}}}, bson.D{bson.E{Key:"$replaceWith", Value:bson.D{bson.E{Key:"name", Value:"$name"}, bson.E{Key:"isAdult", Value:"$isAdult"}}}}}
/*
[
{
"$addFields": {
"isAdult": {
"$gte": ["$age", 18]
}
}
},
{
"$replaceWith": {
"name": "$name",
"isAdult": "$isAdult"
}
}
]
*/
aggregation.NewStageBuilder().
AddFields(aggregation.Gte("isAdult", "$age", 18)).
ReplaceWith(bsonx.NewD().Add("name", "$name").Add("isAdult", "$isAdult").Build()).Build()
// mongo.Pipeline{bson.D{bson.E{Key:"$bucket", Value:bson.D{bson.E{Key:"groupBy", Value:"$age"}, bson.E{Key:"boundaries", Value:[]interface {}{0, 19, 31, 46, +Inf}}, bson.E{Key:"default", Value:"Other"}, bson.E{Key:"output", Value:bson.D{bson.E{Key:"count", Value:bson.D{bson.E{Key:"$sum", Value:1}}}, bson.E{Key:"names", Value:bson.D{bson.E{Key:"$push", Value:"$name"}}}}}}}}}
/*
[
{
$bucket: {
groupBy: "$age", // 指定分组的依据字段
boundaries: [0, 19, 31, 46, Infinity], // 定义年龄分组的边界
default: "Other", // 对于不满足任何边界条件的文档,将其分配到一个默认的桶
output: {
"count": { $sum: 1 }, // 计算每个桶中的文档数
"names": { $push: "$name" } // 收集每个桶中所有用户的名字
}
}
}
]
*/
aggregation.NewStageBuilder().Bucket(
"$age",
[]any{0, 19, 31, 46, math.Inf(1)},
&aggregation.BucketOptions{
DefaultKey: "Other",
Output: aggregation.NewBuilder().Sum("count", 1).Push("names", "$name").Build(),
},
).Build()
```
### 聚合表达式
聚合表达式构建器用于轻松构建聚合管道的各个表达式(`Expressions`),如 `$add`, `$subtract` 等。
通过 `aggregation.BsonBuilder()` 创建一个新的构建器实例,然后调用相应的方法来构建表达式。
```go
// bson.D{bson.E{Key:"isAdult", Value:bson.D{bson.E{Key:"$gte", Value:[]interface {}{"$age", 18}}}}}
/*
{
"isAdult": {
"$gte": ["$age", 18]
}
}
*/
aggregation.Gte("isAdult", "$age", 18)
// bson.D{bson.E{Key:"birthYear", Value:bson.D{bson.E{Key:"$subtract", Value:[]interface {}{2024, "$age"}}}}}
/*
{
"birthYear": {
"$subtract": [2024, "$age"]
}
}
*/
aggregation.Subtract("birthYear", 2024, "$age")
// bson.D{bson.E{Key:"age", Value:bson.D{bson.E{Key:"$gt", Value:[]interface {}{18}}}}}
/*
{
"age": {
"$gt": 18
}
}
*/
aggregation.Gt("age", 18)
// bson.D{bson.E{Key:"isAdult", Value:bson.D{bson.E{Key:"$gte", Value:[]interface {}{"$age", 18}}}}, bson.E{Key:"birthYear", Value:bson.D{bson.E{Key:"$subtract", Value:[]interface {}{2024, "$age"}}}}}
/*
{
"isAdult": {
"$gte": ["$age", 18]
},
"birthYear": {
"$subtract": [2024, "$age"]
}
}
*/
aggregation.NewBuilder().
Gte("isAdult", "$age", 18).
Subtract("birthYear", 2024, "$age").Build()
// bson.D{bson.E{Key:"count", Value:bson.D{bson.E{Key:"$sum", Value:1}}}, bson.E{Key:"names", Value:bson.D{bson.E{Key:"$push", Value:"$name"}}}}
/*
{
"count": {
"$sum": 1
},
"names": {
"$push": "$name"
}
}
*/
aggregation.NewBuilder().Sum("count", 1).Push("names", "$name").Build()
// bson.D{bson.E{Key:"count", Value:bson.D{bson.E{Key:"$sum", Value:1}}}, bson.E{Key:"averageAge", Value:bson.D{bson.E{Key:"$avg", Value:"$age"}}}, bson.E{Key:"names", Value:bson.D{bson.E{Key:"$push", Value:"$name"}}}}
/*{
"count": {
"$sum": 1
},
"averageAge": {
"$avg": "$age"
},
"names": {
"$push": "$name"
}
}
*/
aggregation.NewBuilder().
Sum("count", 1).
Avg("averageAge", "$age").
Push("names", "$name").Build()
```
更多用法可前往官网文档 [Aggregation 包 | go mongox](https://go-mongox.dev/build/aggregation/introduction.html) 进行查看。
# 插件化编程
`go mongox` 支持插件化编程,它提供了一种灵活的方式在数据库操作的前后插入自定义的逻辑,从而增强应用的可扩展性和可维护性。
`go mongox` 提供了 `RegisterPlugin` 和 `UnregisterPlugin` 方法来注册和删除插件。
```go
type User struct {
mongox.Model `bson:"inline"`
Name string `bson:"name"`
Age int `bson:"age"`
}
// 注册插件
mongox.RegisterPlugin("after find", func(ctx context.Context, opCtx *operation.OpContext, opts ...any) error {
if user, ok := opCtx.Doc.(*User); ok {
fmt.Println(user)
}
if users, ok := opCtx.Doc.([]*User); ok {
fmt.Println(users)
}
return nil
}, operation.OpTypeAfterFind)
// 移除插件
mongox.RemovePlugin("after find", operation.OpTypeAfterFind)
```
## 激活内置的插件(钩子)
`go mongox` 库内置了三个实用的 `hook` 钩子:
- `field` 钩子:自动化更新默认的 `field` 字段
- `model` 钩子:针对模型(结构体)设置钩子函数,这些钩子函数会在 `MongoDB` 的集合操作前后被调用。
- `validator` 钩子:利用结构体的标签(`tag`)去对字段值进行校验。
`go mongox` 库默认不激活这些钩子,如果你想激活它们,可以参考以下代码:
```go
mongox.InitPlugin(&mongox.PluginConfig{
EnableDefaultFieldHook: true,
EnableModelHook: true,
EnableValidationHook: true,
// 覆盖默认的校验器,当 EnableValidationHook 为 true 时生效
Validate: nil,
})
```
这三个内置钩子的内容会在下面进行介绍。
更多用法可前往官网文档 [插件化编程 | go mongox](https://go-mongox.dev/plugins/plugins.html) 进行查看。
# 结构体 tag 校验
`go mongox` 库支持利用结构体的标签(`tag`)去对字段值进行校验,这一校验功能基于 [playground/validator](https://github.com/go-playground/validator) 库提供的所有结构体校验规则。
```go
type User struct {
mongox.Model `bson:"inline"`
Name string `bson:"name"`
Age uint8 `validate:"gte=0,lte=130"` // 确保年龄在 0 到 130 岁之间
Email string `json:"e-mail" validate:"required,email"` // 表示这个字段在数据验证时是必需的,并且必须符合电子邮箱的格式。
FavouriteColor string `validate:"hexcolor|rgb|rgba"` // 确保提供的颜色值要么是十六进制颜色码,要么是RGB或RGBA格式。
}
```
结构体 tag 校验功能默认是关闭的,如需开启,请使用 `mongox.InitPlugin` 函数。
更多用法可前往官网文档 [结构体校验 | go mongox](https://go-mongox.dev/validation/struct-validation.html) 进行查看。
# 内置 Model
`go mongox` 内置了一个 `Model` 结构体,它包含了 `ID`、`CreatedAt` 和 `UpdatedAt` 三个字段。
```go
type Model struct {
ID primitive.ObjectID `bson:"_id,omitempty"`
CreatedAt time.Time `bson:"created_at"`
UpdatedAt time.Time `bson:"updated_at"`
}
func (m *Model) DefaultId() {
if m.ID.IsZero() {
m.ID = primitive.NewObjectID()
}
}
func (m *Model) DefaultCreatedAt() {
if m.CreatedAt.IsZero() {
m.CreatedAt = time.Now().Local()
}
}
func (m *Model) DefaultUpdatedAt() {
m.UpdatedAt = time.Now().Local()
}
```
这个结构体实现了 `DefaultModelHook` 接口,如果初始化插件时将 `EnableDefaultFieldHook` 设置为 `true`(详情请参考 [启用内置插件-钩子](https://go-mongox.dev/plugins/plugins.html#启用内置插件-钩子) ),`go mongox` 库将自动化地处理文档的创建、更新操作中的 `ID` 和时间的赋值。
更多用法可前往官网文档 [内置 Model | go mongox](https://go-mongox.dev/model.html) 进行查看。
# Hook 钩子
## 模型钩子(Model Hooks)
你可以针对模型(结构体)设置钩子函数,这些钩子函数会在 `MongoDB` 的集合操作前后被调用。例如,你可以在插入文档前后对文档进行处理,或者在查询文档后对文档进行处理。
```go
type User struct {
mongox.Model `bson:"inline"`
Name string `bson:"name"`
Age int `bson:"age"`
}
func (u *User) BeforeInsert(ctx context.Context) error {
fmt.Println("BeforeInsert called")
return nil
}
func (u *User) AfterInsert(ctx context.Context) error {
fmt.Println("AfterInsert called")
return nil
}
func (u *User) AfterFind(ctx context.Context) error {
fmt.Println("AfterFind called")
return nil
}
func (u *User) BeforeUpsert(ctx context.Context) error {
fmt.Println("BeforeUpsert called")
return nil
}
func (u *User) AfterUpsert(ctx context.Context) error {
fmt.Println("AfterUpsert called")
return nil
}
```
更多用法可前往官网文档 [模型钩子(Model Hooks) | go mongox](https://go-mongox.dev/hooks/model-hooks.html) 进行查看。
## 一次性钩子
`go mongox` 支持一次性钩子,你可以在查询、插入、删除、更新和保存文档的前后执行一些操作。
```go
// 插入操作
_, err = userColl.Creator().
RegisterBeforeHooks(func(ctx context.Context, opContext *creator.OpContext[User], opts ...any) error {
fmt.Println("BeforeHook called")
fmt.Println(opContext.Doc)
fmt.Println(opContext.Col != nil)
return nil
}).
RegisterAfterHooks(func(ctx context.Context, opContext *creator.OpContext[User], opts ...any) error {
fmt.Println("AfterHook called")
fmt.Println(opContext.Doc)
fmt.Println(opContext.Col != nil)
return nil
}).
InsertOne(context.Background(), &User{Name: "Mingyong Chen", Age: 18})
// 查询操作
_, err = userColl.Finder().
RegisterBeforeHooks(func(ctx context.Context, opContext *finder.OpContext, opts ...any) error {
fmt.Println("BeforeHook called")
fmt.Println(opContext.Filter)
return nil
}).
RegisterAfterHooks(func(ctx context.Context, opContext *finder.AfterOpContext[User], opts ...any) error {
fmt.Println("AfterHook called")
fmt.Println(opContext.Filter)
fmt.Println(opContext.Doc)
return nil
}).
Filter(query.Eq("name", "陈明勇")).FindOne(context.Background())
// 更新操作
_, err = userColl.Updater().
RegisterBeforeHooks(func(ctx context.Context, opContext *updater.BeforeOpContext, opts ...any) error {
fmt.Println("BeforeHook called")
fmt.Println(opContext.Filter)
fmt.Println(opContext.Updates)
fmt.Println(opContext.Col != nil)
return nil
}).
RegisterAfterHooks(func(ctx context.Context, opContext *updater.AfterOpContext, opts ...any) error {
fmt.Println("AfterHook called")
fmt.Println(opContext.Filter)
fmt.Println(opContext.Updates)
fmt.Println(opContext.Col != nil)
return nil
}).
Filter(query.Eq("name", "陈明勇")).
Updates(update.Set("age", 19)).
UpdateOne(context.Background())
// 删除操作
_, err = userColl.Deleter().
RegisterBeforeHooks(func(ctx context.Context, opContext *deleter.BeforeOpContext, opts ...any) error {
fmt.Println("BeforeHook called")
fmt.Println(opContext.Filter)
fmt.Println(opContext.Col != nil)
return nil
}).
RegisterAfterHooks(func(ctx context.Context, opContext *deleter.AfterOpContext, opts ...any) error {
fmt.Println("AfterHook called")
fmt.Println(opContext.Filter)
fmt.Println(opContext.Col != nil)
return nil
}).
Filter(query.Eq("name", "Mingyong Chen")).
DeleteOne(context.Background())
```
更多用法可前往官网文档 [一次性钩子 | go mongox](https://go-mongox.dev/hooks/one-time-hooks.html) 进行查看。
# 小结
本文详细介绍了 `go mongox` 库的关键模块,包括创建指定约束类型的泛型 `Collection`、灵活的 `BSON` 构建器、基础的 `CRUD` 操作、聚合操作、以及插件和钩子机制,并提供了相应的使用示例。
> 仓库地址:https://github.com/chenmingyong0423/go-mongox
>
> 官网文档:https://go-mongox.dev
>
> 欢迎体验 `go mongox` 库,也期待您的贡献。如果您觉得这个库对您有帮助,请给它一个 Star 支持!
有疑问加站长微信联系(非本文作者))