2016-12-03 95 views
13

我正在学习GraphQL所以我建立了一个小项目。假设我有2个型号,UserCommentGraphQL查询与表加入

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, 

}); 

关系是1:很多,用户可以有很多评论。
我已经建立了这样的模式:

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

执行查询的一个用户,他的评论与1级的要求做,就像这样:

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

据我所知,客户将得到结果使用1个查询,这是使用GraphQL的好处。但服务器将执行2个查询,一个用于用户,另一个用于他的评论。
我的问题是,构建GraphQL模式和类型以及在表之间组合联接的最佳实践是什么,以便服务器也可以使用1请求执行查询?

回答

7

您引用的概念称为批处理。有几个图书馆提供这个功能。例如:

  • Dataloader:被Facebook保持通用应用程序,提供 “在各种后端一致的API并通过配料和缓存减少请求那些后端”

  • join-monster

    :“A GraphQL-TO-用于批量数据提取的SQL查询执行层“。

+0

如果你喜欢的视频,在这里是一个谈加入怪物:https://www.youtube.com/watch?v=Y7AdMIuXOgs :) – marktani

+0

谢谢您为您的文章。你会选哪一个?一方面,我有脸书和1800星级图书馆批量一些请求,但另一方面,我有一个图书馆批量一切到1请求。 – itaied

+0

我们在Graphcool的Dataloader上做了很好的体验,当时我们使用graphql-js运行一个节点GraphQL后端。无法分享加入怪物的任何经验。我认为它们在功能和用法上略有差异,所以你需要测试它我猜:) – marktani

0

对于任何使用.NET和GraphQL for .NET包的人,我已经制作了一个将GraphQL Query转换为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; 
      } 
     } 
    } 
} 

假设我们有以下查询:

var include = context.GetIncludeString(); 

产生以下字符串:

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

我们可以在现场的 “决心” 的方法创建一个变量

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

和它传递到实体Framwork:

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