Как я должен загружать ассоциацию, используя 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 ]
})
})
}
Работает как чемпион. Может быть, есть и обратная сторона, которую я еще не видел. Я думаю, время покажет.
Оставьте это открытым какое-то время на случай, если у кого-то есть лучший ответ (включая меня).