Skip to content

Commit 72de20d

Browse files
authored
Merge pull request #5 from VictoriaMetrics/fields-comparison
implement fields comparison
2 parents 1acc88c + f88749a commit 72de20d

2 files changed

Lines changed: 89 additions & 26 deletions

File tree

lib/logsql/select.go

Lines changed: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2479,11 +2479,39 @@ func (v *selectTranslatorVisitor) translateExpr(expr ast.Expr) (string, error) {
24792479
}
24802480

24812481
func (v *selectTranslatorVisitor) translateComparison(left, right ast.Expr, cmp comparisonKind) (string, error) {
2482-
field, lit, flipped, err := v.extractFieldAndLiteral(left, right)
2482+
leftField, leftIsField, err := v.fieldNameFromExpr(left)
24832483
if err != nil {
24842484
return "", err
24852485
}
2486+
rightField, rightIsField, err := v.fieldNameFromExpr(right)
2487+
if err != nil {
2488+
return "", err
2489+
}
2490+
2491+
switch {
2492+
case leftIsField && rightIsField:
2493+
return translateFieldComparison(leftField, rightField, cmp)
2494+
case leftIsField:
2495+
lit, err := literalFromExpr(right)
2496+
if err != nil {
2497+
return "", err
2498+
}
2499+
return buildFieldLiteralComparison(leftField, lit, false, cmp)
2500+
case rightIsField:
2501+
lit, err := literalFromExpr(left)
2502+
if err != nil {
2503+
return "", err
2504+
}
2505+
return buildFieldLiteralComparison(rightField, lit, true, cmp)
2506+
default:
2507+
return "", &TranslationError{
2508+
Code: http.StatusBadRequest,
2509+
Message: "translator: comparison requires identifier and literal",
2510+
}
2511+
}
2512+
}
24862513

2514+
func buildFieldLiteralComparison(field string, lit literalValue, flipped bool, cmp comparisonKind) (string, error) {
24872515
switch cmp {
24882516
case comparisonEqual:
24892517
clause := field + ":" + lit.format()
@@ -2519,6 +2547,31 @@ func (v *selectTranslatorVisitor) translateComparison(left, right ast.Expr, cmp
25192547
}
25202548
}
25212549

2550+
func translateFieldComparison(leftField, rightField string, cmp comparisonKind) (string, error) {
2551+
switch cmp {
2552+
case comparisonEqual:
2553+
return fmt.Sprintf("%s:eq_field(%s)", leftField, rightField), nil
2554+
case comparisonNotEqual:
2555+
clause := fmt.Sprintf("%s:eq_field(%s)", leftField, rightField)
2556+
return "-" + clause, nil
2557+
case comparisonLess:
2558+
return fmt.Sprintf("%s:lt_field(%s)", leftField, rightField), nil
2559+
case comparisonLessEqual:
2560+
return fmt.Sprintf("%s:le_field(%s)", leftField, rightField), nil
2561+
case comparisonGreater:
2562+
clause := fmt.Sprintf("%s:le_field(%s)", leftField, rightField)
2563+
return "-" + clause, nil
2564+
case comparisonGreaterEqual:
2565+
clause := fmt.Sprintf("%s:lt_field(%s)", leftField, rightField)
2566+
return "-" + clause, nil
2567+
default:
2568+
return "", &TranslationError{
2569+
Code: http.StatusBadRequest,
2570+
Message: "translator: unsupported comparison kind",
2571+
}
2572+
}
2573+
}
2574+
25222575
func (v *selectTranslatorVisitor) translateBetweenExpr(expr *ast.BetweenExpr) (string, error) {
25232576
if expr == nil {
25242577
return "", &TranslationError{
@@ -2749,31 +2802,6 @@ func (v *selectTranslatorVisitor) rawFieldName(ident *ast.Identifier) (string, e
27492802
return field, nil
27502803
}
27512804

2752-
func (v *selectTranslatorVisitor) extractFieldAndLiteral(left, right ast.Expr) (string, literalValue, bool, error) {
2753-
if name, ok, err := v.fieldNameFromExpr(left); err != nil {
2754-
return "", literalValue{}, false, err
2755-
} else if ok {
2756-
lit, err := literalFromExpr(right)
2757-
if err != nil {
2758-
return "", literalValue{}, false, err
2759-
}
2760-
return name, lit, false, nil
2761-
}
2762-
if name, ok, err := v.fieldNameFromExpr(right); err != nil {
2763-
return "", literalValue{}, false, err
2764-
} else if ok {
2765-
lit, err := literalFromExpr(left)
2766-
if err != nil {
2767-
return "", literalValue{}, false, err
2768-
}
2769-
return name, lit, true, nil
2770-
}
2771-
return "", literalValue{}, false, &TranslationError{
2772-
Code: http.StatusBadRequest,
2773-
Message: "translator: comparison requires identifier and literal",
2774-
}
2775-
}
2776-
27772805
func (v *selectTranslatorVisitor) fieldNameFromExpr(expr ast.Expr) (string, bool, error) {
27782806
switch e := expr.(type) {
27792807
case *ast.Identifier:

lib/logsql/select_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,41 @@ func TestToLogsQLSuccess(t *testing.T) {
201201
sql: "SELECT * FROM logs WHERE message LIKE '_foo'",
202202
expected: "message:~\"^.foo$\"",
203203
},
204+
{
205+
name: "compare fields equality",
206+
sql: "SELECT * FROM logs WHERE user_id = customer_id",
207+
expected: "user_id:eq_field(customer_id)",
208+
},
209+
{
210+
name: "compare fields inequality",
211+
sql: "SELECT * FROM logs WHERE duration != max_duration",
212+
expected: "-duration:eq_field(max_duration)",
213+
},
214+
{
215+
name: "compare fields less than",
216+
sql: "SELECT * FROM logs WHERE duration < max_duration",
217+
expected: "duration:lt_field(max_duration)",
218+
},
219+
{
220+
name: "compare fields less or equal",
221+
sql: "SELECT * FROM logs WHERE duration <= max_duration",
222+
expected: "duration:le_field(max_duration)",
223+
},
224+
{
225+
name: "compare fields greater than",
226+
sql: "SELECT * FROM logs WHERE duration > max_duration",
227+
expected: "-duration:le_field(max_duration)",
228+
},
229+
{
230+
name: "compare fields greater or equal",
231+
sql: "SELECT * FROM logs WHERE duration >= max_duration",
232+
expected: "-duration:lt_field(max_duration)",
233+
},
234+
{
235+
name: "compare function fields equality",
236+
sql: "SELECT * FROM logs WHERE LOWER(user) = LOWER(customer)",
237+
expected: "* | format \"<lc:user>\" as __filter_expr_1 | format \"<lc:customer>\" as __filter_expr_2 | filter __filter_expr_1:eq_field(__filter_expr_2) | delete __filter_expr_1, __filter_expr_2",
238+
},
204239
{
205240
name: "arithmetic projection",
206241
sql: "SELECT (duration_ms / 1000) AS duration_s FROM logs",

0 commit comments

Comments
 (0)