簡體   English   中英

使用包含和屬性字段將 Apollo GraphQL 查詢解析為 Sequelize 查詢

[英]Parsing Apollo GraphQL query into Sequelize query with include and attributes fields

我正在與 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我也願意。

文藝青年最愛的-我怎么能阿波羅GraphQL查詢的模型和領域轉移到includeattributessequelize查詢?

它可能會繞過這個問題,但我想知道像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),
      });
    } ... 
  }

我終於找到了這個問題的解決方案(有點)。

const mapAttributes = (projectors, models) => {
    const map = {};
    const set = [];
    Object.keys(projectors).forEach((projector) => {
        if (typeof projectors[projector] === 'object') {
            map[projector] = mapAttributes(projectors[projector], models.slice(1));
        } else {
            set.push(projector);
            map[models[0]] = set;
        }
    });
    return map;
};

這里的projectors是 graphql 模式的對象符號,就像我們從info.fieldNodes[0].selectionSet.selections得到的對象一樣

和模型,是迭代的有序方式。 所以在上面的例子中它會像

['company', 'companyOffices', 'users']等。

從最終的地圖中,我們進入了一個整潔的結構,從中我們可以很好地獲取屬性。

最后,當您返回參數時,您可能需要將 sequelize 輸出轉換為 graphql 類型

const sequelizeToGraphql = (results = [], vouchers = [], postings = []) => {
    const final = { particulars: [] };
    results.forEach((result) => {
        vouchers.forEach((voucher) => {
            final[voucher] = result[voucher];
        });
        const obj = {};
        postings.forEach((posting) => {
            obj[posting] = result[`posting.${posting}`];
        });
        final.particulars.push(obj);
    });
    return final;
};

這里的voucherspostings是我sequqlize中給我的表名,同理自己修改

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM