【问题标题】:GraphQL queries with tables join using Node.js使用 Node.js 连接表的 GraphQL 查询
【发布时间】:2017-04-18 04:11:42
【问题描述】:

我正在学习GraphQL,所以我建立了一个小项目。假设我有 2 个模型,UserComment

const Comment = Model.define('Comment', {
  content: {
    type: DataType.TEXT,
    allowNull: false,
    validate: {
      notEmpty: true,
    },
  },
});

const User = Model.define('User', {
  name: {
    type: DataType.STRING,
    allowNull: false,
    validate: {
      notEmpty: true,
    },
  },
  phone: DataType.STRING,
  picture: DataType.STRING,
});

关系是one-to-many,其中一个用户可以有许多 cmets。
我已经建立了这样的架构:

const UserType = new GraphQLObjectType({
  name: 'User',
  fields: () => ({
    id: {
      type: GraphQLString
    },
    name: {
      type: GraphQLString
    },
    phone: {
      type: GraphQLString
    },
    comments: {
      type: new GraphQLList(CommentType),
      resolve: user => user.getComments()
    }
  })
});

还有查询:

const user = {
  type: UserType,
  args: {
    id: {
      type: new GraphQLNonNull(GraphQLString)
    }
  },
  resolve(_, {id}) => User.findById(id)
};

对用户及其 cmets 执行查询是通过 1 个请求完成的,如下所示:

{
  User(id:"1"){
    Comments{
      content
    }
  }
}

据我了解,客户端将使用 1 个查询获得结果,这是使用 GraphQL 的好处。但是服务器会执行 2 个查询,一个是针对用户,另一个是针对他的 cmets。

我的问题是,构建GraphQL 架构和类型以及在表之间组合连接以使服务器也可以通过 1 个请求执行查询的最佳实践是什么?

【问题讨论】:

    标签: sql node.js sequelize.js graphql


    【解决方案1】:

    您所指的概念称为批处理。有几个图书馆提供这个。例如:

    • Dataloader:由 Facebook 维护的通用实用程序,可“在各种后端提供一致的 API,并通过批处理和缓存减少对这些后端的请求”

    • join-monster:“用于批量数据提取的 GraphQL-to-SQL 查询执行层。”

    【讨论】:

    • 如果你喜欢视频,这里有一个关于加入怪物的讨论:youtube.com/watch?v=Y7AdMIuXOgs:)
    • 感谢您的帖子。你会选哪一个?一方面,我有 facebook 和 1800 星库可以批处理一些请求,但另一方面我有一个库可以将所有内容批处理到 1 个请求。
    • 我们在 Graphcool 使用 Dataloader 获得了很好的体验,当时我们使用 graphql-js 运行节点 GraphQL 后端。无法分享任何关于加入怪物的经验。我认为它们在功能和用法上略有不同,所以我猜你需要对其进行测试:)
    • 批处理对我来说是不同的!批处理意味着获取一组 ID 和(如在 1 + N 场景中)并通过一个查询将它们全部获取。 join monster 和Photonjs 做的更好,他们利用 SQL 连接通过一个 SQL 查询来完成 GQL 查询。
    【解决方案2】:

    对于使用 .NET 和 GraphQL for .NET 包的任何人,我已经制作了一个扩展方法,将 GraphQL 查询转换为实体框架包含。

    public static class ResolveFieldContextExtensions
    {
        public static string GetIncludeString(this ResolveFieldContext<object> source)
        {
            return string.Join(',', GetIncludePaths(source.FieldAst));
        }
    
        private static IEnumerable<Field> GetChildren(IHaveSelectionSet root)
        {
            return root.SelectionSet.Selections.Cast<Field>()
                                               .Where(x => x.SelectionSet.Selections.Any());
        }
    
        private static IEnumerable<string> GetIncludePaths(IHaveSelectionSet root)
        {
            var q = new Queue<Tuple<string, Field>>();
            foreach (var child in GetChildren(root))
                q.Enqueue(new Tuple<string, Field>(child.Name.ToPascalCase(), child));
    
            while (q.Any())
            {
                var node = q.Dequeue();
                var children = GetChildren(node.Item2).ToList();
                if (children.Any())
                {
                    foreach (var child in children)
                        q.Enqueue(new Tuple<string, Field>
                                      (node.Item1 + "." + child.Name.ToPascalCase(), child));
    
                }
                else
                {
                    yield return node.Item1;
                }
            }}}
    

    假设我们有以下查询:

    query {
      getHistory {
        id
        product {
          id
          category {
            id
            subCategory {
              id
            }
            subAnything {
              id
            }
          }
        }
      }
    }
    

    我们可以在字段的“resolve”方法中创建一个变量:

    var include = context.GetIncludeString();
    

    生成以下字符串:

    "Product.Category.SubCategory,Product.Category.SubAnything"
    

    并将其传递给实体框架:

    public Task<TEntity> Get(TKey id, string include)
    {
        var query = Context.Set<TEntity>();
        if (!string.IsNullOrEmpty(include))
        {
            query = include.Split(',', StringSplitOptions.RemoveEmptyEntries)
                           .Aggregate(query, (q, p) => q.Include(p));
        }
        return query.SingleOrDefaultAsync(c => c.Id.Equals(id));
    }
    

    【讨论】:

      猜你喜欢
      • 2018-04-25
      • 2019-01-26
      • 2019-04-06
      • 1970-01-01
      • 2021-04-20
      • 2021-05-10
      • 2019-01-27
      • 1970-01-01
      • 2021-09-09
      相关资源
      最近更新 更多