go-mongox:简单高效,让文档操作和 bson 数据构造更流畅

前言

Go 语言中使用 MongoDB 官方框架进行集合操作时,深深感到构建 bson 数据是一件非常繁琐的工作。字段、逗号,括号等符号的排列,让我感觉仿佛是在进行一场拼图游戏。因此我在想,有没有一个能让我丝滑,高效操作 MongoDB 的第三方框架呢,遗憾的是,并没有找到符合我预期的框架,索性我就自己动手开发了一个,这就是 go-mongox 框架的由来。

如果你也有类似我的这种感受,相信 go-mongox 框架能给你带来不一样的体验。

go-mongox

go-mongox 基于 泛型MongoDB 官方框架进行了二次封装,它通过使用链式调用的方式,让我们能够丝滑地操作文档。同时,其还提供了多种类型的 bson 构造器,帮助我们高效的构建 bson 数据。

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

官方文档链接:go-mongox.dev

该框架处于初期阶段,希望通过集思广益的方式,邀请各位开发者共同参与,提出宝贵的建议和意见,共同打造一个更强大、更灵活的框架。期待着您的积极参与和宝贵反馈,共同推动go-mongox不断进步。

功能

  • 文档的 CRUD 操作
  • 聚合操作
  • 内置基本的 Model 结构体,自动化更新默认的 field 字段
  • 支持 bson 数据的构建
  • Hooks
  • 支持插件化编程

安装

go get github.com/chenmingyong0423/go-mongox@latest

collection 集合操作

基于泛型的 collection 形态初始化

package main

import (
    "context"

    "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 Post struct {
    Id      primitive.ObjectID `bson:"_id,omitempty"`
    Title   string `bson:"title"`
    Author  string `bson:"author"`
    Content string `bson:"content"`
}

func main() {
    // 你需要预先创建一个 *mongo.Collection 对象
    mongoCollection := newCollection()
    // 使用 Post 结构体作为泛型参数创建一个 collection
    postCollection := mongox.NewCollection[Post](mongoCollection)
}

// 示例代码,不是最佳的创建方式
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 装饰器。

Creator 创造器

Creator 是一个创造器,用于执行插入相关的操作。

插入单个文档

// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go-mongox/collection/creator/insert_one.go
package main

import (
    "chenmingyong0423/blog/tutorial-code/go-mongox/collection"
    "context"
    "fmt"

    "go.mongodb.org/mongo-driver/mongo/options"

    "github.com/chenmingyong0423/go-mongox"
)

func main() {
    // 你需要预先创建一个 *mongo.Collection 对象
    mongoCollection := collection.NewCollection()
    // 使用 Post 结构体作为泛型参数创建一个 collection
    postCollection := mongox.NewCollection[collection.Post](mongoCollection)

    ctx := context.Background()
    // 插入一个文档
    doc := collection.Post{Title: "go 语言 go-mongox 库的使用教程", Author: "陈明勇", Content: "go-mongox 旨在提供更方便和高效的MongoDB数据操作体验。"}
    oneResult, err := postCollection.Creator().InsertOne(ctx, doc)
    if err != nil {
        panic(err)
    }
    fmt.Println(oneResult.InsertedID != nil) // true

    // 携带 option 参数
    oneResult, err = postCollection.Creator().InsertOne(ctx, doc, options.InsertOne().SetComment("test"))
    if err != nil {
        panic(err)
    }
    fmt.Println(oneResult.InsertedID != nil) // true
}

基于 postCollection 实例,我们可以通过 Creator() 方法创建一个创造器,然后进行插入操作。

InsertOne 方法与官方的 API 同名,作用是插入一条数据。如果我们需要设置 options 参数,可以将其作为该方法的第三个参数传递。

插入多个文档

// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go-mongox/collection/creator/insert_many.go
package main

import (
    "chenmingyong0423/blog/tutorial-code/go-mongox/collection"
    "context"
    "fmt"

    "github.com/chenmingyong0423/go-mongox"
    "go.mongodb.org/mongo-driver/mongo/options"
)

func main() {
    // 你需要预先创建一个 *mongo.Collection 对象
    mongoCollection := collection.NewCollection()
    // 使用 Post 结构体作为泛型参数创建一个 collection
    postCollection := mongox.NewCollection[collection.Post](mongoCollection)

    ctx := context.Background()

    docs := []collection.Post{
        {Title: "go-mongox", Author: "陈明勇", Content: "..."},
        {Title: "builder", Author: "陈明勇", Content: "..."},
    }
    manyResult, err := postCollection.Creator().InsertMany(ctx, docs)
    if err != nil {
        panic(err)
    }
    fmt.Println(len(manyResult.InsertedIDs) == 2) // true

    // 携带 option 参数
    manyResult, err = postCollection.Creator().InsertMany(ctx, docs, options.InsertMany().SetComment("test"))
    if err != nil {
        panic(err)
    }
    fmt.Println(len(manyResult.InsertedIDs) == 2) // true
}

InsertMany 方法与官方的 API 同名,作用是插入多条数据。如果我们需要设置 options 参数,可以将其作为该方法的第三个参数传递。

Finder 查询器

Finder 是一个查询器,用于执行查询相关的操作。

查询单个文档

// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go-mongox/collection/finder/find_one.go
package main

import (
    "chenmingyong0423/blog/tutorial-code/go-mongox/collection"
    "context"
    "fmt"

    "go.mongodb.org/mongo-driver/mongo/options"

    "github.com/chenmingyong0423/go-mongox"
    "github.com/chenmingyong0423/go-mongox/bsonx"
    "github.com/chenmingyong0423/go-mongox/builder/query"
)

func main() {
    // 你需要预先创建一个 *mongo.Collection 对象
    mongoCollection := collection.NewCollection()
    // 使用 Post 结构体作为泛型参数创建一个 collection
    postCollection := mongox.NewCollection[collection.Post](mongoCollection)
    oneResult, err := postCollection.Creator().InsertOne(context.Background(), collection.Post{Title: "go-mongox", Author: "陈明勇", Content: "go-mongox 旨在提供更方便和高效的MongoDB数据操作体验。"})
    if err != nil {
        panic(err)
    }

    // 查询单个文档
    post, err := postCollection.Finder().Filter(query.Id(oneResult.InsertedID)).FindOne(context.Background())
    if err != nil {
        panic(err)
    }
    fmt.Println(post)

    // 设置 *options.FindOneOptions 参数
    post2, err := postCollection.Finder().
        Filter(query.Id(oneResult.InsertedID)).
        FindOne(context.Background(), options.FindOne().SetProjection(bsonx.M("content", 0)))
    if err != nil {
        panic(err)
    }
    fmt.Println(post2)

    // - map 作为 filter 条件
    post3, err := postCollection.Finder().Filter(map[string]any{"_id": oneResult.InsertedID}).FindOne(context.Background())
    if err != nil {
        panic(err)
    }
    fmt.Println(post3)

    // - 复杂条件查询
    // -- 使用 query 包构造复杂的 bson: bson.D{bson.E{Key: "title", Value: bson.M{"$eq": "go"}}, bson.E{Key: "author", Value: bson.M{"$eq": "陈明勇"}}}
    post4, err := postCollection.Finder().
        Filter(query.BsonBuilder().Eq("title", "go-mongox").Eq("author", "陈明勇").Build()).
        FindOne(context.Background())
    if err != nil {
        panic(err)
    }
    fmt.Println(post4)
}

基于 postCollection 实例,我们可以通过 Finder() 方法创建一个查询器,然后进行查询操作。

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

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

查询多个文档

// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go-mongox/collection/finder/find.go
package main

import (
    "chenmingyong0423/blog/tutorial-code/go-mongox/collection"
    "context"
    "fmt"

    "github.com/chenmingyong0423/go-mongox"
    "github.com/chenmingyong0423/go-mongox/bsonx"
    "github.com/chenmingyong0423/go-mongox/builder/query"
    "go.mongodb.org/mongo-driver/mongo/options"
)

