def query( self, projects, environments=None, sort_by="date", limit=100, cursor=None, count_hits=False, paginator_options=None, search_filters=None, date_from=None, date_to=None, ): from sentry.models import Group, GroupStatus, GroupSubscription search_filters = search_filters if search_filters is not None else [] # ensure projects are from same org if len({p.organization_id for p in projects}) != 1: raise RuntimeError("Cross organization search not supported") if paginator_options is None: paginator_options = {} group_queryset = Group.objects.filter(project__in=projects).exclude( status__in=[ GroupStatus.PENDING_DELETION, GroupStatus.DELETION_IN_PROGRESS, GroupStatus.PENDING_MERGE, ]) qs_builder_conditions = { "status": QCallbackCondition(lambda status: Q(status=status)), "bookmarked_by": QCallbackCondition(lambda user: Q( bookmark_set__project__in=projects, bookmark_set__user=user)), "assigned_to": QCallbackCondition( functools.partial(assigned_to_filter, projects=projects)), "unassigned": QCallbackCondition( functools.partial(unassigned_filter, projects=projects)), "subscribed_by": QCallbackCondition( lambda user: Q(id__in=GroupSubscription.objects.filter( project__in=projects, user=user, is_active=True). values_list("group"))), "active_at": ScalarCondition("active_at"), } group_queryset = QuerySetBuilder(qs_builder_conditions).build( group_queryset, search_filters) # filter out groups which are beyond the retention period retention = quotas.get_event_retention( organization=projects[0].organization) if retention: retention_window_start = timezone.now() - timedelta(days=retention) else: retention_window_start = None # TODO: This could be optimized when building querysets to identify # criteria that are logically impossible (e.g. if the upper bound # for last seen is before the retention window starts, no results # exist.) if retention_window_start: group_queryset = group_queryset.filter( last_seen__gte=retention_window_start) # TODO: It's possible `first_release` could be handled by Snuba. if environments is not None: environment_ids = [environment.id for environment in environments] group_queryset = group_queryset.filter( groupenvironment__environment_id__in=environment_ids) group_queryset = QuerySetBuilder({ "first_release": QCallbackCondition(lambda version: Q( # if environment(s) are selected, we just filter on the group # environment's first_release attribute. groupenvironment__first_release__organization_id=projects[ 0].organization_id, groupenvironment__first_release__version=version, groupenvironment__environment_id__in=environment_ids, )), "first_seen": ScalarCondition( "groupenvironment__first_seen", {"groupenvironment__environment_id__in": environment_ids}, ), }).build(group_queryset, search_filters) else: group_queryset = QuerySetBuilder({ "first_release": QCallbackCondition(lambda release_version: Q( # if no specific environments are supplied, we either choose any # groups/issues whose first release matches the given release_version, Q(first_release_id__in=Release.objects.filter( version=release_version, organization_id=projects[0].organization_id, )) | # or we choose any groups whose first occurrence in any environment and the latest release at # the time of the groups' first occurrence matches the given # release_version Q(id__in=GroupEnvironment.objects.filter( first_release__version=release_version, first_release__organization_id=projects[0]. organization_id, environment__organization_id=projects[ 0].organization_id, ).values_list("group_id")))), "first_seen": ScalarCondition("first_seen"), }).build(group_queryset, search_filters) query_executor = PostgresSnubaQueryExecutor() return query_executor.query( projects, retention_window_start, group_queryset, environments, sort_by, limit, cursor, count_hits, paginator_options, search_filters, date_from, date_to, )
def _get_query_executor(self, *args, **kwargs): return PostgresSnubaQueryExecutor()
def _get_query_executor(self, *args: Any, **kwargs: Any) -> AbstractQueryExecutor: return PostgresSnubaQueryExecutor()