2016-12-03 94 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)); 
}