Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Go 语言 mongox 库:简化操作、安全、高效、可扩展、BSON 构建

Go 语言 mongox 库:简化操作、安全、高效、可扩展、BSON 构建

原创
作者头像
陈明勇
发布于 2024-06-08 13:53:15
发布于 2024-06-08 13:53:15
3154
举报
文章被收录于专栏:Go 技术Go 技术Go技术干货

前言

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
AI代码解释
复制
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
AI代码解释
复制
// 插入单个文档
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)

InsertOneInsertMany 方法与官方的 API 同名,前者用于插入单个文档,后者用于插入多个文档。如果我们需要设置 options 参数,可以将其作为这两个方法的第三个参数传递。

更多用法可前往官网文档 Creator 创造器 | go mongox 进行查看。

Finder 查询器

通过 Finder() 方法获取一个新的泛型的查询器对象,即 Finder[T]。通过 Finder[T] 的方法,我们能够执行相关的查询操作。

示例

代码语言:go
AI代码解释
复制
// 查询单个文档
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())

FindOneFind 方法与官方的 API 同名,前者用于查询单个文档,后者用于查询多个文档。我们可以通过 Filter 方法设置 查询条件,如果我们需要设置 options 参数,可以将其作为这两个方法的第二个参数传递。

对于简单的查询条件,我们可以使用 query 包提供的函数进行构建,例如 query.(id);对于复杂的查询条件,我们可以使用 query 包提供的 Builder 构建器进行构建。query 包的用法会在下面的内容进行详细地介绍。

更多用法可前往官网文档 Finder 查询器 | go mongox 进行查看。

Updater 更新器

通过 Updater() 方法获取一个新的泛型的更新器对象,即 Updater[T]。通过 Updater[T] 的方法,我们能够执行相关的更新操作。

示例

代码语言:go
AI代码解释
复制
// 更新单个文档
// 通过 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())

UpdateOneUpdateMany 方法与官方的 API 同名,前者用于更新单个文档,后者用于更新多个文档。我们可以通过 Filter 方法设置 文档匹配的条件,如果我们需要设置 options 参数,可以将其作为这两个方法的第二个参数传递。

对于更新参数,我们可以使用 Updates 方法进行设置。该方法接收 bsonmap 等合法的更新操作语句。上面的例子使用了 update 包里的 Set 对更新操作语句进行构建。

更多用法可前往官网文档 Updater 更新器 | go mongox 进行查看。

Deleter 删除器

通过 Deleter() 方法获取一个新的泛型的删除器对象,即 Deleter[T]。通过 Deleter[T] 的方法,我们能够执行相关的删除操作。

示例

代码语言:go
AI代码解释
复制
// 删除单个文档
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())

DeleteOneDeleteMany 方法与官方的 API 同名,前者用于删除单个文档,后者用于删除多个文档。我们可以通过 Filter 方法设置 文档匹配的条件。如果我们需要设置 options 参数,可以将其作为这两个方法的第二个参数传递。

更多用法可前往官网文档 Deleter 删除器 | go mongox 进行查看。

Aggregator 聚合器

通过 Aggregator() 获取一个新的泛型的聚合器对象,即 Aggregator[T any],通过 Aggregator[T any] 的方法,我们能够执行相关的聚合操作。

聚合器实现了三个方法:

  • Pipeline() 用于设置聚合管道。
  • Aggregate() 用于执行聚合操作,返回的查询结果类型与 T 一致。
  • AggregateWithParse() 也是用于执行聚合操作,但使用场景不一样。当聚合结果的类型与 T 不一致时,使用 AggregateWithParse() 方法可以将结果解析到指定的对象里。
代码语言:go
AI代码解释
复制
// 忽略年龄字段,只查询名字
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 进行查看。

BSON 数据构建

Go mongox 设计了多种不同类型的 BSON 构建器和函数,为我们开发者在不同场景下构建 BSON 数据提供了强大的支持。无论是数据查询、更新,还是执行复杂的聚合操作,开发者都可以在 bsonxqueryupdate 以及 aggregation 专门的包中找到合适的构建器或函数。这些 BSON 构建器和函数不仅优化了代码的编写过程,还显著提高了开发效率,使得处理复杂的 BSON 数据变得既简单又高效。

