Как я должен загружать ассоциацию, используя graphql-sequelize без `include`?

Я использую 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'
//     }
// }

Итак, после проверки исходного кода graphql-sequelize'sresolverи определив, что второй аргумент передается через аксессор sqlz, я добавил include в мой распознаватель GQL, вот так:

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

И вот тогда я получаю фатальную ошибку. Исследования показывают, что люди, использующие graphql-sequelize, теперь не разрешают include по соображениям производительности, в пользу загрузчика данных sequelize, который я в настоящее время не использую и не планирую использовать.

Но даже если бы я был, это оставляет без ответа вопрос о том, как они ожидают, что мы получим связанные строки в resolver, Я нашел загадочный комментарий, который предполагает, что есть альтернатива под названием 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скрытый геттер получает значение из 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, и, как следствие, мое определение типа GQL для Driver технически ниже этой функции разрешения. Я начал создавать кучу сахара для составления GQL-типов (особенно когда речь идет о встраивании вторичных сущностей в праймериз), и эта перспектива побуждает меня думать, что resolverконечные точки должны иметь равномерную форму возврата. Т.е. было бы плохо, если бы преобразователи для некоторых сущностей испускали неработающие экземпляры модели sqlz, а другие возвращали исправные.

Как мы должны реализовать нетерпеливую загрузку в graphql-sequelize? resolver без include?

Версии пакета:

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

1 ответ

Я попробовал две вещи:

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

Это работает, но дважды попадает в основную таблицу, что составляет мега брутто.

Итак, я решил просто выбросить "помощник" graphql-sequelize resolverи запрограммируйте напрямую в API apollo-server-express:

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

Работает как чемпион. Может быть, есть и обратная сторона, которую я еще не видел. Я думаю, время покажет.

Оставьте это открытым какое-то время на случай, если у кого-то есть лучший ответ (включая меня).

Другие вопросы по тегам