1616limitations under the License.
1717-----------------------------------------------------------------
1818"""
19-
19+ import itertools
2020from abc import ABC ,abstractmethod
2121from collections .abc import Callable
2222from typing import ClassVar ,Optional ,Union
2323
2424from app .translator .const import DEFAULT_VALUE_TYPE
25- from app .translator .core .context_vars import return_only_first_query_ctx_var
25+ from app .translator .core .const import TOKEN_TYPE
26+ from app .translator .core .context_vars import return_only_first_query_ctx_var ,wrap_query_with_meta_info_ctx_var
2627from app .translator .core .custom_types .tokens import LogicalOperatorType ,OperatorType
2728from app .translator .core .custom_types .values import ValueType
2829from app .translator .core .escape_manager import EscapeManager
2930from app .translator .core .exceptions .core import NotImplementedException ,StrictPlatformException
3031from app .translator .core .exceptions .parser import UnsupportedOperatorException
3132from app .translator .core .functions import PlatformFunctions
3233from app .translator .core .mapping import DEFAULT_MAPPING_NAME ,BasePlatformMappings ,LogSourceSignature ,SourceMapping
33- from app .translator .core .models .field import Field ,FieldValue ,Keyword
34+ from app .translator .core .models .field import Field ,FieldField , FieldValue ,Keyword
3435from app .translator .core .models .functions .base import Function ,RenderedFunctions
3536from app .translator .core .models .identifier import Identifier
3637from app .translator .core .models .platform_details import PlatformDetails
3738from app .translator .core .models .query_container import MetaInfoContainer ,RawQueryContainer ,TokenizedQueryContainer
3839from app .translator .core .str_value_manager import StrValue ,StrValueManager
39- from app .translator .core .tokenizer import TOKEN_TYPE
4040
4141
42- class BaseQueryFieldValue (ABC ):
42+ class BaseFieldValueRender (ABC ):
4343details :PlatformDetails = None
4444escape_manager :EscapeManager = None
4545str_value_manager :StrValueManager = None
4646
4747def __init__ (self ,or_token :str ):
48- self .field_value :dict [str ,Callable [[str ,DEFAULT_VALUE_TYPE ],str ]]= {
48+ self .modifiers_map :dict [str ,Callable [[str ,DEFAULT_VALUE_TYPE ],str ]]= {
4949OperatorType .EQ :self .equal_modifier ,
5050OperatorType .NOT_EQ :self .not_equal_modifier ,
5151OperatorType .LT :self .less_modifier ,
@@ -155,11 +155,20 @@ def apply_value(self, value: Union[str, int], value_type: str = ValueType.value)
155155return self .escape_manager .escape (value ,value_type )
156156
157157def apply_field_value (self ,field :str ,operator :Identifier ,value :DEFAULT_VALUE_TYPE )-> str :
158- if modifier_function := self .field_value .get (operator .token_type ):
158+ if modifier_function := self .modifiers_map .get (operator .token_type ):
159159return modifier_function (field ,value )
160160raise UnsupportedOperatorException (operator .token_type )
161161
162162
163+ class BaseFieldFieldRender (ABC ):
164+ operators_map :ClassVar [dict [str ,str ]]= {}
165+
166+ def apply_field_field (self ,field_left :str ,operator :Identifier ,field_right :str )-> str :
167+ if mapped_operator := self .operators_map .get (operator .token_type ):
168+ return f"{ field_left } { mapped_operator } { field_right } "
169+ raise UnsupportedOperatorException (operator .token_type )
170+
171+
163172class QueryRender (ABC ):
164173comment_symbol :str = None
165174details :PlatformDetails = None
@@ -180,6 +189,13 @@ def render_not_supported_functions(self, not_supported_functions: list) -> str:
180189not_supported_functions_str = "\n " .join (line_template + func .lstrip ()for func in not_supported_functions )
181190return "\n \n " + self .wrap_with_comment (f"{ self .unsupported_functions_text } \n { not_supported_functions_str } " )
182191
192+ def wrap_with_not_supported_functions (self ,query :str ,not_supported_functions :Optional [list ]= None )-> str :
193+ if not_supported_functions and wrap_query_with_meta_info_ctx_var .get ():
194+ rendered_not_supported = self .render_not_supported_functions (not_supported_functions )
195+ return query + rendered_not_supported
196+
197+ return query
198+
183199def wrap_with_comment (self ,value :str )-> str :
184200return f"{ self .comment_symbol } { value } "
185201
@@ -199,13 +215,14 @@ class PlatformQueryRender(QueryRender):
199215group_token = "(%s)"
200216query_parts_delimiter = " "
201217
202- field_value_map = BaseQueryFieldValue (or_token = or_token )
218+ field_field_render = BaseFieldFieldRender ()
219+ field_value_render = BaseFieldValueRender (or_token = or_token )
203220
204221raw_log_field_pattern_map :ClassVar [dict [str ,str ]]= None
205222
206223def __init__ (self ):
207224super ().__init__ ()
208- self .operator_map = {
225+ self .logical_operators_map = {
209226LogicalOperatorType .AND :f"{ self .and_token } " ,
210227LogicalOperatorType .OR :f"{ self .or_token } " ,
211228LogicalOperatorType .NOT :f"{ self .not_token } " ,
@@ -233,31 +250,34 @@ def map_field(self, field: Field, source_mapping: SourceMapping) -> list[str]:
233250
234251def apply_token (self ,token :Union [FieldValue ,Keyword ,Identifier ],source_mapping :SourceMapping )-> str :
235252if isinstance (token ,FieldValue ):
236- if token .alias :
237- field_name = token .alias .name
238- else :
239- mapped_fields = self .map_field (token .field ,source_mapping )
240- if len (mapped_fields )> 1 :
241- return self .group_token % self .operator_map [LogicalOperatorType .OR ].join (
242- [
243- self .field_value_map .apply_field_value (
244- field = field ,operator = token .operator ,value = token .value
245- )
246- for field in mapped_fields
247- ]
248- )
249-
250- field_name = mapped_fields [0 ]
251-
252- return self .field_value_map .apply_field_value (field = field_name ,operator = token .operator ,value = token .value )
253-
253+ mapped_fields = [token .alias .name ]if token .alias else self .map_field (token .field ,source_mapping )
254+ joined = self .logical_operators_map [LogicalOperatorType .OR ].join (
255+ [
256+ self .field_value_render .apply_field_value (field = field ,operator = token .operator ,value = token .value )
257+ for field in mapped_fields
258+ ]
259+ )
260+ return self .group_token % joined if len (mapped_fields )> 1 else joined
261+ if isinstance (token ,FieldField ):
262+ alias_left ,field_left = token .alias_left ,token .field_left
263+ mapped_fields_left = [alias_left .name ]if alias_left else self .map_field (field_left ,source_mapping )
264+ alias_right ,field_right = token .alias_right ,token .field_right
265+ mapped_fields_right = [alias_right .name ]if alias_right else self .map_field (field_right ,source_mapping )
266+ cross_paired_fields = list (itertools .product (mapped_fields_left ,mapped_fields_right ))
267+ joined = self .logical_operators_map [LogicalOperatorType .OR ].join (
268+ [
269+ self .field_field_render .apply_field_field (pair [0 ],token .operator ,pair [1 ])
270+ for pair in cross_paired_fields
271+ ]
272+ )
273+ return self .group_token % joined if len (cross_paired_fields )> 1 else joined
254274if isinstance (token ,Function ):
255275func_render = self .platform_functions .manager .get_in_query_render (token .name )
256276return func_render .render (token ,source_mapping )
257277if isinstance (token ,Keyword ):
258- return self .field_value_map .apply_field_value (field = "" ,operator = token .operator ,value = token .value )
278+ return self .field_value_render .apply_field_value (field = "" ,operator = token .operator ,value = token .value )
259279if token .token_type in LogicalOperatorType :
260- return self .operator_map .get (token .token_type )
280+ return self .logical_operators_map .get (token .token_type )
261281
262282return token .token_type
263283
@@ -273,8 +293,8 @@ def generate_query(self, tokens: list[TOKEN_TYPE], source_mapping: SourceMapping
273293raise StrictPlatformException (self .details .name ,"" ,source_mapping .source_id ,sorted (unmapped_fields ))
274294return "" .join (result_values )
275295
276- def wrap_query_with_meta_info (self ,meta_info : MetaInfoContainer , query : str )-> str :
277- if meta_info and (meta_info .id or meta_info .title ):
296+ def wrap_with_meta_info (self ,query : str , meta_info : Optional [ MetaInfoContainer ] )-> str :
297+ if wrap_query_with_meta_info_ctx_var . get () and meta_info and (meta_info .id or meta_info .title ):
278298meta_info_dict = {
279299"name: " :meta_info .title ,
280300"uuid: " :meta_info .id ,
@@ -307,11 +327,8 @@ def finalize_query(
307327** kwargs ,# noqa: ARG002
308328 )-> str :
309329query = self ._join_query_parts (prefix ,query ,functions )
310- query = self .wrap_query_with_meta_info (meta_info = meta_info ,query = query )
311- if not_supported_functions :
312- rendered_not_supported = self .render_not_supported_functions (not_supported_functions )
313- return query + rendered_not_supported
314- return query
330+ query = self .wrap_with_meta_info (query ,meta_info )
331+ return self .wrap_with_not_supported_functions (query ,not_supported_functions )
315332
316333@staticmethod
317334def unique_queries (queries_map :dict [str ,str ])-> dict [str ,dict [str ]]:
@@ -342,7 +359,7 @@ def _get_source_mappings(self, source_mapping_ids: list[str]) -> list[SourceMapp
342359
343360return source_mappings
344361
345- def _generate_from_raw_query_container (self ,query_container :RawQueryContainer )-> str :
362+ def generate_from_raw_query_container (self ,query_container :RawQueryContainer )-> str :
346363return self .finalize_query (
347364prefix = "" ,query = query_container .query ,functions = "" ,meta_info = query_container .meta_info
348365 )
@@ -380,7 +397,7 @@ def generate_raw_log_fields(self, fields: list[Field], source_mapping: SourceMap
380397defined_raw_log_fields .append (prefix )
381398return "\n " .join (defined_raw_log_fields )
382399
383- def _generate_from_tokenized_query_container (self ,query_container :TokenizedQueryContainer )-> str :
400+ def generate_from_tokenized_query_container (self ,query_container :TokenizedQueryContainer )-> str :
384401queries_map = {}
385402errors = []
386403source_mappings = self ._get_source_mappings (query_container .meta_info .source_mapping_ids )
@@ -417,6 +434,6 @@ def _generate_from_tokenized_query_container(self, query_container: TokenizedQue
417434
418435def generate (self ,query_container :Union [RawQueryContainer ,TokenizedQueryContainer ])-> str :
419436if isinstance (query_container ,RawQueryContainer ):
420- return self ._generate_from_raw_query_container (query_container )
437+ return self .generate_from_raw_query_container (query_container )
421438
422- return self ._generate_from_tokenized_query_container (query_container )
439+ return self .generate_from_tokenized_query_container (query_container )