func main() {
    ctx := context.Background()

    // 你需要预先创建一个 *mongo.Collection 对象
    mongoCollection := collection.NewCollection()
    // 使用 Post 结构体作为泛型参数创建一个 collection
    postCollection := mongox.NewCollection[collection.Post](mongoCollection)
    _, err := postCollection.Creator().InsertMany(ctx, []collection.Post{
        {Title: "go-mongox", Author: "陈明勇", Content: "go-mongox 旨在提供更方便和高效的MongoDB数据操作体验。"},
        {Title: "mongo", Author: "陈明勇", Content: "..."},
    })
    if err != nil {
        panic(err)
    }

    // 查询多个文档
    // bson.D{bson.E{Key: "title", Value: bson.M{"$in": []string{"go-mongox", "mongo"}}}}
    posts, err := postCollection.Finder().
        Filter(query.In("title", []string{"go-mongox", "mongo"}...)).
        Find(ctx)
    if err != nil {
        panic(err)
    }
    for _, post := range posts {
        fmt.Println(post)
    }

    // 设置 *options.FindOptions 参数
    // bson.D{bson.E{Key: "title", Value: bson.M{"$in": []string{"go-mongox", "mongo"}}}}
    posts2, err := postCollection.Finder().
        Filter(query.In("title", []string{"go-mongox", "mongo"}...)).
        Find(ctx, options.Find().SetProjection(bsonx.M("content", 0)))
    if err != nil {
        panic(err)
    }
    for _, post := range posts2 {
        fmt.Println(post)
    }
}

Find 方法与官方的 API 同名,作用是查询多个文档。如果我们需要设置 options 参数,可以将其作为 Find 方法的第二个参数传递。

在上面的例子中,为了构造 $in 查询语句,我们使用了 query 包提供的泛型函数 In

Updater 更新器

Updater 是一个更新器,用于执行更新相关的操作。

更新单个文档

// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go-mongox/collection/updater/update_one.go
package main

import (
    "chenmingyong0423/blog/tutorial-code/go-mongox/collection"
    "context"
    "fmt"

    "go.mongodb.org/mongo-driver/mongo/options"

    "github.com/chenmingyong0423/go-mongox/builder/query"

    "github.com/chenmingyong0423/go-mongox"
    "github.com/chenmingyong0423/go-mongox/bsonx"
    "github.com/chenmingyong0423/go-mongox/builder/update"
    "github.com/chenmingyong0423/go-mongox/types"
)

func main() {
    ctx := context.Background()

    // 你需要预先创建一个 *mongo.Collection 对象
    mongoCollection := collection.NewCollection()
    // 使用 Post 结构体作为泛型参数创建一个 collection
    postCollection := mongox.NewCollection[collection.Post](mongoCollection)
    oneResult, err := postCollection.Creator().InsertOne(ctx, collection.Post{Title: "go-mongox", Author: "陈明勇", Content: "go-mongox 旨在提供更方便和高效的MongoDB数据操作体验。"})
    if err != nil {
        panic(err)
    }

    // 更新单个文档
    // 通过 update 包构建 bson 更新语句
    updateResult, err := postCollection.Updater().
        Filter(query.Id(oneResult.InsertedID)).
        Updates(update.Set("title", "golang")).
        UpdateOne(ctx)
    if err != nil {
        panic(err)
    }
    fmt.Println(updateResult.ModifiedCount == 1) // true

    // - 使用 map 构造更新数据,并设置 *options.UpdateOptions,执行 upsert 操作
    updateResult2, err := postCollection.Updater().
        Filter(bsonx.Id("custom-id")).
        UpdatesWithOperator(types.Set, bsonx.M("title", "mongo")).
        UpdateOne(ctx, options.Update().SetUpsert(true))
    if err != nil {
        panic(err)
    }
    fmt.Println(updateResult2.UpsertedID.(string) == "custom-id") // true
}

基于 postCollection 实例,我们可以通过 Updater() 方法创建一个更新器,然后进行更新操作。

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

对于更新操作参数,我们可以使用以下两个方法进行设置:

  • Updates 方法:该方法接收 bsonmap 等合法的更新操作语句。上面的例子使用了 update 包里的 Set 对更新操作语句进行构造。
  • UpdatesWithOperator 方法:该方法的第一个参数为更新操作符,第二个参数为预期更新的数据。上面的例子使用了 bsonx 包构造 bson 数据。

