+from django.conf import settings
from django.db.models import Func
-from django.contrib.postgres.search import SearchVector, SearchQuery, SearchQueryField, SearchHeadline as SH
-
-
-
-class UnaccentTSVector(Func):
- function = 'UNACCENT'
- template = '%(function)s(%(expressions)s::text)::tsvector'
-
-
-class Unaccent(Func):
- function = 'UNACCENT'
-
-
-class ConcatTSVector(Func):
- function = 'CONCAT'
- template = '%(function)s(%(expressions)s)::tsvector'
-
-
-class UnaccentTSQuery(Func):
- function = 'UNACCENT'
- template = '%(function)s(%(expressions)s::text)::tsquery'
- output_field = SearchQueryField()
-
-
-class TSV(Func):
+from django.contrib.postgres.search import SearchQuery, SearchVectorField
+
+
+class UnaccentSearchQuery(SearchQuery):
+ '''
+ The idea is to run unaccent *after* the query is already passed through the language dictionary.
+ '''
+ def as_sql(self, *args, **kwargs):
+ sql, params = super().as_sql(*args, **kwargs)
+ if settings.SEARCH_USE_UNACCENT:
+ sql = f'unaccent({sql}::text)::tsquery'
+ return sql, params
+
+
+class UnaccentSearchVector(Func):
+ '''
+ We do the indexing twice, to account for non-diacritic versions.
+ For example: user enters 'róże' -> stem to 'róża' -> unaccent to 'roza'.
+ But user enters 'roze' -> stem leaves it as is, so we need original form in the vector.
+ '''
function='to_tsvector'
- template = '''unaccent(
- %(function)s('polish', %(expressions)s)::text)::tsvector ||
- to_tsvector(
- 'polish_simple',
- unaccent(%(expressions)s)
- )'''
-
-
-def build_search_vector(*fields):
- return TSV(*fields)
-
-
-def build_search_query(*fields, **kwargs):
- return UnaccentTSQuery(SearchQuery(*fields, **kwargs))
-
-
-
-class SearchHeadline(SH):
-
- def __init__(
- self,
- expression,
- query,
- *,
- config=None,
- start_sel=None,
- stop_sel=None,
- max_words=None,
- min_words=None,
- short_word=None,
- highlight_all=None,
- max_fragments=None,
- fragment_delimiter=None,
- ):
- options = {
- "StartSel": start_sel,
- "StopSel": stop_sel,
- "MaxWords": max_words,
- "MinWords": min_words,
- "ShortWord": short_word,
- "HighlightAll": highlight_all,
- "MaxFragments": max_fragments,
- "FragmentDelimiter": fragment_delimiter,
- }
- self.options = {
- option: value for option, value in options.items() if value is not None
- }
- expressions = (expression, query)
- if config is not None:
- config = SearchConfig.from_parameter(config)
- expressions = (config,) + expressions
- Func.__init__(self, *expressions)
+ if settings.SEARCH_USE_UNACCENT:
+ template = f'''unaccent(
+ %(function)s('{settings.SEARCH_CONFIG}', %(expressions)s)::text)::tsvector ||
+ to_tsvector(
+ '{settings.SEARCH_CONFIG_SIMPLE}',
+ unaccent(%(expressions)s)
+ )'''
+ output_field = SearchVectorField()