如何使用gqlgen golang和mongodb实现graphql的数据加载器

问题描述 投票:0回答:1

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"
}
mongodb go graphql gqlgen
1个回答
0
投票

在阅读了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 存储库

© www.soinside.com 2019 - 2024. All rights reserved.