Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(graphql): Introducing Nested Filtering for Dgraph GraphQL API with Optimized Query Support #9100

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions graphql/e2e/auth/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -974,3 +974,43 @@ type Home @auth(
favouriteMember: HomeMember
}
# union testing - end

"""
This types are used to validate nested filting.
"""
type Nested_X @auth(
query: { rule: """
query {
queryNested_X(filter: {y: {s: {eq: "y"}}}){
__typename
}
}
"""}
) {
b: Boolean @search
y: Nested_Y @hasInverse(field: x) @search
}

type Nested_Y implements Nested_Z @auth(
query: {
or: [
{ rule: "{ $rbac: { eq: \"positive\" } }" }
{ and: [
{ rule: "{ $rbac: { eq: \"uncertain\" } }" }
{ rule: """
query {
queryNested_Y(filter: {x: {b: true}}){
__typename
}
}
"""
}
]
}]
}) {
s: String @search(by: [hash])
}

interface Nested_Z {
x: Nested_X @search
}
27 changes: 27 additions & 0 deletions graphql/resolve/auth_delete_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -880,3 +880,30 @@
}
}

- name: "Delete selection by nested filter"
gqlquery: |
mutation {
deleteNested_X(filter: {y: {s: {eq: "-"}}}) {
numUids
}
}
variables:
jwtvar:
dgmutations:
- deletejson: |
[{
"uid": "uid(x)"
},{
"Nested_Z.x": {"uid": "uid(x)"},
"uid": "uid(Nested_Y_3)"
}]
dgquery: |-
query {
x as deleteNested_X(func: type(Nested_X)) @filter((uid(deleteNested_X_y))) {
uid
Nested_Y_3 as Nested_X.y
}
var(func: type(Nested_Y)) @filter(eq(Nested_Y.s, "-")) {
deleteNested_X_y as Nested_Z.x
}
}
109 changes: 109 additions & 0 deletions graphql/resolve/auth_query_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2148,3 +2148,112 @@
Person.id : uid
}
}

-
name: "Query positive auth rules with nested filter"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you write one test around auth also? Where we have a auth filter on the query, so that we can make sure no uids are returned that shouldn't be

gqlquery: |
query{
queryNested_X(filter: {y: {s: {eq: "-"}}}) {
b
y { s }
}
}
jwtvar:
rbac: positive
dgquery: |-
query {
queryNested_X(func: uid(Nested_XRoot)) {
Nested_X.b : Nested_X.b
Nested_X.y : Nested_X.y @filter(uid(Nested_Y_1)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the filter appearing here? I don't think it's required? I am guessing that your intention is that If someone has a filter on root query, we should propagate that here as well? The problem is, what if I want to do a nested query where y.s has some value, but i want to see all the order values for that y as well. Lets just make it a filter at the root level for now, and then we can add a new api for y { s }. Something like y @filter(s: eq()) {s}

Nested_Y.s : Nested_Y.s
dgraph.uid : uid
}
dgraph.uid : uid
}
var(func: type(Nested_Y)) @filter(eq(Nested_Y.s, "-")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am guessing you have made the change from the RFC(requirement for @search), so that you don't have to use @cascade?

queryNested_X_y as Nested_Z.x
}
Nested_XRoot as var(func: uid(Nested_X_3)) @filter(uid(Nested_X_Auth4))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we combine all of these queries? We have a lot of optimizations for multiple filters together.

Nested_X_3 as var(func: type(Nested_X)) @filter((uid(queryNested_X_y)))
Nested_X_Auth4 as var(func: uid(Nested_X_3)) @filter((uid(Nested_X_Auth4_y))) @cascade
var(func: type(Nested_Y)) @filter(eq(Nested_Y.s, "y")) {
Nested_X_Auth4_y as Nested_Z.x
}
var(func: uid(Nested_XRoot)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This query seems unnecessary? We are just repeating the queryNested_X

Nested_Y_2 as Nested_X.y
}
Nested_Y_1 as var(func: uid(Nested_Y_2))
}

-
name: "Query negative auth rules with nested filter"
gqlquery: |
query{
queryNested_X(filter: {y: {s: {eq: "-"}}}) {
b
y { s }
}
}
jwtvar:
rbac: negative
dgquery: |-
query {
queryNested_X(func: uid(Nested_XRoot)) {
Nested_X.b : Nested_X.b
dgraph.uid : uid
}
queryNested_X_y as var()
Nested_XRoot as var(func: uid(Nested_X_3)) @filter(uid(Nested_X_Auth4))
Nested_X_3 as var(func: type(Nested_X)) @filter((uid(queryNested_X_y)))
Nested_X_Auth4 as var(func: uid(Nested_X_3)) @filter((uid(Nested_X_Auth4_y))) @cascade
var(func: type(Nested_Y)) @filter(eq(Nested_Y.s, "y")) {
Nested_X_Auth4_y as Nested_Z.x
}
}

