GraphQL - how to filter a hierarchy? ("customers who ordered gizmos last month")
Suppose the type hierarchy Customer -(hasMany)-> Orders -(hasMany)-> OrderLines
Something like that:
Customer { Name Orders [ { OrderId Date OrderLines [ { ItemCount ItemName } ] } ] } I want to query for all of this tree and filter properties at any level of the tree.
For example: Get all customers who ordered "gizmos".
This is what I tried: at each level of the hierarchy, I specify additional arguments that will filter based on the properties available at this level:
Customer (Name) { Name Orders (OrderId, Date) [ { OrderId Date OrderLines (ItemCount, ItemName) [ { ItemCount ItemName } ] } ] } GraphQL requires that I determine how to allow each type in the hierarchy, so when resolving, I filter based on the arguments in the query.
But what if I only point the filter at a deep level? e.g. ItemName : 'gizmo'
Assuming that there is only one order line in the system containing a gizmo, I would expect to get this answer:
[{ Name: "cust12", Orders [{ OrderId: "ade32f", OrderLines: [{ ItemCount: 50000, //customer really likes gizmos ItemName: "gizmo" }] }] }] But in fact, I get all customers (there is no filter there), all their orders (without filter) and all , mostly empty (elements inside are filtered).
[{ Name: "cust12", Orders [ { OrderId: "aaaaaa", OrderLines: [ ] }, { OrderId: "ade32f", OrderLines: [{ ItemCount: 50000, ItemName: "gizmo" }] }, { OrderId: "bbbbbb", OrderLines: [ ] }, { OrderId: "cccccc", OrderLines: [ ] } ] }, { Name: "cust345", Orders [ { OrderId: "eeeeee", OrderLines: [ ] }, { OrderId: "ffffff", OrderLines: [ ] } ] }] GraphQL calls the converters from top to bottom: - receive all (filtered) customers - for each of them receive all (filtered) orders - for each of them receive all (filtered) order lines
Due to the downward nature of the call to resolvers, I get a lot more data than I expected.
How do I approach this?
Communication filters
In fact, this is a more complex topic than it seems at first glance. The problem is that the current filter condition expresses
get all customers but include only items named "gizmo"
but you really want
get all customers associated with at least one item named "gizmo"
get all customers associated with at least one item named "gizmo"
An elegant solution to this problem is to add relationship filters to the schema. In your case, it might look like this:
query { Customer(filter: { orders_some: { orderLines_some: { item: { itemName: "gizmo" } } } }) { Name Orders { OrderId Date OrderLines { ItemCount ItemName } } } } Using
orders_some: { orderLines_some: { item: { itemName: "gizmo" } } } we get only those customers who are indirectly associated with the item with the name "gizmo", what exactly we wanted.
Two more examples:
get all customers who are not associated with any item named "gizmo"
query { Customer(filter: { orders_none: { orderLines_some: { item: { itemName: "gizmo" } } } }) { Name Orders { OrderId Date OrderLines { ItemCount ItemName } } } } get all customers where all their orders contain some order line with an item named "gizmo"
query { Customer(filter: { orders_every: { orderLines_some: { item: { itemName: "gizmo" } } } }) { Name Orders { OrderId Date OrderLines { ItemCount ItemName } } } } Relationship filters every , some and none are an integral part of the Graphcool APIs - you can read more here .