updated search v2 with full text search
This commit is contained in:
@ -1,16 +1,34 @@
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
|
||||||
from django.contrib.postgres.search import TrigramSimilarity
|
from cookbook.models import Recipe
|
||||||
from django.db.models import Q, Case, When, Value
|
|
||||||
from django.forms import IntegerField
|
|
||||||
|
|
||||||
from cookbook.models import ViewLog
|
|
||||||
from recipes import settings
|
from recipes import settings
|
||||||
|
from django.contrib.postgres.aggregates import StringAgg
|
||||||
|
from django.contrib.postgres.search import (
|
||||||
|
SearchQuery, SearchRank, SearchVector, TrigramSimilarity,
|
||||||
|
)
|
||||||
|
from django.db import models
|
||||||
|
from django.db.models import Q
|
||||||
|
from django.utils import translation
|
||||||
|
|
||||||
|
|
||||||
def search_recipes(request, queryset, params):
|
DICTIONARY = {
|
||||||
search_string = params.get('query', '')
|
# TODO find custom dictionaries - maybe from here https://www.postgresql.org/message-id/CAF4Au4x6X_wSXFwsQYE8q5o0aQZANrvYjZJ8uOnsiHDnOVPPEg%40mail.gmail.com
|
||||||
|
# 'hy': 'Armenian',
|
||||||
|
# 'ca': 'Catalan',
|
||||||
|
# 'cs': 'Czech',
|
||||||
|
'nl': 'dutch',
|
||||||
|
'en': 'english',
|
||||||
|
'fr': 'french',
|
||||||
|
'de': 'german',
|
||||||
|
'it': 'italian',
|
||||||
|
# 'lv': 'Latvian',
|
||||||
|
'es': 'spanish',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def search_recipes(queryset, params):
|
||||||
|
search_string = params.get('query', '').strip()
|
||||||
search_keywords = params.getlist('keywords', [])
|
search_keywords = params.getlist('keywords', [])
|
||||||
search_foods = params.getlist('foods', [])
|
search_foods = params.getlist('foods', [])
|
||||||
search_books = params.getlist('books', [])
|
search_books = params.getlist('books', [])
|
||||||
@ -23,20 +41,26 @@ def search_recipes(request, queryset, params):
|
|||||||
search_random = params.get('random', False)
|
search_random = params.get('random', False)
|
||||||
search_last_viewed = int(params.get('last_viewed', 0))
|
search_last_viewed = int(params.get('last_viewed', 0))
|
||||||
|
|
||||||
if search_last_viewed > 0:
|
if settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2', 'django.db.backends.postgresql'] and search_string != '':
|
||||||
last_viewed_recipes = ViewLog.objects.filter(created_by=request.user, space=request.space,
|
# queryset = queryset.annotate(similarity=TrigramSimilarity('name', search_string), )
|
||||||
created_at__gte=datetime.now() - timedelta(days=14)).order_by('pk').values_list('recipe__pk', flat=True).distinct()
|
# .filter(Q(similarity__gt=0.1) | Q(name__unaccent__icontains=search_string)).order_by('-similarity')
|
||||||
|
language = DICTIONARY.get(translation.get_language(), 'simple')
|
||||||
return queryset.filter(pk__in=last_viewed_recipes[len(last_viewed_recipes) - min(len(last_viewed_recipes), search_last_viewed):])
|
search_query = SearchQuery(
|
||||||
|
search_string,
|
||||||
queryset = queryset.annotate(
|
config=language,
|
||||||
new_recipe=Case(When(created_at__gte=(datetime.now() - timedelta(days=7)), then=Value(100)),
|
search_type="websearch"
|
||||||
default=Value(0), )).order_by('-new_recipe', 'name')
|
)
|
||||||
|
search_vectors = (
|
||||||
if settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2',
|
SearchVector('search_vector')
|
||||||
'django.db.backends.postgresql']:
|
+ SearchVector(StringAgg('steps__ingredients__food__name', delimiter=' '), weight='B', config=language)
|
||||||
queryset = queryset.annotate(similarity=TrigramSimilarity('name', search_string), ).filter(
|
+ SearchVector(StringAgg('keywords__name', delimiter=' '), weight='B', config=language))
|
||||||
Q(similarity__gt=0.1) | Q(name__unaccent__icontains=search_string)).order_by('-similarity')
|
search_rank = SearchRank(search_vectors, search_query)
|
||||||
|
queryset = (
|
||||||
|
queryset.annotate(
|
||||||
|
search=search_vectors,
|
||||||
|
rank=search_rank,)
|
||||||
|
.filter(Q(search=search_query))
|
||||||
|
.order_by('-rank'))
|
||||||
else:
|
else:
|
||||||
queryset = queryset.filter(name__icontains=search_string)
|
queryset = queryset.filter(name__icontains=search_string)
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ from django.contrib.postgres.search import (
|
|||||||
SearchQuery, SearchRank, SearchVector, TrigramSimilarity,
|
SearchQuery, SearchRank, SearchVector, TrigramSimilarity,
|
||||||
)
|
)
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.db.models import Q
|
||||||
from django.utils import translation
|
from django.utils import translation
|
||||||
|
|
||||||
DICTIONARY = {
|
DICTIONARY = {
|
||||||
@ -27,21 +28,44 @@ class RecipeSearchManager(models.Manager):
|
|||||||
def search(self, search_text, space):
|
def search(self, search_text, space):
|
||||||
language = DICTIONARY.get(translation.get_language(), 'simple')
|
language = DICTIONARY.get(translation.get_language(), 'simple')
|
||||||
search_query = SearchQuery(
|
search_query = SearchQuery(
|
||||||
search_text, config=language
|
search_text,
|
||||||
|
config=language,
|
||||||
|
search_type="websearch"
|
||||||
)
|
)
|
||||||
search_vectors = (
|
search_vectors = (
|
||||||
SearchVector('search_vector')
|
SearchVector('search_vector')
|
||||||
+ SearchVector(StringAgg('steps__ingredients__food__name', delimiter=' '), weight='B', config=language)
|
+ SearchVector(StringAgg('steps__ingredients__food__name', delimiter=' '), weight='B', config=language)
|
||||||
+ SearchVector(StringAgg('keywords__name', delimiter=' '), weight='B', config=language))
|
+ SearchVector(StringAgg('keywords__name', delimiter=' '), weight='B', config=language))
|
||||||
search_rank = SearchRank(search_vectors, search_query)
|
search_rank = SearchRank(search_vectors, search_query)
|
||||||
# the results from trigram were really, really bad
|
# USING TRIGRAM BREAKS WEB SEARCH
|
||||||
# trigram = (
|
# ADDING MULTIPLE TRIGRAMS CREATES DUPLICATE RESULTS
|
||||||
# TrigramSimilarity('name', search_text)
|
# DISTINCT NOT COMPAITBLE WITH ANNOTATE
|
||||||
# + TrigramSimilarity('description', search_text)
|
# trigram_name = (TrigramSimilarity('name', search_text))
|
||||||
# + TrigramSimilarity('steps__ingredients__food__name', search_text)
|
# trigram_description = (TrigramSimilarity('description', search_text))
|
||||||
# + TrigramSimilarity('keywords__name', search_text))
|
# trigram_food = (TrigramSimilarity('steps__ingredients__food__name', search_text))
|
||||||
|
# trigram_keyword = (TrigramSimilarity('keywords__name', search_text))
|
||||||
|
# adding additional trigrams created duplicates
|
||||||
|
# + TrigramSimilarity('description', search_text)
|
||||||
|
# + TrigramSimilarity('steps__ingredients__food__name', search_text)
|
||||||
|
# + TrigramSimilarity('keywords__name', search_text)
|
||||||
return (
|
return (
|
||||||
self.get_queryset()
|
self.get_queryset()
|
||||||
.annotate(search=search_vectors, rank=search_rank)
|
.annotate(
|
||||||
.filter(search=search_query)
|
search=search_vectors,
|
||||||
|
rank=search_rank,
|
||||||
|
#trigram=trigram_name+trigram_description+trigram_food+trigram_keyword
|
||||||
|
# trigram_name=trigram_name,
|
||||||
|
# trigram_description=trigram_description,
|
||||||
|
# trigram_food=trigram_food,
|
||||||
|
# trigram_keyword=trigram_keyword
|
||||||
|
)
|
||||||
|
.filter(
|
||||||
|
Q(search=search_query)
|
||||||
|
# | Q(trigram_name__gt=0.1)
|
||||||
|
# | Q(name__icontains=search_text)
|
||||||
|
# | Q(trigram_name__gt=0.2)
|
||||||
|
# | Q(trigram_description__gt=0.2)
|
||||||
|
# | Q(trigram_food__gt=0.2)
|
||||||
|
# | Q(trigram_keyword__gt=0.2)
|
||||||
|
)
|
||||||
.order_by('-rank'))
|
.order_by('-rank'))
|
||||||
|
Reference in New Issue
Block a user