Skip to content

Commit ade3ece

Browse files
committed
Elasticsearch implementation for unified query converter
1 parent c76b589 commit ade3ece

File tree

6 files changed

+1572
-54
lines changed

6 files changed

+1572
-54
lines changed

common/persistence/visibility/factory.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ func newVisibilityStoreFromDataStoreConfig(
250250
searchAttributesMapperProvider,
251251
visibilityDisableOrderByClause,
252252
visibilityEnableManualPagination,
253+
visibilityEnableUnifiedQueryConverter,
253254
metricsHandler,
254255
logger,
255256
)
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
package elasticsearch
2+
3+
import (
4+
"strings"
5+
"time"
6+
7+
"github.com/olivere/elastic/v7"
8+
"github.com/temporalio/sqlparser"
9+
"go.temporal.io/server/common/persistence/visibility/store/query"
10+
)
11+
12+
type queryConverter struct{}
13+
14+
var _ query.StoreQueryConverter[elastic.Query] = (*queryConverter)(nil)
15+
16+
func (c *queryConverter) GetDatetimeFormat() string {
17+
return time.RFC3339Nano
18+
}
19+
20+
func (c *queryConverter) BuildParenExpr(expr elastic.Query) (elastic.Query, error) {
21+
if expr == nil {
22+
return nil, nil
23+
}
24+
return expr, nil
25+
}
26+
27+
func (c *queryConverter) BuildNotExpr(expr elastic.Query) (elastic.Query, error) {
28+
if expr == nil {
29+
return nil, nil
30+
}
31+
return elastic.NewBoolQuery().MustNot(expr), nil
32+
}
33+
34+
func (c *queryConverter) BuildAndExpr(exprs ...elastic.Query) (elastic.Query, error) {
35+
validExprs := make([]elastic.Query, 0, len(exprs))
36+
for _, e := range exprs {
37+
if e != nil {
38+
validExprs = append(validExprs, e)
39+
}
40+
}
41+
if len(validExprs) == 0 {
42+
return nil, nil
43+
}
44+
if len(validExprs) == 1 {
45+
return validExprs[0], nil
46+
}
47+
return elastic.NewBoolQuery().Filter(validExprs...), nil
48+
}
49+
50+
func (c *queryConverter) BuildOrExpr(exprs ...elastic.Query) (elastic.Query, error) {
51+
validExprs := make([]elastic.Query, 0, len(exprs))
52+
for _, e := range exprs {
53+
if e != nil {
54+
validExprs = append(validExprs, e)
55+
}
56+
}
57+
if len(validExprs) == 0 {
58+
return nil, nil
59+
}
60+
if len(validExprs) == 1 {
61+
return validExprs[0], nil
62+
}
63+
return elastic.NewBoolQuery().Should(validExprs...).MinimumNumberShouldMatch(1), nil
64+
}
65+
66+
func (c *queryConverter) ConvertComparisonExpr(
67+
operator string,
68+
col *query.SAColName,
69+
value any,
70+
) (elastic.Query, error) {
71+
var res elastic.Query
72+
negate := false
73+
colName := col.FieldName
74+
switch operator {
75+
case sqlparser.GreaterEqualStr:
76+
res = elastic.NewRangeQuery(colName).Gte(value)
77+
case sqlparser.LessEqualStr:
78+
res = elastic.NewRangeQuery(colName).Lte(value)
79+
case sqlparser.GreaterThanStr:
80+
res = elastic.NewRangeQuery(colName).Gt(value)
81+
case sqlparser.LessThanStr:
82+
res = elastic.NewRangeQuery(colName).Lt(value)
83+
case sqlparser.EqualStr, sqlparser.NotEqualStr:
84+
res = elastic.NewTermQuery(colName, value)
85+
negate = operator == sqlparser.NotEqualStr
86+
case sqlparser.InStr, sqlparser.NotInStr:
87+
res = elastic.NewTermsQuery(colName, value.([]any)...)
88+
negate = operator == sqlparser.NotInStr
89+
default:
90+
return nil, query.NewOperatorNotSupportedError(col.Alias, col.ValueType, operator)
91+
}
92+
93+
if negate {
94+
res, _ = c.BuildNotExpr(res)
95+
}
96+
return res, nil
97+
}
98+
99+
func (c *queryConverter) ConvertKeywordComparisonExpr(
100+
operator string,
101+
col *query.SAColName,
102+
value any,
103+
) (elastic.Query, error) {
104+
colName := col.FieldName
105+
switch operator {
106+
case sqlparser.StartsWithStr, sqlparser.NotStartsWithStr:
107+
v, ok := value.(string)
108+
if !ok {
109+
return nil, query.NewConverterError(
110+
"%s: right-hand side of operator '%s' must be a string",
111+
query.InvalidExpressionErrMessage,
112+
strings.ToUpper(operator),
113+
)
114+
}
115+
var res elastic.Query = elastic.NewPrefixQuery(colName, v)
116+
if operator == sqlparser.NotStartsWithStr {
117+
res, _ = c.BuildNotExpr(res)
118+
}
119+
return res, nil
120+
default:
121+
return c.ConvertComparisonExpr(operator, col, value)
122+
}
123+
}
124+
125+
func (c *queryConverter) ConvertKeywordListComparisonExpr(
126+
operator string,
127+
col *query.SAColName,
128+
value any,
129+
) (elastic.Query, error) {
130+
return c.ConvertKeywordComparisonExpr(operator, col, value)
131+
}
132+
133+
func (c *queryConverter) ConvertTextComparisonExpr(
134+
operator string,
135+
col *query.SAColName,
136+
value any,
137+
) (elastic.Query, error) {
138+
colName := col.FieldName
139+
switch operator {
140+
case sqlparser.EqualStr:
141+
return elastic.NewMatchQuery(colName, value), nil
142+
case sqlparser.NotEqualStr:
143+
return elastic.NewBoolQuery().MustNot(elastic.NewMatchQuery(colName, value)), nil
144+
default:
145+
return nil, query.NewOperatorNotSupportedError(col.Alias, col.ValueType, operator)
146+
}
147+
}
148+
149+
func (c *queryConverter) ConvertRangeExpr(
150+
operator string,
151+
col *query.SAColName,
152+
from, to any,
153+
) (elastic.Query, error) {
154+
colName := col.FieldName
155+
switch operator {
156+
case sqlparser.BetweenStr:
157+
return elastic.NewRangeQuery(colName).Gte(from).Lte(to), nil
158+
case sqlparser.NotBetweenStr:
159+
return elastic.NewBoolQuery().MustNot(elastic.NewRangeQuery(colName).Gte(from).Lte(to)), nil
160+
default:
161+
// This should be impossible since the query parser only calls this function with one of those
162+
// operators strings.
163+
return nil, query.NewConverterError(
164+
"%s: unexpected operator '%s' for range condition",
165+
query.MalformedSqlQueryErrMessage,
166+
strings.ToUpper(operator),
167+
)
168+
}
169+
}
170+
171+
func (c *queryConverter) ConvertIsExpr(
172+
operator string,
173+
col *query.SAColName,
174+
) (elastic.Query, error) {
175+
colName := col.FieldName
176+
switch operator {
177+
case sqlparser.IsNullStr:
178+
return elastic.NewBoolQuery().MustNot(elastic.NewExistsQuery(colName)), nil
179+
case sqlparser.IsNotNullStr:
180+
return elastic.NewExistsQuery(colName), nil
181+
default:
182+
// This should be impossible since the query parser only calls this function with one of those
183+
// operators strings.
184+
return nil, query.NewConverterError(
185+
"%s: 'IS' operator can only be used as 'IS NULL' or 'IS NOT NULL'",
186+
query.InvalidExpressionErrMessage,
187+
)
188+
}
189+
}

0 commit comments

Comments
 (0)