mirror of
https://github.com/flibusta-apps/book_library_server.git
synced 2025-12-06 15:15:36 +01:00
Add allowed_langs filter
This commit is contained in:
@@ -17,7 +17,10 @@ SELECT ARRAY(
|
||||
) as sml,
|
||||
(
|
||||
SELECT count(*) FROM book_authors
|
||||
LEFT JOIN books ON (books.id = book AND books.is_deleted = 'f')
|
||||
LEFT JOIN books
|
||||
ON (books.id = book AND
|
||||
books.is_deleted = 'f' AND
|
||||
books.lang = ANY(:langs ::text[]))
|
||||
WHERE author = authors.id
|
||||
) as books_count
|
||||
FROM authors
|
||||
@@ -28,7 +31,10 @@ SELECT ARRAY(
|
||||
) AND
|
||||
EXISTS (
|
||||
SELECT * FROM book_authors
|
||||
LEFT JOIN books ON (books.id = book AND books.is_deleted = 'f')
|
||||
LEFT JOIN books
|
||||
ON (books.id = book AND
|
||||
books.is_deleted = 'f' AND
|
||||
books.lang = ANY(:langs ::text[]))
|
||||
WHERE author = authors.id
|
||||
)
|
||||
)
|
||||
@@ -45,5 +51,23 @@ class AuthorTGRMSearchService(TRGMSearchService):
|
||||
GET_OBJECT_IDS_QUERY = GET_OBJECT_IDS_QUERY
|
||||
|
||||
|
||||
GET_RANDOM_OBJECT_ID_QUERY = """
|
||||
WITH filtered_authors AS (
|
||||
SELECT id FROM authors
|
||||
WHERE EXISTS (
|
||||
SELECT * FROM book_authors
|
||||
LEFT JOIN books
|
||||
ON (books.id = book AND
|
||||
books.is_deleted = 'f' AND
|
||||
books.lang = ANY(:langs ::text[]))
|
||||
WHERE author = authors.id
|
||||
)
|
||||
)
|
||||
SELECT id FROM filtered_authors
|
||||
ORDER BY RANDOM() LIMIT 1;
|
||||
"""
|
||||
|
||||
|
||||
class GetRandomAuthorService(GetRandomService):
|
||||
MODEL_CLASS = Author
|
||||
GET_RANDOM_OBJECT_ID_QUERY = GET_RANDOM_OBJECT_ID_QUERY
|
||||
|
||||
@@ -13,6 +13,7 @@ SELECT ARRAY(
|
||||
WITH filtered_books AS (
|
||||
SELECT id, similarity(title, :query) as sml FROM books
|
||||
WHERE books.title % :query AND books.is_deleted = 'f'
|
||||
AND books.lang = ANY(:langs ::text[])
|
||||
)
|
||||
SELECT fbooks.id FROM filtered_books as fbooks
|
||||
ORDER BY fbooks.sml DESC, fbooks.id
|
||||
@@ -76,5 +77,16 @@ class BookCreator:
|
||||
return await cls._create_remote_book(data)
|
||||
|
||||
|
||||
GET_RANDOM_OBJECT_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;
|
||||
"""
|
||||
|
||||
|
||||
class GetRandomBookService(GetRandomService):
|
||||
MODEL_CLASS = BookDB
|
||||
GET_RANDOM_OBJECT_ID_QUERY = GET_RANDOM_OBJECT_ID_QUERY
|
||||
|
||||
@@ -54,8 +54,12 @@ class TRGMSearchService(Generic[T]):
|
||||
return cls.GET_OBJECT_IDS_QUERY
|
||||
|
||||
@classmethod
|
||||
async def _get_object_ids(cls, query_data: str) -> list[int]:
|
||||
row = await cls.database.fetch_one(cls.object_ids_query, {"query": query_data})
|
||||
async def _get_object_ids(
|
||||
cls, query_data: str, allowed_langs: list[str]
|
||||
) -> list[int]:
|
||||
row = await cls.database.fetch_one(
|
||||
cls.object_ids_query, {"query": query_data, "langs": allowed_langs}
|
||||
)
|
||||
|
||||
if row is None:
|
||||
raise ValueError("Something is wrong!")
|
||||
@@ -63,16 +67,20 @@ class TRGMSearchService(Generic[T]):
|
||||
return row["array"]
|
||||
|
||||
@classmethod
|
||||
def get_cache_key(cls, query_data: str) -> str:
|
||||
def get_cache_key(cls, query_data: str, allowed_langs: list[str]) -> str:
|
||||
model_class_name = cls.model.__class__.__name__
|
||||
return f"{model_class_name}_{query_data}"
|
||||
allowed_langs_part = ",".join(allowed_langs)
|
||||
return f"{model_class_name}_{query_data}_{allowed_langs_part}"
|
||||
|
||||
@classmethod
|
||||
async def get_cached_ids(
|
||||
cls, query_data: str, redis: aioredis.Redis
|
||||
cls,
|
||||
query_data: str,
|
||||
allowed_langs: list[str],
|
||||
redis: aioredis.Redis,
|
||||
) -> Optional[list[int]]:
|
||||
try:
|
||||
key = cls.get_cache_key(query_data)
|
||||
key = cls.get_cache_key(query_data, allowed_langs)
|
||||
data = await redis.get(key)
|
||||
|
||||
if data is None:
|
||||
@@ -85,25 +93,32 @@ class TRGMSearchService(Generic[T]):
|
||||
|
||||
@classmethod
|
||||
async def cache_object_ids(
|
||||
cls, query_data: str, object_ids: list[int], redis: aioredis.Redis
|
||||
cls,
|
||||
query_data: str,
|
||||
allowed_langs: list[str],
|
||||
object_ids: list[int],
|
||||
redis: aioredis.Redis,
|
||||
):
|
||||
try:
|
||||
key = cls.get_cache_key(query_data)
|
||||
key = cls.get_cache_key(query_data, allowed_langs)
|
||||
await redis.set(key, orjson.dumps(object_ids), ex=cls.CACHE_TTL)
|
||||
except aioredis.RedisError as e:
|
||||
print(e)
|
||||
|
||||
@classmethod
|
||||
async def get_objects(
|
||||
cls, query_data: str, redis: aioredis.Redis
|
||||
cls,
|
||||
query_data: str,
|
||||
redis: aioredis.Redis,
|
||||
allowed_langs: list[str],
|
||||
) -> tuple[int, list[T]]:
|
||||
params = cls.get_raw_params()
|
||||
|
||||
cached_object_ids = await cls.get_cached_ids(query_data, redis)
|
||||
cached_object_ids = await cls.get_cached_ids(query_data, allowed_langs, redis)
|
||||
|
||||
if cached_object_ids is None:
|
||||
object_ids = await cls._get_object_ids(query_data)
|
||||
await cls.cache_object_ids(query_data, object_ids, redis)
|
||||
object_ids = await cls._get_object_ids(query_data, allowed_langs)
|
||||
await cls.cache_object_ids(query_data, allowed_langs, object_ids, redis)
|
||||
else:
|
||||
object_ids = cached_object_ids
|
||||
|
||||
@@ -120,23 +135,19 @@ class TRGMSearchService(Generic[T]):
|
||||
return len(object_ids), await queryset.filter(id__in=limited_object_ids).all()
|
||||
|
||||
@classmethod
|
||||
async def get(cls, query: str, redis: aioredis.Redis) -> Page[T]:
|
||||
async def get(
|
||||
cls, query: str, redis: aioredis.Redis, allowed_langs: list[str]
|
||||
) -> Page[T]:
|
||||
params = cls.get_params()
|
||||
|
||||
total, objects = await cls.get_objects(query, redis)
|
||||
total, objects = await cls.get_objects(query, redis, allowed_langs)
|
||||
|
||||
return CustomPage.create(items=objects, 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
|
||||
GET_RANDOM_OBJECT_ID_QUERY: Optional[str] = None
|
||||
|
||||
@classmethod
|
||||
@property
|
||||
@@ -150,7 +161,15 @@ class GetRandomService(Generic[T]):
|
||||
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)
|
||||
@property
|
||||
def random_object_id_query(cls) -> str:
|
||||
assert (
|
||||
cls.GET_RANDOM_OBJECT_ID_QUERY is not None
|
||||
), f"GET_OBJECT_IDS_QUERY in {cls.__name__} don't set!"
|
||||
return cls.GET_RANDOM_OBJECT_ID_QUERY
|
||||
|
||||
@classmethod
|
||||
async def get_random_id(cls, allowed_langs: list[str]) -> int:
|
||||
return await cls.database.fetch_val(
|
||||
cls.random_object_id_query, {"langs": allowed_langs}
|
||||
)
|
||||
|
||||
@@ -10,14 +10,20 @@ SELECT ARRAY (
|
||||
similarity(name, :query) as sml,
|
||||
(
|
||||
SELECT count(*) FROM book_sequences
|
||||
LEFT JOIN books ON (books.id = book AND books.is_deleted = 'f')
|
||||
LEFT JOIN books
|
||||
ON (books.id = book AND
|
||||
books.is_deleted = 'f' AND
|
||||
books.lang = ANY(:langs ::text[]))
|
||||
WHERE sequence = sequences.id
|
||||
) as books_count
|
||||
FROM sequences
|
||||
WHERE name % :query AND
|
||||
EXISTS (
|
||||
SELECT * FROM book_sequences
|
||||
LEFT JOIN books ON (books.id = book AND books.is_deleted = 'f')
|
||||
LEFT JOIN books
|
||||
ON (books.id = book AND
|
||||
books.is_deleted = 'f' AND
|
||||
books.lang = ANY(:langs ::text[]))
|
||||
WHERE sequence = sequences.id
|
||||
)
|
||||
)
|
||||
@@ -34,5 +40,23 @@ class SequenceTGRMSearchService(TRGMSearchService):
|
||||
GET_OBJECT_IDS_QUERY = GET_OBJECT_IDS_QUERY
|
||||
|
||||
|
||||
GET_RANDOM_OBJECT_ID_QUERY = """
|
||||
WITH filtered_sequences AS (
|
||||
SELECT id FROM sequences
|
||||
WHERE EXISTS (
|
||||
SELECT * FROM book_sequences
|
||||
LEFT JOIN books
|
||||
ON (books.id = book AND
|
||||
books.is_deleted = 'f' AND
|
||||
books.lang = ANY(:langs ::text[]))
|
||||
WHERE sequence = sequences.id
|
||||
)
|
||||
)
|
||||
SELECT id FROM filtered_sequences
|
||||
ORDER BY RANDOM() LIMIT 1;
|
||||
"""
|
||||
|
||||
|
||||
class GetRandomSequenceService(GetRandomService):
|
||||
MODEL_CLASS = Sequence
|
||||
GET_RANDOM_OBJECT_ID_QUERY = GET_RANDOM_OBJECT_ID_QUERY
|
||||
|
||||
Reference in New Issue
Block a user