Разбор запроса Apollo GraphQL в запрос Sequelize с полями включения и атрибутов - PullRequest
1 голос
/ 15 мая 2019

Я работаю с Apollo, создаю распознаватели для моих запросов GraphQL.

Для эффективности я хочу получить список запрашиваемых моделей (с соответствующей вложенностью) и поля, запрашиваемые у каждой из этих моделей. Таким образом, я могу передать эту информацию в sequelize, чтобы присоединиться к моделям только тогда, когда это необходимо, и выбрать только необходимые поля.

Резольвер передает эту информацию в объекте info.

(obj, args, { models }, info) => ...

Из объекта info поля, вложенные модели и их соответствующие выбранные поля открываются по этому пути:

info.fieldNodes[0].selectionSet.selections

Моя проблема заключается в анализе этой структуры (каким-то рекурсивным способом, который я себе представляю) в разумную структуру, которую я могу передать в sequelize запросах.

Пример запроса к GraphQL:

{
  getCompany(id: 1) {
    id
    name
    companyOffices {
      id
      users {
        id
        title
        userLinks {
          id
          linkUrl
        }
      }
    }
  }
}

, который генерирует следующее на info.fieldNodes[0].selectionSet.selections (сокращая некоторые поля для краткости):

[
   {
      "kind":"Field",
      "name":{
         "kind":"Name",
         "value":"id"
      }
   },
   {
      "kind":"Field",
      "name":{
         "kind":"Name",
         "value":"name"
      }
   },
   {
      "kind":"Field",
      "name":{
         "kind":"Name",
         "value":"companyOffices"
      },
      "selectionSet":{
         "kind":"SelectionSet",
         "selections":[
            {
               "kind":"Field",
               "name":{
                  "kind":"Name",
                  "value":"id"
               }
            },
            {
               "kind":"Field",
               "name":{
                  "kind":"Name",
                  "value":"users"
               },
               "selectionSet":{
                  "kind":"SelectionSet",
                  "selections":[
                     {
                        "kind":"Field",
                        "name":{
                           "kind":"Name",
                           "value":"id"
                        }
                     },
                     {
                        "kind":"Field",
                        "name":{
                           "kind":"Name",
                           "value":"title"
                        }
                     },
                     {
                        "kind":"Field",
                        "name":{
                           "kind":"Name",
                           "value":"userLinks"
                        },
                        "selectionSet":{
                           "kind":"SelectionSet",
                           "selections":[
                              {
                                 "kind":"Field",
                                 "name":{
                                    "kind":"Name",
                                    "value":"id"
                                 }
                              },
                              {
                                 "kind":"Field",
                                 "name":{
                                    "kind":"Name",
                                    "value":"linkUrl"
                                 }
                              }
                           ]
                        }
                     }
                  ]
               }
            }
         ]
      }
   }
]

Используя эту информацию, я хочу создать запрос, подобный следующему:

  const company = await models.Company.findOne({
    where: { id: args.id },
    attributes: // DYNAMIC BASED ON QUERY
    include: // DYNAMIC BASED ON QUERY
  });

Так что мне нужно проанализировать запрос GraphQL, приведенный выше, примерно до такой структуры, как в приведенном выше info объекте:

{
  attributes: ["id", "name"],
  include: [
    {
      model: "companyOffices",
      attributes: ["id"],
      include: [
        {
          model: users,
          attributes: ["id", "title"],
          include: [{ model: "userLinks", attributes: ["id", "linkUrl"] }]
        }
      ]
    }
  ]
};

Но мне непонятно, как добиться этого с помощью рекурсии, чтобы все не запуталось. Если есть более простой способ достижения этой динамики include / attributes, я тоже открыт для этого.

tl; dr - как перенести модели и поля запроса Apollo GraphQL в include и attributes запроса sequelize?

1 Ответ

0 голосов
/ 15 мая 2019

Возможно, проблема решена, но мне интересно, может ли что-то вроде graphql-sequelize помочь с чем-то вроде этого. Если нет, то я использовал эту стратегию для выполнения атрибутов вашего вопроса.

const mapAttributes = (model, { fieldNodes }) => {
  // get the fields of the Model (columns of the table)
  const columns = new Set(Object.keys(model.rawAttributes));
  const requested_attributes = fieldNodes[0].selectionSet.selections
    .map(({ name: { value } }) => value);
  // filter the attributes against the columns
  return requested_attributes.filter(attribute => columns.has(attribute));
};
User: async (
    _, // instance (not used in Type resolver)
    { username, ... }, // arguments 
    { models: { User }, ... }, // context
    info,
  ) => {
    if (username) {  
      // (only requested columns queried)
      return User.findOne({
        where: { username },
        attributes: mapAttributes(User, info),
      });
    } ... 
  }
...