@@ -69,9 +69,113 @@ func BuildAndOrExpression(tree AndOrExpression) (string, error) {
69
69
return "" , nil
70
70
}
71
71
72
+ func CrossProductExpressions (and1 []AndOrExpression , and2 []AndOrExpression ) []AndOrExpression {
73
+ outExpr := []AndOrExpression {}
74
+ for i := range and1 {
75
+ for j := range and2 {
76
+ children := AndOrExpression {
77
+ And : true ,
78
+ Children : []AndOrExpression {and1 [i ], and2 [j ]},
79
+ }
80
+ outExpr = append (outExpr , children )
81
+ }
82
+ }
83
+ return outExpr
84
+ }
85
+
86
+ // UnwindOrExpressions in order to transform query with or relations into a union
87
+ // query which is more performant, an AndOrExpression is transformed into a list of AndExpressions
88
+ func UnwindOrExpressions (tree AndOrExpression ) ([]AndOrExpression , error ) {
89
+ if tree .Expression != "" {
90
+ child := AndOrExpression {Children : []AndOrExpression {tree }, And : true }
91
+ return []AndOrExpression {child }, nil
92
+ } else if ! tree .And {
93
+ exprs := []AndOrExpression {}
94
+ for i := range tree .Children {
95
+ nestedExpressions , err := UnwindOrExpressions (tree .Children [i ])
96
+ if err != nil {
97
+ return nil , err
98
+ }
99
+ exprs = append (exprs , nestedExpressions ... )
100
+ }
101
+ return exprs , nil
102
+ } else if tree .And {
103
+ exprs := []AndOrExpression {}
104
+ for i := range tree .Children {
105
+ expr , err := UnwindOrExpressions (tree .Children [i ])
106
+ if err != nil {
107
+ return nil , err
108
+ }
109
+
110
+ if len (exprs ) == 0 {
111
+ exprs = append (exprs , expr ... )
112
+ } else {
113
+ exprs = CrossProductExpressions (exprs , expr )
114
+ }
115
+ }
116
+ return exprs , nil
117
+ }
118
+ return nil , fmt .Errorf ("Unable to detect kind of node" )
119
+ }
120
+
72
121
func (sqt * SQLQueryTranslator ) buildSQLSelect (
122
+ distinct bool , projections []string , projectionTypes []Projection , fromTables []string ,
123
+ whereExpressions AndOrExpression , groupBy []int , limit int , offset int ) (string , error ) {
124
+ var sqlQuery string
125
+
126
+ andExpressions , err := UnwindOrExpressions (whereExpressions )
127
+ if err != nil {
128
+ return "" , err
129
+ }
130
+
131
+ if len (andExpressions ) > 1 {
132
+ singleQueries := []string {}
133
+ for _ , where := range andExpressions {
134
+ singleQuery , err := sqt .buildSingleSQLSelect (false , projections , fromTables , where , nil , 0 , 0 )
135
+ if err != nil {
136
+ return "" , err
137
+ }
138
+ singleQueries = append (singleQueries , fmt .Sprintf ("(%s)" , singleQuery ))
139
+ }
140
+ if distinct {
141
+ sqlQuery = strings .Join (singleQueries , "\n UNION\n " )
142
+ } else {
143
+ sqlQuery = strings .Join (singleQueries , "\n UNION ALL\n " )
144
+ }
145
+
146
+ if len (groupBy ) > 0 {
147
+ groupByProjections := []string {}
148
+ for i := range groupBy {
149
+ groupByProjections = append (groupByProjections , projections [groupBy [i ]])
150
+ }
151
+
152
+ sqlQuery = fmt .Sprintf ("SELECT %s FROM\n (%s)\n GROUP BY %s" ,
153
+ strings .Join (projections , ", " ), sqlQuery , strings .Join (groupByProjections , "," ))
154
+ }
155
+
156
+ if limit > 0 {
157
+ sqlQuery += fmt .Sprintf ("\n LIMIT %d" , limit )
158
+ }
159
+
160
+ if offset > 0 {
161
+ sqlQuery += fmt .Sprintf ("\n OFFSET %d" , offset )
162
+ }
163
+
164
+ } else {
165
+ and := AndOrExpression {And : true , Children : andExpressions }
166
+ singleQuery , err := sqt .buildSingleSQLSelect (distinct , projections , fromTables , and , groupBy , limit , offset )
167
+ if err != nil {
168
+ return "" , err
169
+ }
170
+ sqlQuery = singleQuery
171
+ }
172
+
173
+ return sqlQuery , nil
174
+ }
175
+
176
+ func (sqt * SQLQueryTranslator ) buildSingleSQLSelect (
73
177
distinct bool , projections []string , fromTables []string ,
74
- whereExpressions AndOrExpression , groupBy []string , limit int , offset int ) (string , error ) {
178
+ whereExpressions AndOrExpression , groupBy []int , limit int , offset int ) (string , error ) {
75
179
76
180
projectionsStr := ""
77
181
if distinct {
@@ -92,7 +196,11 @@ func (sqt *SQLQueryTranslator) buildSQLSelect(
92
196
}
93
197
94
198
if len (groupBy ) > 0 {
95
- sqlQuery += fmt .Sprintf ("\n GROUP BY %s" , strings .Join (groupBy , ", " ))
199
+ groupByProjection := make ([]string , len (groupBy ))
200
+ for i := range groupBy {
201
+ groupByProjection [i ] = projections [groupBy [i ]]
202
+ }
203
+ sqlQuery += fmt .Sprintf ("\n GROUP BY %s" , strings .Join (groupByProjection , ", " ))
96
204
}
97
205
98
206
if limit > 0 {
@@ -154,10 +262,10 @@ func (sqt *SQLQueryTranslator) Translate(query *query.QueryCypher) (*SQLTranslat
154
262
projectionTypes := make ([]Projection , 0 )
155
263
from := make ([]string , 0 )
156
264
157
- unaggregatedProjectionItems := make ([] string , 0 )
265
+ unaggregatedProjectionItems := [] int {}
158
266
aggregationRequired := false
159
267
160
- for _ , p := range query .QuerySinglePartQuery .ProjectionBody .ProjectionItems {
268
+ for i , p := range query .QuerySinglePartQuery .ProjectionBody .ProjectionItems {
161
269
projectionVisitor := ProjectionVisitor {QueryGraph : & sqt .QueryGraph }
162
270
err := projectionVisitor .ParseExpression (& p .Expression )
163
271
if err != nil {
@@ -170,7 +278,7 @@ func (sqt *SQLQueryTranslator) Translate(query *query.QueryCypher) (*SQLTranslat
170
278
}
171
279
172
280
if ! projectionVisitor .Aggregation {
173
- unaggregatedProjectionItems = append (unaggregatedProjectionItems , projection )
281
+ unaggregatedProjectionItems = append (unaggregatedProjectionItems , i )
174
282
} else {
175
283
aggregationRequired = true
176
284
}
@@ -197,18 +305,24 @@ func (sqt *SQLQueryTranslator) Translate(query *query.QueryCypher) (*SQLTranslat
197
305
})
198
306
}
199
307
200
- // Append assets constraints
201
- andExpressions .Children = append (andExpressions .Children , typesConstraints )
308
+ if len (typesConstraints .Children ) > 0 {
309
+ // Append assets constraints
310
+ andExpressions .Children = append (andExpressions .Children , typesConstraints )
311
+ }
202
312
}
203
313
for i , r := range sqt .QueryGraph .Relations {
204
314
alias := fmt .Sprintf ("r%d" , i )
205
315
from = append (from , fmt .Sprintf ("relations %s" , alias ))
206
316
317
+ typesConstraints := AndOrExpression {And : false }
207
318
for _ , label := range r .Labels {
208
- andExpressions .Children = append (andExpressions .Children , AndOrExpression {
319
+ typesConstraints .Children = append (typesConstraints .Children , AndOrExpression {
209
320
Expression : fmt .Sprintf ("%s.type = '%s'" , alias , label ),
210
321
})
211
322
}
323
+ if len (typesConstraints .Children ) > 0 {
324
+ andExpressions .Children = append (andExpressions .Children , typesConstraints )
325
+ }
212
326
213
327
out := AndOrExpression {
214
328
And : true ,
@@ -280,7 +394,6 @@ func (sqt *SQLQueryTranslator) Translate(query *query.QueryCypher) (*SQLTranslat
280
394
}
281
395
andExpressions .Children = append (andExpressions .Children , orExpression )
282
396
}
283
-
284
397
}
285
398
}
286
399
@@ -309,11 +422,14 @@ func (sqt *SQLQueryTranslator) Translate(query *query.QueryCypher) (*SQLTranslat
309
422
offset = int (skipVisitor .Skip )
310
423
}
311
424
312
- andExpressions .Children = append (andExpressions .Children , filterExpressions )
425
+ if len (filterExpressions .Children ) > 0 {
426
+ andExpressions .Children = append (andExpressions .Children , filterExpressions )
427
+ }
313
428
314
429
sqlQuery , err := sqt .buildSQLSelect (
315
430
query .QuerySinglePartQuery .ProjectionBody .Distinct ,
316
431
projections ,
432
+ projectionTypes ,
317
433
from ,
318
434
andExpressions ,
319
435
unaggregatedProjectionItems ,
0 commit comments