From b946ec3f3446c508d811551a6ac1b607130f41da Mon Sep 17 00:00:00 2001 From: Abhimanyu Singh Gaur <12651351+abhimanyusinghgaur@users.noreply.github.com> Date: Thu, 27 Aug 2020 14:11:17 +0530 Subject: [PATCH 1/2] fix(GraphQL): Link xids properly if there are duplicate xids within the same add request. (#6265) Fixes GRAPHQL-641 See https://discuss.dgraph.io/t/residual-issue-linking-grandchild-in-mutation-with-custom-ids/9613 for the issue details. If the same nested xid was used as a leaf node within an addType mutation, it was only linked the first time that it was used and not subsequently for later objects. This change fixes that by adding the linkage mutations and only removing the mutation which was creating the object with the xid again. (cherry picked from commit 12b27160fa0c8bc3ad668a24ef557c6bdb6cd9c0) # Conflicts: # graphql/e2e/directives/dgraph_directives_test.go # graphql/e2e/normal/normal_test.go # graphql/resolve/add_mutation_test.yaml --- graphql/e2e/common/common.go | 1 + graphql/e2e/common/mutation.go | 254 +++++++ .../e2e/directives/dgraph_directives_test.go | 397 +---------- graphql/e2e/directives/schema.graphql | 10 + graphql/e2e/directives/schema_response.json | 658 ++++++++++++++++++ graphql/e2e/normal/normal_test.go | 397 +---------- graphql/e2e/normal/schema.graphql | 10 + graphql/e2e/normal/schema_response.json | 658 ++++++++++++++++++ graphql/resolve/add_mutation_test.yaml | 76 +- graphql/resolve/mutation_rewriter.go | 21 +- 10 files changed, 1685 insertions(+), 797 deletions(-) create mode 100644 graphql/e2e/directives/schema_response.json create mode 100644 graphql/e2e/normal/schema_response.json diff --git a/graphql/e2e/common/common.go b/graphql/e2e/common/common.go index a9ee1e79261..7fa2c5c14f0 100644 --- a/graphql/e2e/common/common.go +++ b/graphql/e2e/common/common.go @@ -301,6 +301,7 @@ func RunAll(t *testing.T) { t.Run("deep mutations", deepMutations) t.Run("add multiple mutations", testMultipleMutations) t.Run("deep XID mutations", deepXIDMutations) + t.Run("three level xid", testThreeLevelXID) t.Run("error in multiple mutations", addMultipleMutationWithOneError) t.Run("dgraph directive with reverse edge adds data correctly", addMutationWithReverseDgraphEdge) diff --git a/graphql/e2e/common/mutation.go b/graphql/e2e/common/mutation.go index 7c6f1b7e942..24ed5f04bc5 100644 --- a/graphql/e2e/common/mutation.go +++ b/graphql/e2e/common/mutation.go @@ -567,6 +567,260 @@ func deepXIDMutations(t *testing.T) { deepXIDTest(t, postExecutor) } +func addComments(t *testing.T, ids []string) { + input := []map[string]interface{}{} + for _, id := range ids { + input = append(input, map[string]interface{}{"id": id}) + } + + params := &GraphQLParams{ + Query: `mutation($input: [AddComment1Input!]!) { + addComment1(input: $input) { + comment1 { + id + } + } + }`, + Variables: map[string]interface{}{ + "input": input, + }, + } + + gqlResponse := postExecutor(t, graphqlURL, params) + RequireNoGQLErrors(t, gqlResponse) +} + +func testThreeLevelXID(t *testing.T) { + + input := `{ + "input": [ + { + "id": "post1", + "comments": [ + { + "id": "comment1", + "replies": [ + { + "id": "reply1" + } + ] + } + ] + }, + { + "id": "post2", + "comments": [ + { + "id": "comment2", + "replies": [ + { + "id": "reply1" + } + ] + } + ] + } + ] + }` + + qinput := make(map[string]interface{}) + err := json.Unmarshal([]byte(input), &qinput) + require.NoError(t, err) + + addPostParams := &GraphQLParams{ + Query: ` mutation($input: [AddPost1Input!]!) { + addPost1(input: $input) { + post1(order: { asc: id }) { + id + comments { + id + replies { + id + } + } + } + } + }`, + Variables: qinput, + } + + bothCommentsLinkedToReply := `{ + "addPost1": { + "post1": [ + { + "id": "post1", + "comments": [ + { + "id": "comment1", + "replies": [ + { + "id": "reply1" + } + ] + } + ] + }, + { + "id": "post2", + "comments": [ + { + "id": "comment2", + "replies": [ + { + "id": "reply1" + } + ] + } + ] + } + ] + } + }` + + firstCommentLinkedToReply := `{ + "addPost1": { + "post1": [ + { + "id": "post1", + "comments": [ + { + "id": "comment1", + "replies": [ + { + "id": "reply1" + } + ] + } + ] + }, + { + "id": "post2", + "comments": [ + { + "id": "comment2", + "replies": [] + } + ] + } + ] + } + }` + + secondCommentLinkedToReply := `{ + "addPost1": { + "post1": [ + { + "id": "post1", + "comments": [ + { + "id": "comment1", + "replies": [] + } + ] + }, + { + "id": "post2", + "comments": [ + { + "id": "comment2", + "replies": [ + { + "id": "reply1" + } + ] + } + ] + } + ] + } + }` + + noCommentsLinkedToReply := `{ + "addPost1": { + "post1": [ + { + "id": "post1", + "comments": [ + { + "id": "comment1", + "replies": [] + } + ] + }, + { + "id": "post2", + "comments": [ + { + "id": "comment2", + "replies": [] + } + ] + } + ] + } + }` + + cases := map[string]struct { + Comments []string + Expected string + ExpectedNumDeletedComments int + }{ + "2nd level nodes don't exist but third level does": { + []string{"reply1"}, + bothCommentsLinkedToReply, + 3, + }, + "2nd level and third level nodes don't exist": { + []string{}, + bothCommentsLinkedToReply, + 3, + }, + "2nd level node exists but third level doesn't": { + []string{"comment1", "comment2"}, + noCommentsLinkedToReply, + 2, + }, + "2nd level and third level nodes exist": { + []string{"comment1", "comment2", "reply1"}, + noCommentsLinkedToReply, + 3, + }, + "one 2nd level node exists and third level node exists": { + []string{"comment1", "reply1"}, + secondCommentLinkedToReply, + 3, + }, + "the other 2nd level node exists and third level node exists": { + []string{"comment2", "reply1"}, + firstCommentLinkedToReply, + 3, + }, + "one 2nd level node exists and third level node doesn't exist": { + []string{"comment1"}, + secondCommentLinkedToReply, + 3, + }, + "other 2nd level node exists and third level node doesn't exist": { + []string{"comment2", "reply1"}, + firstCommentLinkedToReply, + 3, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + addComments(t, tc.Comments) + gqlResponse := postExecutor(t, graphqlURL, addPostParams) + RequireNoGQLErrors(t, gqlResponse) + testutil.CompareJSON(t, tc.Expected, string(gqlResponse.Data)) + + deleteGqlType(t, "Post1", map[string]interface{}{}, 2, nil) + deleteGqlType(t, "Comment1", map[string]interface{}{}, tc.ExpectedNumDeletedComments, + nil) + }) + } +} + func deepXIDTest(t *testing.T, executeRequest requestExecutor) { newCountry := &country{ Name: "A Country", diff --git a/graphql/e2e/directives/dgraph_directives_test.go b/graphql/e2e/directives/dgraph_directives_test.go index a2237913066..82a15bd5eec 100644 --- a/graphql/e2e/directives/dgraph_directives_test.go +++ b/graphql/e2e/directives/dgraph_directives_test.go @@ -23,6 +23,7 @@ import ( "github.com/dgraph-io/dgraph/graphql/e2e/common" "github.com/pkg/errors" + "github.com/stretchr/testify/require" ) func TestRunAll_WithDgraphDirectives(t *testing.T) { @@ -32,401 +33,11 @@ func TestRunAll_WithDgraphDirectives(t *testing.T) { } func TestSchema_WithDgraphDirectives(t *testing.T) { - expectedDgraphSchema := ` -{ - "schema": [{ - "predicate": "Category.name", - "type": "string" - }, { - "predicate": "Category.posts", - "type": "uid", - "list": true - }, { - "predicate": "Country.name", - "type": "string", - "index": true, - "tokenizer": ["trigram", "hash"] - }, { - "predicate": "Human.starships", - "type": "uid", - "list": true - }, { - "predicate": "Movie.name", - "type": "string" - }, { - "predicate": "MovieDirector.name", - "type": "string" - }, { - "predicate": "State.capital", - "type": "string" - }, { - "predicate": "State.name", - "type": "string" - }, { - "predicate": "State.xcode", - "type": "string", - "index": true, - "tokenizer": ["trigram", "hash"], - "upsert": true - }, { - "predicate": "User.name", - "type": "string", - "index": true, - "tokenizer": ["hash"], - "upsert": true - }, { - "predicate": "appears_in", - "type": "string", - "index": true, - "tokenizer": ["hash"], - "list": true - }, { - "predicate": "credits", - "type": "float" - }, { - "predicate": "test.dgraph.author.country", - "type": "uid" - }, { - "predicate": "test.dgraph.author.dob", - "type": "datetime", - "index": true, - "tokenizer": ["year"] - }, { - "predicate": "test.dgraph.author.name", - "type": "string", - "index": true, - "tokenizer": ["hash", "trigram"] - }, { - "predicate": "test.dgraph.author.posts", - "type": "uid", - "list": true - }, { - "predicate": "test.dgraph.author.reputation", - "type": "float", - "index": true, - "tokenizer": ["float"] - }, { - "predicate": "test.dgraph.employee.en.ename", - "type": "string" - }, { - "predicate": "dgraph.graphql.schema", - "type": "string" - }, { - "predicate": "dgraph.graphql.xid", - "type": "string", - "index": true, - "tokenizer": ["exact"], - "upsert": true - }, { - "predicate": "test.dgraph.topic", - "type": "string", - "index": true, - "tokenizer": ["exact"] - }, { - "predicate": "dgraph.type", - "type": "string", - "index": true, - "tokenizer": ["exact"], - "list": true - }, { - "predicate": "directed.movies", - "type": "uid", - "reverse": true, - "list": true - }, { - "predicate": "hasStates", - "type": "uid", - "list": true - }, { - "predicate": "inCountry", - "type": "uid" - }, { - "predicate": "is_published", - "type": "bool", - "index": true, - "tokenizer": ["bool"] - }, { - "predicate": "myPost.category", - "type": "uid" - }, { - "predicate": "myPost.numLikes", - "type": "int", - "index": true, - "tokenizer": ["int"] - }, { - "predicate": "myPost.postType", - "type": "string", - "index": true, - "tokenizer": ["hash", "trigram"] - }, { - "predicate": "myPost.tags", - "type": "string", - "index": true, - "tokenizer": ["exact"], - "list": true - }, { - "predicate": "myPost.title", - "type": "string", - "index": true, - "tokenizer": ["term", "fulltext"] - }, { - "predicate": "performance.character.name", - "type": "string", - "index": true, - "tokenizer": ["exact"] - }, { - "predicate": "post.author", - "type": "uid" - }, { - "predicate": "pwd", - "type": "password" - }, { - "predicate": "roboDroid.primaryFunction", - "type": "string" - }, { - "predicate": "star.ship.length", - "type": "float" - }, { - "predicate": "star.ship.name", - "type": "string", - "index": true, - "tokenizer": ["term"] - }, { - "predicate": "text", - "type": "string", - "index": true, - "tokenizer": ["fulltext"] - }, { - "predicate": "People.xid", - "type": "string", - "index": true, - "tokenizer": ["hash"], - "upsert": true - }, { - "predicate": "post", - "type": "string" - },{ - "predicate": "职业", - "type": "string" - }, { - "predicate": "People.name", - "type": "string" - }, { - "predicate": "Teacher.subject", - "type": "string" - }, { - "predicate": "Teacher.teaches", - "type": "uid", - "list": true - }, { - "predicate": "Student.taughtBy", - "type": "uid", - "list": true - }, { - "predicate": "Thing.name", - "type": "string" - }, { - "predicate": "ThingOne.color", - "type": "string" - }, { - "predicate": "ThingOne.usedBy", - "type": "string" - }, { - "predicate": "ThingTwo.color", - "type": "string" - }, { - "predicate": "ThingTwo.owner", - "type": "string" - }], - "types": [{ - "fields": [{ - "name": "Category.name" - }, { - "name": "Category.posts" - }], - "name": "Category" - }, { - "fields": [{ - "name": "Country.name" - }, { - "name": "hasStates" - }], - "name": "Country" - }, { - "fields": [{ - "name": "test.dgraph.employee.en.ename" - }, { - "name": "performance.character.name" - }, { - "name": "appears_in" - }, { - "name": "Human.starships" - }, { - "name": "credits" - }], - "name": "Human" - }, { - "fields": [{ - "name": "Movie.name" - }], - "name": "Movie" - }, { - "fields": [{ - "name": "MovieDirector.name" - }, { - "name": "directed.movies" - }], - "name": "MovieDirector" - }, { - "fields": [{ - "name": "State.xcode" - }, { - "name": "State.name" - }, { - "name": "State.capital" - }, { - "name": "inCountry" - }], - "name": "State" - }, { - "fields": [{ - "name": "User.name" - }, { - "name": "pwd" - }], - "name": "User" - }, { - "fields": [{ - "name": "test.dgraph.author.name" - }, { - "name": "test.dgraph.author.dob" - }, { - "name": "test.dgraph.author.reputation" - }, { - "name": "test.dgraph.author.country" - }, { - "name": "test.dgraph.author.posts" - }], - "name": "test.dgraph.author" - }, { - "fields": [{ - "name": "test.dgraph.employee.en.ename" - }], - "name": "test.dgraph.employee.en" - }, { - "fields": [{ - "name": "dgraph.graphql.schema" - }, { - "name": "dgraph.graphql.xid" - }], - "name": "dgraph.graphql" - }, { - "fields": [{ - "name": "post" - }, { - "name": "职业" - }], - "name": "Message" - }, { - "fields": [{ - "name": "myPost.title" - }, { - "name": "text" - }, { - "name": "myPost.tags" - }, { - "name": "test.dgraph.topic" - }, { - "name": "myPost.numLikes" - }, { - "name": "is_published" - }, { - "name": "myPost.postType" - }, { - "name": "post.author" - }, { - "name": "myPost.category" - }], - "name": "myPost" - }, { - "fields": [{ - "name": "performance.character.name" - }, { - "name": "appears_in" - }], - "name": "performance.character" - }, { - "fields": [{ - "name": "performance.character.name" - }, { - "name": "appears_in" - }, { - "name": "roboDroid.primaryFunction" - }], - "name": "roboDroid" - }, { - "fields": [{ - "name": "star.ship.name" - }, { - "name": "star.ship.length" - }], - "name": "star.ship" - }, { - "fields": [{ - "name": "People.xid" - }, { - "name": "People.name" - }], - "name": "People" - }, { - "fields": [{ - "name": "People.xid" - }, { - "name": "People.name" - }, { - "name": "Teacher.subject" - }, { - "name": "Teacher.teaches" - }], - "name": "Teacher" - }, { - "fields": [{ - "name": "People.xid" - }, { - "name": "People.name" - }, { - "name": "Student.taughtBy" - }], - "name": "Student" - }, { - "fields": [{ - "name": "Thing.name" - }], - "name": "Thing" - }, { - "fields": [{ - "name": "Thing.name" - }, { - "name": "ThingOne.color" - }, { - "name": "ThingOne.usedBy" - }], - "name": "ThingOne" - }, { - "fields": [{ - "name": "Thing.name" - }, { - "name": "ThingTwo.color" - }, { - "name": "ThingTwo.owner" - }], - "name": "ThingTwo" - }] -} - ` + b, err := ioutil.ReadFile("schema_response.json") + require.NoError(t, err) t.Run("graphql schema", func(t *testing.T) { - common.SchemaTest(t, expectedDgraphSchema) + common.SchemaTest(t, string(b)) }) } diff --git a/graphql/e2e/directives/schema.graphql b/graphql/e2e/directives/schema.graphql index 223576a096b..eff992d8fb4 100644 --- a/graphql/e2e/directives/schema.graphql +++ b/graphql/e2e/directives/schema.graphql @@ -162,3 +162,13 @@ type ThingTwo implements Thing { color: String owner: String } + +type Post1 { + id: String! @id + comments: [Comment1] +} + +type Comment1 { + id: String! @id + replies: [Comment1] +} \ No newline at end of file diff --git a/graphql/e2e/directives/schema_response.json b/graphql/e2e/directives/schema_response.json new file mode 100644 index 00000000000..06442d0df1d --- /dev/null +++ b/graphql/e2e/directives/schema_response.json @@ -0,0 +1,658 @@ +{ + "schema": [ + { + "index": true, + "list": true, + "predicate": "dgraph.cors", + "tokenizer": [ + "exact" + ], + "type": "string", + "upsert": true + }, + { + "predicate": "Category.name", + "type": "string" + }, + { + "predicate": "Category.posts", + "type": "uid", + "list": true + }, + { + "predicate": "Comment1.id", + "type": "string", + "index": true, + "tokenizer": [ + "hash" + ], + "upsert": true + }, + { + "predicate": "Comment1.replies", + "type": "uid", + "list": true + }, + { + "predicate": "Country.name", + "type": "string", + "index": true, + "tokenizer": [ + "hash", + "trigram" + ] + }, + { + "predicate": "Human.starships", + "type": "uid", + "list": true + }, + { + "predicate": "Movie.name", + "type": "string" + }, + { + "predicate": "MovieDirector.name", + "type": "string" + }, + { + "predicate": "People.name", + "type": "string" + }, + { + "predicate": "People.xid", + "type": "string", + "index": true, + "tokenizer": [ + "hash" + ], + "upsert": true + }, + { + "predicate": "Post1.comments", + "type": "uid", + "list": true + }, + { + "predicate": "Post1.id", + "type": "string", + "index": true, + "tokenizer": [ + "hash" + ], + "upsert": true + }, + { + "predicate": "State.capital", + "type": "string" + }, + { + "predicate": "State.name", + "type": "string" + }, + { + "predicate": "State.xcode", + "type": "string", + "index": true, + "tokenizer": [ + "hash", + "trigram" + ], + "upsert": true + }, + { + "predicate": "Student.taughtBy", + "type": "uid", + "list": true + }, + { + "predicate": "Teacher.subject", + "type": "string" + }, + { + "predicate": "Teacher.teaches", + "type": "uid", + "list": true + }, + { + "predicate": "Thing.name", + "type": "string" + }, + { + "predicate": "ThingOne.color", + "type": "string" + }, + { + "predicate": "ThingOne.usedBy", + "type": "string" + }, + { + "predicate": "ThingTwo.color", + "type": "string" + }, + { + "predicate": "ThingTwo.owner", + "type": "string" + }, + { + "predicate": "User.name", + "type": "string", + "index": true, + "tokenizer": [ + "hash" + ], + "upsert": true + }, + { + "predicate": "appears_in", + "type": "string", + "index": true, + "tokenizer": [ + "hash" + ], + "list": true + }, + { + "predicate": "credits", + "type": "float" + }, + { + "predicate": "dgraph.graphql.schema", + "type": "string" + }, + { + "predicate": "dgraph.graphql.xid", + "type": "string", + "index": true, + "tokenizer": [ + "exact" + ], + "upsert": true + }, + { + "predicate": "dgraph.type", + "type": "string", + "index": true, + "tokenizer": [ + "exact" + ], + "list": true + }, + { + "predicate": "directed.movies", + "type": "uid", + "reverse": true, + "list": true + }, + { + "predicate": "hasStates", + "type": "uid", + "list": true + }, + { + "predicate": "inCountry", + "type": "uid" + }, + { + "predicate": "is_published", + "type": "bool", + "index": true, + "tokenizer": [ + "bool" + ] + }, + { + "predicate": "myPost.category", + "type": "uid" + }, + { + "predicate": "myPost.numLikes", + "type": "int", + "index": true, + "tokenizer": [ + "int" + ] + }, + { + "predicate": "myPost.numViews", + "type": "int", + "index": true, + "tokenizer": [ + "int" + ] + }, + { + "predicate": "myPost.postType", + "type": "string", + "index": true, + "tokenizer": [ + "hash", + "trigram" + ] + }, + { + "predicate": "myPost.tags", + "type": "string", + "index": true, + "tokenizer": [ + "exact" + ], + "list": true + }, + { + "predicate": "myPost.title", + "type": "string", + "index": true, + "tokenizer": [ + "fulltext", + "term" + ] + }, + { + "predicate": "performance.character.name", + "type": "string", + "index": true, + "tokenizer": [ + "exact" + ] + }, + { + "predicate": "post", + "type": "string" + }, + { + "predicate": "post.author", + "type": "uid" + }, + { + "predicate": "pwd", + "type": "password" + }, + { + "predicate": "roboDroid.primaryFunction", + "type": "string" + }, + { + "predicate": "star.ship.length", + "type": "float" + }, + { + "predicate": "star.ship.name", + "type": "string", + "index": true, + "tokenizer": [ + "term" + ] + }, + { + "predicate": "test.dgraph.author.country", + "type": "uid" + }, + { + "predicate": "test.dgraph.author.dob", + "type": "datetime", + "index": true, + "tokenizer": [ + "year" + ] + }, + { + "predicate": "test.dgraph.author.name", + "type": "string", + "index": true, + "tokenizer": [ + "hash", + "trigram" + ] + }, + { + "predicate": "test.dgraph.author.posts", + "type": "uid", + "list": true + }, + { + "predicate": "test.dgraph.author.reputation", + "type": "float", + "index": true, + "tokenizer": [ + "float" + ] + }, + { + "predicate": "test.dgraph.employee.en.ename", + "type": "string" + }, + { + "predicate": "test.dgraph.topic", + "type": "string", + "index": true, + "tokenizer": [ + "exact" + ] + }, + { + "predicate": "text", + "type": "string", + "index": true, + "tokenizer": [ + "fulltext" + ] + }, + { + "predicate": "职业", + "type": "string" + } + ], + "types": [ + { + "fields": [ + { + "name": "Category.name" + }, + { + "name": "Category.posts" + } + ], + "name": "Category" + }, + { + "fields": [ + { + "name": "Comment1.id" + }, + { + "name": "Comment1.replies" + } + ], + "name": "Comment1" + }, + { + "fields": [ + { + "name": "Country.name" + }, + { + "name": "hasStates" + } + ], + "name": "Country" + }, + { + "fields": [ + { + "name": "test.dgraph.employee.en.ename" + }, + { + "name": "performance.character.name" + }, + { + "name": "appears_in" + }, + { + "name": "Human.starships" + }, + { + "name": "credits" + } + ], + "name": "Human" + }, + { + "fields": [ + { + "name": "post" + }, + { + "name": "职业" + } + ], + "name": "Message" + }, + { + "fields": [ + { + "name": "Movie.name" + } + ], + "name": "Movie" + }, + { + "fields": [ + { + "name": "MovieDirector.name" + }, + { + "name": "directed.movies" + } + ], + "name": "MovieDirector" + }, + { + "fields": [ + { + "name": "People.xid" + }, + { + "name": "People.name" + } + ], + "name": "People" + }, + { + "fields": [ + { + "name": "Post1.id" + }, + { + "name": "Post1.comments" + } + ], + "name": "Post1" + }, + { + "fields": [ + { + "name": "State.xcode" + }, + { + "name": "State.name" + }, + { + "name": "State.capital" + }, + { + "name": "inCountry" + } + ], + "name": "State" + }, + { + "fields": [ + { + "name": "People.xid" + }, + { + "name": "People.name" + }, + { + "name": "Student.taughtBy" + } + ], + "name": "Student" + }, + { + "fields": [ + { + "name": "People.xid" + }, + { + "name": "People.name" + }, + { + "name": "Teacher.subject" + }, + { + "name": "Teacher.teaches" + } + ], + "name": "Teacher" + }, + { + "fields": [ + { + "name": "Thing.name" + } + ], + "name": "Thing" + }, + { + "fields": [ + { + "name": "Thing.name" + }, + { + "name": "ThingOne.color" + }, + { + "name": "ThingOne.usedBy" + } + ], + "name": "ThingOne" + }, + { + "fields": [ + { + "name": "Thing.name" + }, + { + "name": "ThingTwo.color" + }, + { + "name": "ThingTwo.owner" + } + ], + "name": "ThingTwo" + }, + { + "fields": [ + { + "name": "User.name" + }, + { + "name": "pwd" + } + ], + "name": "User" + }, + { + "fields": [ + { + "name": "dgraph.graphql.schema" + }, + { + "name": "dgraph.graphql.xid" + } + ], + "name": "dgraph.graphql" + }, + { + "fields": [ + { + "name": "myPost.title" + }, + { + "name": "text" + }, + { + "name": "myPost.tags" + }, + { + "name": "test.dgraph.topic" + }, + { + "name": "myPost.numLikes" + }, + { + "name": "myPost.numViews" + }, + { + "name": "is_published" + }, + { + "name": "myPost.postType" + }, + { + "name": "post.author" + }, + { + "name": "myPost.category" + } + ], + "name": "myPost" + }, + { + "fields": [ + { + "name": "performance.character.name" + }, + { + "name": "appears_in" + } + ], + "name": "performance.character" + }, + { + "fields": [ + { + "name": "performance.character.name" + }, + { + "name": "appears_in" + }, + { + "name": "roboDroid.primaryFunction" + } + ], + "name": "roboDroid" + }, + { + "fields": [ + { + "name": "star.ship.name" + }, + { + "name": "star.ship.length" + } + ], + "name": "star.ship" + }, + { + "fields": [ + { + "name": "test.dgraph.author.name" + }, + { + "name": "test.dgraph.author.dob" + }, + { + "name": "test.dgraph.author.reputation" + }, + { + "name": "test.dgraph.author.country" + }, + { + "name": "test.dgraph.author.posts" + } + ], + "name": "test.dgraph.author" + }, + { + "fields": [ + { + "name": "test.dgraph.employee.en.ename" + } + ], + "name": "test.dgraph.employee.en" + } + ] +} \ No newline at end of file diff --git a/graphql/e2e/normal/normal_test.go b/graphql/e2e/normal/normal_test.go index 31b8b5b9865..3e1a487340c 100644 --- a/graphql/e2e/normal/normal_test.go +++ b/graphql/e2e/normal/normal_test.go @@ -23,6 +23,7 @@ import ( "github.com/dgraph-io/dgraph/graphql/e2e/common" "github.com/pkg/errors" + "github.com/stretchr/testify/require" ) func TestRunAll_Normal(t *testing.T) { @@ -30,401 +31,11 @@ func TestRunAll_Normal(t *testing.T) { } func TestSchema_Normal(t *testing.T) { - expectedDgraphSchema := ` - { - "schema": [{ - "predicate": "Author.country", - "type": "uid" - }, { - "predicate": "Author.dob", - "type": "datetime", - "index": true, - "tokenizer": ["year"] - }, { - "predicate": "Author.name", - "type": "string", - "index": true, - "tokenizer": ["hash", "trigram"] - }, { - "predicate": "Author.posts", - "type": "uid", - "list": true - }, { - "predicate": "Author.reputation", - "type": "float", - "index": true, - "tokenizer": ["float"] - }, { - "predicate": "Category.name", - "type": "string" - }, { - "predicate": "Category.posts", - "type": "uid", - "list": true - }, { - "predicate": "Character.appearsIn", - "type": "string", - "index": true, - "tokenizer": ["hash"], - "list": true - }, { - "predicate": "Character.name", - "type": "string", - "index": true, - "tokenizer": ["exact"] - }, { - "predicate": "Country.name", - "type": "string", - "index": true, - "tokenizer": ["trigram", "hash"] - }, { - "predicate": "Country.states", - "type": "uid", - "list": true - }, { - "predicate": "Droid.primaryFunction", - "type": "string" - }, { - "predicate": "Employee.ename", - "type": "string" - }, { - "predicate": "Human.starships", - "type": "uid", - "list": true - }, { - "predicate": "Human.totalCredits", - "type": "float" - }, { - "predicate": "Movie.director", - "type": "uid", - "list": true - }, { - "predicate": "Movie.name", - "type": "string" - }, { - "predicate": "MovieDirector.directed", - "type": "uid", - "list": true - }, { - "predicate": "MovieDirector.name", - "type": "string" - }, { - "predicate": "Post.author", - "type": "uid" - }, { - "predicate": "Post.category", - "type": "uid" - }, { - "predicate": "Post.isPublished", - "type": "bool", - "index": true, - "tokenizer": ["bool"] - }, { - "predicate": "Post.numLikes", - "type": "int", - "index": true, - "tokenizer": ["int"] - }, { - "predicate": "Post.postType", - "type": "string", - "index": true, - "tokenizer": ["hash", "trigram"] - }, { - "predicate": "Post.tags", - "type": "string", - "index": true, - "tokenizer": ["exact"], - "list": true - }, { - "predicate": "Post.text", - "type": "string", - "index": true, - "tokenizer": ["fulltext"] - }, { - "predicate": "Post.title", - "type": "string", - "index": true, - "tokenizer": ["term", "fulltext"] - }, { - "predicate": "Post.topic", - "type": "string", - "index": true, - "tokenizer": ["exact"] - }, { - "predicate": "Starship.length", - "type": "float" - }, { - "predicate": "Starship.name", - "type": "string", - "index": true, - "tokenizer": ["term"] - }, { - "predicate": "State.country", - "type": "uid" - }, { - "predicate": "State.capital", - "type": "string" - }, { - "predicate": "State.name", - "type": "string" - }, { - "predicate": "State.xcode", - "type": "string", - "index": true, - "tokenizer": ["trigram", "hash"], - "upsert": true - }, { - "predicate": "User.name", - "type": "string", - "index": true, - "tokenizer": ["hash"], - "upsert": true - }, { - "predicate": "People.xid", - "type": "string", - "index": true, - "tokenizer": ["hash"], - "upsert": true - }, { - "predicate": "People.name", - "type": "string" - }, { - "predicate": "Teacher.subject", - "type": "string" - }, { - "predicate": "Teacher.teaches", - "type": "uid", - "list": true - }, { - "predicate": "Student.taughtBy", - "type": "uid", - "list": true - }, { - "predicate": "User.password", - "type": "password" - }, { - "predicate": "dgraph.graphql.schema", - "type": "string" - }, { - "predicate": "dgraph.graphql.xid", - "type": "string", - "index": true, - "tokenizer": ["exact"], - "upsert": true - }, { - "predicate": "dgraph.type", - "type": "string", - "index": true, - "tokenizer": ["exact"], - "list": true - }, { - "predicate": "Person.name", - "type": "string" - }, { - "predicate": "Thing.name", - "type": "string" - }, { - "predicate": "ThingOne.color", - "type": "string" - }, { - "predicate": "ThingOne.usedBy", - "type": "string" - }, { - "predicate": "ThingTwo.color", - "type": "string" - }, { - "predicate": "ThingTwo.owner", - "type": "string" - }], - "types": [{ - "fields": [{ - "name": "Author.name" - }, { - "name": "Author.dob" - }, { - "name": "Author.reputation" - }, { - "name": "Author.country" - }, { - "name": "Author.posts" - }], - "name": "Author" - }, { - "fields": [{ - "name": "Category.name" - }, { - "name": "Category.posts" - }], - "name": "Category" - }, { - "fields": [{ - "name": "Character.name" - }, { - "name": "Character.appearsIn" - }], - "name": "Character" - }, { - "fields": [{ - "name": "Country.name" - }, { - "name": "Country.states" - }], - "name": "Country" - }, { - "fields": [{ - "name": "Character.name" - }, { - "name": "Character.appearsIn" - }, { - "name": "Droid.primaryFunction" - }], - "name": "Droid" - }, { - "fields": [{ - "name": "Employee.ename" - }], - "name": "Employee" - }, { - "fields": [{ - "name": "Employee.ename" - }, { - "name": "Character.name" - }, { - "name": "Character.appearsIn" - }, { - "name": "Human.starships" - }, { - "name": "Human.totalCredits" - }], - "name": "Human" - }, { - "fields": [{ - "name": "Movie.name" - }, { - "name": "Movie.director" - }], - "name": "Movie" - }, { - "fields": [{ - "name": "MovieDirector.name" - }, { - "name": "MovieDirector.directed" - }], - "name": "MovieDirector" - }, { - "fields": [{ - "name": "Post.title" - }, { - "name": "Post.text" - }, { - "name": "Post.tags" - }, { - "name": "Post.topic" - }, { - "name": "Post.numLikes" - }, { - "name": "Post.isPublished" - }, { - "name": "Post.postType" - }, { - "name": "Post.author" - }, { - "name": "Post.category" - }], - "name": "Post" - }, { - "fields": [{ - "name": "Starship.name" - }, { - "name": "Starship.length" - }], - "name": "Starship" - }, { - "fields": [{ - "name": "State.xcode" - }, { - "name": "State.name" - }, { - "name": "State.capital" - }, { - "name": "State.country" - }], - "name": "State" - }, { - "fields": [{ - "name": "User.name" - }, { - "name": "User.password" - }], - "name": "User" - }, { - "fields": [{ - "name": "People.xid" - }, { - "name": "People.name" - }], - "name": "People" - }, { - "fields": [{ - "name": "People.xid" - }, { - "name": "People.name" - }, { - "name": "Teacher.subject" - }, { - "name": "Teacher.teaches" - }], - "name": "Teacher" - }, { - "fields": [{ - "name": "People.xid" - }, { - "name": "People.name" - }, { - "name": "Student.taughtBy" - }], - "name": "Student" - }, { - "fields": [{ - "name": "Person.name" - }], - "name": "Person" - }, { - "fields": [{ - "name": "Thing.name" - }], - "name": "Thing" - }, { - "fields": [{ - "name": "Thing.name" - }, { - "name": "ThingOne.color" - }, { - "name": "ThingOne.usedBy" - }], - "name": "ThingOne" - }, { - "fields": [{ - "name": "Thing.name" - }, { - "name": "ThingTwo.color" - }, { - "name": "ThingTwo.owner" - }], - "name": "ThingTwo" - }, { - "fields": [{ - "name": "dgraph.graphql.schema" - }, { - "name": "dgraph.graphql.xid" - }], - "name": "dgraph.graphql" - }] - } - ` + b, err := ioutil.ReadFile("schema_response.json") + require.NoError(t, err) t.Run("graphql schema", func(t *testing.T) { - common.SchemaTest(t, expectedDgraphSchema) + common.SchemaTest(t, string(b)) }) } diff --git a/graphql/e2e/normal/schema.graphql b/graphql/e2e/normal/schema.graphql index d7e93e3d3ad..41ac10b39e0 100644 --- a/graphql/e2e/normal/schema.graphql +++ b/graphql/e2e/normal/schema.graphql @@ -161,4 +161,14 @@ type ThingTwo implements Thing { id: ID! color: String owner: String +} + +type Post1 { + id: String! @id + comments: [Comment1] +} + +type Comment1 { + id: String! @id + replies: [Comment1] } \ No newline at end of file diff --git a/graphql/e2e/normal/schema_response.json b/graphql/e2e/normal/schema_response.json new file mode 100644 index 00000000000..69d8cedf25a --- /dev/null +++ b/graphql/e2e/normal/schema_response.json @@ -0,0 +1,658 @@ +{ + "schema": [ + { + "index": true, + "list": true, + "predicate": "dgraph.cors", + "tokenizer": [ + "exact" + ], + "type": "string", + "upsert": true + }, + { + "predicate": "Author.country", + "type": "uid" + }, + { + "predicate": "Author.dob", + "type": "datetime", + "index": true, + "tokenizer": [ + "year" + ] + }, + { + "predicate": "Author.name", + "type": "string", + "index": true, + "tokenizer": [ + "hash", + "trigram" + ] + }, + { + "predicate": "Author.posts", + "type": "uid", + "list": true + }, + { + "predicate": "Author.reputation", + "type": "float", + "index": true, + "tokenizer": [ + "float" + ] + }, + { + "predicate": "Category.name", + "type": "string" + }, + { + "predicate": "Category.posts", + "type": "uid", + "list": true + }, + { + "predicate": "Character.appearsIn", + "type": "string", + "index": true, + "tokenizer": [ + "hash" + ], + "list": true + }, + { + "predicate": "Character.name", + "type": "string", + "index": true, + "tokenizer": [ + "exact" + ] + }, + { + "predicate": "Comment1.id", + "type": "string", + "index": true, + "tokenizer": [ + "hash" + ], + "upsert": true + }, + { + "predicate": "Comment1.replies", + "type": "uid", + "list": true + }, + { + "predicate": "Country.name", + "type": "string", + "index": true, + "tokenizer": [ + "hash", + "trigram" + ] + }, + { + "predicate": "Country.states", + "type": "uid", + "list": true + }, + { + "predicate": "Droid.primaryFunction", + "type": "string" + }, + { + "predicate": "Employee.ename", + "type": "string" + }, + { + "predicate": "Human.starships", + "type": "uid", + "list": true + }, + { + "predicate": "Human.totalCredits", + "type": "float" + }, + { + "predicate": "Movie.director", + "type": "uid", + "list": true + }, + { + "predicate": "Movie.name", + "type": "string" + }, + { + "predicate": "MovieDirector.directed", + "type": "uid", + "list": true + }, + { + "predicate": "MovieDirector.name", + "type": "string" + }, + { + "predicate": "People.name", + "type": "string" + }, + { + "predicate": "People.xid", + "type": "string", + "index": true, + "tokenizer": [ + "hash" + ], + "upsert": true + }, + { + "predicate": "Person.name", + "type": "string" + }, + { + "predicate": "Post.author", + "type": "uid" + }, + { + "predicate": "Post.category", + "type": "uid" + }, + { + "predicate": "Post.isPublished", + "type": "bool", + "index": true, + "tokenizer": [ + "bool" + ] + }, + { + "predicate": "Post.numLikes", + "type": "int", + "index": true, + "tokenizer": [ + "int" + ] + }, + { + "predicate": "Post.numViews", + "type": "int", + "index": true, + "tokenizer": [ + "int" + ] + }, + { + "predicate": "Post.postType", + "type": "string", + "index": true, + "tokenizer": [ + "hash", + "trigram" + ] + }, + { + "predicate": "Post.tags", + "type": "string", + "index": true, + "tokenizer": [ + "exact" + ], + "list": true + }, + { + "predicate": "Post.text", + "type": "string", + "index": true, + "tokenizer": [ + "fulltext" + ] + }, + { + "predicate": "Post.title", + "type": "string", + "index": true, + "tokenizer": [ + "fulltext", + "term" + ] + }, + { + "predicate": "Post.topic", + "type": "string", + "index": true, + "tokenizer": [ + "exact" + ] + }, + { + "predicate": "Post1.comments", + "type": "uid", + "list": true + }, + { + "predicate": "Post1.id", + "type": "string", + "index": true, + "tokenizer": [ + "hash" + ], + "upsert": true + }, + { + "predicate": "Starship.length", + "type": "float" + }, + { + "predicate": "Starship.name", + "type": "string", + "index": true, + "tokenizer": [ + "term" + ] + }, + { + "predicate": "State.capital", + "type": "string" + }, + { + "predicate": "State.country", + "type": "uid" + }, + { + "predicate": "State.name", + "type": "string" + }, + { + "predicate": "State.xcode", + "type": "string", + "index": true, + "tokenizer": [ + "hash", + "trigram" + ], + "upsert": true + }, + { + "predicate": "Student.taughtBy", + "type": "uid", + "list": true + }, + { + "predicate": "Teacher.subject", + "type": "string" + }, + { + "predicate": "Teacher.teaches", + "type": "uid", + "list": true + }, + { + "predicate": "Thing.name", + "type": "string" + }, + { + "predicate": "ThingOne.color", + "type": "string" + }, + { + "predicate": "ThingOne.usedBy", + "type": "string" + }, + { + "predicate": "ThingTwo.color", + "type": "string" + }, + { + "predicate": "ThingTwo.owner", + "type": "string" + }, + { + "predicate": "User.name", + "type": "string", + "index": true, + "tokenizer": [ + "hash" + ], + "upsert": true + }, + { + "predicate": "User.password", + "type": "password" + }, + { + "predicate": "dgraph.graphql.schema", + "type": "string" + }, + { + "predicate": "dgraph.graphql.xid", + "type": "string", + "index": true, + "tokenizer": [ + "exact" + ], + "upsert": true + }, + { + "predicate": "dgraph.type", + "type": "string", + "index": true, + "tokenizer": [ + "exact" + ], + "list": true + } + ], + "types": [ + { + "fields": [ + { + "name": "Author.name" + }, + { + "name": "Author.dob" + }, + { + "name": "Author.reputation" + }, + { + "name": "Author.country" + }, + { + "name": "Author.posts" + } + ], + "name": "Author" + }, + { + "fields": [ + { + "name": "Category.name" + }, + { + "name": "Category.posts" + } + ], + "name": "Category" + }, + { + "fields": [ + { + "name": "Character.name" + }, + { + "name": "Character.appearsIn" + } + ], + "name": "Character" + }, + { + "fields": [ + { + "name": "Comment1.id" + }, + { + "name": "Comment1.replies" + } + ], + "name": "Comment1" + }, + { + "fields": [ + { + "name": "Country.name" + }, + { + "name": "Country.states" + } + ], + "name": "Country" + }, + { + "fields": [ + { + "name": "Character.name" + }, + { + "name": "Character.appearsIn" + }, + { + "name": "Droid.primaryFunction" + } + ], + "name": "Droid" + }, + { + "fields": [ + { + "name": "Employee.ename" + } + ], + "name": "Employee" + }, + { + "fields": [ + { + "name": "Employee.ename" + }, + { + "name": "Character.name" + }, + { + "name": "Character.appearsIn" + }, + { + "name": "Human.starships" + }, + { + "name": "Human.totalCredits" + } + ], + "name": "Human" + }, + { + "fields": [ + { + "name": "Movie.name" + }, + { + "name": "Movie.director" + } + ], + "name": "Movie" + }, + { + "fields": [ + { + "name": "MovieDirector.name" + }, + { + "name": "MovieDirector.directed" + } + ], + "name": "MovieDirector" + }, + { + "fields": [ + { + "name": "People.xid" + }, + { + "name": "People.name" + } + ], + "name": "People" + }, + { + "fields": [ + { + "name": "Person.name" + } + ], + "name": "Person" + }, + { + "fields": [ + { + "name": "Post.title" + }, + { + "name": "Post.text" + }, + { + "name": "Post.tags" + }, + { + "name": "Post.topic" + }, + { + "name": "Post.numLikes" + }, + { + "name": "Post.numViews" + }, + { + "name": "Post.isPublished" + }, + { + "name": "Post.postType" + }, + { + "name": "Post.author" + }, + { + "name": "Post.category" + } + ], + "name": "Post" + }, + { + "fields": [ + { + "name": "Post1.id" + }, + { + "name": "Post1.comments" + } + ], + "name": "Post1" + }, + { + "fields": [ + { + "name": "Starship.name" + }, + { + "name": "Starship.length" + } + ], + "name": "Starship" + }, + { + "fields": [ + { + "name": "State.xcode" + }, + { + "name": "State.name" + }, + { + "name": "State.capital" + }, + { + "name": "State.country" + } + ], + "name": "State" + }, + { + "fields": [ + { + "name": "People.xid" + }, + { + "name": "People.name" + }, + { + "name": "Student.taughtBy" + } + ], + "name": "Student" + }, + { + "fields": [ + { + "name": "People.xid" + }, + { + "name": "People.name" + }, + { + "name": "Teacher.subject" + }, + { + "name": "Teacher.teaches" + } + ], + "name": "Teacher" + }, + { + "fields": [ + { + "name": "Thing.name" + } + ], + "name": "Thing" + }, + { + "fields": [ + { + "name": "Thing.name" + }, + { + "name": "ThingOne.color" + }, + { + "name": "ThingOne.usedBy" + } + ], + "name": "ThingOne" + }, + { + "fields": [ + { + "name": "Thing.name" + }, + { + "name": "ThingTwo.color" + }, + { + "name": "ThingTwo.owner" + } + ], + "name": "ThingTwo" + }, + { + "fields": [ + { + "name": "User.name" + }, + { + "name": "User.password" + } + ], + "name": "User" + }, + { + "fields": [ + { + "name": "dgraph.graphql.schema" + }, + { + "name": "dgraph.graphql.xid" + } + ], + "name": "dgraph.graphql" + } + ] +} \ No newline at end of file diff --git a/graphql/resolve/add_mutation_test.yaml b/graphql/resolve/add_mutation_test.yaml index 76a0e85bbbb..a780c466024 100644 --- a/graphql/resolve/add_mutation_test.yaml +++ b/graphql/resolve/add_mutation_test.yaml @@ -2216,8 +2216,8 @@ - name: "Deep mutation three level xid" gqlmutation: | - mutation($auth: AddPost1Input!) { - addPost1(input: [$auth]) { + mutation($auth: [AddPost1Input!]!) { + addPost1(input: $auth) { post1 { id comments { @@ -2232,7 +2232,7 @@ gqlvariables: | { - "auth": { + "auth": [{ "id": "post1", "comments": [{ "id": "comment1", @@ -2240,7 +2240,16 @@ "id": "reply1" }] }] - } + }, + { + "id": "post2", + "comments": [{ + "id": "comment2", + "replies": [{ + "id": "reply1" + }] + }] + }] } dgquery: |- query { @@ -2250,8 +2259,12 @@ Comment16 as Comment16(func: eq(Comment1.id, "reply1")) @filter(type(Comment1)) { uid } + Comment110 as Comment110(func: eq(Comment1.id, "comment2")) @filter(type(Comment1)) { + uid + } } dgmutations: + # Create comment1 if it doesn't exist. - setjson: | { "Comment1.id": "comment1", @@ -2261,6 +2274,7 @@ "uid": "_:Comment14" } cond: "@if(eq(len(Comment14), 0))" + # Create reply1 if it doesn't exist. - setjson: | { "Comment1.id": "reply1", @@ -2270,15 +2284,51 @@ "uid": "_:Comment16" } cond: "@if(eq(len(Comment16), 0) AND eq(len(Comment14), 0))" + # Link comment1 and reply1 if reply exists but comment doesn't. - setjson: | {"uid":"_:Comment14", "Comment1.replies": [{"uid": "uid(Comment16)"}]} cond: "@if(eq(len(Comment16), 1) AND eq(len(Comment14), 0))" + # Link comment1 and reply1 if both don't exist. - setjson: | {"uid":"_:Comment14", "Comment1.replies": [{"uid": "_:Comment16"}]} cond: "@if(eq(len(Comment16), 0) AND eq(len(Comment14), 0))" + # useless mutation which needs investigation - setjson: | {"uid":"uid(Comment16)"} cond: "@if(eq(len(Comment16), 1) AND eq(len(Comment14), 0))" + # create comment2 if it doesn't exist + - setjson: | + { + "Comment1.id": "comment2", + "dgraph.type": [ + "Comment1" + ], + "uid": "_:Comment110" + } + cond: "@if(eq(len(Comment110), 0))" + # Create reply1 if it doesn't exist. + - setjson: | + { + "Comment1.id": "reply1", + "dgraph.type": [ + "Comment1" + ], + "uid": "_:Comment16" + } + cond: "@if(eq(len(Comment16), 0) AND eq(len(Comment110), 0))" + # Link comment2 and reply1 if reply exists but comment doesn't. + - setjson: | + {"uid":"_:Comment110", "Comment1.replies": [{"uid": "uid(Comment16)"}]} + cond: "@if(eq(len(Comment16), 1) AND eq(len(Comment110), 0))" + # Link comment2 and reply1 if both don't exist. + - setjson: | + {"uid":"_:Comment110", "Comment1.replies": [{"uid": "_:Comment16"}]} + cond: "@if(eq(len(Comment16), 0) AND eq(len(Comment110), 0))" + # useless mutation which needs investigation + + - setjson: | + {"uid":"uid(Comment16)"} + cond: "@if(eq(len(Comment16), 1) AND eq(len(Comment110), 0))" dgquerysec: |- query { Post12 as Post12(func: eq(Post1.id, "post1")) @filter(type(Post1)) { @@ -2287,6 +2337,12 @@ Comment14 as Comment14(func: eq(Comment1.id, "comment1")) @filter(type(Comment1)) { uid } + Post18 as Post18(func: eq(Post1.id, "post2")) @filter(type(Post1)) { + uid + } + Comment110 as Comment110(func: eq(Comment1.id, "comment2")) @filter(type(Comment1)) { + uid + } } dgmutationssec: - setjson: | @@ -2300,6 +2356,18 @@ "uid":"_:Post12" } cond: "@if(eq(len(Post12), 0) AND eq(len(Comment14), 1))" + - setjson: | + { + "Post1.comments": + [{ + "uid":"uid(Comment110)" + }], + "Post1.id":"post2", + "dgraph.type":["Post1"], + "uid":"_:Post18" + } + cond: "@if(eq(len(Post18), 0) AND eq(len(Comment110), 1))" + - name: "Add mutation error on @id field for empty value" gqlmutation: | diff --git a/graphql/resolve/mutation_rewriter.go b/graphql/resolve/mutation_rewriter.go index 4e23d28b24d..0106948a916 100644 --- a/graphql/resolve/mutation_rewriter.go +++ b/graphql/resolve/mutation_rewriter.go @@ -1065,7 +1065,17 @@ func rewriteObject( newObj["uid"] = myUID frag := newFragment(newObj) frag.newNodes[variable] = typ + results := &mutationRes{secondPass: []*mutationFragment{frag}} + if xid != nil && !atTopLevel && !xidEncounteredFirstTime && deepXID <= 2 { + // If this is an xid that has been encountered before, e.g. think add mutations with + // multiple objects as input. In that case we don't need to add the fragment to create this + // object, so we clear it out. We do need other fragments for linking this node to its + // parent which are added later. + // If deepXID > 2 then even if the xid has been encountered before we still keep it and + // build its mutation to cover all possible scenarios. + results.secondPass = results.secondPass[:0] + } // if xidString != "", then we are adding with an xid. In which case, we have to ensure // as part of the upsert that the xid doesn't already exist. @@ -1104,13 +1114,15 @@ func rewriteObject( } var childrenFirstPass []*mutationFragment - // we build the mutation to add object here. If XID != nil, we would then move it to // firstPass from secondPass (frag). // if this object has an xid, then we don't need to // rewrite its children if we have encountered it earlier. - if xidString == "" || xidEncounteredFirstTime { + + // For deepXIDs even if the xid has been encountered before, we should build the mutation for + // this object. + if xidString == "" || xidEncounteredFirstTime || deepXID > 2 { var fields []string for field := range obj { fields = append(fields, field) @@ -1211,11 +1223,6 @@ func rewriteObject( results.secondPass = appendFragments(results.secondPass, []*mutationFragment{xidFrag}) } - // if !xidEncounteredFirstTime, we have already seen the relevant fragments. - if xid != nil && !atTopLevel && !xidEncounteredFirstTime { - results.firstPass = []*mutationFragment{} - } - return results } From 373666b2c83621763f89296e0adb380ef349e996 Mon Sep 17 00:00:00 2001 From: Abhimanyu Singh Gaur <12651351+abhimanyusinghgaur@users.noreply.github.com> Date: Mon, 21 Sep 2020 11:19:25 +0530 Subject: [PATCH 2/2] fix test --- graphql/e2e/directives/schema_response.json | 21 --------------------- graphql/e2e/normal/schema_response.json | 21 --------------------- 2 files changed, 42 deletions(-) diff --git a/graphql/e2e/directives/schema_response.json b/graphql/e2e/directives/schema_response.json index 06442d0df1d..62d2291bd12 100644 --- a/graphql/e2e/directives/schema_response.json +++ b/graphql/e2e/directives/schema_response.json @@ -1,15 +1,5 @@ { "schema": [ - { - "index": true, - "list": true, - "predicate": "dgraph.cors", - "tokenizer": [ - "exact" - ], - "type": "string", - "upsert": true - }, { "predicate": "Category.name", "type": "string" @@ -213,14 +203,6 @@ "int" ] }, - { - "predicate": "myPost.numViews", - "type": "int", - "index": true, - "tokenizer": [ - "int" - ] - }, { "predicate": "myPost.postType", "type": "string", @@ -572,9 +554,6 @@ { "name": "myPost.numLikes" }, - { - "name": "myPost.numViews" - }, { "name": "is_published" }, diff --git a/graphql/e2e/normal/schema_response.json b/graphql/e2e/normal/schema_response.json index 69d8cedf25a..8bdbb09677e 100644 --- a/graphql/e2e/normal/schema_response.json +++ b/graphql/e2e/normal/schema_response.json @@ -1,15 +1,5 @@ { "schema": [ - { - "index": true, - "list": true, - "predicate": "dgraph.cors", - "tokenizer": [ - "exact" - ], - "type": "string", - "upsert": true - }, { "predicate": "Author.country", "type": "uid" @@ -174,14 +164,6 @@ "int" ] }, - { - "predicate": "Post.numViews", - "type": "int", - "index": true, - "tokenizer": [ - "int" - ] - }, { "predicate": "Post.postType", "type": "string", @@ -508,9 +490,6 @@ { "name": "Post.numLikes" }, - { - "name": "Post.numViews" - }, { "name": "Post.isPublished" },