Skip to content

Commit

Permalink
Combine metadata fields (#63)
Browse files Browse the repository at this point in the history
* Combine metadata fields

* Add TestGetNodeTitleAndGetAllMetadataString

* Determine order of getAllMetadataString

* Do go fmt

* Use github.com/golang/protobuf

* Revert "Use github.com/golang/protobuf"

This reverts commit 20aa681.

* Remove unused function

* Refactor (*Node).String()
  • Loading branch information
apstndb committed Jun 9, 2020
1 parent cfc5125 commit b83c050
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 21 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ require (
google.golang.org/api v0.21.0
google.golang.org/genproto v0.0.0-20200416231807-8751e049a2a0
google.golang.org/grpc v1.28.1
google.golang.org/protobuf v1.21.0
)
67 changes: 46 additions & 21 deletions query_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package main

import (
"fmt"
"sort"
"strings"

"github.com/xlab/treeprint"
Expand Down Expand Up @@ -88,32 +89,56 @@ func (n *Node) IsVisible() bool {
}

func (n *Node) String() string {
operator := n.PlanNode.DisplayName
metadata := getAllMetadataString(n)
return operator + " " + metadata
}

func getMetadataString(node *Node, key string) (string, bool) {
if node.PlanNode.Metadata == nil {
return "", false
}
if v, ok := node.PlanNode.Metadata.Fields[key]; ok {
return v.GetStringValue(), true
} else {
return "", false
metadataFields := n.PlanNode.GetMetadata().GetFields()

var operator string
{
var components []string
for _, s := range []string{
metadataFields["call_type"].GetStringValue(),
metadataFields["iterator_type"].GetStringValue(),
strings.TrimSuffix(metadataFields["scan_type"].GetStringValue(), "Scan"),
n.PlanNode.GetDisplayName(),
} {
if s != "" {
components = append(components, s)
}
}
operator = strings.Join(components, " ")
}
}

func getAllMetadataString(node *Node) string {
if node.PlanNode.Metadata == nil {
return ""

var metadata string
{
fields := make([]string, 0)
for k, v := range metadataFields {
switch k {
case "call_type", "iterator_type": // Skip because it is displayed in node title
continue
case "scan_target": // Skip because it is combined with scan_type
continue
case "subquery_cluster_node": // Skip because it is useless without displaying node id
continue
case "scan_type":
fields = append(fields, fmt.Sprintf("%s: %s",
strings.TrimSuffix(v.GetStringValue(), "Scan"),
metadataFields["scan_target"].GetStringValue()))
default:
fields = append(fields, fmt.Sprintf("%s: %s", k, v.GetStringValue()))
}
}

sort.Strings(fields)

if len(fields) != 0 {
metadata = fmt.Sprintf(`(%s)`, strings.Join(fields, ", "))
}
}

fields := make([]string, 0)
for k, v := range node.PlanNode.Metadata.Fields {
fields = append(fields, fmt.Sprintf("%s: %s", k, v.GetStringValue()))
if metadata == "" {
return operator
}
return fmt.Sprintf(`(%s)`, strings.Join(fields, ", "))
return operator + " " + metadata
}

func renderTree(tree treeprint.Tree, linkType string, node *Node) {
Expand Down
90 changes: 90 additions & 0 deletions query_plan_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package main

import (
"testing"

"google.golang.org/genproto/googleapis/spanner/v1"
"google.golang.org/protobuf/types/known/structpb"
)

func TestNodeString(t *testing.T) {
for _, test := range []struct {
title string
node *Node
want string
}{
{"Distributed Union with call_type=Local",
&Node{PlanNode: &spanner.PlanNode{
DisplayName: "Distributed Union",
Metadata: &structpb.Struct{
Fields: map[string]*structpb.Value{
"call_type": {Kind: &structpb.Value_StringValue{StringValue: "Local"}},
"subquery_cluster_node": {Kind: &structpb.Value_StringValue{StringValue: "4"}},
},
},
}}, "Local Distributed Union",
},
{"Scan with scan_type=IndexScan and Full scan=true",
&Node{PlanNode: &spanner.PlanNode{
DisplayName: "Scan",
Metadata: &structpb.Struct{
Fields: map[string]*structpb.Value{
"scan_type": {Kind: &structpb.Value_StringValue{StringValue: "IndexScan"}},
"scan_target": {Kind: &structpb.Value_StringValue{StringValue: "SongsBySongName"}},
"Full scan": {Kind: &structpb.Value_StringValue{StringValue: "true"}},
},
},
}}, "Index Scan (Full scan: true, Index: SongsBySongName)"},
{ "Scan with scan_type=TableScan",
&Node{PlanNode: &spanner.PlanNode{
DisplayName: "Scan",
Metadata: &structpb.Struct{
Fields: map[string]*structpb.Value{
"scan_type": {Kind: &structpb.Value_StringValue{StringValue: "TableScan"}},
"scan_target": {Kind: &structpb.Value_StringValue{StringValue: "Songs"}},
},
},
}}, "Table Scan (Table: Songs)"},
{"Scan with scan_type=BatchScan",
&Node{PlanNode: &spanner.PlanNode{
DisplayName: "Scan",
Metadata: &structpb.Struct{
Fields: map[string]*structpb.Value{
"scan_type": {Kind: &structpb.Value_StringValue{StringValue: "BatchScan"}},
"scan_target": {Kind: &structpb.Value_StringValue{StringValue: "$v2"}},
},
},
}}, "Batch Scan (Batch: $v2)"},
{"Sort Limit with call_type=Local",
&Node{PlanNode: &spanner.PlanNode{
DisplayName: "Sort Limit",
Metadata: &structpb.Struct{
Fields: map[string]*structpb.Value{
"call_type": {Kind: &structpb.Value_StringValue{StringValue: "Local"}},
},
},
}}, "Local Sort Limit"},
{"Sort Limit with call_type=Global",
&Node{PlanNode: &spanner.PlanNode{
DisplayName: "Sort Limit",
Metadata: &structpb.Struct{
Fields: map[string]*structpb.Value{
"call_type": {Kind: &structpb.Value_StringValue{StringValue: "Global"}},
},
},
}}, "Global Sort Limit"},
{"Aggregate with iterator_type=Stream",
&Node{PlanNode: &spanner.PlanNode{
DisplayName: "Aggregate",
Metadata: &structpb.Struct{
Fields: map[string]*structpb.Value{
"iterator_type": {Kind: &structpb.Value_StringValue{StringValue: "Stream"}},
},
},
}}, "Stream Aggregate"},
} {
if got := test.node.String(); got != test.want {
t.Errorf("%s: node.String() = %q but want %q", test.title, got, test.want)
}
}
}

0 comments on commit b83c050

Please sign in to comment.