From d933d6d5bc9f9fcd910f52eaa6614843bf8e642f Mon Sep 17 00:00:00 2001 From: spetrunin Date: Thu, 10 Jul 2025 23:10:08 +0300 Subject: [PATCH 1/4] fix: parent entity jump by making it is the last possible option of selecting node --- ...ation_rule_validate_empty_selection_set.go | 36 ++++++++++ .../graphql_datasource/graphql_datasource.go | 9 ++- .../engine/plan/datasource_filter_visitor.go | 66 ++++++++++--------- 3 files changed, 79 insertions(+), 32 deletions(-) create mode 100644 v2/pkg/astvalidation/operation_rule_validate_empty_selection_set.go diff --git a/v2/pkg/astvalidation/operation_rule_validate_empty_selection_set.go b/v2/pkg/astvalidation/operation_rule_validate_empty_selection_set.go new file mode 100644 index 0000000000..97ec304b63 --- /dev/null +++ b/v2/pkg/astvalidation/operation_rule_validate_empty_selection_set.go @@ -0,0 +1,36 @@ +package astvalidation + +import ( + "fmt" + + "github.com/wundergraph/graphql-go-tools/v2/pkg/ast" + "github.com/wundergraph/graphql-go-tools/v2/pkg/astvisitor" +) + +// ValidateEmptySelectionSets validates if selection sets are not empty +// should be used only when the operation is created on the fly +func ValidateEmptySelectionSets() Rule { + return func(walker *astvisitor.Walker) { + visitor := emptySelectionSetVisitor{ + Walker: walker, + } + walker.RegisterEnterDocumentVisitor(&visitor) + walker.RegisterEnterSelectionSetVisitor(&visitor) + } +} + +type emptySelectionSetVisitor struct { + *astvisitor.Walker + operation, definition *ast.Document +} + +func (r *emptySelectionSetVisitor) EnterDocument(operation, definition *ast.Document) { + r.operation = operation + r.definition = definition +} + +func (r *emptySelectionSetVisitor) EnterSelectionSet(ref int) { + if r.operation.SelectionSetIsEmpty(ref) { + r.Walker.StopWithInternalErr(fmt.Errorf("astvalidation selection set on path %s is empty", r.Walker.Path.DotDelimitedString())) + } +} diff --git a/v2/pkg/engine/datasource/graphql_datasource/graphql_datasource.go b/v2/pkg/engine/datasource/graphql_datasource/graphql_datasource.go index 97383c93ac..2bc7bb4531 100644 --- a/v2/pkg/engine/datasource/graphql_datasource/graphql_datasource.go +++ b/v2/pkg/engine/datasource/graphql_datasource/graphql_datasource.go @@ -1684,11 +1684,18 @@ type printKit struct { var ( printKitPool = &sync.Pool{ New: func() any { + validator := astvalidation.DefaultOperationValidator() + // as we are creating operation programmatically in the graphql datasource planner, + // we need to catch incorrect behavior of the planner + // as graphql datasource planner should visit only selection sets which has fields, + // landed to the current planner + validator.RegisterRule(astvalidation.ValidateEmptySelectionSets()) + return &printKit{ buf: &bytes.Buffer{}, parser: astparser.NewParser(), printer: astprinter.NewPrinter(nil), - validator: astvalidation.DefaultOperationValidator(), + validator: validator, normalizer: astnormalization.NewWithOpts( astnormalization.WithExtractVariables(), astnormalization.WithRemoveFragmentDefinitions(), diff --git a/v2/pkg/engine/plan/datasource_filter_visitor.go b/v2/pkg/engine/plan/datasource_filter_visitor.go index c0a998729d..1c68bfd548 100644 --- a/v2/pkg/engine/plan/datasource_filter_visitor.go +++ b/v2/pkg/engine/plan/datasource_filter_visitor.go @@ -529,36 +529,9 @@ func (f *DataSourceFilter) selectDuplicateNodes(secondPass bool) { continue } - // 2. Lookup for the first parent root node with enabled entity resolver - // when we haven't found a possible duplicate - // we need to find parent node which is a root node and has enabled entity resolver, e.g. the point in the query from where we could jump - // it is a parent entity jump case + // stages 2,3,4 - are stages when choices are equal, and we should select first available node - if f.checkNodes(itemIDs, - func(i int) bool { - if f.nodes.items[i].IsExternal && !f.nodes.items[i].IsProvided { - return false - } - - parents := f.findPossibleParents(i) - if len(parents) > 0 { - if f.selectWithExternalCheck(i, ReasonStage3SelectNodeUnderFirstParentRootNode) { - for _, parent := range parents { - f.nodes.items[parent].selectWithReason(ReasonStage3SelectParentRootNodeWithEnabledEntityResolver, f.enableSelectionReasons) - } - - return true - } - } - return false - }, - nil) { - continue - } - - // stages 3,4,5 - are stages when choices are equal, and we should select first available node - - // 3. we choose first available leaf node + // 2. we choose the first available leaf node if f.checkNodes(itemIDs, func(i int) bool { return f.selectWithExternalCheck(i, ReasonStage3SelectAvailableLeafNode) @@ -582,7 +555,7 @@ func (f *DataSourceFilter) selectDuplicateNodes(secondPass bool) { continue } - // 4. if node is not a leaf we select a node which could provide more selections on the same source + // 3. if node is not a leaf we select a node which could provide more selections on the same source currentChildNodeCount := -1 currentItemIDx := -1 @@ -605,7 +578,7 @@ func (f *DataSourceFilter) selectDuplicateNodes(secondPass bool) { continue } - // 5. We check here not leaf nodes which could provide keys to the child nodes + // 4. We check here not leaf nodes which could provide keys to the child nodes // this rule one of the rules responsible for the shareable nodes if f.checkNodes(itemIDs, func(i int) bool { @@ -630,6 +603,37 @@ func (f *DataSourceFilter) selectDuplicateNodes(secondPass bool) { }) { continue } + + // 5. Lookup for the first parent root node with the enabled entity resolver. + // When we haven't found a possible duplicate - + // we need to find the parent node which is a root node and has enabled entity resolver, + // e.g., the point in the query from where we could jump. + // It is a parent entity jump case, and it is less preferable, + // than direct entity jump, so it should go last. + + // TODO: replace with all nodes check - select smallest parent entity chain + if f.checkNodes(itemIDs, + func(i int) bool { + if f.nodes.items[i].IsExternal && !f.nodes.items[i].IsProvided { + return false + } + + parents := f.findPossibleParents(i) + if len(parents) > 0 { + if f.selectWithExternalCheck(i, ReasonStage3SelectNodeUnderFirstParentRootNode) { + for _, parent := range parents { + f.nodes.items[parent].selectWithReason(ReasonStage3SelectParentRootNodeWithEnabledEntityResolver, f.enableSelectionReasons) + } + + return true + } + } + return false + }, + nil) { + continue + } + } } From 00e61d24f4fdc454b7b8659785fe4d018cd4f533 Mon Sep 17 00:00:00 2001 From: spetrunin Date: Mon, 14 Jul 2025 15:23:45 +0300 Subject: [PATCH 2/4] fix: fix selecting parent of unique items --- .../engine/plan/datasource_filter_visitor.go | 61 ++++++++----------- 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/v2/pkg/engine/plan/datasource_filter_visitor.go b/v2/pkg/engine/plan/datasource_filter_visitor.go index 1c68bfd548..d45cbccc19 100644 --- a/v2/pkg/engine/plan/datasource_filter_visitor.go +++ b/v2/pkg/engine/plan/datasource_filter_visitor.go @@ -197,10 +197,11 @@ func (f *DataSourceFilter) collectNodes(dataSources []DataSource, existingNodes } const ( - ReasonStage1Unique = "stage1: unique" - ReasonStage1SameSourceParent = "stage1: same source parent of unique node" - ReasonStage1SameSourceLeafChild = "stage1: same source leaf child of unique node" - ReasonStage1SameSourceLeafSibling = "stage1: same source leaf sibling of unique node" + ReasonStage1Unique = "stage1: unique" + ReasonStage1SameSourceParent = "stage1: same source parent of unique node" + ReasonStage1SameSourceLeafChild = "stage1: same source leaf child of unique node" + ReasonStage1SameSourceLeafSibling = "stage1: same source leaf sibling of unique node" + ReasonStage1SameSourceChildWithPossibleSelections = "stage1: select non leaf child of unique node which have possible child selections on the same source" ReasonStage2SameSourceNodeOfSelectedParent = "stage2: node on the same source as selected parent" ReasonStage2SameSourceNodeOfSelectedChild = "stage2: node on the same source as selected child" @@ -218,9 +219,6 @@ const ( ) // selectUniqueNodes - selects nodes (e.g. fields) which are unique to a single datasource -// In addition we select: -// - parent of such node if the node is a leaf and not nested under the fragment -// - siblings nodes func (f *DataSourceFilter) selectUniqueNodes() { for i := range f.nodes.items { @@ -228,63 +226,56 @@ func (f *DataSourceFilter) selectUniqueNodes() { continue } - isNodeUnique := f.nodes.isNodeUnique(i) - if !isNodeUnique { + if !f.nodes.isNodeUnique(i) { continue } // unique nodes always have priority f.nodes.items[i].selectWithReason(ReasonStage1Unique, f.enableSelectionReasons) - if !f.nodes.items[i].onFragment { // on a first stage do not select parent of nodes on fragments - // if node parents of the unique node is on the same source, prioritize it too - f.selectUniqNodeParentsUpToRootNode(i) - } - - // if node has leaf children on the same source, prioritize them too - children := f.nodes.childNodesOnSameSource(i) - for _, child := range children { - if f.nodes.isLeaf(child) && f.nodes.isNodeUnique(child) { - f.nodes.items[child].selectWithReason(ReasonStage1SameSourceLeafChild, f.enableSelectionReasons) - } - } - - // prioritize leaf siblings of the node on the same source - siblings := f.nodes.siblingNodesOnSameSource(i) - for _, sibling := range siblings { - if f.nodes.isLeaf(sibling) && f.nodes.isNodeUnique(sibling) { - f.nodes.items[sibling].selectWithReason(ReasonStage1SameSourceLeafSibling, f.enableSelectionReasons) - } - } + f.selectUniqNodeParentsUpToRootNode(i) } } func (f *DataSourceFilter) selectUniqNodeParentsUpToRootNode(i int) { // When we have a chain of datasource child nodes, we should select every parent until we reach the root node - // as root node is a starting point from where we could get all these child nodes + // as a root node is a starting point from where we could get all these child nodes if f.nodes.items[i].IsRootNode { - // no need to select parent of a root node here + // no need to select the parent of a root node here // as it could be resolved by itself return } + rootNodeFound := false + nodesIdsToSelect := make([]int, 0, 2) current := i for { parentIdx, ok := f.nodes.parentNodeOnSameSource(current) if !ok { break } + nodesIdsToSelect = append(nodesIdsToSelect, parentIdx) - if !f.selectWithExternalCheck(parentIdx, ReasonStage1SameSourceParent) { - // when we see an external node in the chain, we stop selectiong process + if f.nodes.items[parentIdx].IsExternal && !f.nodes.items[i].IsProvided { + // such parent can't be selected break } - current = parentIdx - if f.nodes.items[current].IsRootNode { + if f.nodes.items[parentIdx].IsRootNode && !f.nodes.items[parentIdx].DisabledEntityResolver { + rootNodeFound = true break } + + current = parentIdx + } + + if !rootNodeFound { + return + } + + for _, parentIdx := range nodesIdsToSelect { + f.nodes.items[parentIdx].selectWithReason(ReasonStage1SameSourceParent, f.enableSelectionReasons) } } From 7eeecc3531726f124e134fa06ac7a5074e89ac02 Mon Sep 17 00:00:00 2001 From: spetrunin Date: Tue, 22 Jul 2025 21:37:39 +0300 Subject: [PATCH 3/4] chore: fix tests --- .../plan/datasource_filter_visitor_test.go | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/v2/pkg/engine/plan/datasource_filter_visitor_test.go b/v2/pkg/engine/plan/datasource_filter_visitor_test.go index f4ef39f1d8..acf02a971e 100644 --- a/v2/pkg/engine/plan/datasource_filter_visitor_test.go +++ b/v2/pkg/engine/plan/datasource_filter_visitor_test.go @@ -291,7 +291,7 @@ func TestFindBestDataSourceSet(t *testing.T) { {TypeName: "Query", FieldName: "user", DataSourceHash: 11, Path: "query.user", ParentPath: "query", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: unique"}}, {TypeName: "User", FieldName: "id", DataSourceHash: 11, Path: "query.user.id", ParentPath: "query.user", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage2: node on the same source as selected parent"}}, {TypeName: "User", FieldName: "name", DataSourceHash: 22, Path: "query.user.name", ParentPath: "query.user", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: unique"}}, - {TypeName: "User", FieldName: "surname", DataSourceHash: 22, Path: "query.user.surname", ParentPath: "query.user", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: same source leaf sibling of unique node"}}, + {TypeName: "User", FieldName: "surname", DataSourceHash: 22, Path: "query.user.surname", ParentPath: "query.user", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: unique"}}, }), }, { @@ -560,8 +560,8 @@ func TestFindBestDataSourceSet(t *testing.T) { {TypeName: "Lines", FieldName: "id", DataSourceHash: 22, Path: "query.user.details.address.lines.id", ParentPath: "query.user.details.address.lines", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage2: node on the same source as selected parent"}}, {TypeName: "Lines", FieldName: "line1", DataSourceHash: 44, Path: "query.user.details.address.lines.line1", ParentPath: "query.user.details.address.lines", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage2: node on the same source as selected sibling"}}, {TypeName: "Lines", FieldName: "line2", DataSourceHash: 44, Path: "query.user.details.address.lines.line2", ParentPath: "query.user.details.address.lines", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: unique"}}, - {TypeName: "Address", FieldName: "name", DataSourceHash: 22, Path: "query.user.details.address.name", ParentPath: "query.user.details.address", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: same source leaf child of unique node"}}, - {TypeName: "Details", FieldName: "age", DataSourceHash: 22, Path: "query.user.details.age", ParentPath: "query.user.details", IsRootNode: false, Selected: true, SelectionReasons: []string{"stage1: same source leaf child of unique node"}}, + {TypeName: "Address", FieldName: "name", DataSourceHash: 22, Path: "query.user.details.address.name", ParentPath: "query.user.details.address", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: unique"}}, + {TypeName: "Details", FieldName: "age", DataSourceHash: 22, Path: "query.user.details.age", ParentPath: "query.user.details", IsRootNode: false, Selected: true, SelectionReasons: []string{"stage1: unique"}}, {TypeName: "User", FieldName: "name", DataSourceHash: 22, Path: "query.user.name", ParentPath: "query.user", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage2: node on the same source as selected parent"}}, }), }, @@ -820,7 +820,7 @@ func TestFindBestDataSourceSet(t *testing.T) { {TypeName: "Query", FieldName: "me", DataSourceHash: 11, Path: "query.me", ParentPath: "query", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: unique"}}, {TypeName: "User", FieldName: "details", DataSourceHash: 33, Path: "query.me.details", ParentPath: "query.me", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: same source parent of unique node"}}, {TypeName: "Details", FieldName: "pets", DataSourceHash: 33, Path: "query.me.details.pets", ParentPath: "query.me.details", IsRootNode: false, Selected: true, SelectionReasons: []string{"stage1: unique"}}, - {TypeName: "Pet", FieldName: "name", DataSourceHash: 33, Path: "query.me.details.pets.name", ParentPath: "query.me.details.pets", IsRootNode: false, Selected: true, SelectionReasons: []string{"stage1: same source leaf child of unique node"}}, + {TypeName: "Pet", FieldName: "name", DataSourceHash: 33, Path: "query.me.details.pets.name", ParentPath: "query.me.details.pets", IsRootNode: false, Selected: true, SelectionReasons: []string{"stage1: unique"}}, }), }, { @@ -829,7 +829,7 @@ func TestFindBestDataSourceSet(t *testing.T) { {TypeName: "Query", FieldName: "me", DataSourceHash: 11, Path: "query.me", ParentPath: "query", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: unique"}}, {TypeName: "User", FieldName: "details", DataSourceHash: 33, Path: "query.me.details", ParentPath: "query.me", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: same source parent of unique node"}}, {TypeName: "Details", FieldName: "pets", DataSourceHash: 33, Path: "query.me.details.pets", ParentPath: "query.me.details", IsRootNode: false, Selected: true, SelectionReasons: []string{"stage1: unique"}}, - {TypeName: "Pet", FieldName: "name", DataSourceHash: 33, Path: "query.me.details.pets.name", ParentPath: "query.me.details.pets", IsRootNode: false, Selected: true, SelectionReasons: []string{"stage1: same source leaf child of unique node"}}, + {TypeName: "Pet", FieldName: "name", DataSourceHash: 33, Path: "query.me.details.pets.name", ParentPath: "query.me.details.pets", IsRootNode: false, Selected: true, SelectionReasons: []string{"stage1: unique"}}, }), }, }, @@ -938,7 +938,7 @@ func TestFindBestDataSourceSet(t *testing.T) { {TypeName: "NestedOne", FieldName: "uniqueOne", DataSourceHash: 11, Path: "query.user.nested.uniqueOne", ParentPath: "query.user.nested", IsRootNode: false, Selected: true, SelectionReasons: []string{"stage1: unique"}}, {TypeName: "NestedOne", FieldName: "uniqueTwo", DataSourceHash: 22, Path: "query.user.nested.uniqueTwo", ParentPath: "query.user.nested", IsRootNode: false, Selected: true, SelectionReasons: []string{"stage1: unique"}}, {TypeName: "User", FieldName: "object", DataSourceHash: 11, Path: "query.user.object", ParentPath: "query.user", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: unique"}}, - {TypeName: "Object", FieldName: "name", DataSourceHash: 11, Path: "query.user.object.name", ParentPath: "query.user.object", IsRootNode: false, Selected: true, SelectionReasons: []string{"stage1: same source leaf child of unique node"}}, + {TypeName: "Object", FieldName: "name", DataSourceHash: 11, Path: "query.user.object.name", ParentPath: "query.user.object", IsRootNode: false, Selected: true, SelectionReasons: []string{"stage1: unique"}}, }), }, { @@ -956,7 +956,7 @@ func TestFindBestDataSourceSet(t *testing.T) { {TypeName: "NestedOne", FieldName: "uniqueOne", DataSourceHash: 11, Path: "query.user.nested.uniqueOne", ParentPath: "query.user.nested", IsRootNode: false, Selected: true, SelectionReasons: []string{"stage1: unique"}}, {TypeName: "NestedOne", FieldName: "uniqueTwo", DataSourceHash: 22, Path: "query.user.nested.uniqueTwo", ParentPath: "query.user.nested", IsRootNode: false, Selected: true, SelectionReasons: []string{"stage1: unique"}}, {TypeName: "User", FieldName: "object", DataSourceHash: 11, Path: "query.user.object", ParentPath: "query.user", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: unique"}}, - {TypeName: "Object", FieldName: "name", DataSourceHash: 11, Path: "query.user.object.name", ParentPath: "query.user.object", IsRootNode: false, Selected: true, SelectionReasons: []string{"stage1: same source leaf child of unique node"}}, + {TypeName: "Object", FieldName: "name", DataSourceHash: 11, Path: "query.user.object.name", ParentPath: "query.user.object", IsRootNode: false, Selected: true, SelectionReasons: []string{"stage1: unique"}}, }), }, }, @@ -992,10 +992,10 @@ func TestFindBestDataSourceSet(t *testing.T) { {TypeName: "PaginatedUser", FieldName: "edges", DataSourceHash: 11, Path: "query.users.edges", ParentPath: "query.users", IsRootNode: false, Selected: true, SelectionReasons: []string{"stage2: node on the same source as selected parent"}}, {TypeName: "UserToEdge", FieldName: "node", DataSourceHash: 11, Path: "query.users.edges.node", ParentPath: "query.users.edges", IsRootNode: false, Selected: true, SelectionReasons: []string{"stage2: node on the same source as selected parent"}}, {TypeName: "User", FieldName: "address", DataSourceHash: 22, Path: "query.users.edges.node.address", ParentPath: "query.users.edges.node", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: unique"}}, - {TypeName: "Address", FieldName: "street", DataSourceHash: 22, Path: "query.users.edges.node.address.street", ParentPath: "query.users.edges.node.address", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: same source leaf child of unique node"}}, + {TypeName: "Address", FieldName: "street", DataSourceHash: 22, Path: "query.users.edges.node.address.street", ParentPath: "query.users.edges.node.address", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: unique"}}, {TypeName: "User", FieldName: "firstName", DataSourceHash: 11, Path: "query.users.edges.node.firstName", ParentPath: "query.users.edges.node", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: unique"}}, {TypeName: "User", FieldName: "id", DataSourceHash: 11, Path: "query.users.edges.node.id", ParentPath: "query.users.edges.node", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage2: node on the same source as selected parent"}}, - {TypeName: "User", FieldName: "lastName", DataSourceHash: 11, Path: "query.users.edges.node.lastName", ParentPath: "query.users.edges.node", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: same source leaf sibling of unique node"}}, + {TypeName: "User", FieldName: "lastName", DataSourceHash: 11, Path: "query.users.edges.node.lastName", ParentPath: "query.users.edges.node", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: unique"}}, }), }, { @@ -1005,10 +1005,10 @@ func TestFindBestDataSourceSet(t *testing.T) { {TypeName: "PaginatedUser", FieldName: "edges", DataSourceHash: 11, Path: "query.users.edges", ParentPath: "query.users", IsRootNode: false, Selected: true, SelectionReasons: []string{"stage2: node on the same source as selected parent"}}, {TypeName: "UserToEdge", FieldName: "node", DataSourceHash: 11, Path: "query.users.edges.node", ParentPath: "query.users.edges", IsRootNode: false, Selected: true, SelectionReasons: []string{"stage2: node on the same source as selected parent"}}, {TypeName: "User", FieldName: "address", DataSourceHash: 22, Path: "query.users.edges.node.address", ParentPath: "query.users.edges.node", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: unique"}}, - {TypeName: "Address", FieldName: "street", DataSourceHash: 22, Path: "query.users.edges.node.address.street", ParentPath: "query.users.edges.node.address", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: same source leaf child of unique node"}}, + {TypeName: "Address", FieldName: "street", DataSourceHash: 22, Path: "query.users.edges.node.address.street", ParentPath: "query.users.edges.node.address", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: unique"}}, {TypeName: "User", FieldName: "firstName", DataSourceHash: 11, Path: "query.users.edges.node.firstName", ParentPath: "query.users.edges.node", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: unique"}}, {TypeName: "User", FieldName: "id", DataSourceHash: 11, Path: "query.users.edges.node.id", ParentPath: "query.users.edges.node", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage2: node on the same source as selected parent"}}, - {TypeName: "User", FieldName: "lastName", DataSourceHash: 11, Path: "query.users.edges.node.lastName", ParentPath: "query.users.edges.node", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: same source leaf sibling of unique node"}}, + {TypeName: "User", FieldName: "lastName", DataSourceHash: 11, Path: "query.users.edges.node.lastName", ParentPath: "query.users.edges.node", IsRootNode: true, Selected: true, SelectionReasons: []string{"stage1: unique"}}, }), }, }, From cae43742db2e806eab81f3d174eed1aab1909628 Mon Sep 17 00:00:00 2001 From: spetrunin Date: Tue, 22 Jul 2025 21:41:38 +0300 Subject: [PATCH 4/4] chore: cleanup --- v2/pkg/engine/plan/datasource_filter_visitor.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/v2/pkg/engine/plan/datasource_filter_visitor.go b/v2/pkg/engine/plan/datasource_filter_visitor.go index d45cbccc19..85f15552c7 100644 --- a/v2/pkg/engine/plan/datasource_filter_visitor.go +++ b/v2/pkg/engine/plan/datasource_filter_visitor.go @@ -197,11 +197,8 @@ func (f *DataSourceFilter) collectNodes(dataSources []DataSource, existingNodes } const ( - ReasonStage1Unique = "stage1: unique" - ReasonStage1SameSourceParent = "stage1: same source parent of unique node" - ReasonStage1SameSourceLeafChild = "stage1: same source leaf child of unique node" - ReasonStage1SameSourceLeafSibling = "stage1: same source leaf sibling of unique node" - ReasonStage1SameSourceChildWithPossibleSelections = "stage1: select non leaf child of unique node which have possible child selections on the same source" + ReasonStage1Unique = "stage1: unique" + ReasonStage1SameSourceParent = "stage1: same source parent of unique node" ReasonStage2SameSourceNodeOfSelectedParent = "stage2: node on the same source as selected parent" ReasonStage2SameSourceNodeOfSelectedChild = "stage2: node on the same source as selected child"