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.models import Author
|
||||||
from app.services.common import TRGMSearchService, GetRandomService
|
from app.services.common import TRGMSearchService, MeiliSearchService, GetRandomService
|
||||||
|
|
||||||
|
|
||||||
GET_OBJECT_IDS_QUERY = """
|
GET_OBJECT_IDS_QUERY = """
|
||||||
@@ -66,3 +66,12 @@ ORDER BY RANDOM() LIMIT 1;
|
|||||||
class GetRandomAuthorService(GetRandomService):
|
class GetRandomAuthorService(GetRandomService):
|
||||||
MODEL_CLASS = Author
|
MODEL_CLASS = Author
|
||||||
GET_RANDOM_OBJECT_ID_QUERY = GET_RANDOM_OBJECT_ID_QUERY
|
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 Author as AuthorDB
|
||||||
from app.models import Book as BookDB
|
from app.models import Book as BookDB
|
||||||
from app.serializers.book import CreateBook, CreateRemoteBook
|
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 = """
|
GET_OBJECT_IDS_QUERY = """
|
||||||
@@ -91,3 +91,12 @@ ORDER BY RANDOM() LIMIT 1;
|
|||||||
class GetRandomBookService(GetRandomService):
|
class GetRandomBookService(GetRandomService):
|
||||||
MODEL_CLASS = BookDB
|
MODEL_CLASS = BookDB
|
||||||
GET_RANDOM_OBJECT_ID_QUERY = GET_RANDOM_OBJECT_ID_QUERY
|
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
|
from typing import Optional, Generic, TypeVar, Union
|
||||||
|
|
||||||
import aioredis
|
import aioredis
|
||||||
from databases import Database
|
from databases import Database
|
||||||
from fastapi_pagination.api import resolve_params
|
from fastapi_pagination.api import resolve_params
|
||||||
from fastapi_pagination.bases import AbstractParams, RawParams
|
from fastapi_pagination.bases import AbstractParams, RawParams
|
||||||
|
import meilisearch
|
||||||
import orjson
|
import orjson
|
||||||
from ormar import Model, QuerySet
|
from ormar import Model, QuerySet
|
||||||
from sqlalchemy import Table
|
from sqlalchemy import Table
|
||||||
|
|
||||||
from app.utils.pagination import Page, CustomPage
|
from app.utils.pagination import Page, CustomPage
|
||||||
|
from core.config import env_config
|
||||||
|
|
||||||
|
|
||||||
T = TypeVar("T", bound=Model)
|
T = TypeVar("T", bound=Model)
|
||||||
|
|
||||||
|
|
||||||
class TRGMSearchService(Generic[T]):
|
class BaseSearchService(Generic[T], abc.ABC):
|
||||||
MODEL_CLASS: Optional[T] = None
|
MODEL_CLASS: Optional[T] = None
|
||||||
SELECT_RELATED: Optional[Union[list[str], str]] = None
|
SELECT_RELATED: Optional[Union[list[str], str]] = None
|
||||||
PREFETCH_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
|
CUSTOM_CACHE_PREFIX: Optional[str] = None
|
||||||
CACHE_TTL = 60 * 60
|
CACHE_TTL = 60 * 60
|
||||||
|
|
||||||
@@ -48,29 +52,14 @@ class TRGMSearchService(Generic[T]):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@property
|
@property
|
||||||
def object_ids_query(cls) -> str:
|
def cache_prefix(cls) -> str:
|
||||||
assert (
|
return cls.CUSTOM_CACHE_PREFIX or cls.model.Meta.tablename
|
||||||
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
|
@classmethod
|
||||||
async def _get_object_ids(
|
async def _get_object_ids(
|
||||||
cls, query_data: str, allowed_langs: list[str]
|
cls, query_data: str, allowed_langs: list[str]
|
||||||
) -> list[int]:
|
) -> 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
|
@classmethod
|
||||||
def get_cache_key(cls, query_data: str, allowed_langs: list[str]) -> str:
|
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)
|
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]):
|
class GetRandomService(Generic[T]):
|
||||||
MODEL_CLASS: Optional[T] = None
|
MODEL_CLASS: Optional[T] = None
|
||||||
GET_RANDOM_OBJECT_ID_QUERY: Optional[str] = None
|
GET_RANDOM_OBJECT_ID_QUERY: Optional[str] = None
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from app.models import Sequence
|
from app.models import Sequence
|
||||||
from app.services.common import TRGMSearchService, GetRandomService
|
from app.services.common import TRGMSearchService, MeiliSearchService, GetRandomService
|
||||||
|
|
||||||
|
|
||||||
GET_OBJECT_IDS_QUERY = """
|
GET_OBJECT_IDS_QUERY = """
|
||||||
@@ -56,3 +56,11 @@ ORDER BY RANDOM() LIMIT 1;
|
|||||||
class GetRandomSequenceService(GetRandomService):
|
class GetRandomSequenceService(GetRandomService):
|
||||||
MODEL_CLASS = Sequence
|
MODEL_CLASS = Sequence
|
||||||
GET_RANDOM_OBJECT_ID_QUERY = GET_RANDOM_OBJECT_ID_QUERY
|
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.models import Author
|
||||||
from app.services.common import TRGMSearchService
|
from app.services.common import TRGMSearchService, MeiliSearchService
|
||||||
|
|
||||||
|
|
||||||
GET_OBJECT_IDS_QUERY = """
|
GET_OBJECT_IDS_QUERY = """
|
||||||
@@ -47,3 +47,13 @@ class TranslatorTGRMSearchService(TRGMSearchService):
|
|||||||
SELECT_RELATED = ["source"]
|
SELECT_RELATED = ["source"]
|
||||||
PREFETCH_RELATED = ["annotations"]
|
PREFETCH_RELATED = ["annotations"]
|
||||||
GET_OBJECT_IDS_QUERY = GET_OBJECT_IDS_QUERY
|
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"
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ from app.serializers.author import (
|
|||||||
TranslatedBook,
|
TranslatedBook,
|
||||||
)
|
)
|
||||||
from app.serializers.author_annotation import AuthorAnnotation
|
from app.serializers.author_annotation import AuthorAnnotation
|
||||||
from app.services.author import AuthorTGRMSearchService, GetRandomAuthorService
|
from app.services.author import AuthorMeiliSearchService, GetRandomAuthorService
|
||||||
from app.services.translator import TranslatorTGRMSearchService
|
from app.services.translator import TranslatorMeiliSearchService
|
||||||
from app.utils.pagination import CustomPage
|
from app.utils.pagination import CustomPage
|
||||||
|
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ async def get_author_books(
|
|||||||
async def search_authors(
|
async def search_authors(
|
||||||
query: str, request: Request, allowed_langs: list[str] = Depends(get_allowed_langs)
|
query: str, request: Request, allowed_langs: list[str] = Depends(get_allowed_langs)
|
||||||
):
|
):
|
||||||
return await AuthorTGRMSearchService.get(
|
return await AuthorMeiliSearchService.get(
|
||||||
query, request.app.state.redis, allowed_langs
|
query, request.app.state.redis, allowed_langs
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -153,6 +153,6 @@ async def get_translated_books(
|
|||||||
async def search_translators(
|
async def search_translators(
|
||||||
query: str, request: Request, allowed_langs: list[str] = Depends(get_allowed_langs)
|
query: str, request: Request, allowed_langs: list[str] = Depends(get_allowed_langs)
|
||||||
):
|
):
|
||||||
return await TranslatorTGRMSearchService.get(
|
return await TranslatorMeiliSearchService.get(
|
||||||
query, request.app.state.redis, allowed_langs
|
query, request.app.state.redis, allowed_langs
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ from app.serializers.book import (
|
|||||||
CreateRemoteBook,
|
CreateRemoteBook,
|
||||||
)
|
)
|
||||||
from app.serializers.book_annotation import BookAnnotation
|
from app.serializers.book_annotation import BookAnnotation
|
||||||
from app.services.book import BookTGRMSearchService, GetRandomBookService, BookCreator
|
from app.services.book import BookMeiliSearchService, GetRandomBookService, BookCreator
|
||||||
from app.utils.pagination import CustomPage
|
from app.utils.pagination import CustomPage
|
||||||
|
|
||||||
|
|
||||||
@@ -137,6 +137,6 @@ async def get_book_annotation(id: int):
|
|||||||
async def search_books(
|
async def search_books(
|
||||||
query: str, request: Request, allowed_langs: list[str] = Depends(get_allowed_langs)
|
query: str, request: Request, allowed_langs: list[str] = Depends(get_allowed_langs)
|
||||||
):
|
):
|
||||||
return await BookTGRMSearchService.get(
|
return await BookMeiliSearchService.get(
|
||||||
query, request.app.state.redis, allowed_langs
|
query, request.app.state.redis, allowed_langs
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from app.models import Book as BookDB
|
|||||||
from app.models import Sequence as SequenceDB
|
from app.models import Sequence as SequenceDB
|
||||||
from app.serializers.sequence import Book as SequenceBook
|
from app.serializers.sequence import Book as SequenceBook
|
||||||
from app.serializers.sequence import Sequence, CreateSequence
|
from app.serializers.sequence import Sequence, CreateSequence
|
||||||
from app.services.sequence import SequenceTGRMSearchService, GetRandomSequenceService
|
from app.services.sequence import SequenceMeiliSearchService, GetRandomSequenceService
|
||||||
from app.utils.pagination import CustomPage
|
from app.utils.pagination import CustomPage
|
||||||
|
|
||||||
|
|
||||||
@@ -67,6 +67,6 @@ async def create_sequence(data: CreateSequence):
|
|||||||
async def search_sequences(
|
async def search_sequences(
|
||||||
query: str, request: Request, allowed_langs: list[str] = Depends(get_allowed_langs)
|
query: str, request: Request, allowed_langs: list[str] = Depends(get_allowed_langs)
|
||||||
):
|
):
|
||||||
return await SequenceTGRMSearchService.get(
|
return await SequenceMeiliSearchService.get(
|
||||||
query, request.app.state.redis, allowed_langs
|
query, request.app.state.redis, allowed_langs
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ class EnvConfig(BaseSettings):
|
|||||||
REDIS_DB: int
|
REDIS_DB: int
|
||||||
REDIS_PASSWORD: Optional[str]
|
REDIS_PASSWORD: Optional[str]
|
||||||
|
|
||||||
|
MEILI_HOST: str
|
||||||
|
MEILI_MASTER_KEY: str
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
env_file = ".env"
|
env_file = ".env"
|
||||||
env_file_encoding = "utf-8"
|
env_file_encoding = "utf-8"
|
||||||
|
|||||||
60
poetry.lock
generated
60
poetry.lock
generated
@@ -212,13 +212,13 @@ pydantic = ">=1.7.2"
|
|||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
gino = ["gino[starlette] (>=1.0.1)", "SQLAlchemy (>=1.3.20)"]
|
gino = ["gino[starlette] (>=1.0.1)", "SQLAlchemy (>=1.3.20)"]
|
||||||
all = ["gino[starlette] (>=1.0.1)", "SQLAlchemy (>=1.3.20)", "databases[postgresql,mysql,sqlite] (>=0.4.0)", "orm (>=0.1.5)", "tortoise-orm[aiosqlite,asyncpg,aiomysql] (>=0.16.18,<0.18.0)", "asyncpg (>=0.24.0)", "ormar (>=0.10.5)", "Django (<3.3.0)", "piccolo (>=0.29,<0.35)", "motor (>=2.5.1,<3.0.0)"]
|
all = ["gino[starlette] (>=1.0.1)", "SQLAlchemy (>=1.3.20)", "databases[postgresql,mysql,sqlite] (>=0.4.0)", "orm (>=0.1.5)", "tortoise-orm[aiosqlite,aiomysql,asyncpg] (>=0.16.18,<0.18.0)", "asyncpg (>=0.24.0)", "ormar (>=0.10.5)", "Django (<3.3.0)", "piccolo (>=0.29,<0.35)", "motor (>=2.5.1,<3.0.0)"]
|
||||||
sqlalchemy = ["SQLAlchemy (>=1.3.20)"]
|
sqlalchemy = ["SQLAlchemy (>=1.3.20)"]
|
||||||
asyncpg = ["SQLAlchemy (>=1.3.20)", "asyncpg (>=0.24.0)"]
|
asyncpg = ["SQLAlchemy (>=1.3.20)", "asyncpg (>=0.24.0)"]
|
||||||
databases = ["databases[postgresql,mysql,sqlite] (>=0.4.0)"]
|
databases = ["databases[postgresql,mysql,sqlite] (>=0.4.0)"]
|
||||||
orm = ["databases[postgresql,mysql,sqlite] (>=0.4.0)", "orm (>=0.1.5)", "typesystem (>=0.2.0,<0.3.0)"]
|
orm = ["databases[postgresql,mysql,sqlite] (>=0.4.0)", "orm (>=0.1.5)", "typesystem (>=0.2.0,<0.3.0)"]
|
||||||
django = ["databases[postgresql,mysql,sqlite] (>=0.4.0)", "Django (<3.3.0)"]
|
django = ["databases[postgresql,mysql,sqlite] (>=0.4.0)", "Django (<3.3.0)"]
|
||||||
tortoise = ["tortoise-orm[aiosqlite,asyncpg,aiomysql] (>=0.16.18,<0.18.0)"]
|
tortoise = ["tortoise-orm[aiosqlite,aiomysql,asyncpg] (>=0.16.18,<0.18.0)"]
|
||||||
ormar = ["ormar (>=0.10.5)"]
|
ormar = ["ormar (>=0.10.5)"]
|
||||||
piccolo = ["piccolo (>=0.29,<0.35)"]
|
piccolo = ["piccolo (>=0.29,<0.35)"]
|
||||||
motor = ["motor (>=2.5.1,<3.0.0)"]
|
motor = ["motor (>=2.5.1,<3.0.0)"]
|
||||||
@@ -312,6 +312,17 @@ category = "main"
|
|||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "meilisearch"
|
||||||
|
version = "0.18.0"
|
||||||
|
description = "The python client for MeiliSearch API."
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
requests = "*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "more-itertools"
|
name = "more-itertools"
|
||||||
version = "8.10.0"
|
version = "8.10.0"
|
||||||
@@ -445,6 +456,24 @@ python-versions = ">=3.5"
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
cli = ["click (>=5.0)"]
|
cli = ["click (>=5.0)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "requests"
|
||||||
|
version = "2.27.1"
|
||||||
|
description = "Python HTTP for Humans."
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
certifi = ">=2017.4.17"
|
||||||
|
charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""}
|
||||||
|
idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""}
|
||||||
|
urllib3 = ">=1.21.1,<1.27"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
|
||||||
|
use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rfc3986"
|
name = "rfc3986"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -521,6 +550,19 @@ category = "main"
|
|||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "urllib3"
|
||||||
|
version = "1.26.8"
|
||||||
|
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
brotli = ["brotlipy (>=0.6.0)"]
|
||||||
|
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
|
||||||
|
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uvicorn"
|
name = "uvicorn"
|
||||||
version = "0.15.0"
|
version = "0.15.0"
|
||||||
@@ -548,7 +590,7 @@ python-versions = "*"
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.9"
|
python-versions = "^3.9"
|
||||||
content-hash = "c6b178194a961ae863f1db7834a3ee79a29311871268547446e5776c2ccebfa3"
|
content-hash = "477f705b7a7d3a78d1e3751b80d5fe9b04c94a5ea44bbad3bf3b81185e83238a"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
aiologger = [
|
aiologger = [
|
||||||
@@ -757,6 +799,10 @@ markupsafe = [
|
|||||||
{file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
|
{file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
|
||||||
{file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
|
{file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
|
||||||
]
|
]
|
||||||
|
meilisearch = [
|
||||||
|
{file = "meilisearch-0.18.0-py3-none-any.whl", hash = "sha256:b3f327cd01d851d3128bac6949f85a0b0c1476229b0e5d711dd76346da1f0fe2"},
|
||||||
|
{file = "meilisearch-0.18.0.tar.gz", hash = "sha256:f697a48845f693b07c63c3462312b209b8c03ba0572da9644c6777849589d18d"},
|
||||||
|
]
|
||||||
more-itertools = [
|
more-itertools = [
|
||||||
{file = "more-itertools-8.10.0.tar.gz", hash = "sha256:1debcabeb1df793814859d64a81ad7cb10504c24349368ccf214c664c474f41f"},
|
{file = "more-itertools-8.10.0.tar.gz", hash = "sha256:1debcabeb1df793814859d64a81ad7cb10504c24349368ccf214c664c474f41f"},
|
||||||
{file = "more_itertools-8.10.0-py3-none-any.whl", hash = "sha256:56ddac45541718ba332db05f464bebfb0768110111affd27f66e0051f276fa43"},
|
{file = "more_itertools-8.10.0-py3-none-any.whl", hash = "sha256:56ddac45541718ba332db05f464bebfb0768110111affd27f66e0051f276fa43"},
|
||||||
@@ -849,6 +895,10 @@ python-dotenv = [
|
|||||||
{file = "python-dotenv-0.19.1.tar.gz", hash = "sha256:14f8185cc8d494662683e6914addcb7e95374771e707601dfc70166946b4c4b8"},
|
{file = "python-dotenv-0.19.1.tar.gz", hash = "sha256:14f8185cc8d494662683e6914addcb7e95374771e707601dfc70166946b4c4b8"},
|
||||||
{file = "python_dotenv-0.19.1-py2.py3-none-any.whl", hash = "sha256:bbd3da593fc49c249397cbfbcc449cf36cb02e75afc8157fcc6a81df6fb7750a"},
|
{file = "python_dotenv-0.19.1-py2.py3-none-any.whl", hash = "sha256:bbd3da593fc49c249397cbfbcc449cf36cb02e75afc8157fcc6a81df6fb7750a"},
|
||||||
]
|
]
|
||||||
|
requests = [
|
||||||
|
{file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"},
|
||||||
|
{file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"},
|
||||||
|
]
|
||||||
rfc3986 = [
|
rfc3986 = [
|
||||||
{file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"},
|
{file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"},
|
||||||
{file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"},
|
{file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"},
|
||||||
@@ -898,6 +948,10 @@ typing-extensions = [
|
|||||||
{file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"},
|
{file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"},
|
||||||
{file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"},
|
{file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"},
|
||||||
]
|
]
|
||||||
|
urllib3 = [
|
||||||
|
{file = "urllib3-1.26.8-py2.py3-none-any.whl", hash = "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed"},
|
||||||
|
{file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"},
|
||||||
|
]
|
||||||
uvicorn = [
|
uvicorn = [
|
||||||
{file = "uvicorn-0.15.0-py3-none-any.whl", hash = "sha256:17f898c64c71a2640514d4089da2689e5db1ce5d4086c2d53699bf99513421c1"},
|
{file = "uvicorn-0.15.0-py3-none-any.whl", hash = "sha256:17f898c64c71a2640514d4089da2689e5db1ce5d4086c2d53699bf99513421c1"},
|
||||||
{file = "uvicorn-0.15.0.tar.gz", hash = "sha256:d9a3c0dd1ca86728d3e235182683b4cf94cd53a867c288eaeca80ee781b2caff"},
|
{file = "uvicorn-0.15.0.tar.gz", hash = "sha256:d9a3c0dd1ca86728d3e235182683b4cf94cd53a867c288eaeca80ee781b2caff"},
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ aiologger = "^0.6.1"
|
|||||||
orjson = "^3.6.4"
|
orjson = "^3.6.4"
|
||||||
aioredis = "^2.0.0"
|
aioredis = "^2.0.0"
|
||||||
httpx = "^0.22.0"
|
httpx = "^0.22.0"
|
||||||
|
meilisearch = "^0.18.0"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
pytest = "^5.2"
|
pytest = "^5.2"
|
||||||
|
|||||||
Reference in New Issue
Block a user