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

我在解决导致性能下降的特定情况时遇到了一些麻烦。我很确定这是可以做到的,但我不知道该怎么做。


这是暴露问题的示例模式:


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。


有可能吗?我在哪里可以找到这种方法的例子?谢谢


烙印99
浏览 110回答 1
1回答

慕盖茨4494581

可以使用数据加载器(文档)解决问题我必须为以下数据源实现Topics:package dataloaderimport (    "github.com/graph-gophers/dataloader")type ctxKey stringconst (    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}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go