-
name: "Query uncertain auth rules with nested filter"
gqlquery: |
query{
queryNested_X(filter: {y: {s: {eq: "-"}}}) {
b
y { s }
}
}
jwtvar:
rbac: uncertain
dgquery: |-
query {
queryNested_X(func: uid(Nested_XRoot)) {
Nested_X.b : Nested_X.b
Nested_X.y : Nested_X.y @filter(uid(Nested_Y_3)) {
Nested_Y.s : Nested_Y.s
dgraph.uid : uid
}
dgraph.uid : uid
}
var(func: uid(queryNested_X_yRoot)) {
queryNested_X_y as Nested_Z.x
}
queryNested_X_yRoot as var(func: uid(Nested_Y_1)) @filter(uid(Nested_Y_Auth2))
Nested_Y_1 as var(func: type(Nested_Y)) @filter(eq(Nested_Y.s, "-"))
Nested_Y_Auth2 as var(func: uid(Nested_Y_1)) @filter((uid(Nested_Y_Auth2_x))) @cascade
var(func: type(Nested_X)) @filter(eq(Nested_X.b, true)) {
Nested_Y_Auth2_x as Nested_X.y
}
Nested_XRoot as var(func: uid(Nested_X_6)) @filter(uid(Nested_X_Auth7))
Nested_X_6 as var(func: type(Nested_X)) @filter((uid(queryNested_X_y)))
Nested_X_Auth7 as var(func: uid(Nested_X_6)) @filter((uid(Nested_X_Auth7_y))) @cascade
var(func: type(Nested_Y)) @filter(eq(Nested_Y.s, "y")) {
Nested_X_Auth7_y as Nested_Z.x
}
var(func: uid(Nested_XRoot)) {
Nested_Y_4 as Nested_X.y
}
Nested_Y_3 as var(func: uid(Nested_Y_4)) @filter(uid(Nested_Y_Auth5))
Nested_Y_Auth5 as var(func: uid(Nested_Y_4)) @filter((uid(Nested_Y_Auth5_x))) @cascade
var(func: type(Nested_X)) @filter(eq(Nested_X.b, true)) {
Nested_Y_Auth5_x as Nested_X.y
}
}

9 changes: 5 additions & 4 deletions graphql/resolve/mutation_rewriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -807,7 +807,7 @@ func (arw *AddRewriter) FromMutationResult(
return nil, errs
}
// No errors are thrown while rewriting queries by Ids.
return rewriteAsQueryByIds(mutation.QueryField(), uids, authRw), nil
return rewriteAsQueryByIds(mutation.QueryField(), uids, authRw, mutation.Alias()), nil
}

// FromMutationResult rewrites the query part of a GraphQL update mutation into a Dgraph query.
Expand Down Expand Up @@ -843,7 +843,7 @@ func (urw *UpdateRewriter) FromMutationResult(
parentVarName: mutation.MutatedType().Name() + "Root",
}
authRw.hasAuthRules = hasAuthRules(mutation.QueryField(), authRw)
return rewriteAsQueryByIds(mutation.QueryField(), uids, authRw), nil
return rewriteAsQueryByIds(mutation.QueryField(), uids, authRw, mutation.Alias()), nil
}

func (arw *AddRewriter) MutatedRootUIDs(
Expand Down Expand Up @@ -996,7 +996,8 @@ func RewriteUpsertQueryFromMutation(
addTypeFunc(dgQuery[0], m.MutatedType().DgraphName())
}

_ = addFilter(dgQuery[0], m.MutatedType(), filter)
_, varQry := addFilter(dgQuery[0], m.MutatedType(), filter, authRw, m.Alias())
dgQuery = append(dgQuery, varQry...)
} else {
// It means this is called from upsert with Add mutation.
// nodeID will be uid of the node to be upserted. We add UID func
Expand Down Expand Up @@ -1113,7 +1114,7 @@ func (drw *deleteRewriter) Rewrite(
}

// these queries are responsible for querying the queryField
queryFieldQry := rewriteAsQuery(queryField, queryAuthRw)
queryFieldQry := rewriteAsQuery(queryField, queryAuthRw, MutationQueryVar)

// we don't want the `x` query to show up in GraphQL JSON response while querying the query
// field. So, need to make it `var` query and remove any children from it as there can be
Expand Down
1 change: 0 additions & 1 deletion graphql/resolve/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ func (qr *queryResolver) rewriteAndExecute(ctx context.Context, query schema.Que
query.ResponseName()))
}
qry := dgraph.AsString(dgQuery)

queryTimer := newtimer(ctx, &dgraphQueryDuration.OffsetDuration)
queryTimer.Start()
resp, err := qr.executor.Execute(ctx, &dgoapi.Request{Query: qry, ReadOnly: true}, query)
Expand Down
Loading
Loading