From 06d091cc609e90974f8da7e7ae153f3c2a83ee46 Mon Sep 17 00:00:00 2001 From: JP Appel Date: Fri, 13 Jun 2025 18:33:34 -0400 Subject: Add only child optimization to query parser --- pkg/query/parser.go | 28 ++++++++++++++++++++++++--- pkg/query/parser_test.go | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/pkg/query/parser.go b/pkg/query/parser.go index 5de803c..d7f4fdd 100644 --- a/pkg/query/parser.go +++ b/pkg/query/parser.go @@ -53,8 +53,11 @@ type Statement struct { Operator opType Value Valuer } + +type Statements []Statement + type Clause struct { - Statements []Statement + Statements Statements Clauses []*Clause Operator clauseOperator } @@ -101,7 +104,7 @@ func (v StringValue) Compare(other Valuer) int { } type DatetimeValue struct { - d time.Time + D time.Time } func (v DatetimeValue) Type() valuerType { @@ -114,7 +117,7 @@ func (v DatetimeValue) Compare(other Valuer) int { return 0 } - return v.d.Compare(o.d) + return v.D.Compare(o.D) } var _ Valuer = StringValue{} @@ -230,6 +233,16 @@ func (root *Clause) Flatten() { stack = stack[:top] hasMerged := false + + // merge if only child clause + if len(node.Statements) == 0 && len(node.Clauses) == 1 { + child := node.Clauses[0] + + node.Operator = child.Operator + node.Statements = child.Statements + node.Clauses = child.Clauses + } + // cannot be "modernized", node.Clauses is modified in loop for i := 0; i < len(node.Clauses); i++ { child := node.Clauses[i] @@ -293,6 +306,15 @@ func (root Clause) Depth() int { return maxHeight } +// Order of clause tree +func (root Clause) Order() int { + count := 0 + for range root.DFS() { + count++ + } + return count +} + func (root *Clause) DFS() iter.Seq[*Clause] { return func(yield func(*Clause) bool) { stack := make([]*Clause, 0, len(root.Clauses)) diff --git a/pkg/query/parser_test.go b/pkg/query/parser_test.go index 7ee4987..c1f25d4 100644 --- a/pkg/query/parser_test.go +++ b/pkg/query/parser_test.go @@ -18,6 +18,26 @@ func TestClause_Flatten(t *testing.T) { &query.Clause{}, query.Clause{}, }, + { + "empty with child", + &query.Clause{ + Operator: query.COP_OR, + Clauses: []*query.Clause{ + { + Operator: query.COP_AND, + Statements: []query.Statement{ + {Category: query.CAT_AUTHOR, Operator: query.OP_AP, Value: query.StringValue{"jp"}}, + }, + }, + }, + }, + query.Clause{ + Operator: query.COP_AND, + Statements: []query.Statement{ + {Category: query.CAT_AUTHOR, Operator: query.OP_AP, Value: query.StringValue{"jp"}}, + }, + }, + }, { "already flat", &query.Clause{ @@ -170,3 +190,33 @@ func TestClause_Flatten(t *testing.T) { }) } } + +func TestParse(t *testing.T) { + tests := []struct { + name string // description of this test case + // Named input parameters for target function. + tokens []query.Token + want *query.Clause + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, gotErr := query.Parse(tt.tokens) + if gotErr != nil { + if !tt.wantErr { + t.Errorf("Parse() failed: %v", gotErr) + } + return + } + if tt.wantErr { + t.Fatal("Parse() succeeded unexpectedly") + } + // TODO: update the condition below to compare got with tt.want. + if true { + t.Errorf("Parse() = %v, want %v", got, tt.want) + } + }) + } +} -- cgit v1.2.3