mirror of
https://github.com/flibusta-apps/book_library_server.git
synced 2025-12-06 15:15:36 +01:00
Add meilisearch
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
from app.models import Author
|
||||
from app.services.common import TRGMSearchService, GetRandomService
|
||||
from app.services.common import TRGMSearchService, MeiliSearchService, GetRandomService
|
||||
|
||||
|
||||
GET_OBJECT_IDS_QUERY = """
|
||||
@@ -66,3 +66,12 @@ ORDER BY RANDOM() LIMIT 1;
|
||||
class GetRandomAuthorService(GetRandomService):
|
||||
MODEL_CLASS = Author
|
||||
GET_RANDOM_OBJECT_ID_QUERY = GET_RANDOM_OBJECT_ID_QUERY
|
||||
|
||||
|
||||
class AuthorMeiliSearchService(MeiliSearchService):
|
||||
MODEL_CLASS = Author
|
||||
SELECT_RELATED = ["source"]
|
||||
PREFETCH_RELATED = ["annotations"]
|
||||
|
||||
MS_INDEX_NAME = "authors"
|
||||
MS_INDEX_LANG_KEY = "author_langs"
|
||||
|
||||
@@ -5,7 +5,7 @@ from fastapi import HTTPException, status
|
||||
from app.models import Author as AuthorDB
|
||||
from app.models import Book as BookDB
|
||||
from app.serializers.book import CreateBook, CreateRemoteBook
|
||||
from app.services.common import TRGMSearchService, GetRandomService
|
||||
from app.services.common import TRGMSearchService, MeiliSearchService, GetRandomService
|
||||
|
||||
|
||||
GET_OBJECT_IDS_QUERY = """
|
||||
@@ -91,3 +91,12 @@ ORDER BY RANDOM() LIMIT 1;
|
||||
class GetRandomBookService(GetRandomService):
|
||||
MODEL_CLASS = BookDB
|
||||
GET_RANDOM_OBJECT_ID_QUERY = GET_RANDOM_OBJECT_ID_QUERY
|
||||
|
||||
|
||||
class BookMeiliSearchService(MeiliSearchService):
|
||||
MODEL_CLASS = BookDB
|
||||
SELECT_RELATED = ["source"]
|
||||
PREFETCH_RELATED = ["authors", "translators", "annotations"]
|
||||
|
||||
MS_INDEX_NAME = "books"
|
||||
MS_INDEX_LANG_KEY = "lang"
|
||||
|
||||
@@ -1,24 +1,28 @@
|
||||
import abc
|
||||
import asyncio
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from typing import Optional, Generic, TypeVar, Union
|
||||
|
||||
import aioredis
|
||||
from databases import Database
|
||||
from fastapi_pagination.api import resolve_params
|
||||
from fastapi_pagination.bases import AbstractParams, RawParams
|
||||
import meilisearch
|
||||
import orjson
|
||||
from ormar import Model, QuerySet
|
||||
from sqlalchemy import Table
|
||||
|
||||
from app.utils.pagination import Page, CustomPage
|
||||
from core.config import env_config
|
||||
|
||||
|
||||
T = TypeVar("T", bound=Model)
|
||||
|
||||
|
||||
class TRGMSearchService(Generic[T]):
|
||||
class BaseSearchService(Generic[T], abc.ABC):
|
||||
MODEL_CLASS: Optional[T] = None
|
||||
SELECT_RELATED: Optional[Union[list[str], str]] = None
|
||||
PREFETCH_RELATED: Optional[Union[list[str], str]] = None
|
||||
GET_OBJECT_IDS_QUERY: Optional[str] = None
|
||||
CUSTOM_CACHE_PREFIX: Optional[str] = None
|
||||
CACHE_TTL = 60 * 60
|
||||
|
||||
@@ -48,29 +52,14 @@ class TRGMSearchService(Generic[T]):
|
||||
|
||||
@classmethod
|
||||
@property
|
||||
def object_ids_query(cls) -> str:
|
||||
assert (
|
||||
cls.GET_OBJECT_IDS_QUERY is not None
|
||||
), f"GET_OBJECT_IDS_QUERY in {cls.__name__} don't set!"
|
||||
return cls.GET_OBJECT_IDS_QUERY
|
||||
def cache_prefix(cls) -> str:
|
||||
return cls.CUSTOM_CACHE_PREFIX or cls.model.Meta.tablename
|
||||
|
||||
@classmethod
|
||||
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!")
|
||||
|
||||
return row["array"]
|
||||
|
||||
@classmethod
|
||||
@property
|
||||
def cache_prefix(cls) -> str:
|
||||
return cls.CUSTOM_CACHE_PREFIX or cls.model.Meta.tablename
|
||||
...
|
||||
|
||||
@classmethod
|
||||
def get_cache_key(cls, query_data: str, allowed_langs: list[str]) -> str:
|
||||
@@ -151,6 +140,92 @@ class TRGMSearchService(Generic[T]):
|
||||
return CustomPage.create(items=objects, total=total, params=params)
|
||||
|
||||
|
||||
class TRGMSearchService(BaseSearchService[T]):
|
||||
GET_OBJECT_IDS_QUERY: Optional[str] = None
|
||||
|
||||
@classmethod
|
||||
@property
|
||||
def object_ids_query(cls) -> str:
|
||||
assert (
|
||||
cls.GET_OBJECT_IDS_QUERY is not None
|
||||
), f"GET_OBJECT_IDS_QUERY in {cls.__name__} don't set!"
|
||||
return cls.GET_OBJECT_IDS_QUERY
|
||||
|
||||
@classmethod
|
||||
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!")
|
||||
|
||||
return row["array"]
|
||||
|
||||
|
||||
class MeiliSearchService(BaseSearchService[T]):
|
||||
MS_INDEX_NAME: Optional[str] = None
|
||||
MS_INDEX_LANG_KEY: Optional[str] = None
|
||||
|
||||
_executor = ThreadPoolExecutor(4)
|
||||
|
||||
@classmethod
|
||||
@property
|
||||
def lang_key(cls) -> str:
|
||||
assert cls.MS_INDEX_LANG_KEY is not None, f"MODEL in {cls.__name__} don't set!"
|
||||
return cls.MS_INDEX_LANG_KEY
|
||||
|
||||
@classmethod
|
||||
@property
|
||||
def index_name(cls) -> str:
|
||||
assert cls.MS_INDEX_NAME is not None, f"MODEL in {cls.__name__} don't set!"
|
||||
return cls.MS_INDEX_NAME
|
||||
|
||||
@classmethod
|
||||
def get_allowed_langs_filter(cls, allowed_langs: list[str]) -> list[list[str]]:
|
||||
return [[f"{cls.lang_key} = {lang}" for lang in allowed_langs]]
|
||||
|
||||
@classmethod
|
||||
def make_request(
|
||||
cls, query: str, allowed_langs_filter: list[list[str]], offset: int
|
||||
):
|
||||
client = meilisearch.Client(env_config.MEILI_HOST, env_config.MEILI_MASTER_KEY)
|
||||
index = client.index(cls.index_name)
|
||||
|
||||
result = index.search(
|
||||
query,
|
||||
{
|
||||
"filter": allowed_langs_filter,
|
||||
"offset": offset,
|
||||
"limit": 630,
|
||||
"attributesToRetrieve": ["id"],
|
||||
},
|
||||
)
|
||||
|
||||
total: int = result["nbHits"]
|
||||
ids: list[int] = [r["id"] for r in result["hits"][:total]]
|
||||
|
||||
return ids
|
||||
|
||||
@classmethod
|
||||
async def _get_object_ids(
|
||||
cls, query_data: str, allowed_langs: list[str]
|
||||
) -> list[int]:
|
||||
params = cls.get_raw_params()
|
||||
|
||||
allowed_langs_filter = cls.get_allowed_langs_filter(allowed_langs)
|
||||
|
||||
return await asyncio.get_event_loop().run_in_executor(
|
||||
cls._executor,
|
||||
cls.make_request,
|
||||
query_data,
|
||||
allowed_langs_filter,
|
||||
params.offset,
|
||||
)
|
||||
|
||||
|
||||
class GetRandomService(Generic[T]):
|
||||
MODEL_CLASS: Optional[T] = None
|
||||
GET_RANDOM_OBJECT_ID_QUERY: Optional[str] = None
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from app.models import Sequence
|
||||
from app.services.common import TRGMSearchService, GetRandomService
|
||||
from app.services.common import TRGMSearchService, MeiliSearchService, GetRandomService
|
||||
|
||||
|
||||
GET_OBJECT_IDS_QUERY = """
|
||||
@@ -56,3 +56,11 @@ ORDER BY RANDOM() LIMIT 1;
|
||||
class GetRandomSequenceService(GetRandomService):
|
||||
MODEL_CLASS = Sequence
|
||||
GET_RANDOM_OBJECT_ID_QUERY = GET_RANDOM_OBJECT_ID_QUERY
|
||||
|
||||
|
||||
class SequenceMeiliSearchService(MeiliSearchService):
|
||||
MODEL_CLASS = Sequence
|
||||
SELECT_RELATED = ["source"]
|
||||
|
||||
MS_INDEX_NAME = "sequences"
|
||||
MS_INDEX_LANG_KEY = "langs"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from app.models import Author
|
||||
from app.services.common import TRGMSearchService
|
||||
from app.services.common import TRGMSearchService, MeiliSearchService
|
||||
|
||||
|
||||
GET_OBJECT_IDS_QUERY = """
|
||||
@@ -47,3 +47,13 @@ class TranslatorTGRMSearchService(TRGMSearchService):
|
||||
SELECT_RELATED = ["source"]
|
||||
PREFETCH_RELATED = ["annotations"]
|
||||
GET_OBJECT_IDS_QUERY = GET_OBJECT_IDS_QUERY
|
||||
|
||||
|
||||
class TranslatorMeiliSearchService(MeiliSearchService):
|
||||
MODEL_CLASS = Author
|
||||
CUSTOM_CACHE_PREFIX = "translator"
|
||||
SELECT_RELATED = ["source"]
|
||||
PREFETCH_RELATED = ["annotations"]
|
||||
|
||||
MS_INDEX_NAME = "authors"
|
||||
MS_INDEX_LANG_KEY = "translator_langs"
|
||||
|
||||
Reference in New Issue
Block a user