2

我在 Node.js 中与 Apollo-Server 和 Express.js 一起使用 Sequelize。

当进行越来越深入的查询时,GraphQL 会循环我的模型并通过 ID 对每个模型进行单独的查询。

例如,如果我得到user(userId) > playthroughs > scores,这将查找该用户(没问题),然后查找所有的游戏通关userId(仍然没什么大不了的),但是为了获得分数,它会循环每个playthroughId并执行对每个完全独立的查询。这是非常低效的,并且导致我的查询花费的时间比他们应该的要长。

而不是循环:

SELECT scoreValue
FROM scores
WHERE playthroughId = id

我真的很想自己抓取数组并像这样执行该循环:

SELECT scoreValue
FROM scores
WHERE playthroughId IN (...ids)

这也发生在我去年使用 Facebook 的参考 GraphQL 时,所以我认为它不是特定于 Apollo 的实现。

我想知道如何调整这些查询,以免它们对性能造成如此大的影响。

示例解析器:

const resolvers = {
    Query: {
        user: (_, values) => User.findOne(formatQuery(values))
            .then(getDataValues),
    },

    Playthrough: {
        score: ({ playthroughId }) => Score.findOne(formatQuery({ playthroughId }))
            .then(getDataValues),
    },

    User: {
        playthroughs: ({ userId }, { take }) => Playthrough.findAll(formatQuery({ userId, take, order: 'playthroughId DESC' }))
            .then(getAllDataValues),
    },
}
4

1 回答 1

0

除了 graphql,facebook 还发布了一个鲜为人知的项目dataloader

它的作用是将同一个刻度中的多个请求批处理为一个。所以你的代码会是这样的

scoreLoader = new Dataloader(keys => {
  return Score.findAll({ where: { id: keys } }).then(() => {
     //Map the results back so they are in the same order as keys
  })
});


score: ({ playthroughId }) => scoreLoader.load(playthroughId).then(getDataValues)

当然,每个领域都有负载会很乏味。因此,您可以改为使用dataloader-sequelize,它将所有对 association.get 的调用(即Playthrough.getScores())和对 findOne / findById 的调用包装到数据加载器调用中,因此多个调用被批处理为一个。

由于您正在构建由 sequelize 支持的 graphql API,您可能还对https://github.com/mickhansen/graphql-sequelize/感兴趣,它为 grahpql 提供 sequelize 特定的帮助程序,并在底层使用 dataloader-sequelize

于 2016-12-19T12:49:15.413 回答