diff --git a/tests/integration/explain/default/with_average_join_test.go b/tests/integration/explain/default/with_average_join_test.go index a48a1b97d2..265ca932ce 100644 --- a/tests/integration/explain/default/with_average_join_test.go +++ b/tests/integration/explain/default/with_average_join_test.go @@ -347,3 +347,49 @@ func TestDefaultExplainRequestWithAverageOnMultipleJoinedFieldsWithFilter(t *tes explainUtils.ExecuteTestCase(t, test) } + +// This test asserts that only a single index join is used (not parallelNode) because the +// _avg reuses the rendered join as they have matching filters (average adds a ne nil filter). +func TestDefaultExplainRequestOneToManyWithAverageAndChildNeNilFilterSharesJoinField(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Explain (default) 1-to-M relation request from many side with average filter shared.", + + Actions: []any{ + explainUtils.SchemaForExplainTests, + + testUtils.ExplainRequest{ + + Request: `query @explain { + Author { + name + _avg(books: {field: rating}) + books(filter: {rating: {_ne: null}}){ + name + } + } + }`, + + ExpectedPatterns: []dataMap{ + { + "explain": dataMap{ + "selectTopNode": dataMap{ + "averageNode": dataMap{ + "countNode": dataMap{ + "sumNode": dataMap{ + "selectNode": dataMap{ + "typeIndexJoin": normalTypeJoinPattern, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + explainUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/explain/default/with_count_join_test.go b/tests/integration/explain/default/with_count_join_test.go index 6c116529a7..3f7802820d 100644 --- a/tests/integration/explain/default/with_count_join_test.go +++ b/tests/integration/explain/default/with_count_join_test.go @@ -260,3 +260,94 @@ func TestDefaultExplainRequestWithCountOnOneToManyJoinedFieldWithManySources(t * explainUtils.ExecuteTestCase(t, test) } + +// This test asserts that only a single index join is used (not parallelNode) because the +// _count reuses the rendered join as they have matching filters. +func TestDefaultExplainRequestOneToManyWithCountWithFilterAndChildFilterSharesJoinField(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Explain (default) 1-to-M relation request from many side with count filter shared.", + + Actions: []any{ + explainUtils.SchemaForExplainTests, + + testUtils.ExplainRequest{ + + Request: `query @explain { + Author { + name + _count(books: {filter: {rating: {_ne: null}}}) + books(filter: {rating: {_ne: null}}){ + name + } + } + }`, + + ExpectedPatterns: []dataMap{ + { + "explain": dataMap{ + "selectTopNode": dataMap{ + "countNode": dataMap{ + "selectNode": dataMap{ + "typeIndexJoin": normalTypeJoinPattern, + }, + }, + }, + }, + }, + }, + }, + }, + } + + explainUtils.ExecuteTestCase(t, test) +} + +// This test asserts that two joins are used (with parallelNode) because _count cannot +// reuse the rendered join as they dont have matching filters. +func TestDefaultExplainRequestOneToManyWithCountAndChildFilterDoesNotShareJoinField(t *testing.T) { + test := testUtils.TestCase{ + + Description: "Explain (default) 1-to-M relation request from many side with count filter not shared.", + + Actions: []any{ + explainUtils.SchemaForExplainTests, + + testUtils.ExplainRequest{ + + Request: `query @explain { + Author { + name + _count(books: {}) + books(filter: {rating: {_ne: null}}){ + name + } + } + }`, + + ExpectedPatterns: []dataMap{ + { + "explain": dataMap{ + "selectTopNode": dataMap{ + "countNode": dataMap{ + "selectNode": dataMap{ + "parallelNode": []dataMap{ + { + "typeIndexJoin": normalTypeJoinPattern, + }, + { + "typeIndexJoin": normalTypeJoinPattern, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + explainUtils.ExecuteTestCase(t, test) +} diff --git a/tests/integration/explain/fixture.go b/tests/integration/explain/fixture.go index 31b819e650..c531d95a84 100644 --- a/tests/integration/explain/fixture.go +++ b/tests/integration/explain/fixture.go @@ -27,6 +27,7 @@ var SchemaForExplainTests = testUtils.SchemaUpdate{ type Book { name: String author: Author + rating: Float pages: Int chapterPages: [Int!] } diff --git a/tests/integration/query/one_to_many/utils.go b/tests/integration/query/one_to_many/utils.go index e8ae79efa2..d1e25df661 100644 --- a/tests/integration/query/one_to_many/utils.go +++ b/tests/integration/query/one_to_many/utils.go @@ -16,8 +16,6 @@ import ( testUtils "github.com/sourcenetwork/defradb/tests/integration" ) -type dataMap = map[string]any - var bookAuthorGQLSchema = (` type Book { name: String diff --git a/tests/integration/query/one_to_many/with_average_filter_test.go b/tests/integration/query/one_to_many/with_average_filter_test.go deleted file mode 100644 index 1404be5962..0000000000 --- a/tests/integration/query/one_to_many/with_average_filter_test.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2022 Democratized Data Foundation -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -package one_to_many - -import ( - "testing" - - testUtils "github.com/sourcenetwork/defradb/tests/integration" -) - -// This test asserts that only a single join is used - the _avg reuses the rendered join as they -// have matching filters (average adds a ne nil filter). -func TestQueryOneToManyWithAverageAndChildNeNilFilterSharesJoinField(t *testing.T) { - test := testUtils.RequestTestCase{ - Description: "One-to-many relation query from many side with average", - Request: `query @explain { - Author { - name - _avg(published: {field: rating}) - published(filter: {rating: {_ne: null}}){ - name - } - } - }`, - Results: []dataMap{ - { - "explain": dataMap{ - "selectTopNode": dataMap{ - "averageNode": dataMap{ - "countNode": dataMap{ - "sources": []dataMap{ - { - "filter": dataMap{ - "rating": dataMap{ - "_ne": nil, - }, - }, - "fieldName": "published", - }, - }, - "sumNode": dataMap{ - "sources": []dataMap{ - { - "filter": dataMap{ - "rating": dataMap{ - "_ne": nil, - }, - }, - "fieldName": "published", - "childFieldName": "rating", - }, - }, - "selectNode": dataMap{ - "_keys": nil, - "filter": nil, - "typeIndexJoin": dataMap{ - "joinType": "typeJoinMany", - "rootName": "author", - "root": dataMap{ - "scanNode": dataMap{ - "filter": nil, - "collectionID": "2", - "collectionName": "Author", - "spans": []dataMap{ - { - "start": "/2", - "end": "/3", - }, - }, - }, - }, - "subTypeName": "published", - "subType": dataMap{ - "selectTopNode": dataMap{ - "selectNode": dataMap{ - "_keys": nil, - "filter": nil, - "scanNode": dataMap{ - "filter": dataMap{ - "rating": dataMap{ - "_ne": nil, - }, - }, - "collectionID": "1", - "collectionName": "Book", - "spans": []dataMap{ - { - "start": "/1", - "end": "/2", - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - - executeTestCase(t, test) -} diff --git a/tests/integration/query/one_to_many/with_count_filter_test.go b/tests/integration/query/one_to_many/with_count_filter_test.go index 9deecae01f..b367e6c856 100644 --- a/tests/integration/query/one_to_many/with_count_filter_test.go +++ b/tests/integration/query/one_to_many/with_count_filter_test.go @@ -153,210 +153,3 @@ func TestQueryOneToManyWithCountWithFilterAndChildFilter(t *testing.T) { executeTestCase(t, test) } - -// This test asserts that only a single join is used - the _count reuses the rendered join as they -// have matching filters. -func TestQueryOneToManyWithCountWithFilterAndChildFilterSharesJoinField(t *testing.T) { - test := testUtils.RequestTestCase{ - Description: "One-to-many relation query from many side with count with filter", - Request: `query @explain { - Author { - name - _count(published: {filter: {rating: {_ne: null}}}) - published(filter: {rating: {_ne: null}}){ - name - } - } - }`, - Results: []dataMap{ - { - "explain": dataMap{ - "selectTopNode": dataMap{ - "countNode": dataMap{ - "sources": []dataMap{ - { - "filter": dataMap{ - "rating": dataMap{ - "_ne": nil, - }, - }, - "fieldName": "published", - }, - }, - "selectNode": dataMap{ - "_keys": nil, - "filter": nil, - "typeIndexJoin": dataMap{ - "joinType": "typeJoinMany", - "rootName": "author", - "root": dataMap{ - "scanNode": dataMap{ - "filter": nil, - "collectionID": "2", - "collectionName": "Author", - "spans": []dataMap{ - { - "start": "/2", - "end": "/3", - }, - }, - }, - }, - "subTypeName": "published", - "subType": dataMap{ - "selectTopNode": dataMap{ - "selectNode": dataMap{ - "_keys": nil, - "filter": nil, - "scanNode": dataMap{ - "filter": dataMap{ - "rating": dataMap{ - "_ne": nil, - }, - }, - "collectionID": "1", - "collectionName": "Book", - "spans": []dataMap{ - { - "start": "/1", - "end": "/2", - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - - executeTestCase(t, test) -} - -// This test asserts that two joins are used - the _count cannot reuse the rendered join as they -// dont have matching filters. -func TestQueryOneToManyWithCountAndChildFilterDoesNotShareJoinField(t *testing.T) { - test := testUtils.RequestTestCase{ - Description: "One-to-many relation query from many side with count", - Request: `query @explain { - Author { - name - _count(published: {}) - published(filter: {rating: {_ne: null}}){ - name - } - } - }`, - Results: []dataMap{ - { - "explain": dataMap{ - "selectTopNode": dataMap{ - "countNode": dataMap{ - "selectNode": dataMap{ - "_keys": nil, - "filter": nil, - "parallelNode": []dataMap{ - { - "typeIndexJoin": dataMap{ - "joinType": "typeJoinMany", - "root": dataMap{ - "scanNode": dataMap{ - "collectionID": "2", - "collectionName": "Author", - "filter": nil, - "spans": []dataMap{ - { - "end": "/3", - "start": "/2", - }, - }, - }, - }, - "rootName": "author", - "subType": dataMap{ - "selectTopNode": dataMap{ - "selectNode": dataMap{ - "_keys": nil, - "filter": nil, - "scanNode": dataMap{ - "collectionID": "1", - "collectionName": "Book", - "filter": dataMap{ - "rating": dataMap{ - "_ne": nil, - }, - }, - "spans": []dataMap{ - { - "end": "/2", - "start": "/1", - }, - }, - }, - }, - }, - }, - "subTypeName": "published", - }, - }, - { - "typeIndexJoin": dataMap{ - "joinType": "typeJoinMany", - "root": dataMap{ - "scanNode": dataMap{ - "collectionID": "2", - "collectionName": "Author", - "filter": nil, - "spans": []dataMap{ - { - "end": "/3", - "start": "/2", - }, - }, - }, - }, - "rootName": "author", - "subType": dataMap{ - "selectTopNode": dataMap{ - "selectNode": dataMap{ - "_keys": nil, - "filter": nil, - "scanNode": dataMap{ - "collectionID": "1", - "collectionName": "Book", - "filter": nil, - "spans": []dataMap{ - { - "end": "/2", - "start": "/1", - }, - }, - }, - }, - }, - }, - "subTypeName": "published", - }, - }, - }, - }, - "sources": []dataMap{ - { - "fieldName": "published", - "filter": nil, - }, - }, - }, - }, - }, - }, - }, - } - - executeTestCase(t, test) -}