更新多个文档

// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go-mongox/collection/updater/update_many.go
package main

import (
    "chenmingyong0423/blog/tutorial-code/go-mongox/collection"
    "context"
    "fmt"

    "github.com/chenmingyong0423/go-mongox"
    "github.com/chenmingyong0423/go-mongox/builder/query"
    "github.com/chenmingyong0423/go-mongox/builder/update"
)

func main() {
    ctx := context.Background()

    // 你需要预先创建一个 *mongo.Collection 对象
    mongoCollection := collection.NewCollection()
    // 使用 Post 结构体作为泛型参数创建一个 collection
    postCollection := mongox.NewCollection[collection.Post](mongoCollection)
    _, err := postCollection.Creator().InsertMany(ctx, []collection.Post{
        {Title: "go-mongox", Author: "陈明勇", Content: "go-mongox 旨在提供更方便和高效的MongoDB数据操作体验。"},
        {Title: "builder", Author: "陈明勇", Content: "..."},
    })
    if err != nil {
        panic(err)
    }

    // 更新多个文档
    updateResult, err := postCollection.Updater().
        Filter(query.In("title", []string{"go-mongox", "builder"}...)).
        Updates(update.Set("title", "golang")).
        UpdateMany(ctx)
    if err != nil {
        panic(err)
    }
    fmt.Println(updateResult.ModifiedCount == 2) // true
}

UpdateMany 方法与官方的 API 同名,作用是更新多个文档。

Deleter 删除器

Deleter 是一个删除器,用于执行删除相关的操作。

删除单个文档

// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go-mongox/collection/deleter/delete_one.go
package main

import (
    "chenmingyong0423/blog/tutorial-code/go-mongox/collection"
    "context"
    "fmt"

    "github.com/chenmingyong0423/go-mongox/builder/query"
    "go.mongodb.org/mongo-driver/mongo/options"

    "github.com/chenmingyong0423/go-mongox"
)

func main() {
    ctx := context.Background()

    // 你需要预先创建一个 *mongo.Collection 对象
    mongoCollection := collection.NewCollection()
    // 使用 Post 结构体作为泛型参数创建一个 collection
    postCollection := mongox.NewCollection[collection.Post](mongoCollection)
    _, err := postCollection.Creator().InsertMany(ctx, []collection.Post{
        {Title: "go-mongox", Author: "陈明勇", Content: "go-mongox 旨在提供更方便和高效的MongoDB数据操作体验。"},
        {Title: "builder", Author: "陈明勇", Content: "..."},
    })
    if err != nil {
        panic(err)
    }

    // 删除单个文档
    deleteResult, err := postCollection.Deleter().Filter(query.Eq("title", "go-mongox")).DeleteOne(ctx)
    if err != nil {
        panic(err)
    }
    fmt.Println(deleteResult.DeletedCount == 1) // true

    // 携带 option 参数
    deleteResult2, err := postCollection.Deleter().Filter(query.Eq("title", "builder")).DeleteOne(ctx, options.Delete().SetComment("test"))
    if err != nil {
        panic(err)
    }
    fmt.Println(deleteResult2.DeletedCount == 1) // true
}

基于 postCollection 实例,我们可以通过 Deleter() 方法创建一个删除器,然后进行删除操作。

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

删除多个文档

// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go-mongox/collection/deleter/delete_many.go
package main

import (
    "chenmingyong0423/blog/tutorial-code/go-mongox/collection"
    "context"
    "fmt"

    "github.com/chenmingyong0423/go-mongox"
    "github.com/chenmingyong0423/go-mongox/builder/query"
)

