Skip to content

Commit

Permalink
feat: Add support to explain countNode attributes. (sourcenetwork#504)
Browse files Browse the repository at this point in the history
- RELEVANT ISSUE(S)

Resolves sourcenetwork#478 

- DESCRIPTION

(1) Adds ability to explain attributes of `countNode`.
(2) Fixes a bug which was omitting the aggregate nodes.

Request:
```
query @Explain {
	author {
		name
		numberOfBooks: _count(books: {})
	}
}
```

Response:
```
{
	"explain": {
		"selectTopNode": {
			"countNode": {
				"filter":         nil,
				"sourceProperty": "books",
				"selectNode": {
					"filter": nil,
					"typeIndexJoin": {
						"scanNode": {
							"collectionID":   "3",
							"collectionName": "author",
							"filter":         nil,
							"spans": []{
								{
									"start": "/3",
									"end":   "/4",
								}
							}
						}
					}
				}
			}
		}
	}
}
```
  • Loading branch information
shahzadlone authored Jun 9, 2022
1 parent 644847f commit 5169c0d
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 66 deletions.
14 changes: 13 additions & 1 deletion query/graphql/planner/count.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,19 @@ func (n *countNode) Source() planNode { return n.plan }
// Explain method returns a map containing all attributes of this node that
// are to be explained, subscribes / opts-in this node to be an explainablePlanNode.
func (n *countNode) Explain() (map[string]interface{}, error) {
return map[string]interface{}{}, nil
explainerMap := map[string]interface{}{}

// Add the filter attribute if it exists.
if n.filter == nil || n.filter.Conditions == nil {
explainerMap[filterLabel] = nil
} else {
explainerMap[filterLabel] = n.filter.Conditions
}

// Add the source property.
explainerMap["sourceProperty"] = n.sourceProperty

return explainerMap, nil
}

func (n *countNode) Next() (bool, error) {
Expand Down
6 changes: 3 additions & 3 deletions query/graphql/planner/planner.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,12 @@ func (p *Planner) expandPlan(plan planNode, parentPlan *selectTopNode) error {
}

func (p *Planner) expandSelectTopNodePlan(plan *selectTopNode, parentPlan *selectTopNode) error {
if err := p.expandPlan(plan.source, plan); err != nil {
if err := p.expandPlan(plan.selectnode, plan); err != nil {
return err
}

// wire up source to plan
plan.plan = plan.source
plan.plan = plan.selectnode

// if group
if plan.group != nil {
Expand Down Expand Up @@ -300,7 +300,7 @@ func (p *Planner) expandGroupNodePlan(plan *selectTopNode) error {
childSelect,
pipe,
false,
&plan.source.(*selectNode).sourceInfo,
&plan.selectnode.sourceInfo,
)
if err != nil {
return err
Expand Down
15 changes: 7 additions & 8 deletions query/graphql/planner/select.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,17 @@ SELECT * From TableA as A JOIN TableB as B ON a.id = b.friend_id
// Wraps a selectNode and all the logic of a plan graph into a single struct for proper plan expansion.
// Executes the top level plan node.
type selectTopNode struct {
source planNode
group *groupNode
sort *sortNode
limit planNode
render *renderNode
aggregates []aggregateNode

// top of the plan graph
plan planNode
// selectnode is used pre-wiring of the plan (before expansion and all).
selectnode *selectNode

// plan -> limit -> sort -> sort.plan = (values -> container | SORT_STRATEGY) -> render -> source
// ... source -> MultiNode -> TypeJoinNode.plan = (typeJoinOne | typeJoinMany) -> scanNode
// plan is the top of the plan graph (the wired and finalized plan graph).
plan planNode
}

func (n *selectTopNode) Kind() string { return "selectTopNode" }
Expand All @@ -76,7 +75,7 @@ func (n *selectTopNode) Spans(spans core.Spans) { n.plan.Spans(spans) }

func (n *selectTopNode) Value() map[string]interface{} { return n.plan.Value() }

func (n *selectTopNode) Source() planNode { return n.source }
func (n *selectTopNode) Source() planNode { return n.plan.Source() }

// Explain method for selectTopNode returns no attributes but is used to
// subscribe / opt-into being an explainablePlanNode.
Expand Down Expand Up @@ -572,7 +571,7 @@ func (p *Planner) SelectFromSource(
}

top := &selectTopNode{
source: s,
selectnode: s,
render: p.render(parsed),
limit: limitPlan,
sort: sortPlan,
Expand Down Expand Up @@ -613,7 +612,7 @@ func (p *Planner) Select(parsed *parser.Select) (planNode, error) {
}

top := &selectTopNode{
source: s,
selectnode: s,
render: p.render(parsed),
limit: limitPlan,
sort: sortPlan,
Expand Down
2 changes: 1 addition & 1 deletion query/graphql/planner/type_join.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ func (n *typeJoinOne) valuesPrimary(doc map[string]interface{}) map[string]inter
doc[subDocField] = map[string]interface{}{}

// create the collection key for the sub doc
slct := n.subType.(*selectTopNode).source.(*selectNode)
slct := n.subType.(*selectTopNode).selectnode
desc := slct.sourceInfo.collectionDescription
subKeyIndexKey := base.MakeDocKey(desc, subDocKeyStr)

Expand Down
133 changes: 80 additions & 53 deletions tests/integration/query/explain/with_count_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,26 +74,31 @@ func TestExplainQueryOneToManyWithACount(t *testing.T) {
},
},

// ----> selectTopNode (explainable but no-attributes)
// ----> selectNode (explainable)
// ----> typeIndexJoin (explainable)
// ----> typeJoinMany (non-explainable)
// ----> scanNode (explainable)
// ----> selectTopNode (explainable but no-attributes)
// ----> countNode (explainable)
// ----> selectNode (explainable)
// ----> typeIndexJoin (explainable)
// ----> typeJoinMany (non-explainable)
// ----> scanNode (explainable)
Results: []dataMap{
{
"explain": dataMap{
"selectTopNode": dataMap{
"selectNode": dataMap{
"filter": nil,
"typeIndexJoin": dataMap{
"scanNode": dataMap{
"collectionID": "3",
"collectionName": "author",
"filter": nil,
"spans": []dataMap{
{
"start": "/3",
"end": "/4",
"countNode": dataMap{
"filter": nil,
"sourceProperty": "books",
"selectNode": dataMap{
"filter": nil,
"typeIndexJoin": dataMap{
"scanNode": dataMap{
"collectionID": "3",
"collectionName": "author",
"filter": nil,
"spans": []dataMap{
{
"start": "/3",
"end": "/4",
},
},
},
},
Expand All @@ -117,7 +122,15 @@ func TestExplainQueryOneToManyMultipleWithCounts(t *testing.T) {
author {
name
numberOfBooks: _count(books: {})
numberOfArticles: _count(articles: {})
numberOfArticles: _count(
articles: {
filter: {
name: {
_eq: "After Guantánamo, Another Injustice"
}
}
}
)
}
}`,

Expand Down Expand Up @@ -169,55 +182,69 @@ func TestExplainQueryOneToManyMultipleWithCounts(t *testing.T) {
},
},

// ----> selectTopNode (explainable but no attributes)
// ----> selectNode (explainable)
// ----> parallelNode (non-explainable but wraps children)
// ----> typeIndexJoin (explainable)
// ----> typeJoinMany (non-explainable)
// ----> scanNode (explainable)
// ----> scanNode (explainable)
// ----> typeIndexJoin (explainable)
// ----> typeJoinMany (non-explainable)
// ----> scanNode (explainable)
// ----> scanNode (explainable)
// ----> selectTopNode (explainable but no attributes)
// ----> countNode (explainable)
// ----> countNode (explainable)
// ----> selectNode (explainable)
// ----> parallelNode (non-explainable but wraps children)
// ----> typeIndexJoin (explainable)
// ----> typeJoinMany (non-explainable)
// ----> multiscanNode (non-explainable)
// ----> scanNode (explainable)
// ----> typeIndexJoin (explainable)
// ----> typeJoinMany (non-explainable)
// ----> multiscanNode (non-explainable)
// ----> scanNode (explainable)
Results: []dataMap{
{
"explain": dataMap{
"selectTopNode": dataMap{
"selectNode": dataMap{
"parallelNode": []dataMap{
{
"typeIndexJoin": dataMap{
"scanNode": dataMap{
"collectionID": "3",
"collectionName": "author",
"filter": nil,
"spans": []dataMap{
{
"start": "/3",
"end": "/4",
"countNode": dataMap{
"filter": nil,
"sourceProperty": "books",
"countNode": dataMap{
"filter": dataMap{
"name": dataMap{
"$eq": "After Guantánamo, Another Injustice",
},
},
"sourceProperty": "articles",
"selectNode": dataMap{
"filter": nil,
"parallelNode": []dataMap{
{
"typeIndexJoin": dataMap{
"scanNode": dataMap{
"collectionID": "3",
"collectionName": "author",
"filter": nil,
"spans": []dataMap{
{
"end": "/4",
"start": "/3",
},
},
},
},
},
},
},
{
"typeIndexJoin": dataMap{
"scanNode": dataMap{
"collectionID": "3",
"collectionName": "author",
"filter": nil,
"spans": []dataMap{
{
"start": "/3",
"end": "/4",
{
"typeIndexJoin": dataMap{
"scanNode": dataMap{
"collectionID": "3",
"collectionName": "author",
"filter": nil,
"spans": []dataMap{
{
"end": "/4",
"start": "/3",
},
},
},
},
},
},
},
},
"filter": nil,
},
},
},
Expand Down

0 comments on commit 5169c0d

Please sign in to comment.