Skip to content

Commit

Permalink
add tool search api
Browse files Browse the repository at this point in the history
  • Loading branch information
Maxim2121512 committed Dec 15, 2024
1 parent a160988 commit b23e783
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 3 deletions.
13 changes: 13 additions & 0 deletions backend/src/core/repositories/tool_repos/itool_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,17 @@ async def get_total_count(self) -> ToolPages:

@abstractmethod
async def exists(self, tool_name: str) -> bool:
pass

@abstractmethod
async def search(
self,
query: str,
page: int,
page_size: int,
category: Optional[List[str]] = None,
type: Optional[List[str]] = None,
min_price: Optional[float] = None,
max_price: Optional[float] = None
) -> List[ToolSummary]:
pass
22 changes: 21 additions & 1 deletion backend/src/core/services/tool_service/tool_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,24 @@ async def get_categories_with_types(self) -> List[CategoryWithTypes]:
)
result.append(category_with_types)

return result
return result

async def search_tools(
self,
query: str,
page: int,
page_size: int,
category: Optional[List[str]] = None,
type: Optional[List[str]] = None,
min_price: Optional[float] = None,
max_price: Optional[float] = None
) -> List[ToolSummary]:
return await self.tool_repo.search(
query=query,
page=page,
page_size=page_size,
category=category,
type=type,
min_price=min_price,
max_price=max_price
)
29 changes: 27 additions & 2 deletions backend/src/infrastructure/api/tool_controller.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import List
from typing import List, Optional
from fastapi import APIRouter, Depends, Query
from src.core.entities.category.category import CategoryName, CategoryCreated, CategoryWithTypes
from src.core.entities.tool.tool import ToolCreated, ToolCreate, ToolSummary, ToolDetails, ToolPages
Expand All @@ -7,13 +7,39 @@
from src.infrastructure.api.security.role_required import role_required
from src.infrastructure.services_instances import get_tool_service
from fastapi.security import OAuth2PasswordBearer
from pydantic import PositiveInt

tool_router = APIRouter()
category_router = APIRouter()
type_router = APIRouter()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")

@tool_router.get(
path="/search",
status_code=200,
response_model=List[ToolSummary]
)
async def search_tools(
query: str = Query(..., min_length=3),
page: PositiveInt = Query(1),
page_size: PositiveInt = Query(12),
category: Optional[List[str]] = Query(None),
type: Optional[List[str]] = Query(None),
min_price: Optional[float] = Query(None),
max_price: Optional[float] = Query(None),
tool_service: ToolService = Depends(get_tool_service)
):
return await tool_service.search_tools(
query=query,
page=page,
page_size=page_size,
category=category,
type=type,
min_price=min_price,
max_price=max_price
)


@tool_router.post(
path="/",
Expand Down Expand Up @@ -43,7 +69,6 @@ async def get_paginated_tools(




@tool_router.get(
"/pages_count",
status_code=200,
Expand Down
18 changes: 18 additions & 0 deletions backend/src/infrastructure/db/mongo.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from src.configs.mongo_config import MongoConfig

mongo_config = config.mongo
collections_config = config.collections

class MongoDB:
client: AsyncIOMotorClient | None = None
Expand All @@ -23,6 +24,23 @@ async def close() -> None:
else:
raise ConnectionError("Client not connected")

@staticmethod
async def create_indexes() -> None:
await MongoDB.db[collections_config.tool_collection].create_index(
[
("name", "text"),
("description", "text"),
("category", "text"),
("type", "text")
],
weights={
"name": 10,
"description": 5,
"category": 2,
"type": 2
},
default_language="russian"
)

@staticmethod
def get_db_instance() -> AsyncIOMotorDatabase:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
class MongoToolRepository(IToolRepository):
def __init__(self, db: AsyncIOMotorDatabase, tool_collection: str):
self.tool_collection = db[tool_collection]
self.tool_collection_name = tool_collection

async def create(self, tool: Tool) -> str:
try:
Expand Down Expand Up @@ -65,5 +66,56 @@ async def exists(self, tool_name: str) -> bool:
try:
count = await self.tool_collection.count_documents({"name": tool_name})
return count > 0
except PyMongoError:
raise DatabaseError()

async def search(
self,
query: str,
page: int,
page_size: int,
category: Optional[List[str]] = None,
type: Optional[List[str]] = None,
min_price: Optional[float] = None,
max_price: Optional[float] = None
) -> List[ToolSummary]:
try:
skip = (page - 1) * page_size

match_stage = {"$match": {"$text": {"$search": query}}}

if category:
match_stage["$match"]["category"] = {"$in": category}
if type:
match_stage["$match"]["type"] = {"$in": type}
if min_price is not None:
match_stage["$match"]["dailyPrice"] = {"$gte": min_price}
if max_price is not None:
match_stage["$match"].setdefault("dailyPrice", {})["$lte"] = max_price


pipeline = [
match_stage,
{"$addFields": {"score": {"$meta": "textScore"}}},
{"$sort": {"score": -1}},
{"$skip": skip},
{"$limit": page_size},
{"$project": {
"_id": 1,
"name": 1,
"dailyPrice": 1,
"images": 1,
"rating": 1,
"description": 1,
}}
]
cursor = self.tool_collection.aggregate(pipeline)

results = []

async for doc in cursor:
results.append(ToolSummary(**doc))

return results
except PyMongoError:
raise DatabaseError()
1 change: 1 addition & 0 deletions backend/src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
@asynccontextmanager
async def lifespan(app: FastAPI):
await MongoDB.connect()
await MongoDB.create_indexes()
yield
await MongoDB.close()

Expand Down

0 comments on commit b23e783

Please sign in to comment.