Last active
December 29, 2023 11:26
-
-
Save surenkov/7c5a9c4ac4e61f246468aee22b83b8d7 to your computer and use it in GitHub Desktop.
Django REST filter around `django.contrib.postgres.search` expressions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from typing import Union, Collection | |
from django.contrib.postgres.search import SearchVector, SearchQuery | |
from django.db.models.expressions import BaseExpression | |
from django_filters import rest_framework as filters | |
from django_filters.constants import EMPTY_VALUES | |
class FullTextSearchFilter(filters.CharFilter): | |
""" Django REST filter around ``django.contrib.postgres.search`` expressions. | |
Accepts ``SearchVector`` instance, string or collection of expressions, | |
``search_type`` for ``SearchQuery`` and locale ``config`` | |
Example: | |
>>> search = FullTextSearchFilter( | |
>>> vector=("field_1", "field__subfield"), # or `SearchVector` instance | |
>>> search_type="phrase", | |
>>> config="french", | |
>>> ) | |
""" | |
_InnerExpr = Union[str, BaseExpression] | |
_VectorExpr = Union[_InnerExpr, Collection[_InnerExpr], SearchVector] | |
def __init__( | |
self, | |
*args, | |
vector: _VectorExpr, | |
suffix: str = "search", | |
search_type: str = "plain", | |
config: str = None, | |
**kwargs, | |
): | |
""" | |
:param vector: Either ``SearchVector``, string or collection of expressions. | |
:param suffix: Appendix for query annotation, optional. | |
:param search_type: ``SearchQuery``'s search type. | |
:param config: Locale config for ``SearchVector`` | |
""" | |
if callable(vector): | |
vector = vector() | |
if isinstance(vector, (str, BaseExpression)): | |
vector = SearchVector(vector, config=config) | |
elif not isinstance(vector, SearchVector): | |
vector = SearchVector(*vector, config=config) | |
self.vector = vector | |
self.suffix = suffix | |
self.search_type = search_type | |
super().__init__(*args, **kwargs) | |
def filter(self, qs, value): | |
if value in EMPTY_VALUES: | |
return qs | |
search_field = self._search_field | |
query = SearchQuery(value, search_type=self.search_type) | |
annotation, lookup = {search_field: self.vector}, {search_field: query} | |
return self.get_method(qs.annotate(**annotation))(**lookup) | |
@property | |
def _search_field(self): | |
return f"{self.field_name}_{self.suffix}" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment