diff --git a/cookbook/helper/recipe_search.py b/cookbook/helper/recipe_search.py index 1f61fbd47..f33601464 100644 --- a/cookbook/helper/recipe_search.py +++ b/cookbook/helper/recipe_search.py @@ -1,16 +1,34 @@ from datetime import datetime, timedelta from functools import reduce -from django.contrib.postgres.search import TrigramSimilarity -from django.db.models import Q, Case, When, Value -from django.forms import IntegerField - -from cookbook.models import ViewLog +from cookbook.models import Recipe 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): - search_string = params.get('query', '') +DICTIONARY = { + # 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_foods = params.getlist('foods', []) search_books = params.getlist('books', []) @@ -24,23 +42,26 @@ def search_recipes(request, queryset, params): search_new = params.get('new', False) search_last_viewed = int(params.get('last_viewed', 0)) - if search_last_viewed > 0: - last_viewed_recipes = ViewLog.objects.filter(created_by=request.user, space=request.space, - created_at__gte=datetime.now() - timedelta(days=14)).order_by('pk').values_list('recipe__pk', flat=True).distinct() - - return queryset.filter(pk__in=last_viewed_recipes[len(last_viewed_recipes) - min(len(last_viewed_recipes), search_last_viewed):]) - - if search_new == 'true': - queryset = queryset.annotate( - new_recipe=Case(When(created_at__gte=(datetime.now() - timedelta(days=7)), then=Value(100)), - default=Value(0), )).order_by('-new_recipe', 'name') - else: - queryset = queryset.order_by('name') - - if settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2', - 'django.db.backends.postgresql']: - queryset = queryset.annotate(similarity=TrigramSimilarity('name', search_string), ).filter( - Q(similarity__gt=0.1) | Q(name__unaccent__icontains=search_string)).order_by('-similarity') + if settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2', 'django.db.backends.postgresql'] and search_string != '': + # queryset = queryset.annotate(similarity=TrigramSimilarity('name', search_string), ) + # .filter(Q(similarity__gt=0.1) | Q(name__unaccent__icontains=search_string)).order_by('-similarity') + language = DICTIONARY.get(translation.get_language(), 'simple') + search_query = SearchQuery( + search_string, + config=language, + search_type="websearch" + ) + search_vectors = ( + SearchVector('search_vector') + + SearchVector(StringAgg('steps__ingredients__food__name', delimiter=' '), weight='B', config=language) + + SearchVector(StringAgg('keywords__name', delimiter=' '), weight='B', config=language)) + 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: queryset = queryset.filter(name__icontains=search_string) diff --git a/cookbook/managers.py b/cookbook/managers.py index baa871d3a..dc08d2bee 100644 --- a/cookbook/managers.py +++ b/cookbook/managers.py @@ -3,6 +3,7 @@ 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 DICTIONARY = { @@ -27,21 +28,44 @@ class RecipeSearchManager(models.Manager): def search(self, search_text, space): language = DICTIONARY.get(translation.get_language(), 'simple') search_query = SearchQuery( - search_text, config=language + search_text, + config=language, + search_type="websearch" ) search_vectors = ( SearchVector('search_vector') + SearchVector(StringAgg('steps__ingredients__food__name', delimiter=' '), weight='B', config=language) + SearchVector(StringAgg('keywords__name', delimiter=' '), weight='B', config=language)) search_rank = SearchRank(search_vectors, search_query) - # the results from trigram were really, really bad - # trigram = ( - # TrigramSimilarity('name', search_text) - # + TrigramSimilarity('description', search_text) - # + TrigramSimilarity('steps__ingredients__food__name', search_text) - # + TrigramSimilarity('keywords__name', search_text)) + # USING TRIGRAM BREAKS WEB SEARCH + # ADDING MULTIPLE TRIGRAMS CREATES DUPLICATE RESULTS + # DISTINCT NOT COMPAITBLE WITH ANNOTATE + # trigram_name = (TrigramSimilarity('name', search_text)) + # trigram_description = (TrigramSimilarity('description', 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 ( self.get_queryset() - .annotate(search=search_vectors, rank=search_rank) - .filter(search=search_query) + .annotate( + 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'))