Add search functionality to API

This commit is contained in:
Automated Action 2025-05-17 16:27:27 +00:00
parent 244afc42b4
commit 6435ac6d82
3 changed files with 139 additions and 9 deletions

View File

@ -8,6 +8,7 @@ A simple REST API built with FastAPI and SQLite.
- SQLite database with SQLAlchemy ORM - SQLite database with SQLAlchemy ORM
- Alembic migrations - Alembic migrations
- CRUD operations for items - CRUD operations for items
- Search functionality with multiple filters
- Health check endpoint - Health check endpoint
- Input validation and error handling - Input validation and error handling
- Pagination and filtering - Pagination and filtering
@ -98,12 +99,45 @@ The API will be available at http://localhost:8000.
### Items ### Items
- `GET /api/v1/items` - List all items (with pagination and filtering) - `GET /api/v1/items` - List all items (with pagination, filtering, and search)
- Query Parameters:
- `query` - Search text in name and description
- `active` - Filter by active status (true/false)
- `price_min` - Filter by minimum price (in cents)
- `price_max` - Filter by maximum price (in cents)
- `skip` - Number of records to skip (for pagination)
- `limit` - Maximum number of records to return (for pagination)
- `GET /api/v1/items/search` - Search for items by name or description
- Query Parameters:
- `query` - Search text in name and description (required)
- `active` - Filter by active status (true/false)
- `price_min` - Filter by minimum price (in cents)
- `price_max` - Filter by maximum price (in cents)
- `skip` - Number of records to skip (for pagination)
- `limit` - Maximum number of records to return (for pagination)
- `GET /api/v1/items/{item_id}` - Get a specific item - `GET /api/v1/items/{item_id}` - Get a specific item
- `POST /api/v1/items` - Create a new item - `POST /api/v1/items` - Create a new item
- `PUT /api/v1/items/{item_id}` - Update an item - `PUT /api/v1/items/{item_id}` - Update an item
- `DELETE /api/v1/items/{item_id}` - Delete an item - `DELETE /api/v1/items/{item_id}` - Delete an item
## Search Functionality
The API supports searching items by:
1. Text matching in name and description fields (case-insensitive)
2. Filtering by active status
3. Price range filtering (minimum and maximum price)
4. Combination of all the above filters
Example search request:
```
GET /api/v1/items/search?query=phone&active=true&price_min=10000&price_max=50000
```
This will find all active items that contain "phone" in their name or description, with a price between 100.00 and 500.00 (prices are stored in cents).
## Environment Variables ## Environment Variables
You can configure the application using environment variables: You can configure the application using environment variables:

View File

@ -1,6 +1,6 @@
from typing import Any, List, Optional from typing import Any, List, Optional
from fastapi import APIRouter, Depends, HTTPException, status from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from app import crud, schemas from app import crud, schemas
@ -14,21 +14,76 @@ router = APIRouter()
response_model=List[schemas.Item], response_model=List[schemas.Item],
status_code=status.HTTP_200_OK, status_code=status.HTTP_200_OK,
summary="Get all items", summary="Get all items",
description="Retrieve all items with pagination and filtering options", description="Retrieve all items with pagination, filtering, and search options",
) )
def read_items( def read_items(
db: Session = Depends(get_db), db: Session = Depends(get_db),
skip: int = 0, query: Optional[str] = Query(None, description="Search text in name and description"),
limit: int = 100, active: Optional[bool] = Query(None, description="Filter by active status"),
active: Optional[bool] = None, price_min: Optional[int] = Query(None, description="Minimum price in cents"),
price_max: Optional[int] = Query(None, description="Maximum price in cents"),
skip: int = Query(0, description="Number of records to skip"),
limit: int = Query(100, description="Maximum number of records to return"),
) -> Any: ) -> Any:
""" """
Retrieve all items with pagination and optional active status filtering. Retrieve items with support for:
- Text search in name and description
- Filtering by active status
- Price range filtering
- Pagination with skip and limit parameters
""" """
if active is not None: # If search query or price filters are provided, use search method
if query is not None or price_min is not None or price_max is not None:
items = crud.item.search(
db,
query=query or "",
skip=skip,
limit=limit,
active=active,
price_min=price_min,
price_max=price_max
)
# Otherwise use existing methods
elif active is not None:
items = crud.item.get_multi_by_active(db, active=active, skip=skip, limit=limit) items = crud.item.get_multi_by_active(db, active=active, skip=skip, limit=limit)
else: else:
items = crud.item.get_multi(db, skip=skip, limit=limit) items = crud.item.get_multi(db, skip=skip, limit=limit)
return items
@router.get(
"/search",
response_model=List[schemas.Item],
status_code=status.HTTP_200_OK,
summary="Search items",
description="Search for items by query text in name and description with additional filters",
)
def search_items(
query: str = Query(..., description="Search text in name and description"),
db: Session = Depends(get_db),
active: Optional[bool] = Query(None, description="Filter by active status"),
price_min: Optional[int] = Query(None, description="Minimum price in cents"),
price_max: Optional[int] = Query(None, description="Maximum price in cents"),
skip: int = Query(0, description="Number of records to skip"),
limit: int = Query(100, description="Maximum number of records to return"),
) -> Any:
"""
Search for items with support for:
- Text search in name and description (required)
- Filtering by active status
- Price range filtering
- Pagination with skip and limit parameters
"""
items = crud.item.search(
db,
query=query,
skip=skip,
limit=limit,
active=active,
price_min=price_min,
price_max=price_max
)
return items return items

View File

@ -1,5 +1,6 @@
from typing import List, Optional from typing import List, Optional, Any
from sqlalchemy import or_
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from app.database.crud_base import CRUDBase from app.database.crud_base import CRUDBase
@ -22,5 +23,45 @@ class CRUDItem(CRUDBase[Item, ItemCreate, ItemUpdate]):
.all() .all()
) )
def search(
self, db: Session, *, query: str, skip: int = 0, limit: int = 100, **filters: Any
) -> List[Item]:
"""
Search for items based on the provided query string and additional filters.
Args:
db: Database session
query: Search query to match against name and description
skip: Number of records to skip
limit: Maximum number of records to return
**filters: Additional filters (e.g., active=True)
Returns:
List of items matching the search criteria
"""
db_query = db.query(Item)
# Apply search query to name and description
if query:
search_filter = or_(
Item.name.ilike(f"%{query}%"),
Item.description.ilike(f"%{query}%"),
)
db_query = db_query.filter(search_filter)
# Apply additional filters
if "active" in filters and filters["active"] is not None:
db_query = db_query.filter(Item.is_active == filters["active"])
# Apply price range filter if provided
if "price_min" in filters and filters["price_min"] is not None:
db_query = db_query.filter(Item.price >= filters["price_min"])
if "price_max" in filters and filters["price_max"] is not None:
db_query = db_query.filter(Item.price <= filters["price_max"])
# Return paginated results
return db_query.offset(skip).limit(limit).all()
item = CRUDItem(Item) item = CRUDItem(Item)