Skip to content

Commit

Permalink
Revert "typing(issue_search): Add typing to issue search backends and…
Browse files Browse the repository at this point in the history
… executors (#29902)"

This reverts commit 38307fc.

Co-authored-by: dfuller via Slack <dfuller@sentry.io>
  • Loading branch information
getsentry-bot and wedamija committed Nov 10, 2021
1 parent 1f74b07 commit 9f9870b
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 204 deletions.
3 changes: 0 additions & 3 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ files = src/sentry/api/bases/external_actor.py,
src/sentry/notifications/**/*.py,
src/sentry/processing/realtime_metrics/,
src/sentry/release_health/**/*.py,
src/sentry/search/base.py,
src/sentry/search/events/constants.py,
src/sentry/search/snuba/*.py,
src/sentry/sentry_metrics/**/*.py,
src/sentry/shared_integrations/constants.py,
src/sentry/snuba/outcomes.py,
Expand Down
38 changes: 13 additions & 25 deletions src/sentry/search/base.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,26 @@
from __future__ import annotations

from datetime import datetime
from typing import TYPE_CHECKING, Any, FrozenSet, Mapping, Optional, Sequence

from sentry.utils.services import Service

ANY = object()

if TYPE_CHECKING:
from sentry.api.event_search import SearchFilter
from sentry.models import Environment, Project
from sentry.utils.cursors import Cursor, CursorResult


class SearchBackend(Service): # type: ignore
class SearchBackend(Service):
__read_methods__ = frozenset(["query"])
__write_methods__: FrozenSet[str] = frozenset()
__write_methods__ = frozenset()
__all__ = __read_methods__ | __write_methods__

def __init__(self, **options: Optional[Mapping[str, Any]]):
def __init__(self, **options):
pass

def query(
self,
projects: Sequence[Project],
environments: Optional[Sequence[Environment]] = None,
sort_by: str = "date",
limit: int = 100,
cursor: Optional[Cursor] = None,
count_hits: bool = False,
paginator_options: Optional[Mapping[str, Any]] = None,
search_filters: Optional[Sequence[SearchFilter]] = None,
date_from: Optional[datetime] = None,
date_to: Optional[datetime] = None,
max_hits: Optional[int] = None,
) -> CursorResult:
projects,
tags=None,
environments=None,
sort_by="date",
limit=100,
cursor=None,
count_hits=False,
paginator_options=None,
**parameters,
):
raise NotImplementedError
123 changes: 41 additions & 82 deletions src/sentry/search/snuba/backend.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
from __future__ import annotations

import functools
from abc import ABCMeta, abstractmethod
from collections import defaultdict
from datetime import datetime, timedelta
from typing import Any, Callable, Dict, Mapping, Optional, Sequence
from datetime import timedelta
from typing import Sequence

from django.db.models import Q, QuerySet
from django.db.models import Q
from django.utils import timezone
from django.utils.functional import SimpleLazyObject

from sentry import quotas
from sentry.api.event_search import SearchFilter
from sentry.exceptions import InvalidSearchQuery
from sentry.models import (
Environment,
Group,
GroupAssignee,
GroupEnvironment,
Expand All @@ -34,17 +30,10 @@
)
from sentry.search.base import SearchBackend
from sentry.search.events.constants import EQUALITY_OPERATORS, OPERATOR_TO_DJANGO
from sentry.search.snuba.executors import (
AbstractQueryExecutor,
CdcPostgresSnubaQueryExecutor,
PostgresSnubaQueryExecutor,
)
from sentry.utils.cursors import Cursor, CursorResult
from sentry.search.snuba.executors import CdcPostgresSnubaQueryExecutor, PostgresSnubaQueryExecutor


def assigned_to_filter(
actors: Sequence[User | Team | None], projects: Sequence[Project], field_filter: str = "id"
) -> Q:
def assigned_to_filter(actors, projects, field_filter="id"):
from sentry.models import OrganizationMember, OrganizationMemberTeam, Team

