Go 中有很多可用于 sql 语言的数据加载器的实现。但是我如何使用 mongodb 在 golang 中实现数据加载器功能。我正在为我的 graphql 代码使用 gqlgen 库。
我想获取所有产品及其类别。我想兑现类别信息
我的 graphql 查询看起来像:
query example {
products: GetProducts{
Id,
Name,
Description,
Price,
Quantity,
Category {
Id,
Name
}
},
}
对于上面的查询,如果有 1000 个产品和 10 个类别,那么我将只查询 10 个类别 1000 次。所以我想兑现它。
我的 Mongodb 产品架构:
{
"_id": {
"$oid": "663c6371bd05f26997e9d528"
},
"name": "Gulab Jamun",
"description": "this is a product",
"price": 210.45,
"quantity": 10,
"category": "663b89711f437c9bcf6d3864"
}
我的 Mongodb 类别架构:
{
"_id": {
"$oid": "663b896d1f437c9bcf6d3863"
},
"name": "electronics"
}
在阅读了dataloaden的文档后,我发现它是一个通用库,不需要我们的数据库只有sql。我们可以在 mongodb 中使用“$in”运算符来批量请求。
我附上我的类别加载器代码以供参考:
package category
import (
"context"
ers "errors"
"graphql_search/models"
"log"
"net/http"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
func (a *api) CategoryLoaderMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
config := CategoryLoaderConfig{
Wait: 2 * time.Millisecond,
MaxBatch: 100,
Fetch: func(keys []string) ([]models.CategoryDB, []error) {
log.Println("Fetch Categories called for keys", keys)
// data we need to populate.
categories := make([]models.CategoryDB, len(keys))
errors := make([]error, len(keys))
keysObjectIds := make([]primitive.ObjectID, len(keys))
for i, key := range keys {
var err error
keysObjectIds[i], err = primitive.ObjectIDFromHex(key)
errors[i] = err
}
// do batch request here to mongodb
// Prepare the filter to find documents by their IDs
filter := bson.M{"_id": bson.M{"$in": keysObjectIds}}
cursor, err := a.Database.Collection("category").Find(context.Background(), filter)
if err != nil {
// Handle the error
for i := range errors {
errors[i] = err
}
return categories, errors
}
defer cursor.Close(context.Background())
// Iterate over the cursor and populate the categories slice
var categoriesGot []models.CategoryDB
err = cursor.All(context.Background(), &categoriesGot)
if err != nil {
// Handle the error
for i := range errors {
errors[i] = err
}
return categories, errors
}
log.Println("Categories got for given keys ", categoriesGot)
// iterate categories got and return exact sequence of data we got in keys
var mp map[string]models.CategoryDB = make(map[string]models.CategoryDB)
for _, d := range categoriesGot {
mp[d.ID.Hex()] = models.CategoryDB{
ID: d.ID,
Name: d.Name,
}
}
// now iterate all keys and enter relevent information
for i, key := range keys {
val, ok := mp[key]
if !ok {
errors[i] = ers.New("An Error getting data from map.")
} else {
categories[i] = val
}
}
return categories, errors
},
}
categoryLoader := NewCategoryLoader(config)
// add the categoryloader inside the context.
ctx := context.WithValue(r.Context(), a.CategoryLoaderKey, categoryLoader)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func (a *api) GetCategoryLoader(ctx context.Context) *CategoryLoader {
return ctx.Value(a.CategoryLoaderKey).(*CategoryLoader)
}
将此中间件添加到您的项目中,然后我们可以从请求的上下文中获取categoryloader对象并使用它来获取类别。例如。
func (a *api) Get(ctx context.Context, id string) (*models.Category, error) {
loader := a.GetCategoryLoader(ctx)
cb, err := loader.Load(id)
if err != nil {
return nil, err
}
// NOTE : COMMENTED CODE IS OLD CODE. THIS IS HOW I USED TO FETCH CATEGORY EACH TIME.
// I WASN'T USING BATCHED REQUEST TO DATABASE AND WAS CALLING REDUDUNT QUERIES.
// ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
// defer cancel()
// objectId, _ := primitive.ObjectIDFromHex(id)
// result := a.Database.Collection("category").FindOne(ctx, bson.M{
// "_id": objectId,
// })
// var categoryDB models.CategoryDB
// err = result.Decode(&categoryDB)
// if err != nil {
// return nil, err
// }
// category := &models.Category{
// ID: categoryDB.ID.Hex(),
// Name: categoryDB.Name,
// }
category := &models.Category{
ID: cb.ID.Hex(),
Name: cb.Name,
}
return category, nil
}
欲了解更多详细信息,请参阅我的 GITHUB 存储库:我的项目的 GITHUB 存储库