Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 37 additions & 3 deletions backend/donations/views/common/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@
from donations.models.ngos import Cause, Ngo


def probably_registration_number(query: str) -> str | None:
"""
Try to extract a registration number from the query string
"""
if query.isnumeric():
return query
elif query.upper().startswith("RO") and query[2:].isnumeric():
return query[2:]
return None


class ConfigureSearch:
@staticmethod
def query(query: str, language_code: str) -> SearchQuery:
Expand Down Expand Up @@ -72,6 +83,19 @@ def search(self, queryset: QuerySet[Any] | None = None) -> QuerySet:
return self.get_search_results(queryset, query, language_code)


class NgoExactSearchMixin(CommonSearchMixin):
@classmethod
def get_search_results(cls, queryset: QuerySet, query: str, language_code: str) -> QuerySet:
query_filter = Q(name__icontains=query)
registration_number = probably_registration_number(query)
if registration_number:
query_filter = query_filter | Q(registration_number=registration_number)

ngos: QuerySet[Ngo] = queryset.filter(query_filter).order_by("name").distinct("name")

return ngos


class NgoSearchMixin(CommonSearchMixin):
@classmethod
def get_search_results(cls, queryset: QuerySet, query: str, language_code: str) -> QuerySet:
Expand Down Expand Up @@ -129,12 +153,22 @@ def get_search_results(cls, queryset: QuerySet, query: str, language_code: str)
class NgoCauseMixedSearchMixin(CommonSearchMixin):
@classmethod
def get_search_results(cls, queryset: QuerySet, query: str, language_code: str) -> QuerySet[Cause]:
ngos = NgoSearchMixin.get_search_results(Ngo.active, query, language_code)
ngos_causes = Cause.public_active.filter(ngo__in=ngos).distinct("name")
exact_causes = queryset.none()
# If exact match search is enabled, only perform it if the search query excluding the first two symbols
# is numeric (in order to look for registration numbers) or if the search query is longer than 20 chars
if settings.ENABLE_NGO_SEARCH_EXACT_MATCH and (probably_registration_number(query) or len(query) > 20):
exact_ngos = NgoExactSearchMixin.get_search_results(Ngo.active, query, language_code)
exact_causes = Cause.public_active.filter(ngo__in=exact_ngos).distinct("name")

if exact_causes:
return exact_causes

fuzzy_ngos = NgoSearchMixin.get_search_results(Ngo.active, query, language_code)
fuzzy_causes = Cause.public_active.filter(ngo__in=fuzzy_ngos).distinct("name")

searched_causes = CauseSearchMixin.get_search_results(queryset, query, language_code)

return searched_causes | ngos_causes
return searched_causes | fuzzy_causes


class DonorSearchMixin(CommonSearchMixin):
Expand Down
1 change: 1 addition & 0 deletions backend/redirectioneaza/settings/feature_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# Search tweaks
ENABLE_NGO_SEARCH_WORD_SIMILARITY = env.bool("ENABLE_NGO_SEARCH_WORD_SIMILARITY", False)
ENABLE_CAUSE_SEARCH_WORD_SIMILARITY = env.bool("ENABLE_CAUSE_SEARCH_WORD_SIMILARITY", False)
ENABLE_NGO_SEARCH_EXACT_MATCH = env.bool("ENABLE_NGO_SEARCH_EXACT_MATCH", True)

# Feature flags
ENABLE_FLAG_CONTACT = env.bool("ENABLE_FLAG_CONTACT", False)
Expand Down