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

本文涉及的产品
云数据库 MongoDB,独享型 2核8GB
推荐场景:
构建全方位客户视图
简介: `go-mongox` 基于 **泛型** 对 `MongoDB` 官方框架进行了二次封装,它通过使用链式调用的方式,让我们能够丝滑地操作文档。同时,其还提供了多种类型的 `bson` 构造器,帮助我们高效的构建 `bson` 数据。

前言

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

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

go-mongox

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

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

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

功能

  • 文档的 crud 操作
  • 聚合操作
  • 构造 bson 数据
  • ······(敬请期待)

安装

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      string `bson:"_id"`
    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"

    "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)

    // 插入一个文档
    doc := collection.Post{
   Id: "1", Title: "go-mongox", Author: "陈明勇", Content: "go-mongox,不一样的体验。"}
    oneResult, err := postCollection.Creator().InsertOne(context.Background(), doc)
    if err != nil {
   
        panic(err)
    }
    fmt.Println(oneResult.InsertedID.(string) == "1") // true

    // 携带 option 参数
    oneResult, err = postCollection.Creator().OneOptions(options.InsertOne().SetComment("test")).InsertOne(context.Background(), collection.Post{
   Id: "2", Title: "go 语言 go-mongox 库的使用教程", Author: "陈明勇", Content: "go-mongox 旨在提供更方便和高效的MongoDB数据操作体验。"})
    if err != nil {
   
        panic(err)
    }
    fmt.Println(oneResult.InsertedID.(string) == "2") // true
}

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

InsertOne 方法与官方的 API 同名,作用是插入一条数据。如果我们想要设置 options 参数,应使用 OneOptions 方法。

可以看到,无论是设置 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)

    docs := []collection.Post{
   
        {
   Id: "1", Title: "go", Author: "陈明勇", Content: "..."},
        {
   Id: "2", Title: "mongo", Author: "陈明勇", Content: "..."},
    }
    manyResult, err := postCollection.Creator().InsertMany(context.Background(), docs)
    if err != nil {
   
        panic(err)
    }
    fmt.Println(len(manyResult.InsertedIDs) == 2) // true
    // 携带 option 参数
    manyResult, err = postCollection.Creator().ManyOptions(options.InsertMany().SetComment("test")).InsertMany(context.Background(), []collection.Post{
   
        {
   Id: "3", Title: "go-mongox", Author: "陈明勇", Content: "..."},
        {
   Id: "4", Title: "builder", Author: "陈明勇", Content: "..."},
    })
    if err != nil {
   
        panic(err)
    }
    fmt.Println(len(manyResult.InsertedIDs) == 2) // true
}

InsertMany 方法与官方的 API 同名,作用是插入多条数据。如果我们想要设置 options 参数,应使用 ManyOptions 方法。

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"

    "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() {
   
    // 你需要预先创建一个 *mongo.Collection 对象
    mongoCollection := collection.NewCollection()
    // 使用 Post 结构体作为泛型参数创建一个 collection
    postCollection := mongox.NewCollection[collection.Post](mongoCollection)
    _, err := postCollection.Creator().InsertOne(context.Background(), collection.Post{
   Id: "1", Title: "go", Author: "陈明勇", Content: "..."})
    if err != nil {
   
        panic(err)
    }
    // 查询单个文档
    post, err := postCollection.Finder().Filter(bsonx.Id("1")).FindOne(context.Background())
    if err != nil {
   
        panic(err)
    }
    fmt.Println(post)

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

    // - map 作为 filter 条件
    post3, err := postCollection.Finder().Filter(map[string]any{
   "_id": "1"}).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").Eq("author", "陈明勇").Build()).
        FindOne(context.Background())
    if err != nil {
   
        panic(err)
    }
    fmt.Println(post4)
}

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

FindOne 方法与官方的 API 同名,作用是查询单个文档。我们可以通过 FilterOneOptions 方法分别设置 查询条件options 参数。

对于简单的查询条件,我们可以使用 bsonx 包提供的函数进行构造,例如 bsonx.Id("1");对于复杂的查询条件,我们可以使用 query 包提供的 BsonBuilder构造器进行构造。这两个包的用法接下来会进行详细地介绍。

查询多个文档

