diff --git a/fastapi_book_server/app/services/author.py b/fastapi_book_server/app/services/author.py index 308eb30..7acb907 100644 --- a/fastapi_book_server/app/services/author.py +++ b/fastapi_book_server/app/services/author.py @@ -1,6 +1,6 @@ from app.models import Author -from app.services.common import TRGMSearchService +from app.services.common import TRGMSearchService, GetRandomService GET_OBJECT_IDS_QUERY = """ @@ -41,3 +41,7 @@ class AuthorTGRMSearchService(TRGMSearchService): MODEL_CLASS = Author PREFETCH_RELATED = ["source", "annotations"] GET_OBJECT_IDS_QUERY = GET_OBJECT_IDS_QUERY + + +class GetRandomAuthorService(GetRandomService): + MODEL_CLASS = Author diff --git a/fastapi_book_server/app/services/book.py b/fastapi_book_server/app/services/book.py index a73052b..43f9507 100644 --- a/fastapi_book_server/app/services/book.py +++ b/fastapi_book_server/app/services/book.py @@ -4,7 +4,7 @@ from fastapi import HTTPException, status from app.models import Book as BookDB, Author as AuthorDB -from app.services.common import TRGMSearchService +from app.services.common import TRGMSearchService, GetRandomService from app.serializers.book import CreateBook, CreateRemoteBook @@ -76,3 +76,7 @@ class BookCreator: return await cls._create_book(data) if isinstance(data, CreateRemoteBook): return await cls._create_remote_book(data) + + +class GetRandomBookService(GetRandomService): + MODEL_CLASS = BookDB diff --git a/fastapi_book_server/app/services/common.py b/fastapi_book_server/app/services/common.py index cfe3ca9..2dafdde 100644 --- a/fastapi_book_server/app/services/common.py +++ b/fastapi_book_server/app/services/common.py @@ -122,3 +122,31 @@ class TRGMSearchService(Generic[T]): total=total, params=params ) + + +GET_RANDOM_OBJECT_ID_QUERY = """ +SELECT id FROM {table} +WHERE id >= RANDOM() * (SELECT MAX(id) FROM {table}) +ORDER BY id LIMIT 1; +""" + + +class GetRandomService(Generic[T]): + MODEL_CLASS: Optional[T] = None + + @classmethod + @property + def model(cls) -> T: + assert cls.MODEL_CLASS is not None, f"MODEL in {cls.__name__} don't set!" + return cls.MODEL_CLASS + + @classmethod + @property + def database(cls) -> Database: + return cls.model.Meta.database + + @classmethod + async def get_random_id(cls) -> int: + table_name = cls.model.Meta.tablename + query = GET_RANDOM_OBJECT_ID_QUERY.format(table=table_name) + return await cls.database.fetch_val(query) diff --git a/fastapi_book_server/app/services/sequence.py b/fastapi_book_server/app/services/sequence.py index c7b5f2e..de89151 100644 --- a/fastapi_book_server/app/services/sequence.py +++ b/fastapi_book_server/app/services/sequence.py @@ -1,6 +1,6 @@ from app.models import Sequence -from app.services.common import TRGMSearchService +from app.services.common import TRGMSearchService, GetRandomService GET_OBJECT_IDS_QUERY = """ @@ -33,3 +33,7 @@ class SequenceTGRMSearchService(TRGMSearchService): MODEL_CLASS = Sequence PREFETCH_RELATED = ["source"] GET_OBJECT_IDS_QUERY = GET_OBJECT_IDS_QUERY + + +class GetRandomSequenceService(GetRandomService): + MODEL_CLASS = Sequence diff --git a/fastapi_book_server/app/views/author.py b/fastapi_book_server/app/views/author.py index 9169efb..8bbb999 100644 --- a/fastapi_book_server/app/views/author.py +++ b/fastapi_book_server/app/views/author.py @@ -9,7 +9,7 @@ from app.utils.pagination import CustomPage from app.models import Author as AuthorDB, AuthorAnnotation as AuthorAnnotationDB, Book as BookDB from app.serializers.author import Author, CreateAuthor, UpdateAuthor, AuthorBook, TranslatedBook from app.serializers.author_annotation import AuthorAnnotation -from app.services.author import AuthorTGRMSearchService +from app.services.author import AuthorTGRMSearchService, GetRandomAuthorService from app.depends import check_token @@ -42,9 +42,7 @@ async def create_author(data: CreateAuthor): @author_router.get("/random", response_model=Author) async def get_random_author(): - author_ids: list[int] = await AuthorDB.objects.values_list("id", flatten=True) - - author_id = random_choice(author_ids) + author_id = await GetRandomAuthorService.get_random_id() return await AuthorDB.objects.prefetch_related(PREFETCH_RELATED).get(id=author_id) diff --git a/fastapi_book_server/app/views/book.py b/fastapi_book_server/app/views/book.py index 7cf8c47..ab8273e 100644 --- a/fastapi_book_server/app/views/book.py +++ b/fastapi_book_server/app/views/book.py @@ -10,7 +10,7 @@ from app.utils.pagination import CustomPage from app.models import Book as BookDB, Author as AuthorDB, BookAnnotation as BookAnnotationDB from app.serializers.book import Book, RemoteBook, BookDetail, CreateBook, UpdateBook, CreateRemoteBook from app.serializers.book_annotation import BookAnnotation -from app.services.book import BookTGRMSearchService, BookCreator +from app.services.book import BookTGRMSearchService, GetRandomBookService, BookCreator from app.filters.book import get_book_filter from app.depends import check_token @@ -41,9 +41,7 @@ async def create_book(data: Union[CreateBook, CreateRemoteBook]): @book_router.get("/random", response_model=BookDetail) async def get_random_book(): - book_ids: list[int] = await BookDB.objects.filter(is_deleted=False).values_list("id", flatten=True) - - book_id = random_choice(book_ids) + book_id = await GetRandomBookService.get_random_id() return await BookDB.objects.select_related(SELECT_RELATED_FIELDS).get(id=book_id) diff --git a/fastapi_book_server/app/views/sequence.py b/fastapi_book_server/app/views/sequence.py index 03bf71c..eac7abb 100644 --- a/fastapi_book_server/app/views/sequence.py +++ b/fastapi_book_server/app/views/sequence.py @@ -8,7 +8,7 @@ from app.utils.pagination import CustomPage from app.models import Sequence as SequenceDB, Book as BookDB, BookSequences as BookSequencesDB from app.serializers.sequence import Sequence, CreateSequence, Book as SequenceBook -from app.services.sequence import SequenceTGRMSearchService +from app.services.sequence import SequenceTGRMSearchService, GetRandomSequenceService from app.depends import check_token @@ -28,9 +28,7 @@ async def get_sequences(): @sequence_router.get("/random", response_model=Sequence) async def get_random_sequence(): - sequence_ids: list[int] = await SequenceDB.objects.values_list("id", flatten=True) - - sequence_id = random_choice(sequence_ids) + sequence_id = await GetRandomSequenceService.get_random_id() return await SequenceDB.objects.get(id=sequence_id)