fix performance issues on /api/foods (#2163)

* fix performance issues on /api/foods

* fix comment

* actually apply query-options
This commit is contained in:
Sören 2023-02-22 05:01:27 +01:00 committed by GitHub
parent 666085b9ca
commit 9d35b0923a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 28 additions and 11 deletions

View File

@ -1,5 +1,7 @@
from pydantic import UUID4
from sqlalchemy import select
from sqlalchemy.orm import joinedload
from sqlalchemy.orm.interfaces import LoaderOption
from mealie.db.models.recipe.ingredient import IngredientFoodModel
from mealie.schema.recipe.recipe_ingredient import IngredientFood
@ -29,3 +31,9 @@ class RepositoryFood(RepositoryGeneric[IngredientFood, IngredientFoodModel]):
def by_group(self, group_id: UUID4) -> "RepositoryFood":
return super().by_group(group_id)
def paging_query_options(self) -> list[LoaderOption]:
return [
joinedload(IngredientFoodModel.extras),
joinedload(IngredientFoodModel.label),
]

View File

@ -7,6 +7,7 @@ from typing import Any, Generic, TypeVar
from fastapi import HTTPException
from pydantic import UUID4, BaseModel
from sqlalchemy import Select, delete, func, select
from sqlalchemy.orm.interfaces import LoaderOption
from sqlalchemy.orm.session import Session
from sqlalchemy.sql import sqltypes
@ -284,6 +285,10 @@ class RepositoryGeneric(Generic[Schema, Model]):
q = self._query().filter(attribute_name == attr_match)
return [eff_schema.from_orm(x) for x in self.session.execute(q).scalars().all()]
def paging_query_options(self) -> list[LoaderOption]:
# Override this in subclasses to specify joinedloads or similar for page_all
return []
def page_all(self, pagination: PaginationQuery, override=None) -> PaginationBase[Schema]:
"""
pagination is a method to interact with the filtered database table and return a paginated result
@ -296,19 +301,18 @@ class RepositoryGeneric(Generic[Schema, Model]):
"""
eff_schema = override or self.schema
q = self._query()
q = self._query().options(*self.paging_query_options())
fltr = self._filter_builder()
q = q.filter_by(**fltr)
q, count, total_pages = self.add_pagination_to_query(q, pagination)
try:
data = self.session.execute(q).scalars().all()
data = self.session.execute(q).unique().scalars().all()
except Exception as e:
self._log_exception(e)
self.session.rollback()
raise e
return PaginationBase(
page=pagination.page,
per_page=pagination.per_page,

View File

@ -2,12 +2,12 @@ from __future__ import annotations
import datetime
import enum
from typing import Any
from uuid import UUID, uuid4
from pydantic import UUID4, Field, validator
from pydantic.utils import GetterDict
from mealie.db.models.recipe.ingredient import IngredientFoodModel
from mealie.schema._mealie import MealieModel
from mealie.schema._mealie.types import NoneFloat
from mealie.schema.response.pagination import PaginationBase
@ -37,14 +37,19 @@ class IngredientFood(CreateIngredientFood):
update_at: datetime.datetime | None
class Config:
orm_mode = True
class _FoodGetter(GetterDict):
def get(self, key: Any, default: Any = None) -> Any:
# Transform extras into key-value dict
if key == "extras":
value = super().get(key, default)
return {x.key_name: x.value for x in value}
@classmethod
def getter_dict(cls, name_orm: IngredientFoodModel):
return {
**GetterDict(name_orm),
"extras": {x.key_name: x.value for x in name_orm.extras},
}
# Keep all other fields as they are
else:
return super().get(key, default)
orm_mode = True
getter_dict = _FoodGetter
class IngredientFoodPagination(PaginationBase):