include_none = False
Expand Down Expand Up @@ -99,7 +88,7 @@ def assigned_to_filter(
return query


def unassigned_filter(unassigned: bool, projects: Sequence[Project], field_filter: str = "id") -> Q:
def unassigned_filter(unassigned, projects, field_filter="id"):
query = Q(
**{
f"{field_filter}__in": GroupAssignee.objects.filter(
Expand All @@ -112,7 +101,7 @@ def unassigned_filter(unassigned: bool, projects: Sequence[Project], field_filte
return query


def linked_filter(linked: bool, projects: Sequence[Project]) -> Q:
def linked_filter(linked, projects):
"""
Builds a filter for whether or not a Group has an issue linked via either
a PlatformExternalIssue or an ExternalIssue.
Expand Down Expand Up @@ -149,9 +138,7 @@ def linked_filter(linked: bool, projects: Sequence[Project]) -> Q:
return query


def first_release_all_environments_filter(
versions: Sequence[str], projects: Sequence[Project]
) -> Q:
def first_release_all_environments_filter(versions, projects):
releases = {
id_: version
for id_, version in Release.objects.filter(
Expand All @@ -177,7 +164,7 @@ def first_release_all_environments_filter(
)


def inbox_filter(inbox: bool, projects: Sequence[Project]) -> Q:
def inbox_filter(inbox, projects):
query = Q(groupinbox__id__isnull=False)
if not inbox:
query = ~query
Expand All @@ -186,9 +173,7 @@ def inbox_filter(inbox: bool, projects: Sequence[Project]) -> Q:
return query


def assigned_or_suggested_filter(
owners: Sequence[User | Team | None], projects: Sequence[Project], field_filter: str = "id"
) -> Q:
def assigned_or_suggested_filter(owners, projects, field_filter="id"):
organization_id = projects[0].organization_id
project_ids = [p.id for p in projects]

Expand Down Expand Up @@ -284,15 +269,15 @@ class Condition:
``QuerySetBuilder``.
"""

def apply(self, queryset: QuerySet, search_filter: SearchFilter) -> QuerySet:
def apply(self, queryset, name, parameters):
raise NotImplementedError


class QCallbackCondition(Condition):
def __init__(self, callback: Callable[[Any], QuerySet]):
def __init__(self, callback):
self.callback = callback

def apply(self, queryset: QuerySet, search_filter: SearchFilter) -> QuerySet:
def apply(self, queryset, search_filter):
value = search_filter.value.raw_value
q = self.callback(value)
if search_filter.operator not in ("=", "!=", "IN", "NOT IN"):
Expand All @@ -312,17 +297,17 @@ class ScalarCondition(Condition):
instances
"""

def __init__(self, field: str, extra: Optional[dict[str, Sequence[int]]] = None):
def __init__(self, field, extra=None):
self.field = field
self.extra = extra

def _get_operator(self, search_filter: SearchFilter) -> str:
def _get_operator(self, search_filter):
django_operator = OPERATOR_TO_DJANGO.get(search_filter.operator, "")
if django_operator:
django_operator = f"__{django_operator}"
return django_operator

def apply(self, queryset: QuerySet, search_filter: SearchFilter) -> QuerySet:
def apply(self, queryset, search_filter):
django_operator = self._get_operator(search_filter)
qs_method = queryset.exclude if search_filter.operator == "!=" else queryset.filter

Expand All @@ -334,10 +319,10 @@ def apply(self, queryset: QuerySet, search_filter: SearchFilter) -> QuerySet:


class QuerySetBuilder:
def __init__(self, conditions: Mapping[str, Condition]):
def __init__(self, conditions):
self.conditions = conditions

def build(self, queryset: QuerySet, search_filters: Sequence[SearchFilter]) -> QuerySet:
def build(self, queryset, search_filters):
for search_filter in search_filters:
name = search_filter.key.name
if name in self.conditions:
Expand All @@ -349,18 +334,18 @@ def build(self, queryset: QuerySet, search_filters: Sequence[SearchFilter]) -> Q
class SnubaSearchBackendBase(SearchBackend, metaclass=ABCMeta):
def query(
self,
projects: Sequence[Project],
environments: Optional[Sequence[Environment]] = None,
sort_by: str = "date",
limit: int = 100,
cursor: Optional[Cursor] = None,
count_hits: bool = False,
paginator_options: Optional[Mapping[str, Any]] = None,
search_filters: Optional[Sequence[SearchFilter]] = None,
date_from: Optional[datetime] = None,
date_to: Optional[datetime] = None,
max_hits: Optional[int] = None,
) -> CursorResult:
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,
max_hits=None,
):
search_filters = search_filters if search_filters is not None else []

# ensure projects are from same org
Expand Down Expand Up @@ -416,14 +401,8 @@ def query(
)

def _build_group_queryset(
self,
projects: Sequence[Project],
environments: Optional[Sequence[Environment]],
search_filters: Sequence[SearchFilter],
retention_window_start: Optional[datetime],
*args: Any,
**kwargs: Any,
) -> QuerySet:
self, projects, environments, search_filters, retention_window_start, *args, **kwargs
):
"""This method should return a QuerySet of the Group model.
How you implement it is up to you, but we generally take in the various search parameters
and filter Group's down using the field's we want to query on in Postgres."""
Expand All @@ -440,12 +419,8 @@ def _build_group_queryset(
return group_queryset

def _initialize_group_queryset(
self,
projects: Sequence[Project],
environments: Optional[Sequence[Environment]],
retention_window_start: Optional[datetime],
search_filters: Sequence[SearchFilter],
) -> QuerySet:
self, projects, environments, retention_window_start, search_filters
):
group_queryset = Group.objects.filter(project__in=projects).exclude(
status__in=[
GroupStatus.PENDING_DELETION,
Expand All @@ -467,41 +442,25 @@ def _initialize_group_queryset(
return group_queryset

@abstractmethod
def _get_queryset_conditions(
self,
projects: Sequence[Project],
environments: Optional[Sequence[Environment]],
search_filters: Sequence[SearchFilter],
) -> Mapping[str, Condition]:
def _get_queryset_conditions(self, projects, environments, search_filters):
"""This method should return a dict of query set fields and a "Condition" to apply on that field."""
raise NotImplementedError

@abstractmethod
def _get_query_executor(
self,
group_queryset: QuerySet,
projects: Sequence[Project],
environments: Optional[Sequence[Environment]],
search_filters: Sequence[SearchFilter],
date_from: Optional[datetime],
date_to: Optional[datetime],
) -> AbstractQueryExecutor:
self, group_queryset, projects, environments, search_filters, date_from, date_to
):
"""This method should return an implementation of the AbstractQueryExecutor
We will end up calling .query() on the class returned by this method"""
raise NotImplementedError


class EventsDatasetSnubaSearchBackend(SnubaSearchBackendBase):
def _get_query_executor(self, *args: Any, **kwargs: Any) -> AbstractQueryExecutor:
def _get_query_executor(self, *args, **kwargs):
return PostgresSnubaQueryExecutor()

def _get_queryset_conditions(
self,
projects: Sequence[Project],
environments: Optional[Sequence[Environment]],
search_filters: Sequence[SearchFilter],
) -> Mapping[str, Condition]:
queryset_conditions: Dict[str, Condition] = {
def _get_queryset_conditions(self, projects, environments, search_filters):
queryset_conditions = {
"status": QCallbackCondition(lambda statuses: Q(status__in=statuses)),
"bookmarked_by": QCallbackCondition(
lambda users: Q(bookmark_set__project__in=projects, bookmark_set__user__in=users)
Expand Down Expand Up @@ -566,5 +525,5 @@ def _get_queryset_conditions(


class CdcEventsDatasetSnubaSearchBackend(EventsDatasetSnubaSearchBackend):
def _get_query_executor(self, *args: Any, **kwargs: Any) -> CdcPostgresSnubaQueryExecutor:
def _get_query_executor(self, *args, **kwargs):
return CdcPostgresSnubaQueryExecutor()
Loading

0 comments on commit 9f9870b

Please sign in to comment.