Apollo GraphQL 服务器:按单独解析的字段过滤(或排序)

Posted

技术标签:

【中文标题】Apollo GraphQL 服务器:按单独解析的字段过滤(或排序)【英文标题】:Apollo GraphQL server: filter (or sort) by a field that is resolved separately 【发布时间】:2018-03-19 01:52:27 【问题描述】:

我可能面临Apollo GraphQL server 的设计限制,我想问一下是否有解决方法。

我的架构包含类型 Thing,该类型具有字段 flag。我希望能够通过flag 的值过滤things,但是如果单独解析该字段似乎是不可能的。如果我想对things 进行排序,也会出现同样的问题。这是一个例子:

  type Thing 
    id: String!
    flag Boolean!
  

  type Query 
    things(onlyWhereFlagIsTrue: Boolean): [Thing!]!
  
const resolvers = 
  Thing: 
    flag: async (id) => 
      const value = await getFlagForThing(id);
      return value;
    
  ,
  Query: 
    async things(obj, onlyWhereFlagIsTrue = false) 
      let result = await getThingsWithoutFlags();
      if (onlyWhereFlagIsTrue) 
        // ↓ this does not work, because flag is still undefined
        result = _.filter(result, ['flag', true]);
      
      return result;
    
  

在解析所有异步字段后,有什么方法可以过滤things?我知道我可以在things 解析器中调用getFlagForThing(id),但这不是重复我自己吗?解析flag 背后的逻辑可能比仅仅调用一个函数要复杂一些。

UPD:这是迄今为止我能找到的最佳解决方案。非常丑陋且难以扩展到其他领域:

const resolvers = 
  Thing: 
    flag: async (id, flag) => 
      // need to check if flag has already been resolved
      // to avoid calling getThingsWithoutFlags() twice
      if (!_.isUndefined(flag)) 
        return flag;
      
      const value = await getFlagForThing(id);
      return value;
    
  ,
  Query: 
    async things(obj, onlyWhereFlagIsTrue = false) 
      let result = await getThingsWithoutFlags();
      if (onlyWhereFlagIsTrue) 
        // asynchroniously resolving flags when needed
        const promises = _.map(result, (id) =>
          getFlagForThing(id)
        );
        const flags = await Promise.all(promises);
        for (let i = 0; i < flags.length; i += 1) 
          result[i].flag = flags[i];
        
        // ↓ this line works now
        result = _.filter(result, ['flag', true]);
      
      return result;
    
  ,
;

【问题讨论】:

【参考方案1】:

我认为这里的问题实际上并不是 Apollo 服务器的限制,更多的是与您有一个带解析器的原始字段这一事实有关。通常,最好仅在字段要返回单独类型时才对字段使用解析器:

Thing 
    id: ID!
    flag: Boolean!
    otherThings: OtherThing


Query 
    things(onlyWhereFlag: Boolean): [Thing!]!

在本例中,为otherThings 单独设置一个解析器会很好,但如果一个字段是原始字段,那么我只需将该字段与 Thing 一起解析即可。 使用您的原始架构:

const filterByKeyValuePair = ([key, value]) => obj => obj[key] === value;

const resolvers = 
    Query: 
        async things(parent,  onlyWhereFlag ) 
            const things = await Promise.all(
                (await getThings()).map(
                    thing =>
                        new Promise(async resolve =>
                            resolve(
                                ...thing,
                                flag: await getFlagForThing(thing)
                            )
                        )
                )
            );

            if (onlyWhereFlag) 
                return things.filter(filterByKeyValuePair(['flag', true]));
             else 
                return things;
            
        
    
;

如果flag 不是原语怎么办?好吧,如果您想按它进行过滤,那么您将有几个不同的选择。这些选项实际上取决于您获取“标志”数据的方式。如果您能提供有关您的架构和数据模型的更多详细信息,我会很乐意详细说明。

【讨论】:

以上是关于Apollo GraphQL 服务器:按单独解析的字段过滤(或排序)的主要内容,如果未能解决你的问题,请参考以下文章

合并 Apollo 服务器的 GraphQL 解析器不使用 Object.assign()

如何使用 Apollo 分离 GraphQL 模式和解析器文件?

Graphql apollo 服务器解析器参数类型

Graphql Apollo Federation - 基于实现服务解析计算域

Apollo GraphQL 服务器:在解析器中,从更高级别获取变量

我们如何从 apollo-server 模块传递 ApolloServer 中的数据源和合并的 graphql 模式和解析器?