mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-09 16:18:00 -05:00
remove "favorite" as default sort order due to performacne issues
This commit is contained in:
@@ -4,7 +4,7 @@ from datetime import date, timedelta
|
|||||||
|
|
||||||
from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector, TrigramSimilarity
|
from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector, TrigramSimilarity
|
||||||
from django.core.cache import caches
|
from django.core.cache import caches
|
||||||
from django.db.models import (Avg, Case, Count, Exists, F, Func, Max, OuterRef, Q, Subquery, Value, When)
|
from django.db.models import (Avg, Case, Count, Exists, F, Func, Max, OuterRef, Q, Subquery, Value, When, FilteredRelation)
|
||||||
from django.db.models.functions import Coalesce, Lower, Substr
|
from django.db.models.functions import Coalesce, Lower, Substr
|
||||||
from django.utils import timezone, translation
|
from django.utils import timezone, translation
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
@@ -21,7 +21,7 @@ from recipes import settings
|
|||||||
class RecipeSearch():
|
class RecipeSearch():
|
||||||
_postgres = settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2', 'django.db.backends.postgresql']
|
_postgres = settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2', 'django.db.backends.postgresql']
|
||||||
|
|
||||||
def __init__(self, request, **params):
|
def __init__(self, request, **params):
|
||||||
self._request = request
|
self._request = request
|
||||||
self._queryset = None
|
self._queryset = None
|
||||||
if f := params.get('filter', None):
|
if f := params.get('filter', None):
|
||||||
@@ -110,7 +110,6 @@ class RecipeSearch():
|
|||||||
)
|
)
|
||||||
self.search_rank = None
|
self.search_rank = None
|
||||||
self.orderby = []
|
self.orderby = []
|
||||||
self._default_sort = ['-favorite'] # TODO add user setting
|
|
||||||
self._filters = None
|
self._filters = None
|
||||||
self._fuzzy_match = None
|
self._fuzzy_match = None
|
||||||
|
|
||||||
@@ -122,7 +121,7 @@ class RecipeSearch():
|
|||||||
self._created_on_filter(created_date=self._createdon)
|
self._created_on_filter(created_date=self._createdon)
|
||||||
self._updated_on_filter(updated_date=self._updatedon)
|
self._updated_on_filter(updated_date=self._updatedon)
|
||||||
self._viewed_on_filter(viewed_date=self._viewedon)
|
self._viewed_on_filter(viewed_date=self._viewedon)
|
||||||
self._favorite_recipes(timescooked=self._timescooked)
|
self._favorite_recipes(times_cooked=self._timescooked)
|
||||||
self._new_recipes()
|
self._new_recipes()
|
||||||
self.keyword_filters(**self._keywords)
|
self.keyword_filters(**self._keywords)
|
||||||
self.food_filters(**self._foods)
|
self.food_filters(**self._foods)
|
||||||
@@ -149,7 +148,7 @@ class RecipeSearch():
|
|||||||
else:
|
else:
|
||||||
order = []
|
order = []
|
||||||
# TODO add userpreference for default sort order and replace '-favorite'
|
# TODO add userpreference for default sort order and replace '-favorite'
|
||||||
default_order = ['-favorite']
|
default_order = ['-name']
|
||||||
# recent and new_recipe are always first; they float a few recipes to the top
|
# recent and new_recipe are always first; they float a few recipes to the top
|
||||||
if self._num_recent:
|
if self._num_recent:
|
||||||
order += ['-recent']
|
order += ['-recent']
|
||||||
@@ -206,7 +205,7 @@ class RecipeSearch():
|
|||||||
else:
|
else:
|
||||||
self._queryset = self._queryset.annotate(simularity=Coalesce(Subquery(simularity), 0.0))
|
self._queryset = self._queryset.annotate(simularity=Coalesce(Subquery(simularity), 0.0))
|
||||||
if self._sort_includes('score') and self._fulltext_include and self._fuzzy_match is not None:
|
if self._sort_includes('score') and self._fulltext_include and self._fuzzy_match is not None:
|
||||||
self._queryset = self._queryset.annotate(score=F('rank')+F('simularity'))
|
self._queryset = self._queryset.annotate(score=F('rank') + F('simularity'))
|
||||||
else:
|
else:
|
||||||
query_filter = Q()
|
query_filter = Q()
|
||||||
for f in [x + '__unaccent__iexact' if x in self._unaccent_include else x + '__iexact' for x in SearchFields.objects.all().values_list('field', flat=True)]:
|
for f in [x + '__unaccent__iexact' if x in self._unaccent_include else x + '__iexact' for x in SearchFields.objects.all().values_list('field', flat=True)]:
|
||||||
@@ -277,8 +276,6 @@ class RecipeSearch():
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _recently_viewed(self, num_recent=None):
|
def _recently_viewed(self, num_recent=None):
|
||||||
# self._queryset = self._queryset.annotate(recent=Value(0))
|
|
||||||
# return
|
|
||||||
if not num_recent:
|
if not num_recent:
|
||||||
if self._sort_includes('lastviewed'):
|
if self._sort_includes('lastviewed'):
|
||||||
self._queryset = self._queryset.annotate(lastviewed=Coalesce(
|
self._queryset = self._queryset.annotate(lastviewed=Coalesce(
|
||||||
@@ -289,25 +286,25 @@ class RecipeSearch():
|
|||||||
'recipe').annotate(recent=Max('created_at')).order_by('-recent')[:num_recent]
|
'recipe').annotate(recent=Max('created_at')).order_by('-recent')[:num_recent]
|
||||||
self._queryset = self._queryset.annotate(recent=Coalesce(Max(Case(When(pk__in=num_recent_recipes.values('recipe'), then='viewlog__pk'))), Value(0)))
|
self._queryset = self._queryset.annotate(recent=Coalesce(Max(Case(When(pk__in=num_recent_recipes.values('recipe'), then='viewlog__pk'))), Value(0)))
|
||||||
|
|
||||||
def _favorite_recipes(self, timescooked=None):
|
def _favorite_recipes(self, times_cooked=None):
|
||||||
if self._sort_includes('favorite') or timescooked:
|
if self._sort_includes('favorite') or times_cooked:
|
||||||
lessthan = '-' in (timescooked or []) or not self._sort_includes('-favorite')
|
less_than = '-' in (times_cooked or []) or not self._sort_includes('-favorite')
|
||||||
if lessthan:
|
if less_than:
|
||||||
default = 1000
|
default = 1000
|
||||||
else:
|
else:
|
||||||
default = 0
|
default = 0
|
||||||
favorite_recipes = CookLog.objects.filter(created_by=self._request.user, space=self._request.space, recipe=OuterRef('pk')
|
favorite_recipes = CookLog.objects.filter(created_by=self._request.user, space=self._request.space, recipe=OuterRef('pk')
|
||||||
).values('recipe').annotate(count=Count('pk', distinct=True)).values('count')
|
).values('recipe').annotate(count=Count('pk', distinct=True)).values('count')
|
||||||
self._queryset = self._queryset.annotate(favorite=Coalesce(Subquery(favorite_recipes), default))
|
self._queryset = self._queryset.annotate(favorite=Coalesce(Subquery(favorite_recipes), default))
|
||||||
if timescooked is None:
|
if times_cooked is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
if timescooked == '0':
|
if times_cooked == '0':
|
||||||
self._queryset = self._queryset.filter(favorite=0)
|
self._queryset = self._queryset.filter(favorite=0)
|
||||||
elif lessthan:
|
elif less_than:
|
||||||
self._queryset = self._queryset.filter(favorite__lte=int(timescooked[1:])).exclude(favorite=0)
|
self._queryset = self._queryset.filter(favorite__lte=int(times_cooked[1:])).exclude(favorite=0)
|
||||||
else:
|
else:
|
||||||
self._queryset = self._queryset.filter(favorite__gte=int(timescooked))
|
self._queryset = self._queryset.filter(favorite__gte=int(times_cooked))
|
||||||
|
|
||||||
def keyword_filters(self, **kwargs):
|
def keyword_filters(self, **kwargs):
|
||||||
if all([kwargs[x] is None for x in kwargs]):
|
if all([kwargs[x] is None for x in kwargs]):
|
||||||
@@ -507,10 +504,10 @@ class RecipeSearch():
|
|||||||
shopping_users = [*self._request.user.get_shopping_share(), self._request.user]
|
shopping_users = [*self._request.user.get_shopping_share(), self._request.user]
|
||||||
|
|
||||||
onhand_filter = (
|
onhand_filter = (
|
||||||
Q(steps__ingredients__food__onhand_users__in=shopping_users) # food onhand
|
Q(steps__ingredients__food__onhand_users__in=shopping_users) # food onhand
|
||||||
| Q(steps__ingredients__food__substitute__onhand_users__in=shopping_users) # or substitute food onhand
|
| Q(steps__ingredients__food__substitute__onhand_users__in=shopping_users) # or substitute food onhand
|
||||||
| Q(steps__ingredients__food__in=self.__children_substitute_filter(shopping_users))
|
| Q(steps__ingredients__food__in=self.__children_substitute_filter(shopping_users))
|
||||||
| Q(steps__ingredients__food__in=self.__sibling_substitute_filter(shopping_users))
|
| Q(steps__ingredients__food__in=self.__sibling_substitute_filter(shopping_users))
|
||||||
)
|
)
|
||||||
makenow_recipes = Recipe.objects.annotate(
|
makenow_recipes = Recipe.objects.annotate(
|
||||||
count_food=Count('steps__ingredients__food__pk', filter=Q(steps__ingredients__food__isnull=False), distinct=True),
|
count_food=Count('steps__ingredients__food__pk', filter=Q(steps__ingredients__food__isnull=False), distinct=True),
|
||||||
@@ -519,10 +516,10 @@ class RecipeSearch():
|
|||||||
steps__ingredients__food__recipe__isnull=True), distinct=True),
|
steps__ingredients__food__recipe__isnull=True), distinct=True),
|
||||||
has_child_sub=Case(When(steps__ingredients__food__in=self.__children_substitute_filter(shopping_users), then=Value(1)), default=Value(0)),
|
has_child_sub=Case(When(steps__ingredients__food__in=self.__children_substitute_filter(shopping_users), then=Value(1)), default=Value(0)),
|
||||||
has_sibling_sub=Case(When(steps__ingredients__food__in=self.__sibling_substitute_filter(shopping_users), then=Value(1)), default=Value(0))
|
has_sibling_sub=Case(When(steps__ingredients__food__in=self.__sibling_substitute_filter(shopping_users), then=Value(1)), default=Value(0))
|
||||||
).annotate(missingfood=F('count_food')-F('count_onhand')-F('count_ignore_shopping')).filter(missingfood=missing)
|
).annotate(missingfood=F('count_food') - F('count_onhand') - F('count_ignore_shopping')).filter(missingfood=missing)
|
||||||
self._queryset = self._queryset.distinct().filter(id__in=makenow_recipes.values('id'))
|
self._queryset = self._queryset.distinct().filter(id__in=makenow_recipes.values('id'))
|
||||||
|
|
||||||
@ staticmethod
|
@staticmethod
|
||||||
def __children_substitute_filter(shopping_users=None):
|
def __children_substitute_filter(shopping_users=None):
|
||||||
children_onhand_subquery = Food.objects.filter(
|
children_onhand_subquery = Food.objects.filter(
|
||||||
path__startswith=OuterRef('path'),
|
path__startswith=OuterRef('path'),
|
||||||
@@ -538,10 +535,10 @@ class RecipeSearch():
|
|||||||
).annotate(child_onhand_count=Exists(children_onhand_subquery)
|
).annotate(child_onhand_count=Exists(children_onhand_subquery)
|
||||||
).filter(child_onhand_count=True)
|
).filter(child_onhand_count=True)
|
||||||
|
|
||||||
@ staticmethod
|
@staticmethod
|
||||||
def __sibling_substitute_filter(shopping_users=None):
|
def __sibling_substitute_filter(shopping_users=None):
|
||||||
sibling_onhand_subquery = Food.objects.filter(
|
sibling_onhand_subquery = Food.objects.filter(
|
||||||
path__startswith=Substr(OuterRef('path'), 1, Food.steplen*(OuterRef('depth')-1)),
|
path__startswith=Substr(OuterRef('path'), 1, Food.steplen * (OuterRef('depth') - 1)),
|
||||||
depth=OuterRef('depth'),
|
depth=OuterRef('depth'),
|
||||||
onhand_users__in=shopping_users
|
onhand_users__in=shopping_users
|
||||||
)
|
)
|
||||||
@@ -745,7 +742,7 @@ class RecipeFacet():
|
|||||||
).filter(depth=depth, count__gt=0
|
).filter(depth=depth, count__gt=0
|
||||||
).values('id', 'name', 'count', 'numchild').order_by(Lower('name').asc())[:200]
|
).values('id', 'name', 'count', 'numchild').order_by(Lower('name').asc())[:200]
|
||||||
else:
|
else:
|
||||||
return queryset.filter(depth=depth).values('id', 'name', 'numchild').order_by(Lower('name').asc())
|
return queryset.filter(depth=depth).values('id', 'name', 'numchild').order_by(Lower('name').asc())
|
||||||
|
|
||||||
def _food_queryset(self, queryset, food=None):
|
def _food_queryset(self, queryset, food=None):
|
||||||
depth = getattr(food, 'depth', 0) + 1
|
depth = getattr(food, 'depth', 0) + 1
|
||||||
@@ -757,4 +754,3 @@ class RecipeFacet():
|
|||||||
).values('id', 'name', 'count', 'numchild').order_by(Lower('name').asc())[:200]
|
).values('id', 'name', 'count', 'numchild').order_by(Lower('name').asc())[:200]
|
||||||
else:
|
else:
|
||||||
return queryset.filter(depth__lte=depth).values('id', 'name', 'numchild').order_by(Lower('name').asc())
|
return queryset.filter(depth__lte=depth).values('id', 'name', 'numchild').order_by(Lower('name').asc())
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user