// 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() {
   
    // 你需要预先创建一个 *mongo.Collection 对象
    mongoCollection := collection.NewCollection()
    // 使用 Post 结构体作为泛型参数创建一个 collection
    postCollection := mongox.NewCollection[collection.Post](mongoCollection)
    _, err := postCollection.Creator().InsertMany(context.Background(), []collection.Post{
   
        {
   Id: "1", Title: "go", Author: "陈明勇", Content: "..."},
        {
   Id: "2", Title: "mongo", Author: "陈明勇", Content: "..."},
    })
    if err != nil {
   
        panic(err)
    }

    // 查询多个文档
    // bson.D{bson.E{Key: "_id", Value: bson.M{"$in": []string{"1", "2"}}}}
    posts, err := postCollection.Finder().Filter(query.BsonBuilder().InString("_id", []string{
   "1", "2"}...).Build()).Find(context.Background())
    if err != nil {
   
        panic(err)
    }
    for _, post := range posts {
   
        fmt.Println(post)
    }

    // 设置 *options.FindOptions 参数
    // bson.D{bson.E{Key: "_id", Value: bson.M{types.In: []string{"1", "2"}}}}
    posts2, err := postCollection.Finder().
        Filter(query.BsonBuilder().InString("_id", []string{
   "1", "2"}...).Build()).
        Options(options.Find().SetProjection(bsonx.M("content", 0))).
        Find(context.Background())
    if err != nil {
   
        panic(err)
    }
    for _, post := range posts2 {
   
        fmt.Println(post)
    }
}

Find 方法与官方的 API 同名,作用是查询多个文档。如果我们想要设置 options 参数,应使用 Options 方法。

在上面的例子中,为了构造 $in 查询语句,我们使用了 BsonBuilder 提供的方法 InString

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"

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

func main() {
   
    // 你需要预先创建一个 *mongo.Collection 对象
    mongoCollection := collection.NewCollection()
    // 使用 Post 结构体作为泛型参数创建一个 collection
    postCollection := mongox.NewCollection[collection.Post](mongoCollection)
    _, err := postCollection.Creator().InsertOne(context.Background(), collection.Post{
   Id: "1", Title: "go", Author: "陈明勇", Content: "..."})
    if err != nil {
   
        panic(err)
    }

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

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

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

UpdateOne 方法与官方的 API 同名,作用是更新单个文档。我们可以通过 FilterOptions 方法分别设置 文档匹配的条件options 参数。

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

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

更新多个文档

// 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/bsonx"
    "github.com/chenmingyong0423/go-mongox/builder/query"
    "github.com/chenmingyong0423/go-mongox/builder/update"
)

func main() {
   
    // 你需要预先创建一个 *mongo.Collection 对象
    mongoCollection := collection.NewCollection()
    // 使用 Post 结构体作为泛型参数创建一个 collection
    postCollection := mongox.NewCollection[collection.Post](mongoCollection)
    _, err := postCollection.Creator().InsertMany(context.Background(), []collection.Post{
   
        {
   Id: "1", Title: "go", Author: "陈明勇", Content: "..."},
        {
   Id: "2", Title: "mongo", Author: "陈明勇", Content: "..."},
    })
    if err != nil {
   
        panic(err)
    }

    // 更新多个文档
    updateResult, err := postCollection.Updater().
        Filter(query.BsonBuilder().InString("_id", []string{
   "1", "2"}...).Build()).
        Updates(update.BsonBuilder().Set(bsonx.M("title", "golang")).Build()).
        UpdateMany(context.Background())
    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"
    "github.com/chenmingyong0423/go-mongox/bsonx"
    "go.mongodb.org/mongo-driver/mongo/options"
)

func main() {
   
    // 你需要预先创建一个 *mongo.Collection 对象
    mongoCollection := collection.NewCollection()
    // 使用 Post 结构体作为泛型参数创建一个 collection
    postCollection := mongox.NewCollection[collection.Post](mongoCollection)
    _, err := postCollection.Creator().InsertMany(context.Background(), []collection.Post{
   
        {
   Id: "1", Title: "go", Author: "陈明勇", Content: "..."},
        {
   Id: "2", Title: "mongo", Author: "陈明勇", Content: "..."},
    })
    if err != nil {
   
        panic(err)
    }

    // 删除单个文档
    deleteResult, err := postCollection.Deleter().Filter(bsonx.Id("1")).DeleteOne(context.Background())
    if err != nil {
   
        panic(err)
    }
    fmt.Println(deleteResult.DeletedCount == 1) // true

    // 携带 option 参数
    deleteResult2, err := postCollection.Deleter().Filter(bsonx.Id("2")).Options(options.Delete().SetComment("test")).DeleteOne(context.Background())
    if err != nil {
   
        panic(err)
    }
    fmt.Println(deleteResult2.DeletedCount == 1) // true
}

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

