Motivation
Currently, when we define any custom DQL query in the GraphQL schema, we have to explicitly define the fields. This results in over-fetching the data from the Dgraph if the selection set of the GraphQL query is a subset of the DQL query. For example for the given schema:-
type Tweets {
id: ID!
text: String! @search(by: [fulltext])
author: User
timestamp: DateTime! @search
}
type User {
screen_name: String! @id
followers: Int @search
tweets: [Tweets] @hasInverse(field: author)
}
type Query {
queryTweetsSortedByAuthorFollowers(search: String!): [Tweets] @custom(dql: """
query q($search: string) {
var(func: type(Tweets)) @filter(anyoftext(Tweets.text, $search)) {
Tweets.author {
followers as User.followers
}
authorFollowerCount as sum(val(followers))
}
queryTweetsSortedByAuthorFollowers(func: uid(authorFollowerCount), orderdesc: val(authorFollowerCount)) {
id: uid
text: Tweets.text
author: Tweets.author {
screen_name: User.screen_name
followers: User.followers
}
timestamp: Tweets.timestamp
}
}
""")
The custom DQL query queryTweetsSortedByAuthFollowers
contains id
, text
, author
and timestamp
in the selectionset.
Now Suppose if we want to do the below GraphQL query:-
query {
queryTweetsSortedByAuthorFollowers{
id
text
timestamp
}
}
Note that in this query we didn’t query for Author
but the DQL query will also fetch the Author
from the Dgraph which should result in certain performance degradation.
We propose to change this behavior so the Dgraph only executes the query with the selection fields provided in the GraphQL query.
User Impact
- It will optimize the custom DQL queries and will be more usable to the community.
Implementation
Currently, we have come up with two possible implementations, which we discuss here.
1- Parse custom DQL query and rewrite on basis of GraphQL selection set.
In this approach, we first parse the DQL query at the time of execution and then rewrite the new DQL query on the basis of the selection set of the GraphQL query. In the rewriting phase, we perform the following steps:-
- Remove the predicates from the custom query if they are not present in the GraphQL selection set.
- Include those fields of the GraphQL query which are not declared in the DQL Query.
2- Use _defer_ keyword and then pick the selection set from GraphQL.
In this case, we will introduce _defer_
keyword to be used in the selection set of the query instead of mentioning any predicate. For example, the above custom query will be like:-
queryTweetsSortedByAuthorFollowers(search: String!): [Tweets] @custom(dql: """
query q($search: string) {
var(func: type(Tweets)) @filter(anyoftext(Tweets.text, $search)) {
Tweets.author {
followers as User.followers
}
authorFollowerCount as sum(val(followers))
}
queryTweetsSortedByAuthorFollowers(func: uid(authorFollowerCount), orderdesc: val(authorFollowerCount)) {
_defer_
}
}
""")
And GraphQL does the rewriting based on the selection set of the GraphQL query.
Challenges
1- To construct the valid DQL query as there may be some unused variables in the DQL query after we remove some of the predicates from the query.
Any other approach to the implementation and comments are highly welcomed.