func main() {
    ctx := context.Background()

    // 你需要预先创建一个 *mongo.Collection 对象
    mongoCollection := collection.NewCollection()
    // 使用 Post 结构体作为泛型参数创建一个 collection
    postCollection := mongox.NewCollection[collection.Post](mongoCollection)
    _, err := postCollection.Creator().InsertMany(ctx, []collection.Post{
        {Title: "go-mongox", Author: "陈明勇", Content: "go-mongox 旨在提供更方便和高效的MongoDB数据操作体验。"},
        {Title: "builder", Author: "陈明勇", Content: "..."},
    })
    if err != nil {
        panic(err)
    }

    // 删除多个文档
    // - 通过 query 包构造复杂的 bson 语句
    deleteResult, err := postCollection.Deleter().
        Filter(query.In("title", "go-mongox", "builder")).
        DeleteMany(ctx)
    if err != nil {
        panic(err)
    }
    fmt.Println(deleteResult.DeletedCount == 2) // true
}

DeleteMany 方法与官方的 API 同名,作用是删除多个文档。

Aggregator 聚合器

Aggregator 是一个聚合器,用于执行聚合相关的操作。

// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go-mongox/collection/aggregator/aggregator.go
package main

import (
    "chenmingyong0423/blog/tutorial-code/go-mongox/collection"
    "context"
    "fmt"

    "github.com/chenmingyong0423/go-mongox"
    "github.com/chenmingyong0423/go-mongox/bsonx"
    "github.com/chenmingyong0423/go-mongox/builder/aggregation"
    "go.mongodb.org/mongo-driver/mongo"
)

func main() {
    ctx := context.Background()

    // 你需要预先创建一个 *mongo.Collection 对象
    mongoCollection := collection.NewCollection()
    // 使用 Post 结构体作为泛型参数创建一个 collection
    postCollection := mongox.NewCollection[collection.Post](mongoCollection)
    _, err := postCollection.Creator().InsertMany(ctx, []collection.Post{
        {Title: "go-mongox", Author: "陈明勇", Content: "go-mongox 旨在提供更方便和高效的MongoDB数据操作体验。"},
        {Title: "builder", Author: "陈明勇", Content: "..."},
    })
    if err != nil {
        panic(err)
    }

    // 聚合操作
    // - 使用 aggregation 包构造 pipeline
    posts, err := postCollection.
        // 去除 content 字段
        Aggregator().Pipeline(aggregation.StageBsonBuilder().Project(bsonx.M("content", 0)).Build()).
        Aggregate(ctx)
    if err != nil {
        panic(err)
    }
    for _, post := range posts {
        fmt.Println(post)
    }

    // 如果我们通过聚合操作更改字段的名称,那么我们可以使用 AggregationWithCallback 方法,然后通过 callback 函数将结果映射到我们预期的结构体中
    type DiffPost struct {
        Id          string `bson:"_id"`
        Title       string `bson:"title"`
        Name        string `bson:"name"` // author → name
        Content     string `bson:"content"`
        Outstanding bool   `bson:"outstanding"`
    }
    result := make([]*DiffPost, 0)
    //  将 author 字段更名为 name,排除 content 字段,添加 outstanding 字段,返回结果为 []*DiffPost
    err = postCollection.Aggregator().
        Pipeline(
            aggregation.StageBsonBuilder().Project(
                bsonx.NewD().Add("name", "$author").Add("author", 1).Add("_id", 1).Add("title", 1).Add("outstanding", aggregation.Eq("$author", "陈明勇")).Build(),
            ).Build(),
        ).
        AggregateWithCallback(ctx, func(ctx context.Context, cursor *mongo.Cursor) error {
            return cursor.All(ctx, &result)
        })

    for _, post := range result {
        fmt.Println(post)
    }
}

基于 postCollection 实例,我们可以通过 Aggregator() 方法创建一个聚合器,然后进行聚合操作。

我们可以通过 Pipeline 方法设置 pipeline 参数。

对于执行聚合操作,有以下两个方法:

  • Aggregate 方法:与官方的 API 同名。
  • AggregateWithCallback 方法:因为我们在创建 collection 装饰器时,使用泛型绑定了一个结构体,如果我们执行聚合操作之后,返回的数据与所绑定的结构体映射不上,这时可以使用该方法将结果映射到指定的结构体里。

bson 数据构造

