diff --git a/fastapi_book_server/app/services/author.py b/fastapi_book_server/app/services/author.py index 1de30fd..7ddf4a9 100644 --- a/fastapi_book_server/app/services/author.py +++ b/fastapi_book_server/app/services/author.py @@ -48,7 +48,7 @@ class AuthorTGRMSearchService(TRGMSearchService): GET_OBJECT_IDS_QUERY = GET_OBJECT_IDS_QUERY -GET_RANDOM_OBJECT_ID_QUERY = """ +GET_OBJECT_ID_QUERY = """ WITH filtered_authors AS ( SELECT id FROM authors WHERE EXISTS ( @@ -58,14 +58,13 @@ WITH filtered_authors AS ( AND books.lang = ANY(:langs ::text[]) ) ) -SELECT id FROM filtered_authors -ORDER BY RANDOM() LIMIT 1; +SELECT id FROM filtered_authors; """ class GetRandomAuthorService(GetRandomService): MODEL_CLASS = Author - GET_RANDOM_OBJECT_ID_QUERY = GET_RANDOM_OBJECT_ID_QUERY + GET_OBJECT_ID_QUERY = GET_OBJECT_ID_QUERY class AuthorMeiliSearchService(MeiliSearchService): diff --git a/fastapi_book_server/app/services/book.py b/fastapi_book_server/app/services/book.py index e57207a..4b0775c 100644 --- a/fastapi_book_server/app/services/book.py +++ b/fastapi_book_server/app/services/book.py @@ -89,19 +89,18 @@ class BookCreator: return await cls._create_remote_book(data) -GET_RANDOM_OBJECT_ID_QUERY = """ +GET_OBJECTS_ID_QUERY = """ WITH filtered_books AS ( SELECT id FROM books WHERE books.is_deleted = 'f' AND books.lang = ANY(:langs ::text[]) ) -SELECT id FROM filtered_books -ORDER BY RANDOM() LIMIT 1; +SELECT id FROM filtered_books; """ class GetRandomBookService(GetRandomService): MODEL_CLASS = BookDB - GET_RANDOM_OBJECT_ID_QUERY = GET_RANDOM_OBJECT_ID_QUERY + GET_OBJECT_ID_QUERY = GET_OBJECT_IDS_QUERY class BookMeiliSearchService(MeiliSearchService): diff --git a/fastapi_book_server/app/services/common.py b/fastapi_book_server/app/services/common.py index 1615414..4df9bcf 100644 --- a/fastapi_book_server/app/services/common.py +++ b/fastapi_book_server/app/services/common.py @@ -1,6 +1,7 @@ import abc import asyncio from concurrent.futures import ThreadPoolExecutor +from random import choice from typing import Optional, Generic, TypeVar, TypedDict, Union import aioredis @@ -102,11 +103,7 @@ class BaseSearchService(Generic[MODEL, QUERY], abc.ABC): print(e) @classmethod - async def get_objects( - cls, query: QUERY, redis: aioredis.Redis - ) -> tuple[int, list[MODEL]]: - params = cls.get_raw_params() - + async def _get_objects(cls, query: QUERY, redis: aioredis.Redis) -> list[int]: cached_object_ids = await cls.get_cached_ids(query, redis) if cached_object_ids is None: @@ -115,6 +112,16 @@ class BaseSearchService(Generic[MODEL, QUERY], abc.ABC): else: object_ids = cached_object_ids + return object_ids + + @classmethod + async def get_limited_objects( + cls, query: QUERY, redis: aioredis.Redis + ) -> tuple[int, list[MODEL]]: + object_ids = await cls._get_objects(query, redis) + + params = cls.get_raw_params() + limited_object_ids = object_ids[params.offset : params.offset + params.limit] queryset: QuerySet[MODEL] = cls.model.objects @@ -134,7 +141,7 @@ class BaseSearchService(Generic[MODEL, QUERY], abc.ABC): async def get(cls, query: QUERY, redis: aioredis.Redis) -> Page[MODEL]: params = cls.get_params() - total, objects = await cls.get_objects(query, redis) + total, objects = await cls.get_limited_objects(query, redis) return CustomPage.create(items=objects, total=total, params=params) @@ -229,7 +236,7 @@ class MeiliSearchService(Generic[MODEL], BaseSearchService[MODEL, SearchQuery]): class GetRandomService(Generic[MODEL]): MODEL_CLASS: Optional[MODEL] = None - GET_RANDOM_OBJECT_ID_QUERY: Optional[str] = None + GET_OBJECTS_ID_QUERY: Optional[str] = None @classmethod @property @@ -244,17 +251,23 @@ class GetRandomService(Generic[MODEL]): @classmethod @property - def random_object_id_query(cls) -> str: + def objects_id_query(cls) -> str: assert ( - cls.GET_RANDOM_OBJECT_ID_QUERY is not None + cls.GET_OBJECTS_ID_QUERY is not None ), f"GET_OBJECT_IDS_QUERY in {cls.__name__} don't set!" - return cls.GET_RANDOM_OBJECT_ID_QUERY + return cls.GET_OBJECTS_ID_QUERY + + @classmethod + async def get_objects(cls, allowed_langs: frozenset[str]) -> list[int]: + objects = await cls.database.fetch_all( + cls.objects_id_query, {"langs": allowed_langs} + ) + return [obj["id"] for obj in objects] @classmethod async def get_random_id(cls, allowed_langs: frozenset[str]) -> int: - return await cls.database.fetch_val( - cls.random_object_id_query, {"langs": allowed_langs} - ) + object_ids = await cls.get_objects(allowed_langs) + return choice(object_ids) class BaseFilterService(Generic[MODEL, QUERY], BaseSearchService[MODEL, QUERY]): diff --git a/fastapi_book_server/app/services/sequence.py b/fastapi_book_server/app/services/sequence.py index f7ea059..f5b70c6 100644 --- a/fastapi_book_server/app/services/sequence.py +++ b/fastapi_book_server/app/services/sequence.py @@ -36,7 +36,7 @@ class SequenceTGRMSearchService(TRGMSearchService): GET_OBJECT_IDS_QUERY = GET_OBJECT_IDS_QUERY -GET_RANDOM_OBJECT_ID_QUERY = """ +GET_OBJECT_ID_QUERY = """ WITH filtered_sequences AS ( SELECT id FROM sequences WHERE EXISTS ( @@ -55,7 +55,7 @@ ORDER BY RANDOM() LIMIT 1; class GetRandomSequenceService(GetRandomService): MODEL_CLASS = Sequence - GET_RANDOM_OBJECT_ID_QUERY = GET_RANDOM_OBJECT_ID_QUERY + GET_OBJECT_ID_QUERY = GET_OBJECT_ID_QUERY class SequenceMeiliSearchService(MeiliSearchService):