Skip to content

Commit 3f2864a

Browse files
authored
Merge pull request #19 from Arkiv-Network/rvdp/pr
Add new query generation strategy
2 parents 18fd3af + f652a4d commit 3f2864a

File tree

2 files changed

+304
-5
lines changed

2 files changed

+304
-5
lines changed

query/exists_method.go

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
package query
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
)
7+
8+
func (t *TopLevel) EvaluateExists(options *QueryOptions) (*SelectQuery, error) {
9+
builder := QueryBuilder{
10+
options: *options,
11+
queryBuilder: &strings.Builder{},
12+
args: []any{},
13+
needsComma: false,
14+
needsWhere: true,
15+
}
16+
17+
builder.queryBuilder.WriteString(strings.Join(
18+
[]string{
19+
"SELECT",
20+
builder.options.columnString(),
21+
"FROM payloads AS e",
22+
},
23+
" ",
24+
))
25+
26+
if builder.options.IncludeData != nil {
27+
if builder.options.IncludeData.Owner {
28+
fmt.Fprintf(builder.queryBuilder,
29+
" INNER JOIN string_attributes AS ownerAttrs INDEXED BY string_attributes_entity_kv_idx"+
30+
" ON e.entity_key = ownerAttrs.entity_key"+
31+
" AND e.from_block = ownerAttrs.from_block"+
32+
" AND ownerAttrs.key = '%s'",
33+
OwnerAttributeKey,
34+
)
35+
}
36+
if builder.options.IncludeData.Expiration {
37+
fmt.Fprintf(builder.queryBuilder,
38+
" INNER JOIN numeric_attributes AS expirationAttrs INDEXED BY numeric_attributes_entity_kv_idx"+
39+
" ON e.entity_key = expirationAttrs.entity_key"+
40+
" AND e.from_block = expirationAttrs.from_block"+
41+
" AND expirationAttrs.key = '%s'",
42+
ExpirationAttributeKey,
43+
)
44+
}
45+
if builder.options.IncludeData.CreatedAtBlock {
46+
fmt.Fprintf(builder.queryBuilder,
47+
" INNER JOIN numeric_attributes AS createdAtBlockAttrs INDEXED BY numeric_attributes_entity_kv_idx"+
48+
" ON e.entity_key = createdAtBlockAttrs.entity_key"+
49+
" AND e.from_block = createdAtBlockAttrs.from_block"+
50+
" AND createdAtBlockAttrs.key = '%s'",
51+
CreatedAtBlockKey,
52+
)
53+
}
54+
if builder.options.IncludeData.LastModifiedAtBlock ||
55+
options.IncludeData.TransactionIndexInBlock ||
56+
options.IncludeData.OperationIndexInTransaction {
57+
fmt.Fprintf(builder.queryBuilder,
58+
" INNER JOIN numeric_attributes AS sequenceAttrs INDEXED BY numeric_attributes_entity_kv_idx"+
59+
" ON e.entity_key = sequenceAttrs.entity_key"+
60+
" AND e.from_block = sequenceAttrs.from_block"+
61+
" AND sequenceAttrs.key = '%s'",
62+
SequenceAttributeKey,
63+
)
64+
}
65+
}
66+
67+
for i, orderBy := range builder.options.OrderByAnnotations {
68+
tableName := ""
69+
indexName := ""
70+
switch orderBy.Type {
71+
case "string":
72+
tableName = "string_attributes"
73+
indexName = "string_attributes_entity_kv_idx"
74+
case "numeric":
75+
tableName = "numeric_attributes"
76+
indexName = "numeric_attributes_entity_kv_idx"
77+
default:
78+
return nil, fmt.Errorf("a type of either 'string' or 'numeric' needs to be provided for the annotation '%s'", orderBy.Name)
79+
}
80+
81+
sortingTable := fmt.Sprintf("arkiv_annotation_sorting%d", i)
82+
83+
keyPlaceholder := builder.pushArgument(orderBy.Name)
84+
85+
fmt.Fprintf(builder.queryBuilder,
86+
" LEFT JOIN %[1]s AS %s INDEXED BY %[4]s"+
87+
" ON %[2]s.entity_key = e.entity_key"+
88+
" AND %[2]s.from_block = e.from_block"+
89+
" AND %[2]s.key = %[3]s",
90+
91+
tableName,
92+
sortingTable,
93+
keyPlaceholder,
94+
indexName,
95+
)
96+
}
97+
98+
err := builder.addPaginationArguments()
99+
if err != nil {
100+
return nil, fmt.Errorf("error adding the pagination condition: %w", err)
101+
}
102+
103+
if builder.needsWhere {
104+
builder.queryBuilder.WriteString(" WHERE ")
105+
builder.needsWhere = false
106+
} else {
107+
builder.queryBuilder.WriteString(" AND ")
108+
}
109+
110+
blockArg := builder.pushArgument(builder.options.AtBlock)
111+
fmt.Fprintf(builder.queryBuilder, "%s BETWEEN e.from_block AND e.to_block - 1", blockArg)
112+
113+
if t.Expression != nil {
114+
t.Expression.addConditions(&builder)
115+
}
116+
117+
builder.queryBuilder.WriteString(" ORDER BY ")
118+
119+
orderColumns := make([]string, 0, len(builder.options.OrderBy))
120+
for _, o := range builder.options.OrderBy {
121+
suffix := ""
122+
if o.Descending {
123+
suffix = " DESC"
124+
}
125+
orderColumns = append(orderColumns, o.Column.Name+suffix)
126+
}
127+
builder.queryBuilder.WriteString(strings.Join(orderColumns, ", "))
128+
129+
fmt.Fprintf(builder.queryBuilder, " LIMIT %d", QueryResultCountLimit)
130+
131+
return &SelectQuery{
132+
Query: builder.queryBuilder.String(),
133+
Args: builder.args,
134+
}, nil
135+
}
136+
137+
func (e *Expression) addConditions(b *QueryBuilder) {
138+
e.Or.addConditions(b)
139+
}
140+
141+
func (e *OrExpression) addConditions(b *QueryBuilder) {
142+
b.queryBuilder.WriteString(" AND (")
143+
e.Left.addConditions(b)
144+
145+
for _, r := range e.Right {
146+
b.queryBuilder.WriteString(") OR (")
147+
r.Expr.addConditions(b)
148+
}
149+
150+
b.queryBuilder.WriteString(")")
151+
}
152+
153+
func (e *AndExpression) addConditions(b *QueryBuilder) {
154+
e.Left.addConditions(b)
155+
156+
for _, r := range e.Right {
157+
b.queryBuilder.WriteString(" AND ")
158+
r.Expr.addConditions(b)
159+
}
160+
161+
}
162+
163+
func (e *EqualExpr) addConditions(b *QueryBuilder) {
164+
var (
165+
attrType string
166+
key string
167+
operation string
168+
value string
169+
)
170+
171+
if e.Assign != nil {
172+
key = b.pushArgument(e.Assign.Var)
173+
val := e.Assign.Value
174+
if val.String != nil {
175+
attrType = "string"
176+
value = b.pushArgument(val.String)
177+
} else {
178+
attrType = "numeric"
179+
value = b.pushArgument(val.Number)
180+
}
181+
182+
operation = "="
183+
if e.Assign.IsNot {
184+
operation = "!="
185+
}
186+
} else if e.Inclusion != nil {
187+
key = b.pushArgument(e.Inclusion.Var)
188+
var values []string
189+
attrType = "string"
190+
if len(e.Inclusion.Values.Strings) > 0 {
191+
values = make([]string, 0, len(e.Inclusion.Values.Strings))
192+
for _, value := range e.Inclusion.Values.Strings {
193+
if e.Inclusion.Var == OwnerAttributeKey ||
194+
e.Inclusion.Var == CreatorAttributeKey ||
195+
e.Inclusion.Var == KeyAttributeKey {
196+
values = append(values, b.pushArgument(strings.ToLower(value)))
197+
} else {
198+
values = append(values, b.pushArgument(value))
199+
}
200+
}
201+
} else {
202+
attrType = "numeric"
203+
values = make([]string, 0, len(e.Inclusion.Values.Numbers))
204+
for _, value := range e.Inclusion.Values.Numbers {
205+
values = append(values, b.pushArgument(value))
206+
}
207+
}
208+
209+
paramStr := strings.Join(values, ", ")
210+
value = fmt.Sprintf("(%s)", paramStr)
211+
212+
operation = "IN"
213+
if e.Inclusion.IsNot {
214+
operation = "NOT IN"
215+
}
216+
} else if e.LessThan != nil {
217+
key = b.pushArgument(e.LessThan.Var)
218+
val := e.LessThan.Value
219+
if val.String != nil {
220+
attrType = "string"
221+
value = b.pushArgument(val.String)
222+
} else {
223+
attrType = "numeric"
224+
value = b.pushArgument(val.Number)
225+
}
226+
operation = "<"
227+
} else if e.LessOrEqualThan != nil {
228+
key = b.pushArgument(e.LessOrEqualThan.Var)
229+
val := e.LessOrEqualThan.Value
230+
if val.String != nil {
231+
attrType = "string"
232+
value = b.pushArgument(val.String)
233+
} else {
234+
attrType = "numeric"
235+
value = b.pushArgument(val.Number)
236+
}
237+
operation = "<="
238+
} else if e.GreaterThan != nil {
239+
key = b.pushArgument(e.GreaterThan.Var)
240+
val := e.GreaterThan.Value
241+
if val.String != nil {
242+
attrType = "string"
243+
value = b.pushArgument(val.String)
244+
} else {
245+
attrType = "numeric"
246+
value = b.pushArgument(val.Number)
247+
}
248+
operation = ">"
249+
} else if e.GreaterOrEqualThan != nil {
250+
key = b.pushArgument(e.GreaterOrEqualThan.Var)
251+
val := e.GreaterOrEqualThan.Value
252+
if val.String != nil {
253+
attrType = "string"
254+
value = b.pushArgument(val.String)
255+
} else {
256+
attrType = "numeric"
257+
value = b.pushArgument(val.Number)
258+
}
259+
operation = ">="
260+
} else if e.Glob != nil {
261+
key = b.pushArgument(e.Glob.Var)
262+
val := e.Glob.Value
263+
attrType = "string"
264+
value = b.pushArgument(val)
265+
266+
operation = "GLOB"
267+
if e.Glob.IsNot {
268+
operation = "NOT GLOB"
269+
}
270+
} else {
271+
panic("EqualExpr::addConditions: unnormalised expression, paren is non-nil")
272+
}
273+
274+
attrTable := "string_attributes"
275+
attrIndex := "string_attributes_entity_kv_idx"
276+
if attrType == "numeric" {
277+
attrTable = "numeric_attributes"
278+
attrIndex = "numeric_attributes_entity_kv_idx"
279+
}
280+
281+
b.queryBuilder.WriteString(strings.Join(
282+
[]string{
283+
"EXISTS (",
284+
"SELECT 1",
285+
"FROM",
286+
attrTable,
287+
"AS a",
288+
"INDEXED BY",
289+
attrIndex,
290+
"WHERE",
291+
"a.entity_key = e.entity_key",
292+
"AND a.from_block = e.from_block",
293+
"AND a.key =",
294+
key,
295+
"AND a.value",
296+
operation,
297+
value,
298+
")",
299+
},
300+
" ",
301+
))
302+
}

query/tables_method.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,10 @@ func (b *QueryBuilder) createLeafQuery(query string) string {
1818
}
1919

2020
func (t *TopLevel) Evaluate(options *QueryOptions) (*SelectQuery, error) {
21-
tableBuilder := strings.Builder{}
22-
args := []any{}
23-
2421
builder := QueryBuilder{
2522
options: *options,
26-
queryBuilder: &tableBuilder,
27-
args: args,
23+
queryBuilder: &strings.Builder{},
24+
args: []any{},
2825
needsComma: false,
2926
needsWhere: true,
3027
}

0 commit comments

Comments
 (0)