GraphQL queries with tables join using Node.js

后端 未结 2 1840
攒了一身酷
攒了一身酷 2021-02-01 14:52

I am learning GraphQL so I built a little project. Let\'s say I have 2 models, User and Comment.

const Comment = Model.defi         


        
相关标签:
2条回答
  • 2021-02-01 15:20

    The concept you are refering to is called batching. There are several libraries out there that offer this. For example:

    • Dataloader: generic utility maintained by Facebook that provides "a consistent API over various backends and reduce requests to those backends via batching and caching"

    • join-monster: "A GraphQL-to-SQL query execution layer for batch data fetching."

    0 讨论(0)
  • 2021-02-01 15:20

    To anyone using .NET and the GraphQL for .NET package, I have made an extension method that converts the GraphQL Query into Entity Framework Includes.

    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;
                }
            }}}
    

    Lets say we have the following query:

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

    We can create a variable in "resolve" method of the field:

    var include = context.GetIncludeString();
    

    which generates the following string:

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

    and pass it to Entity Framework:

    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));
    }
    
    0 讨论(0)
提交回复
热议问题