DeleteOne 方法与官方的 API 同名,作用是删除单个文档。我们可以通过 FilterOptions 方法分别设置 文档匹配的条件options 参数。

删除多个文档

// 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() {
   
    // 你需要预先创建一个 *mongo.Collection 对象
    mongoCollection := collection.NewCollection()
    // 使用 Post 结构体作为泛型参数创建一个 collection
    postCollection := mongox.NewCollection[collection.Post](mongoCollection)
    _, err := postCollection.Creator().InsertMany(context.Background(), []collection.Post{
   
       {
   Id: "1", Title: "go", Author: "陈明勇", Content: "..."},
       {
   Id: "2", Title: "mongo", Author: "陈明勇", Content: "..."},
    })
    if err != nil {
   
       panic(err)
    }

    // 删除多个文档
    // - 通过 query 包构造复杂的 bson 语句
    deleteResult, err := postCollection.Deleter().Filter(query.BsonBuilder().InString("_id", []string{
   "1", "2"}...).Build()).DeleteMany(context.Background())
    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"
    "github.com/chenmingyong0423/go-mongox/types"
    "go.mongodb.org/mongo-driver/mongo"
)

func main() {
   
    // 你需要预先创建一个 *mongo.Collection 对象
    mongoCollection := collection.NewCollection()
    // 使用 Post 结构体作为泛型参数创建一个 collection
    postCollection := mongox.NewCollection[collection.Post](mongoCollection)
    _, err := postCollection.Creator().InsertMany(context.Background(), []collection.Post{
   
        {
   Id: "1", Title: "go", Author: "陈明勇", Content: "..."},
        {
   Id: "2", Title: "mongo", Author: "陈明勇", Content: "..."},
    })
    if err != nil {
   
        panic(err)
    }

    // 聚合操作
    // - 使用 aggregation 包构造 pipeline
    posts, err := postCollection.
        Aggregator().Pipeline(aggregation.StageBsonBuilder().Project(bsonx.M("content", 0)).Build()).
        Aggregate(context.Background())
    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.D(types.KV("name", "$author"), types.KV("author", 1), types.KV("_id", 1), types.KV("title", 1), types.KV("outstanding", aggregation.BsonBuilder().Eq("$author", "陈明勇").Build()))).Build(),
        ).
        AggregateWithCallback(context.Background(), func(ctx context.Context, cursor *mongo.Cursor) error {
   
            return cursor.All(ctx, &result)
        })

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

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

我们可以通过 PipelineAggregateOptions 方法分别设置 pipelineoptions 参数。

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

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

Builder 构造器

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.D{bson.E{Key:"姓名", Value:"陈明勇"}, bson.E{Key:"手机号", Value:"1888***1234"}}
    d := bsonx.D(bsonx.KV("姓名", "陈明勇"), bsonx.KV("手机号", "1888***1234"))
    fmt.Printf("%#v\n\n", d)

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

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

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

特别注意的是,使用 bsonx.D 方法构造数据时,传入的参数,需要使用 bsonx.KV 方法进行传递,目的是强约束 key-value 的类型。

query 查询构造器

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

// 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:"陈明勇"}}
    d := query.BsonBuilder().Add(bsonx.KV("姓名", "陈明勇")).Build()
    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:"age", Value:bson.D{
   {Key:"$in", Value:[]int{18, 19, 20}}}}}
    d = query.BsonBuilder().InInt("age", 18, 19, 20).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\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.BsonBuilder().ElemMatch("result", bsonx.D(bsonx.KV("$gte", 80), bsonx.KV("$lt", 85))).Build()
    fmt.Printf("%#v", d)
}

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

update 更新构造器

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

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

