Corp/backend/search/backends.py
Игорь Надеин 4f6244ab1f
Some checks failed
Deploy production / deploy (push) Failing after 2s
Fix CORP runtime errors and harden deployment and media access
2026-09-10 16:40:08 +03:00

38 lines
1.8 KiB
Python

"""Full-text search with a narrow fallback for stop-word abbreviations."""
import re
from django.db import connections
from watson.backends import PostgresSearchBackend
from watson.models import SearchEntry
from watson.search import search as watson_search
class AbbreviationPostgresSearchBackend(PostgresSearchBackend):
def do_search(self, engine_slug, queryset, search_text):
# English stemming drops short names such as IT as stop words. Only
# empty, short single-token tsqueries need a literal title fallback.
# Normal queries retain Watson's GIN-backed full-text search unchanged.
if 2 <= len(search_text) <= 8 and search_text.isalnum():
with connections[queryset.db].cursor() as cursor:
cursor.execute(
"SELECT numnode(to_tsquery(%s::regconfig, %s)) = 0",
(self.search_config, self.escape_postgres_query(search_text)),
)
is_stop_word = cursor.fetchone()[0]
if is_stop_word:
# Match an entire token, not 'it' inside 'Security'. Do not scan
# descriptions or extracted document contents for stop words.
pattern = rf"(^|[^[:alnum:]_]){re.escape(search_text)}([^[:alnum:]_]|$)"
return queryset.filter(title__iregex=pattern)
return super().do_search(engine_slug, queryset, search_text)
def search_with_abbreviations(search_text):
backend_name = None
if connections[SearchEntry.objects.db].vendor == "postgresql":
backend_name = "search.backends.AbbreviationPostgresSearchBackend"
# Watson still applies its engine/model/live-queryset filters. Candidates
# also go through the API's normal object ACL, counting and pagination.
return watson_search(search_text, backend_name=backend_name)