bsonx 包

bsonx 提供提供了一系列便捷的函数和构建器去构建 BSON 数据,旨在简化 BSON 数据的构建过程。

代码语言:go
AI代码解释
复制
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 进行查看。

query 查询构建

query 包为构建 MongoDB 查询条件提供了便捷方法。它包括一系列函数和构建器,旨在简化查询条件的构建。对于复杂查询条件,构建器通过链式调用方式,提供了灵活的构建手段;对于简单需求,则可直接利用函数来快速构建查询条件。

代码语言:go
AI代码解释
复制
// 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 进行查看。

update 更新构建器

update 包为构建 MongoDB 更新文档提供了便捷方法。它包括一系列函数和构建器,旨在简化更新文档的构建。对于复杂的更新文档,构建器通过链式调用方式,提供了灵活的构建手段;对于简单需求,则可直接利用函数来快速构建更新文档。

代码语言:go
AI代码解释
复制
// 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 进行查看。

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
AI代码解释
复制
// 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
AI代码解释
复制
// 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 进行查看。

插件化编程

go mongox 支持插件化编程,它提供了一种灵活的方式在数据库操作的前后插入自定义的逻辑,从而增强应用的可扩展性和可维护性。

go mongox 提供了 RegisterPluginUnregisterPlugin 方法来注册和删除插件。

代码语言:go
AI代码解释
复制
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
AI代码解释
复制
mongox.InitPlugin(&mongox.PluginConfig{
	EnableDefaultFieldHook: true,
	EnableModelHook:        true,
	EnableValidationHook:   true,
	// 覆盖默认的校验器,当 EnableValidationHook 为 true 时生效
	Validate: nil,
})

这三个内置钩子的内容会在下面进行介绍。

更多用法可前往官网文档 插件化编程 | go mongox 进行查看。

结构体 tag 校验

go mongox 库支持利用结构体的标签(tag)去对字段值进行校验,这一校验功能基于 playground/validator 库提供的所有结构体校验规则。

代码语言:go
AI代码解释
复制
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 进行查看。

内置 Model

go mongox 内置了一个 Model 结构体,它包含了 IDCreatedAtUpdatedAt 三个字段。

代码语言:go
AI代码解释
复制
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(详情请参考 启用内置插件-钩子 ),go mongox 库将自动化地处理文档的创建、更新操作中的 ID 和时间的赋值。

更多用法可前往官网文档 内置 Model | go mongox 进行查看。

Hook 钩子

模型钩子(Model Hooks)

你可以针对模型(结构体)设置钩子函数,这些钩子函数会在 MongoDB 的集合操作前后被调用。例如,你可以在插入文档前后对文档进行处理,或者在查询文档后对文档进行处理。

代码语言:go
AI代码解释
复制
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 进行查看。

一次性钩子

go mongox 支持一次性钩子,你可以在查询、插入、删除、更新和保存文档的前后执行一些操作。

代码语言:go
AI代码解释
复制
// 插入操作
_, 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 进行查看。

小结

本文详细介绍了 go mongox 库的关键模块,包括创建指定约束类型的泛型 Collection、灵活的 BSON 构建器、基础的 CRUD 操作、聚合操作、以及插件和钩子机制,并提供了相应的使用示例。

仓库地址:https://github.com/chenmingyong0423/go-mongox

官网文档:https://go-mongox.dev

欢迎体验 go mongox 库,也期待您的贡献。如果您觉得这个库对您有帮助,请给它一个 Star 支持!

