news trigram search

This commit is contained in:
Kuroshini 2020-01-21 18:58:26 +03:00
parent de50d6491f
commit 6a36bb5a13
2 changed files with 55 additions and 2 deletions

View File

@ -1,6 +1,8 @@
"""Filters from application News""" """Filters from application News"""
from django_filters import rest_framework as filters from django.core.validators import EMPTY_VALUES
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_filters import rest_framework as filters
from rest_framework.serializers import ValidationError
from news import models from news import models
@ -29,6 +31,7 @@ class NewsListFilterSet(filters.FilterSet):
(SORT_BY_START_CHOICE, "start"), (SORT_BY_START_CHOICE, "start"),
) )
sort_by = filters.ChoiceFilter(method='sort_by_field', choices=SORT_BY_CHOICES) sort_by = filters.ChoiceFilter(method='sort_by_field', choices=SORT_BY_CHOICES)
search = filters.CharFilter(method='search_news')
class Meta: class Meta:
"""Meta class""" """Meta class"""
@ -41,8 +44,16 @@ class NewsListFilterSet(filters.FilterSet):
'tag_value__in', 'tag_value__in',
'state', 'state',
'sort_by', 'sort_by',
'search',
) )
def search_news(self, queryset, name, value):
if value not in EMPTY_VALUES:
if len(value) < 3:
raise ValidationError({'detail': _('Type at least 3 characters to search please.')})
return queryset.trigram_search(value)
return queryset
def in_tags(self, queryset, name, value): def in_tags(self, queryset, name, value):
tags = value.split('__') tags = value.split('__')
return queryset.filter(tags__value__in=tags) return queryset.filter(tags__value__in=tags)

View File

@ -6,7 +6,9 @@ from django.contrib.contenttypes import fields as generic
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.contrib.postgres.fields import HStoreField from django.contrib.postgres.fields import HStoreField
from django.db import models from django.db import models
from django.db.models import Case, When from django.db.models import Case, When, Q, F
from django.db.models.functions import Cast
from django.contrib.postgres.search import TrigramSimilarity
from django.urls.exceptions import NoReverseMatch from django.urls.exceptions import NoReverseMatch
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -144,6 +146,46 @@ class NewsQuerySet(TranslationQuerysetMixin):
def by_locale(self, locale): def by_locale(self, locale):
return self.filter(title__icontains=locale) return self.filter(title__icontains=locale)
def trigram_search(self, search_value: str):
"""Search with mistakes by name or last name."""
return self.annotate(
description_str=Cast('description', models.TextField()),
title_str=Cast('title', models.TextField()),
subtitle_str=Cast('subtitle', models.TextField()),
search_contains_match=Case(
models.When(Q(description_str__icontains=search_value) | Q(title_str__icontains=search_value) | Q(
subtitle_str__icontains=search_value), then=100),
default=0,
output_field=models.FloatField(),
),
description_similarity=models.Case(
models.When(
Q(description__isnull=False),
then=TrigramSimilarity('description_str', search_value.lower()),
),
default=0,
output_field=models.FloatField()
),
title_similarity=models.Case(
models.When(
Q(title__isnull=False),
then=TrigramSimilarity('title_str', search_value.lower()),
),
default=0,
output_field=models.FloatField()
),
subtitle_similarity=models.Case(
models.When(
Q(subtitle__isnull=False),
then=TrigramSimilarity('subtitle_str', search_value.lower()),
),
default=0,
output_field=models.FloatField()
),
relevance=(F('search_contains_match') + F('description_similarity') + F('title_similarity') + F(
'subtitle_similarity'))
).filter(relevance__gte=0.3).order_by('-relevance')
class News(GalleryMixin, BaseAttributes, TranslatedFieldsMixin, HasTagsMixin, class News(GalleryMixin, BaseAttributes, TranslatedFieldsMixin, HasTagsMixin,
FavoritesMixin): FavoritesMixin):