为了账号安全,请及时绑定邮箱和手机立即绑定

gqlgen go,通过添加一个解析器减少数据库调用

gqlgen go,通过添加一个解析器减少数据库调用

Go
烙印99 2022-12-05 17:17:56
我在解决导致性能下降的特定情况时遇到了一些麻烦。我很确定这是可以做到的,但我不知道该怎么做。这是暴露问题的示例模式:type Answer{    answerId: String!    text: String!    topic: Topic!}type Topic {    topicId: String!    name: String!    level: Int!}extend type Query {    answer(answerId: String!): Answer!    answers: [Answer!]! }我遵循了文档,特别是这部分https://gqlgen.com/getting-started/#dont-eagerly-fetch-the-user 从我的架构中,它生成以下解析器:func (r *queryResolver) Answer(ctx context.Context, answerId string) (*models.Answer, error) {...#Single Query which retrives single record of Answer from DB.#Fills a model Answer with the Id and the text#Proceeds by calling the Topic resolver...}func (r *queryResolver) Answers(ctx context.Context) ([]*models.Answer, error) {...#Single Query which retrives list of Answers from DB#Fills a list of model Answer with the Id and the text-->#For each element of that list, it calls the Topic resolver...}func (r *answerResolver) Topic(ctx context.Context, obj *models.Answer) (*models.Topic, error) {...#Single Query which retrives single record of Topic from DB#Return a model Topic with id, name and level...}当使用参数answer调用查询时,将触发解析器,它解析属性并调用解析器。解析器按预期工作,检索并将其合并到并返回。answerIdanswertextTopicTopicTopicAnswer当answers查询在没有answerId参数的情况下被调用时,answer解析器被触发,它answers通过单个查询检索列表。然后,对于该列表的每个元素,它调用Topic解析器。Topic 检索 a并将其Topic合并到单 例中Answer并返回。结果在这两种情况下都可以,但是answers如果我要求很多答案,查询就会成为性能问题。对于每个答案,Topic解析器都会被触发并执行查询以检索单个记录。前任。如果我有 2 个答案 --> 1 个查询[Answer0, Answer1],然后 1 个查询 Topic0和 1 个Topic1前任。10 个答案 --> 1 个[Answer0, ..., Answer9],然后每个 10 个TopicN我想获得一些topic数组解析器,例如func (r *answersResolver) Topics(ctx context.Context, obj *[]models.Answer) (*[]models.Topic, error) {...#Single Query which retrives list of Topics from DB#Return a list of model Topic with id, name and level...}我希望返回数组的每个元素都与数组的相应元素合并Answers。有可能吗?我在哪里可以找到这种方法的例子?谢谢
查看完整描述

1 回答

?
慕盖茨4494581

TA贡献1850条经验 获得超11个赞

可以使用数据加载器(文档)解决问题


我必须为以下数据源实现Topics:


package dataloader


import (

    "github.com/graph-gophers/dataloader"

)


type ctxKey string


const (

    loadersKey = ctxKey("dataloaders")

)



type TopicReader struct {

    conn *sql.DB

}


func (t *TopicReader) GetTopics(ctx context.Context, keys dataloader.Keys) []*dataloader.Result {

    topicIDs := make([]string, len(keys))

    for ix, key := range keys {

        topicIDs[ix] = key.String()

    }

    res := u.db.Exec(

        r.Conn,

        "SELECT id, name, level

        FROM topics

        WHERE id IN (?" + strings.Repeat(",?", len(topicIDs-1)) + ")",

        topicIDs...,

    )

    defer res.Close()


    output := make([]*dataloader.Result, len(keys))

    for index, _ := range keys {

            output[index] = &dataloader.Result{Data: res[index], Error: nil}

    }

    return output

}


type Loaders struct {

    TopicLoader *dataloader.Loader

}



func NewLoaders(conn *sql.DB) *Loaders {

    topicReader := &TopicReader{conn: conn}

    loaders := &Loaders{

        TopicLoader: dataloader.NewBatchedLoader(t.GetTopics),

    }

    return loaders

}


func Middleware(loaders *Loaders, next http.Handler) http.Handler {

    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

        nextCtx := context.WithValue(r.Context(), loadersKey, loaders)

        r = r.WithContext(nextCtx)

        next.ServeHTTP(w, r)

    })

}


func For(ctx context.Context) *Loaders {

    return ctx.Value(loadersKey).(*Loaders)

}


func GetTopic(ctx context.Context, topicID string) (*model.Topic, error) {

    loaders := For(ctx)

    thunk := loaders.TopicLoader.Load(ctx, dataloader.StringKey(topicID))

    result, err := thunk()

    if err != nil {

        return nil, err

    }

    return result.(*model.Topic), nil

}



查看完整回答
反对 回复 2022-12-05
  • 1 回答
  • 0 关注
  • 109 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信