我正在参与2024腾讯技术创作特训营最新征文,快来和我瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
4 条评论
热度
最新
牛逼,很丝滑的 mongo 库
牛逼,很丝滑的 mongo 库
112举报
纵享丝滑
纵享丝滑
回复回复点赞举报
哇,感谢大佬,真的可以,很全面的使用说明啊!🌹
哇,感谢大佬,真的可以,很全面的使用说明啊!🌹
111举报
/抱拳
/抱拳
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
go-mongox:简单高效,让文档操作和 bson 数据构造更流畅
在 Go 语言中使用 MongoDB 官方框架进行集合操作时,深深感到构建 bson 数据是一件非常繁琐的工作。字段、逗号,括号等符号的排列,让我感觉仿佛是在进行一场拼图游戏。因此我在想,有没有一个能让我丝滑,高效操作 MongoDB 的第三方框架呢,遗憾的是,并没有找到符合我预期的框架,索性我就自己动手开发了一个,这就是 go-mongox 框架的由来。
陈明勇
2023/11/14
5170
go-mongox:简单高效,让文档操作和 bson 数据构造更流畅
效率提升 80%:go-mongox 让复杂的 BSON 数据编写变得简单
在开发使用 MongoDB 的 Go 应用中,我们避免不了要编写 BSON 格式的数据。对于简单的 BSON 格式数据,我们可以轻松快捷地进行编写。而面对复杂的 BSON 格式数据,我们可能需要自己构建多层嵌套的文档,这不仅耗时而且容易出错,一次微小的遗漏或错误就可能导致无法获得预期的结果,增加了开发的难度和调试的时间。
陈明勇
2024/04/07
3740
效率提升 80%:go-mongox 让复杂的 BSON 数据编写变得简单
Go Mongox 开源库设计分享:简化 MongoDB 开发的最佳实践
在使用 Go 语言操作 MongoDB 时,Go 开发者的首选库通常是由 MongoDB 官方团队推出的 mongo-go-driver。这个库是专为 Go 语言开发者打造的,支持 MongoDB 的主要功能,并与最新版本的 MongoDB 兼容。通过 mongo-go-driver,Go 开发者可以便捷地连接数据库,并且能对集合进行查询、插入、更新、删除的操作。
陈明勇
2024/11/28
5186
Go Mongox 开源库设计分享:简化 MongoDB 开发的最佳实践
Go每日一库之199:go-mongox(简单高效、链式调用的mongodb库)
go-mongox 基于 泛型 对 MongoDB 官方框架进行了二次封装,它通过使用链式调用的方式,让我们能够丝滑地操作文档。同时,其还提供了多种类型的 bson 构造器,帮助我们高效的构建 bson 数据。
luckpunk
2025/01/08
1021
Go Mongox:轻松实现 MongoDB 时间字段自动填充
 时,例如执行插入或更新操作,我们需要手动设置这些时间字段的值。然而,每次手动赋值不仅繁琐,还容易导致代码重复。那么,是否可以在程序层面实现自动填充呢?
