@@ -256,7 +256,100 @@ def process_query(query):
256256 results .append (part .strip ())
257257
258258 return results
259+
260+
261+ def is_operator_next (expression : str , position : int ) -> str :
262+ """Check in the expression if there is an operator from the list at the given position
263+ and returns it if so.
264+ """
265+ for operator in aditional_operators :
266+ if position < len (expression )- len (operator ) and expression [position :position + len (operator )] == operator :
267+ return operator
268+ return ""
269+
270+
271+ def split_composite_filter (filter_to_split : str ) -> tuple [list [str ], list [str ]]:
272+ """Function to split a filter made of two or more filters separated with an operator.
273+ The split is done at the first level of the filter only.
274+
275+ Examples:
276+ - used on "(field1 or condition1) and (field2 or condition2)" it will return
277+ ["field1 or condition1", "field2 or condition2"] with operators = ["and"]
278+ - used on "field1 or condition1" it will return ["field1", "condition1"] with operators = ["or"]
279+
280+ Note that if the input filter is like "(field1 and condition1)" the parenthesis will be removed and
281+ it will be considered as "field1 and condition1", but if it's like "SomeInfo(field1 and condition1)"
282+ then it won't be considered as a composite filter and won't be splitted.
283+ """
284+ splitted_filter : list [str ] = []
285+ current = []
286+ operators = []
287+ depth = 0
288+ i = 0
289+
290+ # Remove parenthesis if useless (ex: "(ex1 and ex2)" but not "(ex1) and (ex2)")
291+ if re .fullmatch (r'\([^()]*\)' , filter_to_split .strip ()):
292+ filter_to_split = filter_to_split .removeprefix ("(" )
293+ filter_to_split = filter_to_split .removesuffix (")" )
294+
295+ # Split filter at depth 0 based on operators (anything outside parenthesis basically)
296+ while i < len (filter_to_split ):
297+ if filter_to_split [i ] == '(' :
298+ depth += 1
299+ current .append (filter_to_split [i ])
300+ i += 1
301+ elif filter_to_split [i ] == ')' :
302+ depth -= 1
303+ current .append (filter_to_split [i ])
304+ i += 1
305+ elif depth == 0 and (operator := is_operator_next (filter_to_split , i )):
306+ splitted_filter .append ('' .join (current ).strip ())
307+ current = []
308+ operators .append (operator .strip ())
309+ i += len (operator )
310+ else :
311+ current .append (filter_to_split [i ])
312+ i += 1
313+
314+ if current :
315+ splitted_filter .append ('' .join (current ).strip ())
316+
317+ # Return subfilters and operators found
318+ return splitted_filter , operators
319+
320+
321+ def process_filter (request , input_filter ):
322+ """Recursive function to go through any filter (composite or not) and return
323+ the result of the full filter.
324+ """
325+ # Split the filter
326+ splitted_filters , operators = split_composite_filter (input_filter )
327+
328+ # If there is only one filter, apply it and gather results
329+ if len (splitted_filters )== 1 :
330+ end_filter = splitted_filters [0 ]
331+ if "Attributes" in end_filter or "OData.CSC" in end_filter :
332+ return process_attributes_search (end_filter , request .args )
333+ return process_products_request (str (end_filter ), request .args )
259334
335+ # If there are two filters, repeat operation on both of them and combine their
336+ # results with the operator retrieved
337+ # elif len(splitted_filters)==2:
338+ # result_filter_1 = process_filter(request, splitted_filters[0])
339+ # result_filter_2 = process_filter(request, splitted_filters[1])
340+ # return process_common_elements(result_filter_1, result_filter_2, operators[0])
341+
342+ # If there are more than two filters, repeat operation on each one and combine its
343+ # results with the ones of the previous one using the correct operator
344+ else :
345+ i = 1
346+ final_results = process_filter (request , splitted_filters [0 ])
347+ while i < len (splitted_filters ):
348+ current_filter_results = process_filter (request , splitted_filters [i ])
349+ final_results = process_common_elements (final_results , current_filter_results , operators [i - 1 ])
350+ return final_results
351+
352+
260353def extract_values_and_operation (part1 , part2 ):
261354 # Regular expression to capture the operation and value between single quotes
262355 pattern = r"(\b(eq|gt|lt)\b)\s+'(.*?)'"
@@ -447,66 +540,7 @@ def query_products():
447540 logger .error (msg )
448541 return Response ("Too complex for adgs sim" , status = HTTPStatus .BAD_REQUEST )
449542
450- if len (qs_parser := request .args ['$filter' ].split (' and ' )) > 2 :
451- outputs = []
452- properties_filter = []
453- attributes_filter = []
454- for filter in qs_parser :
455- if any (property in filter for property in ["contains" , "PublicationDate" ]):
456- properties_filter .append (filter )
457- elif "OData.CSC.StringAttribute" in filter :
458- attributes_filter .append (filter )
459-
460- if len (properties_filter ) > 4 or len (attributes_filter ) > 4 :
461- msg = "Too complex for adgs sim"
462- logger .error (msg )
463- return Response ("Too complex for adgs sim" , status = HTTPStatus .BAD_REQUEST )
464- # Tempfix, when filter is very complex, use only GT / LT
465- properties_filter = [f .split (" or " )[0 ].strip ("'\" ()" ) if " or " in f else f for f in properties_filter ]
466- # Process each property in the filter
467- processed_requests = [
468- process_products_request (prop .strip ("'\" " ), request .args )
469- for prop in properties_filter
470- ]
471-
472- # Combine the processed requests based on the number of filters
473- if len (processed_requests ) in {1 , 2 , 3 }:
474- if len (processed_requests ) == 1 :
475- outputs .append (processed_requests [0 ])
476- else :
477- common_elements = processed_requests [0 ]
478- for req in processed_requests [1 :]:
479- common_elements = process_common_elements (common_elements , req , "and" )
480- outputs .append (common_elements )
481- if not attributes_filter :
482- # If there are no attributes to process, just return this
483- return Response (status = outputs [0 ].status , response = outputs [0 ].data , headers = request .args )
484-
485-
486- # Handle attributes_filter processing
487- if len (attributes_filter ) in {2 , 4 }:
488- for i in range (0 , len (attributes_filter ), 2 ):
489- outputs .append (process_attributes_search (f"{ attributes_filter [i ]} and { attributes_filter [i + 1 ]} " , request .args ))
490-
491- try :
492- return process_common_elements (outputs [0 ], outputs [1 ], "and" )
493- except IndexError :
494- return Response (status = HTTPStatus .OK , response = json .dumps ({"value" : []}))
495-
496- if "Attributes" in request .args ['$filter' ] or "OData.CSC" in request .args ['$filter' ]:
497- return process_attributes_search (request .args ['$filter' ], request .args )
498- if any (header in request .args ["$filter" ] for header in aditional_operators ):
499- pattern = r"(\S+ \S+ \S+) (\S+) (\S+ \S+ \S+)"
500- groups = re .search (pattern , request .args ["$filter" ])
501- if groups :
502- first_request , operator , second_request = groups .group (1 ), groups .group (2 ), groups .group (3 )
503- # split and processes the requests
504- first_response = process_products_request (first_request .replace ('"' , "" ), request .args )
505- second_response = process_products_request (second_request .replace ('"' , "" ), request .args )
506- # Load response data to a json dict
507- return process_common_elements (first_response , second_response , operator )
508-
509- return process_products_request (str (request .args ["$filter" ]), request .args )
543+ return process_filter (request , request .args ['$filter' ])
510544
511545
512546@app .route ("/Products(<Id>)/$value" , methods = ["GET" ])
0 commit comments