go-mongox 框架提供了以下几种类型的构造器:

  • universal: 简单而又通用的 bson 数据构造函数。
  • query: 查询构造,用于构造查询操作所需的 bson 数据。
  • update: 更新构造,用于构造更新操作所需的 bson 数据。
  • aggregation: 聚合操作构造,包含两种,一种是用于构造聚合 stage 阶段所需的 bson 数据,另一种是用于构造除了 stage 阶段以外的 bson 数据。

universal 通用构造

我们可以使用 bsonx 包里的一些函数进行 bson 数据的构造,例如 bsonx.Mbsonx.Idbsonx.D 等等。

// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go-mongox/builder/universal.go
package main

import (
    "fmt"

    "github.com/chenmingyong0423/go-mongox/bsonx"
)

func main() {
    // bson.M{"姓名": "陈明勇"}
    m := bsonx.M("姓名", "陈明勇")
    fmt.Printf("%#v\n\n", m)

    // bson.M{"_id": "陈明勇"}
    id := bsonx.Id("陈明勇")
    fmt.Printf("%#v\n\n", id)

    // bson.E{Key:"姓名", Value:"陈明勇"}
    e := bsonx.E("姓名", "陈明勇")
    fmt.Printf("%#v\n\n", e)

    // bson.D{bson.E{Key:"姓名", Value:"陈明勇"}, bson.E{Key:"手机号", Value:"1888***1234"}}
    d := bsonx.D(bsonx.E("姓名", "陈明勇"), bsonx.E("手机号", "1888***1234"))
    fmt.Printf("%#v\n\n", d)
    // 我们还可以使用 bsonx.DBuilder 来构建 bson.D
    d2 := bsonx.NewD().Add("姓名", "陈明勇").Add("手机号", "1888***1234").Build()
    fmt.Printf("%#v\n\n", d2)

    // bson.A{"陈明勇", "1888***1234"}
    a := bsonx.A("陈明勇", "1888***1234")
    fmt.Printf("%#v", a)
}

bsonx 包暂时提供了这些构造函数,后面会持续添加更多有用的函数。

query 查询构造

query 包可以帮我们构造出查询相关的 bson 数据,例如 $in$gt$and 等等。

构造类型包含两种:

  • 直接构造
    query 包提供许多构造函数,能够帮我们快速地构造简单的 bson 数据,例如 query.Idquery.Inquery.Eq 等。

  • 构造器构造
    简单的 bson 数据,我们只需要使用 query 包提供的函数即可。但当我们需要构造复杂或多级条件的 bson 数据时,我们可以使用 BsonBuilder 构造器。

// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go-mongox/builder/query.go
package main

import (
    "fmt"

    "github.com/chenmingyong0423/go-mongox/bsonx"

    "github.com/chenmingyong0423/go-mongox/builder/query"
)

func main() {
    // 通过函数直接构造
    // bson.D{bson.E{Key:"姓名", Value:bson.D{bson.E{Key:"$eq", Value:"陈明勇"}}}}
    d := query.Eq("姓名", "陈明勇")
    fmt.Printf("%#v\n\n", d)

    //  bson.D{bson.E{Key:"age", Value:bson.D{{Key:"$in", Value:[]int{18, 19, 20}}}}}
    d = query.In("age", 18, 19, 20)
    fmt.Printf("%#v\n\n", d)

    // elemMatch
    // bson.D{bson.E{Key: "result", Value: bson.D{bson.E{Key: "$elemMatch", Value: bson.D{bson.E{Key: "$gte", Value: 80}, bson.E{Key: "$lt", Value: 85}}}}}}
    d = query.ElemMatch("result", bsonx.D(bsonx.E("$gte", 80), bsonx.E("$lt", 85)))
    fmt.Printf("%#v\n\n", d)

    // 通过构造器构造
    // bson.D{bson.E{Key:"age", Value:bson.D{{Key:"$gt", Value:18}, bson.E{Key:"$lt", Value:25}}}}
    d = query.BsonBuilder().Gt("age", 18).Lt("age", 25).Build()
    fmt.Printf("%#v\n\n", d)

    // bson.d{bson.E{Key: "$and", Value: []any{bson.D{{Key: "x", Value: bson.D{{Key: "$ne", Value: 0}}}}, bson.D{{Key: "y", Value: bson.D{{Key: "$gt", Value: 0}}}}}}
    d = query.BsonBuilder().And(
        query.BsonBuilder().Ne("x", 0).Build(),
        query.BsonBuilder().Gt("y", 0).Build(),
    ).Build()
    fmt.Printf("%#v\n\n", d)

    // bson.D{bson.E{Key:"qty", Value:bson.D{{Key:"$exists", Value:true}, bson.E{Key:"$nin", Value:[]int{5, 15}}}}}
    d = query.BsonBuilder().Exists("qty", true).NinInt("qty", 5, 15).Build()
    fmt.Printf("%#v", d)
}

