【问题标题】:How am I supposed to eagerly load an association using graphql-sequelize without `include`?我应该如何使用 graphql-sequelize 急切地加载关联而不使用 `include`?
【发布时间】:2018-08-26 01:32:33
【问题描述】:

我正在使用 graphql-sequelize 来实现 GraphQL 端点。我刚刚创建了一个新的辅助实体并将其与我的一个主要实体相关联,现在我正在尝试在获取主要实体时包含相关的辅助实体。当我尝试实现该提取时出现以下错误:

AssertionError ERR_ASSERTION Include support has been removed in favor of dataloader batching

我的旧的基于解析器的 gql 端点如下所示:

driver: {
    type: MyGQLTypes.Driver,
    resolve: resolver(Driver)
}

我从代码库其他地方的实验中知道这通常有效:

const driver = Driver.getById(`${uuid}`, {
    include: { model: Helmet }
})
// yields:
// {
//     id: '<uuid>',
//     name: 'Tom',
//     helmetNumber: '470',
//     Helmet: {
//         number: '470',
//         type: 'cool',
//         color: 'red'
//     }
// }

因此,在检查 source code of graphql-sequelize's resolver 并确定第二个参数已传递给 sqlz 访问器之后,我将相同的 include 添加到我的 GQL 解析器中,如下所示:

driver: {
    type: MyGQLTypes.Driver,
    resolve: resolver(Driver, {
        include: { model: Helmet }
    })
}

那是我得到致命错误的时候。研究表明,graphql-sequelize 的人们现在不允许 include 使用 performance reasons,而是支持 sequelize 的数据加载器,我目前没有使用,也没有计划使用。

但即使我是,它也没有回答他们希望我们如何在resolver 中获取相关行的问题。我发现a cryptic comment 表明有一个替代方案称为join,但我无法找到它的文档,并且从臀部射击失败了:

driver: {
    type: MyGQLTypes.Driver,
    resolve: resolver(Driver, {

        include: { model: Helmet } // fatal error: disallowed

        join: { model: Helmet } // no effect, no error

        join: Helmet // no effect, no error

    })
}

我能想到的就是添加一个after 例程来手动获取关联模型,然后将该结果附加到主模型的同一位置,如果它以标准方式获取(即使用include),像这样:

driver: {
    type: MyGQLTypes.Driver,
    resolve: resolver(Driver, {

        // can't do this:
        include: { model: Helmet }

        // so I try to emulate it this way:
        after: async (driver) => {

            driver.Helmet = await driver.getHelmet()

            return driver
        }

    })
}

当代码执行并找到我期望的模型时,它无法将找到的模型附加到同一位置。

Sqlz 实例不是普通哈希:它们有一个 dataValues 属性,其中包含来自数据库的原始列值,当您引用 myModel.colName 时,隐藏的 getter 会从 myModel.dataValues[ colName ] 获取值。实验表明,通过include 进行预加载会导致关联模型出现在dataValues 结构中,可能与所需的任何隐藏访问器一起出现,如下所示:

{
    dataValues: {
        id: '<uuid>',
        name: 'Tom',
        helmetNumber: '470',
        Helmet: {
            dataValues: {
                number: '470,
                type: 'cool',
                color: 'red'
            }
        }
    }
}

我很确定我可以将获取的模型附加到那里:

driver.dataValues.Helmet = await driver.getHelmet()

但我认为我们应该避免摆弄dataValues,这似乎是笨拙的。

我正在使用 apollo-server-express,它(正确地)根据 GQL 模式验证解析器返回值,因此,我对 Driver 的 GQL 类型定义在技术上是此解析的下游功能。我已经开始构建一堆用于组合 GQL 类型的糖(特别是在将次要实体嵌入到主要实体中时),这种观点鼓励我认为基于resolver 的端点应该具有统一形状的回报。 IE。如果某些实体的解析器发出损坏的 sqlz 模型实例,而其他实体返回健康的,那就不好了。

如果没有include,我们应该如何在graphql-sequelize 的resolver 中实现预加载?

软件包版本:

apollo-server-express@1.3.2
graphql-sequelize@5.6.1
sequelize@4.35.1
dataloader-sequelize@1.6.3

【问题讨论】:

    标签: sequelize.js graphql apollo-server


    【解决方案1】:

    我尝试了两件事:

    driver: {
        type: MyGQLTypes.Driver,
        resolve: resolver(Driver, {
            after: driver => {
                return Driver.findById(driver.id, {
                    include: [ Helmet ]
                })
            }
        })
    }
    

    这行得通,但它两次命中主表,这是 mega Gross。

    所以,我决定扔掉 graphql-sequelize 的“助手”resolver,直接编程到 apollo-server-express 的 API:

    driver: {
        type: MyGQLTypes.Driver,
        resolve: (parent, args, context, info) => {
            return Driver.findById(args.id, {
                include: [ Helmet ]
            })
        })
    }
    

    像冠军一样工作。可能有一个我还没有看到的缺点。我想时间会证明一切的。

    让这个打开一段时间,以防其他人(包括我自己)有更好的答案。

    【讨论】:

    • 一年没有任何其他贡献,所以我将自己的答案标记为解决方案。
    • 您是否从使用数据加载器(例如缓存)中获得任何好处?你在用 graphql-sequelize 做什么吗?
    • 我相信dataloader本质上是一个read-through cache,而且我想我看到有人提到它也可以批量查询。所以,我假设它主要是关于性能。我选择不使用它,因为通过自动缩放微服务来解决性能问题似乎不太复杂。
    猜你喜欢
    • 2018-07-28
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    • 2015-02-24
    • 2011-03-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多