go-mongox:簡單高效,讓文檔操作和 bson 數據構造更流暢

大家好,我是 陳明勇,一個熱愛技術,喜歡鑽研技術的程序員。

前言

Go 語言中使用 MongoDB 官方框架進行集合操作時,深深感到構建 bson 數據是一件非常繁瑣的工作。字段、逗號,括號等符號的排列,讓我感覺彷彿是在進行一場拼圖遊戲。因此我在想,有沒有一個能讓我絲滑,高效操作 MongoDB 的第三方框架呢,遺憾的是,並沒有找到符合我預期的框架,索性我就自己動手開發了一個,這就是 go-mongox 框架的由來。

如果你也有類似我的這種感受,相信 go-mongox 框架能給你帶來不一樣的體驗。

go-mongox

go-mongox 基於 泛型MongoDB 官方框架進行了二次封裝,它通過使用鏈式調用的方式,讓我們能夠絲滑地操作文檔。同時,其還提供了多種類型的 bson 構造器,幫助我們高效的構建 bson 數據。

倉庫地址:https://github.com/chenmingyong0423/go-mongox

該框架處於初期階段,希望通過集思廣益的方式,邀請各位開發者共同參與,提出寶貴的建議和意見,共同打造一個更強大、更靈活的框架。期待着您的積極參與和寶貴反饋,共同推動go-mongox不斷進步。

功能

安裝

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 參數。

對於更新操作參數,我們可以使用以下兩個方法進行設置:

更新多個文檔

// 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() 方法創建一個聚合器,然後進行聚合操作。

我們可以通過 Pipeline 和 AggregateOptions 方法分別設置 pipeline 和 options 參數。

對於執行聚合操作,有以下兩個方法:

Builder 構造器

go-mongox 框架提供了以下幾種類型的構造器:

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

// 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不斷進步。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/7FE2Ji2Ln1woohqa26NH_A