def visit_aggregate_duration_filter(self, node, children): (negation, search_key, _, operator, search_value) = children operator = handle_negation(negation, operator) try: # Even if the search value matches duration format, only act as # duration for certain columns function = resolve_field(search_key.name, self.params, functions_acl=FUNCTIONS.keys()) is_duration_key = False if function.aggregate is not None: args = function.aggregate[1] if isinstance(args, list): is_duration_key = all(self.is_duration_key(arg) for arg in args) else: is_duration_key = self.is_duration_key(args) if is_duration_key: aggregate_value = parse_duration(*search_value) else: # Duration overlaps with numeric values with `m` (million vs # minutes). So we fall through to numeric if it's not a # duration key # # TODO(epurkhiser): Should we validate that the field is # numeric and do some other fallback if it's not? aggregate_value = parse_numeric_value(*search_value) except ValueError: raise InvalidSearchQuery(f"Invalid aggregate query condition: {search_key}") except InvalidQuery as exc: raise InvalidSearchQuery(str(exc)) return AggregateFilter(search_key, operator, SearchValue(aggregate_value))
def visit_aggregate_filter(self, node, children): (negation, search_key, _, operator, search_value) = children operator = self.handle_negation(negation, operator) search_value = search_value[0] if not isinstance(search_value, RegexNode) else search_value try: aggregate_value = None if search_value.expr_name in ["duration_format", "percentage_format"]: # Even if the search value matches duration format, only act as duration for certain columns function = resolve_field( search_key.name, self.params, functions_acl=FUNCTIONS.keys() ) if function.aggregate is not None: if search_value.expr_name == "percentage_format" and self.is_percentage_key( function.aggregate[0] ): aggregate_value = parse_percentage(*search_value.match.groups()) # Extract column and function name out so we can check if we should parse as duration elif search_value.expr_name == "duration_format" and self.is_duration_key( function.aggregate[1] ): aggregate_value = parse_duration(*search_value.match.groups()) if aggregate_value is None: aggregate_value = parse_numeric_value(*search_value.match.groups()) except ValueError: raise InvalidSearchQuery(f"Invalid aggregate query condition: {search_key}") except InvalidQuery as exc: raise InvalidSearchQuery(str(exc)) return AggregateFilter(search_key, operator, SearchValue(aggregate_value))
def visit_aggregate_numeric_filter(self, node, children): (negation, search_key, _, operator, search_value) = children operator = handle_negation(negation, operator) try: aggregate_value = parse_numeric_value(*search_value) except InvalidQuery as exc: raise InvalidSearchQuery(str(exc)) return AggregateFilter(search_key, operator, SearchValue(aggregate_value))
def _handle_numeric_filter(self, search_key, operator, search_value): operator = get_operator_value(operator) if self.is_numeric_key(search_key.name): try: search_value = SearchValue(parse_numeric_value(*search_value)) except InvalidQuery as exc: raise InvalidSearchQuery(str(exc)) return SearchFilter(search_key, operator, search_value) return self._handle_text_filter(search_key, operator, SearchValue("".join(search_value)))
def visit_numeric_in_filter(self, node, children): (negation, search_key, _, search_value) = children operator = handle_negation(negation, "IN") if self.is_numeric_key(search_key.name): try: search_value = SearchValue([parse_numeric_value(*val) for val in search_value]) except InvalidQuery as exc: raise InvalidSearchQuery(str(exc)) return SearchFilter(search_key, operator, search_value) search_value = SearchValue(["".join(value) for value in search_value]) return self._handle_basic_filter(search_key, operator, search_value)
def visit_numeric_filter(self, node, children): (search_key, _, (value, )) = children operator = value[0] if isinstance(operator, Node): if isinstance(operator.expr, Optional): operator = "=" else: operator = operator.text else: operator = operator[0] if operator == "[": operator = "IN" search_value = self.process_list(value[1], value[2]) else: search_value = value[1] if self.is_numeric_key(search_key.name): try: search_value = SearchValue([ parse_numeric_value(*val.match.groups()) for val in search_value ] if operator == "IN" else parse_numeric_value( *search_value.match.groups())) except InvalidQuery as exc: raise InvalidSearchQuery(str(exc)) return SearchFilter(search_key, operator, search_value) else: if operator != "IN": search_value = search_value.text else: search_value = [v.text for v in search_value] search_value = SearchValue(operator + search_value if operator not in ( "=", "IN") else search_value) operator = "=" if operator not in ("=", "IN") else operator return self._handle_basic_filter(search_key, operator, search_value)
def _handle_numeric_filter(self, search_key, operator, search_value): if isinstance(operator, Node): operator = "=" if isinstance(operator.expr, Optional) else operator.text else: operator = operator[0] if self.is_numeric_key(search_key.name): try: search_value = SearchValue(parse_numeric_value(*search_value)) except InvalidQuery as exc: raise InvalidSearchQuery(str(exc)) return SearchFilter(search_key, operator, search_value) return self._handle_text_filter(search_key, operator, SearchValue("".join(search_value)))
def visit_numeric_in_filter(self, node, children): (search_key, _, value) = children operator = "IN" search_value = self.process_list(value[1], value[2]) if self.is_numeric_key(search_key.name): try: search_value = SearchValue([ parse_numeric_value(*val.match.groups()) for val in search_value ]) except InvalidQuery as exc: raise InvalidSearchQuery(str(exc)) return SearchFilter(search_key, operator, search_value) else: search_value = SearchValue([v.text for v in search_value]) return self._handle_basic_filter(search_key, operator, search_value)
def visit_numeric_filter(self, node, children): (search_key, _, operator, search_value) = children if isinstance(operator, Node): operator = "=" if isinstance(operator.expr, Optional) else operator.text else: operator = operator[0] if self.is_numeric_key(search_key.name): try: search_value = SearchValue( parse_numeric_value(*search_value.match.groups())) except InvalidQuery as exc: raise InvalidSearchQuery(str(exc)) return SearchFilter(search_key, operator, search_value) else: search_value = search_value.text search_value = SearchValue( operator + search_value if operator != "=" else search_value) return self._handle_basic_filter(search_key, "=", search_value)
def node_visitor(token): if token["type"] == "spaces": return None if token["type"] == "filter": # Filters with an invalid reason raises to signal to the test # runner that we should expect this exception if token.get("invalid"): raise InvalidSearchQuery(token["invalid"]["reason"]) # Transform the operator to match for list values if token["value"]["type"] in ["valueTextList", "valueNumberList"]: operator = "NOT IN" if token["negated"] else "IN" else: # Negate the operator if the filter is negated to match operator = token["operator"] or "=" operator = f"!{operator}" if token["negated"] else operator key = node_visitor(token["key"]) value = node_visitor(token["value"]) if token["filter"] == "boolean" and token["negated"]: operator = "=" value = SearchValue(raw_value=1 if value.raw_value == 0 else 0) return SearchFilter(key, operator, value) if token["type"] == "keySimple": return SearchKey(name=token["value"]) if token["type"] == "keyExplicitTag": return SearchKey(name=f"tags[{token['key']['value']}]") if token["type"] == "keyAggregate": name = node_visitor(token["name"]).name # Consistent join aggregate function parameters args = ", ".join(arg["value"]["value"] for arg in token["args"]["args"]) return AggregateKey(name=f"{name}({args})") if token["type"] == "valueText": # Noramlize values by removing the escaped quotes value = token["value"].replace('\\"', '"') return SearchValue(raw_value=value) if token["type"] == "valueNumber": return SearchValue( raw_value=parse_numeric_value(token["value"], token["unit"])) if token["type"] == "valueTextList": return SearchValue( raw_value=[item["value"]["value"] for item in token["items"]]) if token["type"] == "valueNumberList": return SearchValue(raw_value=[ item["value"]["rawValue"] for item in token["items"] ]) if token["type"] == "valueIso8601Date": return SearchValue(raw_value=parse_datetime_string(token["value"])) if token["type"] == "valueDuration": return SearchValue( raw_value=parse_duration(token["value"], token["unit"])) if token["type"] == "valueRelativeDate": return SearchValue( raw_value=parse_duration(token["value"], token["unit"])) if token["type"] == "valueBoolean": return SearchValue(raw_value=int(token["value"])) if token["type"] == "freeText": if token["quoted"]: # Normalize quotes value = token["value"].replace('\\"', '"') else: # Normalize spacing value = token["value"].strip(" ") if value == "": return None return SearchFilter( key=SearchKey(name="message"), operator="=", value=SearchValue(raw_value=value), )