Skip to content

Commit 8864f1d

Browse files
committed
Add support for patterns in the where clause.
1 parent cceeed5 commit 8864f1d

File tree

8 files changed

+373
-179
lines changed

8 files changed

+373
-179
lines changed

internal/knowledge/query_expression_builder.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ import (
77
"github.com/clems4ever/go-graphkb/internal/query"
88
)
99

10+
// ExpressionBuilder build a SQL part based on a Cypher expression
1011
type ExpressionBuilder struct {
1112
QueryGraph *QueryGraph
1213

1314
parser *ExpressionParser
1415
visitor *SQLExpressionVisitor
1516
}
1617

18+
// NewExpressionBuilder create a new instance of expression builder
1719
func NewExpressionBuilder(queryGraph *QueryGraph) *ExpressionBuilder {
1820
visitor := SQLExpressionVisitor{}
1921
visitor.queryGraph = queryGraph
@@ -24,6 +26,7 @@ func NewExpressionBuilder(queryGraph *QueryGraph) *ExpressionBuilder {
2426
}
2527
}
2628

29+
// Build the SQL expression from the Cypher expression
2730
func (eb *ExpressionBuilder) Build(q *query.QueryExpression) (string, error) {
2831
err := eb.parser.ParseExpression(q)
2932
if err != nil {
@@ -32,6 +35,7 @@ func (eb *ExpressionBuilder) Build(q *query.QueryExpression) (string, error) {
3235
return eb.visitor.expression, nil
3336
}
3437

38+
// SQLExpressionVisitor visitor used to build the SQL part from the Cypher expression.
3539
type SQLExpressionVisitor struct {
3640
ExpressionVisitorBase
3741

@@ -49,6 +53,10 @@ type SQLExpressionVisitor struct {
4953

5054
functionInvocation string
5155

56+
// This expression should contain the EXIST(SELECT ...) expression
57+
// a Cypher where clause containing a pattern is translated as SQL EXIST clause.
58+
relationshipsPatternExpression string
59+
5260
propertyLabelsExpression string
5361

5462
comparisonExpression string
@@ -156,10 +164,14 @@ func (sev *SQLExpressionVisitor) OnExitPropertyOrLabelsExpression(e query.QueryP
156164
} else if sev.parenthesizedExpression != "" {
157165
sev.propertyLabelsExpression = fmt.Sprintf("(%s)", sev.parenthesizedExpression)
158166
sev.parenthesizedExpression = ""
167+
} else if sev.relationshipsPatternExpression != "" {
168+
sev.propertyLabelsExpression = sev.relationshipsPatternExpression
169+
sev.relationshipsPatternExpression = ""
159170
}
160171
return nil
161172
}
162173

174+
// OnExitFunctionInvocation build the SQL snippet calling the function
163175
func (sev *SQLExpressionVisitor) OnExitFunctionInvocation(name string, distinct bool) error {
164176
distinctStr := ""
165177
if distinct {
@@ -175,6 +187,27 @@ func (sev *SQLExpressionVisitor) OnExitFunctionInvocation(name string, distinct
175187
return nil
176188
}
177189

190+
// OnExitRelationshipsPattern build the SQL query that will be put in the EXIST clause
191+
func (sev *SQLExpressionVisitor) OnExitRelationshipsPattern(q query.QueryRelationshipsPattern, id int) error {
192+
scope := Scope{Context: WhereContext, ID: id}
193+
194+
// Build the constraints for the patterns in the WHERE clause
195+
whereExpressions, from, err := buildSQLConstraintsFromPatterns(sev.queryGraph, nil, scope)
196+
if err != nil {
197+
return fmt.Errorf("Unable to deduce SQL constraints for EXISTS query")
198+
}
199+
200+
// Build a SELECT query such as SELECT 1 FROM assets a0 WHERE a0.type = 'mytype'.
201+
// This is then wrapped into an EXISTS SQL clause
202+
query, err := buildBasicSingleSQLSelect(false, []string{"1"}, from, *whereExpressions)
203+
if err != nil {
204+
return fmt.Errorf("Unable to build SQL query for EXISTS query: %v", err)
205+
}
206+
sev.relationshipsPatternExpression = fmt.Sprintf("EXISTS (%s)", query)
207+
sev.expression = ""
208+
return nil
209+
}
210+
178211
func (sev *SQLExpressionVisitor) OnExitStringListNullOperatorExpression(e query.QueryStringListNullOperatorExpression) error {
179212
if sev.stringExpression != "" {
180213
expression := sev.propertyLabelsExpression[1 : len(sev.propertyLabelsExpression)-1]

internal/knowledge/query_expression_parser.go

Lines changed: 39 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -19,64 +19,6 @@ const (
1919
PropertyExprType ExpressionType = iota
2020
)
2121

22-
// ExpressionVisitor a visitor of expression
23-
type ExpressionVisitor interface {
24-
OnEnterPropertyOrLabelsExpression(e query.QueryPropertyOrLabelsExpression) error
25-
OnExitPropertyOrLabelsExpression(e query.QueryPropertyOrLabelsExpression) error
26-
27-
OnEnterStringListNullOperatorExpression(e query.QueryStringListNullOperatorExpression) error
28-
OnExitStringListNullOperatorExpression(e query.QueryStringListNullOperatorExpression) error
29-
30-
OnVariable(name string) error
31-
OnVariablePropertiesPath(propertiesPath []string) error
32-
33-
OnStringLiteral(value string) error
34-
OnDoubleLiteral(value float64) error
35-
OnIntegerLiteral(value int64) error
36-
OnBooleanLiteral(value bool) error
37-
38-
OnEnterFunctionInvocation(name string, distinct bool) error
39-
OnExitFunctionInvocation(name string, distinct bool) error
40-
41-
OnEnterParenthesizedExpression() error
42-
OnExitParenthesizedExpression() error
43-
44-
OnStringOperator(operator query.StringOperator) error
45-
46-
OnEnterUnaryExpression() error
47-
OnExitUnaryExpression() error
48-
49-
OnEnterPowerOfExpression() error
50-
OnExitPowerOfExpression() error
51-
52-
OnEnterMultipleDivideModuloExpression() error
53-
OnExitMultipleDivideModuloExpression() error
54-
OnMultiplyDivideModuloOperator(operator query.MultiplyDivideModuloOperator) error
55-
56-
OnEnterAddOrSubtractExpression() error
57-
OnExitAddOrSubtractExpression() error
58-
OnAddOrSubtractOperator(operator query.AddOrSubtractOperator) error
59-
60-
OnEnterComparisonExpression() error
61-
OnExitComparisonExpression() error
62-
OnComparisonOperator(operator query.ComparisonOperator) error
63-
64-
OnEnterNotExpression(not bool) error
65-
OnExitNotExpression(not bool) error
66-
67-
OnEnterAndExpression() error
68-
OnExitAndExpression() error
69-
70-
OnEnterXorExpression() error
71-
OnExitXorExpression() error
72-
73-
OnEnterOrExpression() error
74-
OnExitOrExpression() error
75-
76-
OnEnterExpression() error
77-
OnExitExpression() error
78-
}
79-
8022
// ExpressionParser is a parser of expression
8123
type ExpressionParser struct {
8224
visitor ExpressionVisitor
@@ -168,13 +110,30 @@ func (ep *ExpressionParser) ParsePropertyOrLabelsExpression(q *query.QueryProper
168110
}
169111
} else if q.Atom.RelationshipsPattern != nil {
170112
parser := NewPatternParser(ep.queryGraph)
113+
// Parse the pattern to push the nodes and relations into the query graph
171114
err := parser.ParseRelationshipsPattern(
172115
q.Atom.RelationshipsPattern,
173116
Scope{Context: WhereContext, ID: ep.patternIDGenerator})
174117
if err != nil {
175118
return err
176119
}
177-
ep.patternIDGenerator++
120+
defer func() { ep.patternIDGenerator++ }()
121+
122+
err = ep.visitor.OnEnterRelationshipsPattern(*q.Atom.RelationshipsPattern, ep.patternIDGenerator)
123+
if err != nil {
124+
return err
125+
}
126+
127+
err = ep.ParseRelationshipsPattern(q.Atom.RelationshipsPattern)
128+
if err != nil {
129+
return err
130+
}
131+
132+
err = ep.visitor.OnExitRelationshipsPattern(*q.Atom.RelationshipsPattern, ep.patternIDGenerator)
133+
if err != nil {
134+
return err
135+
}
136+
178137
} else {
179138
return fmt.Errorf("Unable to parse property or labels expression")
180139
}
@@ -187,6 +146,27 @@ func (ep *ExpressionParser) ParsePropertyOrLabelsExpression(q *query.QueryProper
187146
return nil
188147
}
189148

149+
// ParseRelationshipsPattern parse a query relationships pattern
150+
func (ep *ExpressionParser) ParseRelationshipsPattern(q *query.QueryRelationshipsPattern) error {
151+
err := ep.visitor.OnNodePattern(q.QueryNodePattern)
152+
if err != nil {
153+
return err
154+
}
155+
156+
for _, pc := range q.QueryPatternElementChains {
157+
err = ep.visitor.OnRelationshipPattern(pc.RelationshipPattern)
158+
if err != nil {
159+
return err
160+
}
161+
162+
err = ep.visitor.OnNodePattern(pc.NodePattern)
163+
if err != nil {
164+
return err
165+
}
166+
}
167+
return nil
168+
}
169+
190170
// ParseStringListNullOperatorExpression parse string list null operator expression
191171
func (ep *ExpressionParser) ParseStringListNullOperatorExpression(q *query.QueryStringListNullOperatorExpression) error {
192172
err := ep.visitor.OnEnterStringListNullOperatorExpression(*q)
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package knowledge
2+
3+
import (
4+
"github.com/clems4ever/go-graphkb/internal/query"
5+
)
6+
7+
// ExpressionVisitor a visitor of expression
8+
type ExpressionVisitor interface {
9+
OnEnterPropertyOrLabelsExpression(e query.QueryPropertyOrLabelsExpression) error
10+
OnExitPropertyOrLabelsExpression(e query.QueryPropertyOrLabelsExpression) error
11+
12+
OnEnterStringListNullOperatorExpression(e query.QueryStringListNullOperatorExpression) error
13+
OnExitStringListNullOperatorExpression(e query.QueryStringListNullOperatorExpression) error
14+
15+
OnVariable(name string) error
16+
OnVariablePropertiesPath(propertiesPath []string) error
17+
18+
OnStringLiteral(value string) error
19+
OnDoubleLiteral(value float64) error
20+
OnIntegerLiteral(value int64) error
21+
OnBooleanLiteral(value bool) error
22+
23+
OnEnterFunctionInvocation(name string, distinct bool) error
24+
OnExitFunctionInvocation(name string, distinct bool) error
25+
26+
OnEnterRelationshipsPattern(q query.QueryRelationshipsPattern, id int) error
27+
OnExitRelationshipsPattern(q query.QueryRelationshipsPattern, id int) error
28+
29+
OnNodePattern(q query.QueryNodePattern) error
30+
OnRelationshipPattern(q query.QueryRelationshipPattern) error
31+
32+
OnEnterParenthesizedExpression() error
33+
OnExitParenthesizedExpression() error
34+
35+
OnStringOperator(operator query.StringOperator) error
36+
37+
OnEnterUnaryExpression() error
38+
OnExitUnaryExpression() error
39+
40+
OnEnterPowerOfExpression() error
41+
OnExitPowerOfExpression() error
42+
43+
OnEnterMultipleDivideModuloExpression() error
44+
OnExitMultipleDivideModuloExpression() error
45+
OnMultiplyDivideModuloOperator(operator query.MultiplyDivideModuloOperator) error
46+
47+
OnEnterAddOrSubtractExpression() error
48+
OnExitAddOrSubtractExpression() error
49+
OnAddOrSubtractOperator(operator query.AddOrSubtractOperator) error
50+
51+
OnEnterComparisonExpression() error
52+
OnExitComparisonExpression() error
53+
OnComparisonOperator(operator query.ComparisonOperator) error
54+
55+
OnEnterNotExpression(not bool) error
56+
OnExitNotExpression(not bool) error
57+
58+
OnEnterAndExpression() error
59+
OnExitAndExpression() error
60+
61+
OnEnterXorExpression() error
62+
OnExitXorExpression() error
63+
64+
OnEnterOrExpression() error
65+
OnExitOrExpression() error
66+
67+
OnEnterExpression() error
68+
OnExitExpression() error
69+
}

internal/knowledge/query_expression_visitor_base.go

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,6 @@ import (
77
// ExpressionVisitorBase expression visitor base interface
88
type ExpressionVisitorBase struct{}
99

10-
func (evb *ExpressionVisitorBase) OnEnterRelationshipsPattern() error {
11-
return nil
12-
}
13-
func (evb *ExpressionVisitorBase) OnExitRelationshipsPattern() error {
14-
return nil
15-
}
16-
17-
func (evb *ExpressionVisitorBase) OnEnterNodePattern() error {
18-
return nil
19-
}
20-
func (evb *ExpressionVisitorBase) OnExitNodePattern() error {
21-
return nil
22-
}
2310
func (evb *ExpressionVisitorBase) OnEnterPropertyOrLabelsExpression(e query.QueryPropertyOrLabelsExpression) error {
2411
return nil
2512
}
@@ -44,6 +31,18 @@ func (evb *ExpressionVisitorBase) OnEnterFunctionInvocation(name string, distinc
4431
func (evb *ExpressionVisitorBase) OnExitFunctionInvocation(name string, distinct bool) error {
4532
return nil
4633
}
34+
func (evb *ExpressionVisitorBase) OnEnterRelationshipsPattern(q query.QueryRelationshipsPattern, id int) error {
35+
return nil
36+
}
37+
func (evb *ExpressionVisitorBase) OnExitRelationshipsPattern(q query.QueryRelationshipsPattern, id int) error {
38+
return nil
39+
}
40+
func (evb *ExpressionVisitorBase) OnNodePattern(q query.QueryNodePattern) error {
41+
return nil
42+
}
43+
func (evb *ExpressionVisitorBase) OnRelationshipPattern(q query.QueryRelationshipPattern) error {
44+
return nil
45+
}
4746
func (evb *ExpressionVisitorBase) OnEnterParenthesizedExpression() error { return nil }
4847
func (evb *ExpressionVisitorBase) OnExitParenthesizedExpression() error { return nil }
4948
func (evb *ExpressionVisitorBase) OnStringOperator(operator query.StringOperator) error { return nil }

internal/knowledge/query_pattern_parser.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func (ep *PatternParser) ParseRelationshipsPattern(q *query.QueryRelationshipsPa
2525
}
2626

2727
for _, z := range q.QueryPatternElementChains {
28-
_, i2, err := ep.queryGraph.PushNode(z.QueryNodePattern, scope)
28+
_, i2, err := ep.queryGraph.PushNode(z.NodePattern, scope)
2929
if err != nil {
3030
return err
3131
}
@@ -47,7 +47,7 @@ func (ep *PatternParser) ParsePatternElement(q *query.QueryPatternElement, scope
4747
}
4848

4949
for _, z := range q.QueryPatternElementChains {
50-
_, i2, err := ep.queryGraph.PushNode(z.QueryNodePattern, scope)
50+
_, i2, err := ep.queryGraph.PushNode(z.NodePattern, scope)
5151
if err != nil {
5252
return err
5353
}

0 commit comments

Comments
 (0)