Skip to content

Commit fd35d58

Browse files
committed
gis-8882 fix elastic eql regex modifier
1 parent 416f5ca commit fd35d58

File tree

1 file changed

+174
-0
lines changed

1 file changed

+174
-0
lines changed
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
from typing import Optional, Union
2+
3+
from app.translator.const import DEFAULT_VALUE_TYPE
4+
from app.translator.core.const import QUERY_TOKEN_TYPE
5+
from app.translator.core.custom_types.tokens import GroupType, LogicalOperatorType, OperatorType
6+
from app.translator.core.custom_types.values import ValueType
7+
from app.translator.core.mapping import LogSourceSignature, SourceMapping
8+
from app.translator.core.models.platform_details import PlatformDetails
9+
from app.translator.core.models.query_container import TokenizedQueryContainer
10+
from app.translator.core.models.query_tokens.field_value import FieldValue
11+
from app.translator.core.models.query_tokens.identifier import Identifier
12+
from app.translator.core.render import BaseFieldValueRender, PlatformQueryRender
13+
from app.translator.core.str_value_manager import StrValueManager
14+
from app.translator.managers import render_manager
15+
from app.translator.platforms.base.lucene.mapping import LuceneMappings
16+
from app.translator.platforms.elasticsearch.const import elastic_eql_query_details
17+
from app.translator.platforms.elasticsearch.mapping import elastic_eql_query_mappings
18+
from app.translator.platforms.elasticsearch.str_value_manager import eql_str_value_manager
19+
20+
21+
class ElasticSearchEQLFieldValue(BaseFieldValueRender):
22+
details: PlatformDetails = elastic_eql_query_details
23+
str_value_manager: StrValueManager = eql_str_value_manager
24+
list_token = ", "
25+
26+
@staticmethod
27+
def _wrap_str_value(value: str) -> str:
28+
return f'"{value}"'
29+
30+
@staticmethod
31+
def _wrap_int_value(value: int) -> str:
32+
return f'"{value}"'
33+
34+
def apply_field(self, field: str) -> str:
35+
if field.count("-") > 0 or field.count(" ") > 0 or field[0].isdigit():
36+
return f"`{field}`"
37+
if field.endswith(".text"):
38+
return field[:-5]
39+
return field
40+
41+
def equal_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
42+
if isinstance(value, list):
43+
values = self.list_token.join(
44+
self._pre_process_value(field, v, value_type=ValueType.value, wrap_str=True, wrap_int=True)
45+
for v in value
46+
)
47+
return f"{self.apply_field(field)} : ({values})"
48+
value = self._pre_process_value(field, value, value_type=ValueType.value, wrap_str=True, wrap_int=True)
49+
return f"{self.apply_field(field)} : {value}"
50+
51+
def less_modifier(self, field: str, value: Union[int, str]) -> str:
52+
value = self._pre_process_value(field, value, value_type=ValueType.value, wrap_str=True, wrap_int=True)
53+
return f"{self.apply_field(field)} < {value}"
54+
55+
def less_or_equal_modifier(self, field: str, value: Union[int, str]) -> str:
56+
value = self._pre_process_value(field, value, value_type=ValueType.value, wrap_str=True, wrap_int=True)
57+
return f"{self.apply_field(field)} <= {value}"
58+
59+
def greater_modifier(self, field: str, value: Union[int, str]) -> str:
60+
value = self._pre_process_value(field, value, value_type=ValueType.value, wrap_str=True, wrap_int=True)
61+
return f"{self.apply_field(field)} > {value}"
62+
63+
def greater_or_equal_modifier(self, field: str, value: Union[int, str]) -> str:
64+
value = self._pre_process_value(field, value, value_type=ValueType.value, wrap_str=True, wrap_int=True)
65+
return f"{self.apply_field(field)} >= {value}"
66+
67+
def not_equal_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
68+
if isinstance(value, list):
69+
values = self.list_token.join(
70+
self._pre_process_value(field, v, value_type=ValueType.value, wrap_str=True, wrap_int=True)
71+
for v in value
72+
)
73+
return f"{self.apply_field(field)} != ({values})"
74+
value = self._pre_process_value(field, value, value_type=ValueType.value, wrap_str=True, wrap_int=True)
75+
return f"{self.apply_field(field)} != {value}"
76+
77+
def contains_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
78+
if isinstance(value, list):
79+
values = self.list_token.join(
80+
f'"*{self._pre_process_value(field, v, value_type=ValueType.value)}*"' for v in value
81+
)
82+
return f"{self.apply_field(field)} : ({values})"
83+
value = self._pre_process_value(field, value, value_type=ValueType.value)
84+
return f'{self.apply_field(field)} : "*{value}*"'
85+
86+
def endswith_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
87+
if isinstance(value, list):
88+
values = self.list_token.join(
89+
f'"*{self._pre_process_value(field, v, value_type=ValueType.value)}"' for v in value
90+
)
91+
return f"{self.apply_field(field)} : ({values})"
92+
value = self._pre_process_value(field, value, value_type=ValueType.value)
93+
return f'{self.apply_field(field)} : "*{value}"'
94+
95+
def startswith_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
96+
if isinstance(value, list):
97+
values = self.list_token.join(
98+
f'"{self._pre_process_value(field, v, value_type=ValueType.value)}*"' for v in value
99+
)
100+
return f"{self.apply_field(field)} : ({values})"
101+
value = self._pre_process_value(field, value, value_type=ValueType.value)
102+
return f'{self.apply_field(field)} : "{value}*"'
103+
104+
def regex_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
105+
if isinstance(value, list):
106+
return f"({self.or_token.join(self.regex_modifier(field=field, value=v) for v in value)})"
107+
value = self._pre_process_value(field, value, value_type=ValueType.regex_value, wrap_int=True)
108+
return f'{self.apply_field(field)} regex~ "{value}.?"'
109+
110+
def keywords(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
111+
if isinstance(value, list):
112+
return f"({self.or_token.join(self.keywords(field=field, value=v) for v in value)})"
113+
return self._pre_process_value(field, value, wrap_str=True)
114+
115+
def is_none(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: # noqa: ARG002
116+
return f"{self.apply_field(field)} == null"
117+
118+
def is_not_none(self, field: str, value: DEFAULT_VALUE_TYPE) -> str: # noqa: ARG002
119+
return f"{self.apply_field(field)} != null"
120+
121+
122+
@render_manager.register
123+
class ElasticSearchEQLQueryRender(PlatformQueryRender):
124+
details: PlatformDetails = elastic_eql_query_details
125+
mappings: LuceneMappings = elastic_eql_query_mappings
126+
or_token = "or"
127+
and_token = "and"
128+
not_token = "not"
129+
comment_symbol = "//"
130+
field_value_render = ElasticSearchEQLFieldValue(or_token=or_token)
131+
132+
def generate_prefix(self, log_source_signature: Optional[LogSourceSignature], functions_prefix: str = "") -> str: # noqa: ARG002
133+
return "any where "
134+
135+
def in_brackets(self, raw_list: list[QUERY_TOKEN_TYPE]) -> list[QUERY_TOKEN_TYPE]:
136+
return [Identifier(token_type=GroupType.L_PAREN), *raw_list, Identifier(token_type=GroupType.R_PAREN)]
137+
138+
def _generate_from_tokenized_query_container_by_source_mapping(
139+
self, query_container: TokenizedQueryContainer, source_mapping: SourceMapping
140+
) -> str:
141+
unmapped_fields = self.mappings.check_fields_mapping_existence(
142+
query_container.meta_info.query_fields,
143+
query_container.meta_info.function_fields_map,
144+
self.platform_functions.manager.supported_render_names,
145+
source_mapping,
146+
)
147+
rendered_functions = self.generate_functions(query_container.functions.functions, source_mapping)
148+
prefix = self.generate_prefix(source_mapping.log_source_signature, rendered_functions.rendered_prefix)
149+
150+
if source_mapping.raw_log_fields:
151+
defined_raw_log_fields = self.generate_raw_log_fields(
152+
fields=query_container.meta_info.query_fields + query_container.meta_info.function_fields,
153+
source_mapping=source_mapping,
154+
)
155+
prefix += f"\n{defined_raw_log_fields}"
156+
if source_mapping.conditions:
157+
for field, value in source_mapping.conditions.items():
158+
tokens = self.in_brackets(query_container.tokens)
159+
extra_tokens = [
160+
FieldValue(source_name=field, operator=Identifier(token_type=OperatorType.EQ), value=value),
161+
Identifier(token_type=LogicalOperatorType.AND),
162+
]
163+
query_container.tokens = self.in_brackets([*extra_tokens, *tokens])
164+
query = self.generate_query(tokens=query_container.tokens, source_mapping=source_mapping)
165+
not_supported_functions = query_container.functions.not_supported + rendered_functions.not_supported
166+
return self.finalize_query(
167+
prefix=prefix,
168+
query=query,
169+
functions=rendered_functions.rendered,
170+
not_supported_functions=not_supported_functions,
171+
unmapped_fields=unmapped_fields,
172+
meta_info=query_container.meta_info,
173+
source_mapping=source_mapping,
174+
)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy