1
+ import re
1
2
try :
2
3
from multicorn import ANY
3
4
except ImportError :
18
19
"min" : "min" ,
19
20
"sum" : "sum" ,
20
21
"count" : "value_count" ,
22
+ "count.*" : None # not mapped to a particular function
21
23
}
22
24
25
+ _OPERATORS_SUPPORTED = [">" , ">=" , "<" , "<=" , "=" , "<>" , "!=" , "~~" ]
26
+
27
+
28
+ def _convert_pattern_match_to_es (expr ):
29
+ def _pg_es_pattern_map (matchobj ):
30
+ if matchobj .group (0 ) == "%" :
31
+ return "*"
32
+ elif matchobj .group (0 ) == "_" :
33
+ return "?"
34
+ elif matchobj .group (0 ) == "\%" :
35
+ return "%"
36
+ elif matchobj .group (0 ) == "\_" :
37
+ return "_"
38
+ elif matchobj .group (0 ) == "*" :
39
+ return "\\ *"
40
+ elif matchobj .group (0 ) == "?" :
41
+ return "\\ ?"
42
+ elif matchobj .group (0 ) == "\\ \\ " :
43
+ return "\\ "
44
+
45
+ return re .sub (r'\\\\|\\?%|\\?_|\*|\?' , _pg_es_pattern_map , fr"{ expr } " )
46
+
23
47
24
48
def _base_qual_to_es (col , op , value , column_map = None ):
25
49
if column_map :
@@ -43,7 +67,7 @@ def _base_qual_to_es(col, op, value, column_map=None):
43
67
return {"bool" : {"must_not" : {"term" : {col : value }}}}
44
68
45
69
if op == "~~" :
46
- return {"match " : {col : value . replace ( "%" , "*" )}}
70
+ return {"wildcard " : {col : _convert_pattern_match_to_es ( value )}}
47
71
48
72
# For unknown operators, get everything
49
73
return {"match_all" : {}}
@@ -82,6 +106,18 @@ def quals_to_es(
82
106
"""Convert a list of Multicorn quals to an ElasticSearch query"""
83
107
ignore_columns = ignore_columns or []
84
108
109
+ query = {
110
+ "query" : {
111
+ "bool" : {
112
+ "must" : [
113
+ _qual_to_es (q , column_map )
114
+ for q in quals
115
+ if q .field_name not in ignore_columns
116
+ ]
117
+ }
118
+ }
119
+ }
120
+
85
121
# Aggregation/grouping queries
86
122
if aggs is not None :
87
123
aggs_query = {
@@ -91,10 +127,18 @@ def quals_to_es(
91
127
}
92
128
}
93
129
for agg_name , agg_props in aggs .items ()
130
+ if agg_name != "count.*"
94
131
}
95
132
96
133
if group_clauses is None :
97
- return {"aggs" : aggs_query }
134
+ if "count.*" in aggs :
135
+ # There is no particular COUNT(*) equivalent in ES, instead
136
+ # for plain aggregations (e.g. no grouping statements), we need
137
+ # to enable the track_total_hits option in order to get an
138
+ # accuate number of matched docs.
139
+ query ["track_total_hits" ] = True
140
+
141
+ query ["aggs" ] = aggs_query
98
142
99
143
if group_clauses is not None :
100
144
group_query = {
@@ -111,17 +155,6 @@ def quals_to_es(
111
155
if aggs is not None :
112
156
group_query ["group_buckets" ]["aggregations" ] = aggs_query
113
157
114
- return { "aggs" : group_query }
158
+ query [ "aggs" ] = group_query
115
159
116
- # Regular query
117
- return {
118
- "query" : {
119
- "bool" : {
120
- "must" : [
121
- _qual_to_es (q , column_map )
122
- for q in quals
123
- if q .field_name not in ignore_columns
124
- ]
125
- }
126
- }
127
- }
160
+ return query
0 commit comments