|
1 | 1 | import uuid
|
2 | 2 |
|
| 3 | +from django.contrib.postgres.indexes import GinIndex |
| 4 | +from django.contrib.postgres.search import ( |
| 5 | + SearchQuery, SearchRank, SearchVectorField, TrigramSimilarity |
| 6 | +) |
3 | 7 | from django.db import models
|
| 8 | +from django.db.models import F, Q |
| 9 | + |
| 10 | + |
| 11 | +class WineQuerySet(models.query.QuerySet): |
| 12 | + def search(self, query): |
| 13 | + search_query = Q( |
| 14 | + Q(search_vector=SearchQuery(query)) |
| 15 | + ) |
| 16 | + return self.annotate( |
| 17 | + variety_headline=SearchHeadline(F('variety'), SearchQuery(query)), |
| 18 | + winery_headline=SearchHeadline(F('winery'), SearchQuery(query)), |
| 19 | + description_headline=SearchHeadline(F('description'), SearchQuery(query)), |
| 20 | + search_rank=SearchRank(F('search_vector'), SearchQuery(query)) |
| 21 | + ).filter(search_query).order_by('-search_rank', 'id') |
4 | 22 |
|
5 | 23 |
|
6 | 24 | class Wine(models.Model):
|
7 |
| - id = models.UUIDField( |
8 |
| - primary_key=True, default=uuid.uuid4, editable=False |
9 |
| - ) |
| 25 | + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) |
10 | 26 | country = models.CharField(max_length=255)
|
11 | 27 | description = models.TextField(null=True, blank=True)
|
12 | 28 | points = models.IntegerField()
|
13 |
| - price = models.DecimalField( |
14 |
| - decimal_places=2, max_digits=10, null=True, blank=True |
15 |
| - ) |
| 29 | + price = models.DecimalField(decimal_places=2, max_digits=10, null=True, blank=True) |
16 | 30 | variety = models.CharField(max_length=255)
|
17 | 31 | winery = models.CharField(max_length=255)
|
| 32 | + search_vector = SearchVectorField(null=True, blank=True) |
| 33 | + |
| 34 | + objects = WineQuerySet.as_manager() |
| 35 | + |
| 36 | + class Meta: |
| 37 | + indexes = [ |
| 38 | + GinIndex(fields=['search_vector'], name='search_vector_index') |
| 39 | + ] |
18 | 40 |
|
19 | 41 | def __str__(self):
|
20 | 42 | return f'{self.id}'
|
| 43 | + |
| 44 | + |
| 45 | +class SearchHeadline(models.Func): |
| 46 | + function = 'ts_headline' |
| 47 | + output_field = models.TextField() |
| 48 | + template = '%(function)s(%(expressions)s, \'StartSel = <mark>, StopSel = </mark>, HighlightAll=TRUE\')' |
| 49 | + |
| 50 | + |
| 51 | +class WineSearchWordQuerySet(models.query.QuerySet): |
| 52 | + def search(self, query): |
| 53 | + return self.annotate( |
| 54 | + similarity=TrigramSimilarity('word', query) |
| 55 | + ).filter(similarity__gte=0.3).order_by('-similarity') |
| 56 | + |
| 57 | + |
| 58 | +class WineSearchWord(models.Model): |
| 59 | + word = models.CharField(max_length=255, unique=True) |
| 60 | + |
| 61 | + objects = WineSearchWordQuerySet.as_manager() |
| 62 | + |
| 63 | + def __str__(self): |
| 64 | + return self.word |
0 commit comments