What’s missing from Dgraph in the GraphQL API
These are things that can be done in ±, but don’t have an equiv in our GraphQL.
Order is the order you find them by scrolling through the docs.
I’ve annotated with e : easy; h : hard; i : impossible; ? : needs thought. Then I expand below on what’s required.
To tackle each one, we should take it, put up an RFC, and work on it as an individual feature.
Query:
- Languages (?)
- Fuzzy Matching (e)
- uid_in (e)
- has (e)
- geo type + geo queries (near, within, contains, intersects) (e)
- pagination after (e) - but there’s other standard GraphQL pagination to look at
- count (h)
- query variables + var blocks (h)
- value variables + math (h)
- aggregation : min, max, sum & avg (h)
- GroupBy (h/i)
- expand (i)
- normalize (i) - because the result is in a different format to the query
- ignorereflex (e)
- password type (e)
- facets (?)
- K-Shortest Path Queries (?)
- Recurse Query (h/i) - result and query are different formats, might be possible to ask the query in the right format though.
Mutation :
- RDF support (?)
- upsert block / conditional upserts (???)
How might we put each of those in to GraphQL
Laguages (?)
Query side, we’d need a GraphQL way to write name@en:pl:.
, so something like name @lang(qry: [en, pl, .])
That should be ok. Mutations, I’m not sure about. Don’t think "rating@en": "tastes good"
is GraphQL compliant.
Needs checking what’s ok for directives in input and variables.
Fuzzy Matching (e)
Easy, just means building a filter like
name: { match: { str: "hi", dist: 10} }
uid_in (e)
Easy, just add a filter
friends: { uid_in: 0x123 }
Would match with other GraphQL filters better if it were
friends: { ids: [ ... ] }
not sure how that maps down to Dgraph though.
has (e)
Easy, just add a filter like
filter: { has: friends }
geo type + geo queries (near, within, contains, intersects) (e)
Easy, the input should already be a JSON that’s compliant, just needs types for it and filters in query for near, within, contains and intersects.
Something like homeTown: { near: { point: [long, lat], distance: N}}
pagination after (e)
Looks easy, but not sure if this is the right sort of pagination to to include.
Sometimes the Relay-style Connections for pagination are used - we’ve been asked about that already: Make Dgraph work with standard GraphQL · Issue #933 · dgraph-io/dgraph · GitHub
So rather than just supporting after, it might be better to look at what pagination is popular/most-useful in GraphQL and support that.
Similarly the node interface could be a useful thing to add. Something like:
node(id: ID!) : Node
and
nodes(ids: [ID!]!) : Node
That might also open the door to adding types to objects - at the moment an object can only get one type. We could use mutations on the node interface to add extra types. So a mutation like
updateNodes(ids: [ID!]!, ...nodePatch...)
Where the ...nodePatch...
lets you do things that apply to all nodes - add types, remove types, etc.
count + query variables + var blocks + value variables + math + aggregation : min, max, sum & avg (h + ?)
These are all related and hard.
For me, this is the most interesting and most general problem. If we have a good answer to this, we have a good answer to federation and joins in GraphQL ← that would be applicable to not just GraphQL on Dgraph, but federating GraphQL services in general.
I started writing about it here sometime ago. I think this is what we should invest in.
GroupBy (h/i)
This is all about variables and aggregation, so it requires a good answer to variables, blocks and aggregations.
expand (i)
This isn’t really possible in GraphQL. Pretty sure Lee had some reasons to reject *
queries. I think it’s to do with the client knowing what it wants, and asking for exactly it’s data requirements.
Even if we had some way of allowing an expand
inside a query block, all other GraphQL tools would show it as an error because it’s not part of the schema.
normalize (i)
Not sure this is possible. The whole point of normalize is to produce a result that’s in a different format to the original. But that contradicts GraphQL, that must give the result in the structure of the underlying types.
There may be some way we could allow the result type if we allowed some sort of mapping type that we could show was a combination of another set of types and then we could have a normalize query that takes a query/filter and produces this result like normalize … but I still don’t think that would be ok because GraphQL should have the query and result structure the same.
ignorereflex (e)
Should be easy - just a matter of allowing the directive and adding to the rewriting.
password type (e)
Easy - GraphSchema supported this. The idea was that the password mapped to the Dgraph password type, and was present in the input when adding an object, but wasn’t built into the objects that could be queried. On top of that you can support way for client-side apps to have users and logins.
facets (?)
No idea - needs lots of thought!
K-Shortest Path Queries (?)
Not sure - GraphQL requires that the query and response are in the same format. Whereas these give a response in a different structure. We might be able to modify the GraphQL version so it somehow says it’s a shortest path query, asks in the right structure and returns path
and _path_
in a combined result.
Recurse Query (h/i)
Like shortest path queries, this has a query in a different structure to the response. We’d need to have a way to write it in the same structure as the result. Should be ok, the Dgraph version is all the edges, but doesn’t have the type information to know what the edges should be attach to.
RDF support (?)
Not 100% sure, if this is possible or desirable. The input to GraphQL is meant to be JSON - e.g. the JSON variables that accompany a query or mutation. It may be possible to have RDF variables in a spec compliant way, but no other tools support that, so it’s not something that would work in any other dev tools (we could build something in to Ratel).
It’s also probably not something that people would be building a GraphQL App around.
Technically actually doing it should be a matter of type checking and non-null checking the input.
upsert block / conditional upserts (??)
No idea.
This would take quite some thought and work - there’s just query and mutation blocks in GraphQL. It’s not clear how to do an upsert block.