陈明勇
2025/02/09
44015
Go高级之Gin框架和Mongodb数据库的联动
这次总结,主要还是我之前打算用Gin写一个自己的个人博客网站来练手,我没有选择常用的MySQL,而是打算使用MongoDB作为个人博客网站数据库,有以下几点原因:
言志志
2023/11/07
1.3K0
Go高级之Gin框架和Mongodb数据库的联动
从 0 到 1 创建、测试并发布属于自己的 Go 开源库
在日常开发中,我们经常使用由组织或个人开发和维护的第三方开源库,这些库大大提高了我们的开发效率,让我们能够专注于实现业务逻辑而不是重复造轮子。如果你对提高代码复用感兴趣,并希望将自己封装的高质量代码发布为一个开源库,那么这篇文章正是为你准备的。
陈明勇
2024/04/22
3200
从 0 到 1 创建、测试并发布属于自己的 Go 开源库
MongoDB 实现自增 ID 的最佳实践
最近有幸观看了 腾讯云开发者社区 发布的 《中国数据库前世今生》 纪录片,该纪录片深入探讨一个时代的数据库演变历程,以及这些大趋势下鲜为人知的小故事。看完以后,我对中国数据库的发展历程有了更深入的认识。感兴趣的小伙伴可以去观看一下。本文介绍的内容也和数据库有关,请看下文!
陈明勇
2024/08/14
8570
MongoDB 实现自增 ID 的最佳实践
Go操作MongoDB
mongoDB是目前比较流行的一个基于分布式文件存储的数据库,它是一个介于关系数据库和非关系数据库(NoSQL)之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
iginkgo18
2021/09/13
2.4K0
Go 操作mongodb
添加mongodb驱动程序 用于go get将 Go 驱动程序添加为依赖项。 go get go.mongodb.org/mongo-driver/mongo 使用方法 创建main.go 文件 package main import ( "context" "fmt" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mon
孤烟
2022/10/08
6580
使用Golang驱动操作MongoDB
mongo.Connect()接受Context和options.ClientOptions对象,该对象用于设置连接字符串和其他驱动程序设置。 通过context.TODO()表示不确定现在使用哪种上下文,但是会在将来添加一个 使用Ping方法来检测是否已正常连接MongoDB
没有故事的陈师傅
2020/11/03
4.9K0
Go高级之关于MongoDB中的BSON
BSON(Binary JSON)是一种二进制表示的JSON格式,用于在MongoDB中存储和传输数据。它是MongoDB的原生数据格式,并且被广泛用于MongoDB的各种操作和功能。
言志志
2023/11/08
1.2K1
Go高级之关于MongoDB中的BSON
使用 docker 轻松部署你的 Go 项目
如果你是一名 Go 开发者,你一定知道用 Go 写代码是一件多爽的事,高效而简洁。那么如果我告诉你,部署 Go 项目同样也可以这么轻松愉快呢?这就是 Docker 的魅力所在。
陈明勇
2024/10/24
1.9K0
使用 docker 轻松部署你的 Go 项目
MongoDB Golang 示例代码
包含 增删改查,索引设置,事务,max,cout等的使用 和 压力测试 主要是要适应习惯 bson.M/A/D的使用 其中事务需要有 replica set集群支持 完整代码如下: package main import ( "context" "flag" "fmt" "go.mongodb.org/mongo-driver/bson" "log" "math/rand" "sync" "time" "go.mongodb.o
IT工作者
2022/07/20
5640
女朋友:你知道嘛,不使用orm框架也可以写动态sql了!
既然要使用数据库,那么第一步我们就来进行数据库连接,我们先来看一下直接使用标准库进行连接库是怎样写的:
Golang梦工厂
2022/07/08
7610
一文掌握 Go 并发模式 Context 上下文
Go 在 1.7 引入了 context 包,目的是为了在不同的 goroutine 之间或跨 API 边界传递超时、取消信号和其他请求范围内的值(与该请求相关的值。这些值可能包括用户身份信息、请求处理日志、跟踪信息等等)。
陈明勇
2023/10/15
9532
一文掌握 Go 并发模式 Context 上下文
MongoDB Go Driver使用帮助文档
正式的MongoDB Go Driver近来变成1.0的GA版本。它现在被认为是一个完整的特性, 并且准备好在正式产品中使用。这篇使用说明书将帮助你开始使用 MongoDB Go Driver。你将会创建一个简单的程序并且学到如何:
MongoDB中文社区
2019/08/20
4.1K0
MongoDB Go Driver使用帮助文档
用 Go 语言轻松构建 MCP 客户端
](https://cloud.tencent.com/developer/article/2509998)。
陈明勇
2025/04/03
1.4K2
用 Go 语言轻松构建 MCP 客户端
go操作elasticsearch示例
这里我使用elasticsearch官方给的go语言包([go-elasticsearch](https://github.com/elastic/go-elasticsearch))
孤烟
2022/03/19
2.2K0
Go学习——使用MongoDB
MongoDB是一个高性能,开源,无模式的文档型数据库,是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。他支持的数据结构非常松散,采用的是类似json的bjson格式来存储数据,因此可以存储比较复杂的数据类型。Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向 对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。
传说之下的花儿
2023/04/16
6550
相关推荐
go-mongox:简单高效,让文档操作和 bson 数据构造更流畅
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档