From 847b7e52385fae1bcb5cb22286819e5dd0a4f857 Mon Sep 17 00:00:00 2001 From: Michael Genson <71845777+michael-genson@users.noreply.github.com> Date: Thu, 19 Sep 2024 23:10:10 -0500 Subject: [PATCH] chore: Optimize Loads on Queries (#4220) Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com> --- mealie/repos/repository_recipes.py | 13 +++---------- mealie/schema/household/group_shopping_list.py | 9 ++++++--- mealie/schema/household/household_preferences.py | 10 ++++++++++ mealie/schema/meal_plan/new_meal.py | 3 ++- mealie/schema/recipe/recipe.py | 10 ++++++++++ mealie/schema/recipe/recipe_timeline_events.py | 3 ++- mealie/schema/user/user.py | 8 ++++++++ 7 files changed, 41 insertions(+), 15 deletions(-) diff --git a/mealie/repos/repository_recipes.py b/mealie/repos/repository_recipes.py index 0c5dd984292..e864c926e38 100644 --- a/mealie/repos/repository_recipes.py +++ b/mealie/repos/repository_recipes.py @@ -7,7 +7,7 @@ from pydantic import UUID4 from slugify import slugify from sqlalchemy.exc import IntegrityError -from sqlalchemy.orm import InstrumentedAttribute, joinedload +from sqlalchemy.orm import InstrumentedAttribute from typing_extensions import Self from mealie.db.models.recipe.category import Category @@ -165,15 +165,6 @@ def page_all( # type: ignore pagination_result = pagination.model_copy() q = sa.select(self.model) - args = [ - joinedload(RecipeModel.recipe_category), - joinedload(RecipeModel.tags), - joinedload(RecipeModel.tools), - joinedload(RecipeModel.user), - ] - - q = q.options(*args) - fltr = self._filter_builder() q = q.filter_by(**fltr) @@ -212,6 +203,8 @@ def page_all( # type: ignore q, count, total_pages = self.add_pagination_to_query(q, pagination_result) + # Apply options late, so they do not get used for counting + q = q.options(*RecipeSummary.loader_options()) try: data = self.session.execute(q).scalars().unique().all() except Exception as e: diff --git a/mealie/schema/household/group_shopping_list.py b/mealie/schema/household/group_shopping_list.py index a0979fe3100..47dbf610c05 100644 --- a/mealie/schema/household/group_shopping_list.py +++ b/mealie/schema/household/group_shopping_list.py @@ -14,6 +14,7 @@ ShoppingListRecipeReference, ) from mealie.db.models.recipe import IngredientFoodModel, RecipeModel +from mealie.db.models.users.users import User from mealie.schema._mealie import MealieModel from mealie.schema._mealie.mealie_model import UpdatedAtField from mealie.schema._mealie.types import NoneFloat @@ -137,7 +138,9 @@ def loader_options(cls) -> list[LoaderOption]: joinedload(ShoppingListItem.label), joinedload(ShoppingListItem.unit), selectinload(ShoppingListItem.recipe_references), - joinedload(ShoppingListItem.shopping_list).joinedload(ShoppingList.user), + joinedload(ShoppingListItem.shopping_list) + .joinedload(ShoppingList.user) + .load_only(User.household_id, User.group_id), ] @@ -232,7 +235,7 @@ def loader_options(cls) -> list[LoaderOption]: .joinedload(ShoppingListRecipeReference.recipe) .joinedload(RecipeModel.tools), selectinload(ShoppingList.label_settings).joinedload(ShoppingListMultiPurposeLabel.label), - joinedload(ShoppingList.user), + joinedload(ShoppingList.user).load_only(User.household_id, User.group_id), ] @@ -279,7 +282,7 @@ def loader_options(cls) -> list[LoaderOption]: .joinedload(ShoppingListRecipeReference.recipe) .joinedload(RecipeModel.tools), selectinload(ShoppingList.label_settings).joinedload(ShoppingListMultiPurposeLabel.label), - joinedload(ShoppingList.user), + joinedload(ShoppingList.user).load_only(User.household_id, User.group_id), ] diff --git a/mealie/schema/household/household_preferences.py b/mealie/schema/household/household_preferences.py index 5c830bb2ded..5cbc80440d1 100644 --- a/mealie/schema/household/household_preferences.py +++ b/mealie/schema/household/household_preferences.py @@ -1,5 +1,9 @@ from pydantic import UUID4, ConfigDict +from sqlalchemy.orm import joinedload +from sqlalchemy.orm.interfaces import LoaderOption +from mealie.db.models.household.household import Household +from mealie.db.models.household.preferences import HouseholdPreferencesModel from mealie.schema._mealie import MealieModel @@ -27,3 +31,9 @@ class SaveHouseholdPreferences(UpdateHouseholdPreferences): class ReadHouseholdPreferences(CreateHouseholdPreferences): id: UUID4 model_config = ConfigDict(from_attributes=True) + + @classmethod + def loader_options(cls) -> list[LoaderOption]: + return [ + joinedload(HouseholdPreferencesModel.household).load_only(Household.group_id), + ] diff --git a/mealie/schema/meal_plan/new_meal.py b/mealie/schema/meal_plan/new_meal.py index fb0efdcf238..a59a63eb5f4 100644 --- a/mealie/schema/meal_plan/new_meal.py +++ b/mealie/schema/meal_plan/new_meal.py @@ -10,6 +10,7 @@ from mealie.db.models.household import GroupMealPlan from mealie.db.models.recipe import RecipeModel +from mealie.db.models.users.users import User from mealie.schema._mealie import MealieModel from mealie.schema.recipe.recipe import RecipeSummary from mealie.schema.response.pagination import PaginationBase @@ -66,7 +67,7 @@ def loader_options(cls) -> list[LoaderOption]: selectinload(GroupMealPlan.recipe).joinedload(RecipeModel.recipe_category), selectinload(GroupMealPlan.recipe).joinedload(RecipeModel.tags), selectinload(GroupMealPlan.recipe).joinedload(RecipeModel.tools), - selectinload(GroupMealPlan.user), + selectinload(GroupMealPlan.user).load_only(User.household_id), ] diff --git a/mealie/schema/recipe/recipe.py b/mealie/schema/recipe/recipe.py index c5d76079ca2..a328614d5c3 100644 --- a/mealie/schema/recipe/recipe.py +++ b/mealie/schema/recipe/recipe.py @@ -14,6 +14,7 @@ from sqlalchemy.orm.interfaces import LoaderOption from mealie.core.config import get_app_dirs +from mealie.db.models.users.users import User from mealie.schema._mealie import MealieModel, SearchType from mealie.schema._mealie.mealie_model import UpdatedAtField from mealie.schema.response.pagination import PaginationBase @@ -121,6 +122,15 @@ def clean_strings(val: Any): return val + @classmethod + def loader_options(cls) -> list[LoaderOption]: + return [ + joinedload(RecipeModel.recipe_category), + joinedload(RecipeModel.tags), + joinedload(RecipeModel.tools), + joinedload(RecipeModel.user).load_only(User.household_id), + ] + class RecipePagination(PaginationBase): items: list[RecipeSummary] diff --git a/mealie/schema/recipe/recipe_timeline_events.py b/mealie/schema/recipe/recipe_timeline_events.py index 4b2d788365b..02de699ceb8 100644 --- a/mealie/schema/recipe/recipe_timeline_events.py +++ b/mealie/schema/recipe/recipe_timeline_events.py @@ -9,6 +9,7 @@ from mealie.core.config import get_app_dirs from mealie.db.models.recipe.recipe_timeline import RecipeTimelineEvent +from mealie.db.models.users.users import User from mealie.schema._mealie import MealieModel from mealie.schema._mealie.mealie_model import UpdatedAtField from mealie.schema.recipe.recipe import Recipe @@ -67,7 +68,7 @@ class RecipeTimelineEventOut(RecipeTimelineEventCreate): def loader_options(cls) -> list[LoaderOption]: return [ joinedload(RecipeTimelineEvent.recipe), - joinedload(RecipeTimelineEvent.user), + joinedload(RecipeTimelineEvent.user).load_only(User.household_id, User.group_id), ] @classmethod diff --git a/mealie/schema/user/user.py b/mealie/schema/user/user.py index 6b1b1a18da8..33f91fffb6b 100644 --- a/mealie/schema/user/user.py +++ b/mealie/schema/user/user.py @@ -8,7 +8,9 @@ from sqlalchemy.orm.interfaces import LoaderOption from mealie.core.config import get_app_dirs, get_app_settings +from mealie.db.models.recipe.recipe import RecipeModel from mealie.db.models.users import User +from mealie.db.models.users.user_to_recipe import UserToRecipe from mealie.db.models.users.users import AuthMethod, LongLiveToken from mealie.schema._mealie import MealieModel from mealie.schema.group.group_preferences import ReadGroupPreferences @@ -88,6 +90,12 @@ class UserRatingUpdate(MealieModel): class UserRatingOut(UserRatingCreate): id: UUID4 + @classmethod + def loader_options(cls) -> list[LoaderOption]: + return [ + joinedload(UserToRecipe.recipe).joinedload(RecipeModel.user).load_only(User.household_id, User.group_id) + ] + class UserRatings(BaseModel, Generic[DataT]): ratings: list[DataT]