query 包提供的方法不止这些,以上只是列举出一些典型的例子,还有更多的用法等着你去探索。

update 更新构造器

update 包可以帮我们构造出更新操作相关的 bson 数据,例如 $set$$inc$push 等等。

构造类型包含两种:

  • 直接构造
    update 包提供许多构造函数,能够帮我们快速地构造简单的 bson 数据,例如 update.SetSimpleupdate.Incupdate.Push 等。

  • 构造器构造
    简单的 bson 数据,我们只需要使用 update 包提供的函数即可。但当我们需要构造复杂或多级条件的 bson 数据时,我们可以使用 BsonBuilder 构造器。

// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go-mongox/builder/update.go
package main

import (
    "fmt"

    "github.com/chenmingyong0423/go-mongox/builder/update"
)

func main() {
    // 通过函数直接构造
    // bson.D{bson.E{Key:"$set", Value:bson.M{"name":"陈明勇"}}}
    u := update.Set("name", "陈明勇")
    fmt.Printf("%#v\n\n", u)

    // bson.D{bson.E{Key:"$inc", Value:bson.D{bson.E{Key:"ratings", Value:-1}}}}
    u = update.BsonBuilder().Inc("ratings", -1).Build()
    fmt.Printf("%#v\n\n", u)

    // bson.D{bson.E{Key:"$push", Value:bson.M{"scores":95}}}
    u = update.BsonBuilder().Push("scores", 95).Build()
    fmt.Printf("%#v\n\n", u)

    // 通过构造器构造
    // bson.D{bson.E{Key:"$set", Value:bson.D{bson.E{Key:"name", Value:"陈明勇"}, bson.E{Key:"sex", Value:"男"}}}}
    u = update.BsonBuilder().Set("name", "陈明勇").Set("sex", "男").Build()
    fmt.Printf("%#v\n\n", u)
    // bson.D{bson.E{Key:"$set", Value:bson.D{bson.E{Key:"name", Value:"陈明勇"}}}, bson.E{Key:"$inc", Value:bson.D{bson.E{Key:"rating Value:-1}}}, bson.E{Key:"$push", Value:bson.D{bson.E{Key:"scores", Value:95}}}}
    u = update.BsonBuilder().Set("name", "陈明勇").Inc("ratings", -1).Push("scores", 95).Build()
    fmt.Printf("%#v\n\n", u)
}

update 包提供的方法不止这些,以上只是列举出一些典型的例子,还有更多的用法等着你去探索。

aggregation 聚合构造器

aggregation 包提供了两个 builder

  • StageBsonBuilder:用于构造 stage 阶段所需的 bson 数据
  • BsonBuilder:用于构造除了 stage 阶段以外的 bson 数据。
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go-mongox/builder/aggregation.go
package main

import (
    "fmt"

    "github.com/chenmingyong0423/go-mongox/bsonx"
    "github.com/chenmingyong0423/go-mongox/builder/aggregation"
    "github.com/chenmingyong0423/go-mongox/types"
)

