如何在GraphQL中建模递归数据结构

时间:2017-06-25 13:38:39

标签: graphql

我有一个树数据结构,我想通过GraphQL API返回。

结构不是特别大(足够小,在一次通话中不会出现问题)。

未设置结构的最大深度。

我已将结构建模为:

type Tag{
    id: String!
    children: [Tag]
}

当想要将标签带到任意深度时,会出现问题。

要让所有孩子(例如)等级3,可以编写如下的查询:

{ tags { id children { id children { id } } } }

有没有办法编写查询以将所有标记返回到任意深度?

如果不是建议在GraphQL API中为上述结构建模的方法是什么。

2 个答案:

答案 0 :(得分:7)

前一段时间,我想出了另一种解决方案,与@WuDo建议的方法相同。

想法是使用ID引用树(每个有其父级的子级)并在ID上平整树,并标记树的根,然后在客户端再次递归地构建树。​​

这样,您不必担心像@samcorcos的答案那样限制查询的深度。

模式:

type Query {
    tags: [Tag]
}

type Tag {
    id: ID!
    children: [ID]
    root: Boolean
}

响应:

{ 
    "tags": [
        {"id": "1", "children": ["2"], "root": true}, 
        {"id": "2", "children": [], "root": false}
    ] 
}

客户端树构建:

import find from 'lodash/find';
import isArray from 'lodash/isArray';

const rootTags = [...tags.map(obj => {...obj)}.filter(tag => tag.root === true)];
const mapChildren = childId => {
    const tag = find(tags, tag => tag.id === childId) || null;

    if (isArray(tag.children) && tag.children.length > 0) {
        tag.children = tag.children.map(mapChildren).filter(tag => tag !== null);
    }
}
const tagTree = rootTags.map(tag => {
    tag.children = tag.children.map(mapChildren).filter(tag => tag !== null);
    return tag;
});

答案 1 :(得分:0)

最好的办法是传递参数并在解析器中使用该参数。您的语法将根据您采用的模式而有所不同,但这是它的要点。



/* 
  Add an argument to your query:
  
  query {
    tags(depth: 3) { 
      id 
      children
    }
  }
*/

export default {
  Query: {
    tags: async (obj, { depth, }, context) => {
      // depending on how you're getting tags, run the function
      // that gets you a list of tags
      const tags = await getTags(obj, { depth, }, context)
        // depending on which ORM you're using, join 
        // `depth` number of times here on `tags.children`
      
      return tags
    }
  }
}




显然,每当你以这种方式递归查询时,你都会冒着数据库爆炸的风险,但只要你知道自己在做什么,就应该没问题。