import (
    "fmt"

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

func main() {
   
    // bson.D{bson.E{Key:"$set", Value:bson.M{"name":"陈明勇"}}}
    u := update.BsonBuilder().Set(bsonx.M("name", "陈明勇")).Build()
    fmt.Printf("%#v\n\n", u)

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

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

    // bson.D{bson.E{Key:"$unset", Value:bson.D{primitive.E{Key:"quantity", Value:""}, bson.E{Key:"instock", Value:""}}}}
    u = update.BsonBuilder().Unset("quantity", "instock").Build()
    fmt.Printf("%#v", 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/builder/query"
    "github.com/chenmingyong0423/go-mongox/types"
)

func main() {
   
    // bson.D{bson.E{Key:"$gt", Value:[]any{"$qty", 250}}}
    gt := aggregation.BsonBuilder().Gt("$qty", 250).Build()
    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:"age", Value:1}, bson.E{Key:"qtyGt250", Value:bson.D{bson.E{Key:"$gt", Value:[]interface {}{"$qty", 250}}}}}}}}
    pipeline := aggregation.StageBsonBuilder().Project(bsonx.D(bsonx.KV("name", 1), bsonx.KV("age", 1), bsonx.KV("qtyGt250", gt))).Build()
    fmt.Printf("%#v\n\n", pipeline)

    // bson.D{bson.E{Key:"$or", Value:[]interface {}{bson.D{bson.E{Key:"score", Value:bson.D{bson.E{Key:"$gt", Value:70}, bson.E{Key:"$lt", Value:90}}}}, bson.D{bson.E{Key:"views", Value:bson.D{bson.E{Key:"$gte", Value:90}}}}}}}
    or := aggregation.BsonBuilder().Or(
       query.BsonBuilder().Gt("score", 70).Lt("score", 90).Build(),
       query.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:"$or", Value:[]any{bson.D{bson.E{Key:"score", Value:bson.D{bson.E{Key:"$gt", Value:70}, bson.E{Key:"$lt", Value:90}}}}, bson.D{bson.E{Key:"views", Value:bson.D{bson.E{Key:"$gte", Value:90}}}}}}}}}, bson.D{bson.E{Key:"$group", Value:bson.D{bson.E{Key:"_id", Value:any(nil)}, bson.E{Key:"count", Value:bson.D{bson.E{Key:"$sum", Value:1}}}}}}}
    pipeline = aggregation.StageBsonBuilder().Match(or).Group(nil, bsonx.E("count", aggregation.BsonBuilder().Sum(1).Build())).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 构造器了。这两个核心是单独存在的,你可以使用其中之一,也可以同时使用。

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

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

相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。   相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
目录
相关文章
|
7月前
|
Go 开发者
掌握Go语言:Go语言结构体,精准封装数据,高效管理实体对象(22)
掌握Go语言:Go语言结构体,精准封装数据,高效管理实体对象(22)
|
4月前
|
数据采集 网络协议 测试技术
使用Go Validator在Go应用中有效验证数据
使用Go Validator在Go应用中有效验证数据
|
5月前
|
JSON 测试技术 Go
零值在go语言和初始化数据
【7月更文挑战第10天】本文介绍在Go语言中如何初始化数据,未初始化的变量会有对应的零值:bool为`false`,int为`0`,byte和string为空,pointer、function、interface及channel为`nil`,slice和map也为`nil`。。本文档作为指南,帮助理解Go的数据结构和正确使用它们。
105 22
零值在go语言和初始化数据
|
4月前
|
存储 算法 Java
Go 通过 Map/Filter/ForEach 等流式 API 高效处理数据
Go 通过 Map/Filter/ForEach 等流式 API 高效处理数据
|
4月前
|
数据采集 缓存 IDE
Go中遇到http code 206和302的获取数据的解决方案
文章提供了解决Go语言中处理HTTP状态码206(部分内容)和302(重定向)的方案,包括如何获取部分数据和真实请求地址的方法,以便程序员能快速完成工作,享受七夕时光。
214 0
Go中遇到http code 206和302的获取数据的解决方案
|
4月前
|
存储 负载均衡 算法
[go 面试] 一致性哈希:数据分片与负载均衡的黄金法则
[go 面试] 一致性哈希:数据分片与负载均衡的黄金法则
|
4月前
|
消息中间件 Kafka Go
从Go channel中批量读取数据
从Go channel中批量读取数据
|
4月前
|
数据采集 网络协议 测试技术
使用Go Validator在Go应用中有效验证数据
使用Go Validator在Go应用中有效验证数据
|
4月前
|
JSON Go 数据格式
Go - 使用工具生成易读的 Protocol 文档
Go - 使用工具生成易读的 Protocol 文档
33 1
|
4月前
|
监控 Serverless Go
Golang 开发函数计算问题之Go 语言中切片扩容时需要拷贝原数组中的数据如何解决
Golang 开发函数计算问题之Go 语言中切片扩容时需要拷贝原数组中的数据如何解决