func main() {
    // bson.D{bson.E{Key:"total", Value:bson.D{bson.E{Key:"$gt", Value:[]interface {}{"$price", "$fee"}}}}}
    gt := aggregation.Gt("total", []any{"$price", "$fee"}...)
    fmt.Printf("%#v\n\n", gt)

    // mongo.Pipeline{bson.D{bson.E{Key:"$project", Value:bson.D{bson.E{Key:"name", Value:1}, bson.E{Key:"qtyGt250", Value:bson.D{bson.E{Key:"total", Value:bson.D{bson.E{Key:"$gt", Value:[]interface {}{"$price", "$fee"}}}}}}}}}}
    pipeline := aggregation.StageBsonBuilder().
        Project(bsonx.NewD().Add("name", 1).Add("qtyGt250", gt).Build()).
        Build()
    fmt.Printf("%#v\n\n", pipeline)

    // bson.D{bson.E{Key:"result", Value:bson.D{bson.E{Key:"$or", Value:[]interface {}{bson.D{bson.E{Key:"$gt", Value:[]interface {}{"score", 70}}, bson.E{Key:"score", Value:bson.D{bson.E{Key:"$lt", Value:[]interface {}{90}}}}}, bson.D{bson.E{Key:"views", Value:bson.D{bson.E{Key:"$gte", Value:[]interface {}{90}}}}}}}}}}
    or := aggregation.BsonBuilder().Or("result", aggregation.BsonBuilder().GtWithoutKey("score", 70).Lt("score", 90).Build(), aggregation.BsonBuilder().Gte("views", 90).Build()).Build()
    fmt.Printf("%#v\n\n", or)

    // mongo.Pipeline{bson.D{bson.E{Key:"$match", Value:bson.D{bson.E{Key:"result", Value:bson.D{bson.E{Key:"$or", Value:[]interface {}{bson.D{bson.E{Key:"$gt", Value:[]interface {}{"score", 70}}, bson.E{Key:"score", Value:bson.D{bson.E{Key:"$lt", Value:[]interface {}{90}}}}}, bson.D{bson.E{Key:"views", Value:bson.D{bson.E{Key:"$gte", Value:[]interface {}{90}}}}}}}}}}}}, bson.D{bson.E{Key:"$group", Value:bson.D{bson.E{Key:"_id", Value:interface {}(nil)}, bson.E{Key:"count", Value:bson.D{bson.E{Key:"$sum", Value:1}}}}}}}
    pipeline = aggregation.StageBsonBuilder().
        Match(or).
        Group(nil, aggregation.Sum("count", 1)...).Build()
    fmt.Printf("%#v\n\n", pipeline)

    // mongo.Pipeline{bson.D{bson.E{Key:"$unwind", Value:"$size"}}}
    pipeline = aggregation.StageBsonBuilder().Unwind("$size", nil).Build()
    fmt.Printf("%#v\n\n", pipeline)

    // mongo.Pipeline{bson.D{bson.E{Key:"$unwind", Value:bson.D{bson.E{Key:"path", Value:"$size"}, bson.E{Key:"includeArrayIndex", Value:"arrayIndex"}, bson.E{Key:"preserveNullAndEmptyArrays", Value:true}}}}}
    pipeline = aggregation.StageBsonBuilder().Unwind("$size", &types.UnWindOptions{
        IncludeArrayIndex:          "arrayIndex",
        PreserveNullAndEmptyArrays: true,
    }).Build()
    fmt.Printf("%#v", pipeline)
}

aggregation 包提供的方法不止这些,以上只是列举出一些典型的例子,还有更多的用法等着你去探索。

小结

本文对 go-mongox 框架进行了详细的介绍,它有两个核心,一个是基于泛型的 colletion 形态,另一个就是 bson 构造器了。这两个核心是单独存在的,你可以使用其中之一,也可以同时使用。

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

该框架处于初期阶段,希望通过集思广益的方式,邀请各位开发者共同参与,提出宝贵的建议和意见,共同打造一个更强大、更灵活的框架。期待着您的积极参与和宝贵反馈,共同推动go-mongox不断进步。

本作品采用《CC 协议》,转载必须注明作者和本文链接
微信在线阅读:程序员陈明勇
Image
讨论数量: 7

最近刚好需要用 mongo ,谢谢大佬的优秀开源
有没有可能创造类似 gorm 那样的 mongo ORM ??

2年前 评论
Image 陈明勇 (楼主) 2年前
Image 陈明勇 (楼主) 2年前
Image 陈明勇 (楼主) 1年前
Image jackLai (作者) 2年前
Image AB 1年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!