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_percentage_filter(self, node, children): (negation, search_key, _, operator, search_value) = children operator = handle_negation(negation, operator) aggregate_value = None try: # Even if the search value matches percentage format, only act as # percentage for certain columns function = resolve_field(search_key.name, self.params, functions_acl=FUNCTIONS.keys()) if function.aggregate is not None and self.is_percentage_key( function.aggregate[0]): aggregate_value = parse_percentage(search_value) except ValueError: raise InvalidSearchQuery( f"Invalid aggregate query condition: {search_key}") except InvalidQuery as exc: raise InvalidSearchQuery(str(exc)) if aggregate_value is not None: return AggregateFilter(search_key, operator, SearchValue(aggregate_value)) # Invalid formats fall back to text match search_value = operator + search_value if operator != "=" else search_value return AggregateFilter(search_key, "=", SearchValue(search_value))
def convert_aggregate_filter_to_snuba_query(aggregate_filter, params): name = aggregate_filter.key.name value = aggregate_filter.value.value if params is not None and name in params.get("aliases", {}): return params["aliases"][name].converter(aggregate_filter) value = (int(to_timestamp(value)) if isinstance(value, datetime) and name != "timestamp" else value) if aggregate_filter.operator in ( "=", "!=") and aggregate_filter.value.value == "": return [["isNull", [name]], aggregate_filter.operator, 1] function = resolve_field(name, params, functions_acl=FUNCTIONS.keys()) if function.aggregate is not None: name = function.aggregate[-1] return [name, aggregate_filter.operator, value]