completed pagination functionality with filter option

This commit is contained in:
Ida Dittrich 2025-11-27 13:52:24 +01:00
parent b603399690
commit 1810a85c99
4 changed files with 482 additions and 11 deletions

View file

@ -26,7 +26,16 @@ class PaginationParams(BaseModel):
page: int = Field(ge=1, description="Current page number (1-based)")
pageSize: int = Field(ge=1, le=1000, description="Number of items per page")
sort: List[SortField] = Field(default_factory=list, description="List of sort fields in priority order")
filters: Optional[Dict[str, Any]] = Field(default=None, description="Filter criteria (structure TBD for future implementation)")
filters: Optional[Dict[str, Any]] = Field(
default=None,
description="""Filter criteria dictionary. Supports:
- General search: {"search": "text"} - searches across all text fields (case-insensitive)
- Field-specific filters:
- Simple equals: {"status": "running"}
- With operator: {"status": {"operator": "equals", "value": "running"}}
- Supported operators: equals/eq, contains, startsWith, endsWith, gt, gte, lt, lte, in, notIn
- Multiple filters are combined with AND logic"""
)
class PaginationRequest(BaseModel):
@ -57,7 +66,7 @@ class PaginationMetadata(BaseModel):
totalItems: int = Field(..., description="Total number of items across all pages (after filters)")
totalPages: int = Field(..., description="Total number of pages (calculated from totalItems / pageSize)")
sort: List[SortField] = Field(..., description="Current sort configuration applied")
filters: Optional[Dict[str, Any]] = Field(default=None, description="Current filters applied (for future use)")
filters: Optional[Dict[str, Any]] = Field(default=None, description="Current filters applied (mirrors request filters)")
class PaginatedResponse(BaseModel, Generic[T]):

View file

@ -236,9 +236,163 @@ class AppObjects:
return self.access.canModify(model_class, recordId)
def _applyFilters(self, records: List[Dict[str, Any]], filters: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Apply filter criteria to records (implementation for future filtering)."""
# TODO: Implement filtering logic when needed
return records
"""
Apply filter criteria to records.
Supports:
- General search: {"search": "text"} - searches across all text fields
- Field-specific filters:
- Simple: {"status": "running"} - equals match
- With operator: {"status": {"operator": "equals", "value": "running"}}
- Operators: equals, contains, gt, gte, lt, lte, in, notIn, startsWith, endsWith
Args:
records: List of record dictionaries to filter
filters: Filter criteria dictionary
Returns:
Filtered list of records
"""
if not filters or not records:
return records
filtered = []
for record in records:
matches = True
# Handle general search across text fields
if "search" in filters:
search_term = str(filters["search"]).lower()
if search_term:
# Search in all string fields
found = False
for key, value in record.items():
if isinstance(value, str) and search_term in value.lower():
found = True
break
elif isinstance(value, (int, float)) and search_term in str(value):
found = True
break
if not found:
matches = False
# Handle field-specific filters
for field_name, filter_value in filters.items():
if field_name == "search":
continue # Already handled above
if field_name not in record:
matches = False
break
record_value = record.get(field_name)
# Handle simple value (equals operator)
if not isinstance(filter_value, dict):
if record_value != filter_value:
matches = False
break
continue
# Handle filter with operator
operator = filter_value.get("operator", "equals")
filter_val = filter_value.get("value")
if operator in ["equals", "eq"]:
if record_value != filter_val:
matches = False
break
elif operator == "contains":
record_str = str(record_value).lower() if record_value is not None else ""
filter_str = str(filter_val).lower() if filter_val is not None else ""
if filter_str not in record_str:
matches = False
break
elif operator == "startsWith":
record_str = str(record_value).lower() if record_value is not None else ""
filter_str = str(filter_val).lower() if filter_val is not None else ""
if not record_str.startswith(filter_str):
matches = False
break
elif operator == "endsWith":
record_str = str(record_value).lower() if record_value is not None else ""
filter_str = str(filter_val).lower() if filter_val is not None else ""
if not record_str.endswith(filter_str):
matches = False
break
elif operator == "gt":
try:
record_num = float(record_value) if record_value is not None else float('-inf')
filter_num = float(filter_val) if filter_val is not None else float('-inf')
if record_num <= filter_num:
matches = False
break
except (ValueError, TypeError):
matches = False
break
elif operator == "gte":
try:
record_num = float(record_value) if record_value is not None else float('-inf')
filter_num = float(filter_val) if filter_val is not None else float('-inf')
if record_num < filter_num:
matches = False
break
except (ValueError, TypeError):
matches = False
break
elif operator == "lt":
try:
record_num = float(record_value) if record_value is not None else float('inf')
filter_num = float(filter_val) if filter_val is not None else float('inf')
if record_num >= filter_num:
matches = False
break
except (ValueError, TypeError):
matches = False
break
elif operator == "lte":
try:
record_num = float(record_value) if record_value is not None else float('inf')
filter_num = float(filter_val) if filter_val is not None else float('inf')
if record_num > filter_num:
matches = False
break
except (ValueError, TypeError):
matches = False
break
elif operator == "in":
if not isinstance(filter_val, list):
filter_val = [filter_val]
if record_value not in filter_val:
matches = False
break
elif operator == "notIn":
if not isinstance(filter_val, list):
filter_val = [filter_val]
if record_value in filter_val:
matches = False
break
else:
# Unknown operator - default to equals
if record_value != filter_val:
matches = False
break
if matches:
filtered.append(record)
return filtered
def _applySorting(self, records: List[Dict[str, Any]], sortFields: List[Any]) -> List[Dict[str, Any]]:
"""Apply multi-level sorting to records using stable sort (sorts from least to most significant field)."""

View file

@ -211,9 +211,163 @@ class ChatObjects:
return self.access.canModify(model_class, recordId)
def _applyFilters(self, records: List[Dict[str, Any]], filters: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Apply filter criteria to records (implementation for future filtering)."""
# TODO: Implement filtering logic when needed
return records
"""
Apply filter criteria to records.
Supports:
- General search: {"search": "text"} - searches across all text fields
- Field-specific filters:
- Simple: {"status": "running"} - equals match
- With operator: {"status": {"operator": "equals", "value": "running"}}
- Operators: equals, contains, gt, gte, lt, lte, in, notIn, startsWith, endsWith
Args:
records: List of record dictionaries to filter
filters: Filter criteria dictionary
Returns:
Filtered list of records
"""
if not filters or not records:
return records
filtered = []
for record in records:
matches = True
# Handle general search across text fields
if "search" in filters:
search_term = str(filters["search"]).lower()
if search_term:
# Search in all string fields
found = False
for key, value in record.items():
if isinstance(value, str) and search_term in value.lower():
found = True
break
elif isinstance(value, (int, float)) and search_term in str(value):
found = True
break
if not found:
matches = False
# Handle field-specific filters
for field_name, filter_value in filters.items():
if field_name == "search":
continue # Already handled above
if field_name not in record:
matches = False
break
record_value = record.get(field_name)
# Handle simple value (equals operator)
if not isinstance(filter_value, dict):
if record_value != filter_value:
matches = False
break
continue
# Handle filter with operator
operator = filter_value.get("operator", "equals")
filter_val = filter_value.get("value")
if operator in ["equals", "eq"]:
if record_value != filter_val:
matches = False
break
elif operator == "contains":
record_str = str(record_value).lower() if record_value is not None else ""
filter_str = str(filter_val).lower() if filter_val is not None else ""
if filter_str not in record_str:
matches = False
break
elif operator == "startsWith":
record_str = str(record_value).lower() if record_value is not None else ""
filter_str = str(filter_val).lower() if filter_val is not None else ""
if not record_str.startswith(filter_str):
matches = False
break
elif operator == "endsWith":
record_str = str(record_value).lower() if record_value is not None else ""
filter_str = str(filter_val).lower() if filter_val is not None else ""
if not record_str.endswith(filter_str):
matches = False
break
elif operator == "gt":
try:
record_num = float(record_value) if record_value is not None else float('-inf')
filter_num = float(filter_val) if filter_val is not None else float('-inf')
if record_num <= filter_num:
matches = False
break
except (ValueError, TypeError):
matches = False
break
elif operator == "gte":
try:
record_num = float(record_value) if record_value is not None else float('-inf')
filter_num = float(filter_val) if filter_val is not None else float('-inf')
if record_num < filter_num:
matches = False
break
except (ValueError, TypeError):
matches = False
break
elif operator == "lt":
try:
record_num = float(record_value) if record_value is not None else float('inf')
filter_num = float(filter_val) if filter_val is not None else float('inf')
if record_num >= filter_num:
matches = False
break
except (ValueError, TypeError):
matches = False
break
elif operator == "lte":
try:
record_num = float(record_value) if record_value is not None else float('inf')
filter_num = float(filter_val) if filter_val is not None else float('inf')
if record_num > filter_num:
matches = False
break
except (ValueError, TypeError):
matches = False
break
elif operator == "in":
if not isinstance(filter_val, list):
filter_val = [filter_val]
if record_value not in filter_val:
matches = False
break
elif operator == "notIn":
if not isinstance(filter_val, list):
filter_val = [filter_val]
if record_value in filter_val:
matches = False
break
else:
# Unknown operator - default to equals
if record_value != filter_val:
matches = False
break
if matches:
filtered.append(record)
return filtered
def _applySorting(self, records: List[Dict[str, Any]], sortFields: List[Any]) -> List[Dict[str, Any]]:
"""Apply multi-level sorting to records using stable sort (sorts from least to most significant field)."""

View file

@ -247,9 +247,163 @@ class ComponentObjects:
return self.access.canModify(model_class, recordId)
def _applyFilters(self, records: List[Dict[str, Any]], filters: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Apply filter criteria to records (implementation for future filtering)."""
# TODO: Implement filtering logic when needed
return records
"""
Apply filter criteria to records.
Supports:
- General search: {"search": "text"} - searches across all text fields
- Field-specific filters:
- Simple: {"status": "running"} - equals match
- With operator: {"status": {"operator": "equals", "value": "running"}}
- Operators: equals, contains, gt, gte, lt, lte, in, notIn, startsWith, endsWith
Args:
records: List of record dictionaries to filter
filters: Filter criteria dictionary
Returns:
Filtered list of records
"""
if not filters or not records:
return records
filtered = []
for record in records:
matches = True
# Handle general search across text fields
if "search" in filters:
search_term = str(filters["search"]).lower()
if search_term:
# Search in all string fields
found = False
for key, value in record.items():
if isinstance(value, str) and search_term in value.lower():
found = True
break
elif isinstance(value, (int, float)) and search_term in str(value):
found = True
break
if not found:
matches = False
# Handle field-specific filters
for field_name, filter_value in filters.items():
if field_name == "search":
continue # Already handled above
if field_name not in record:
matches = False
break
record_value = record.get(field_name)
# Handle simple value (equals operator)
if not isinstance(filter_value, dict):
if record_value != filter_value:
matches = False
break
continue
# Handle filter with operator
operator = filter_value.get("operator", "equals")
filter_val = filter_value.get("value")
if operator in ["equals", "eq"]:
if record_value != filter_val:
matches = False
break
elif operator == "contains":
record_str = str(record_value).lower() if record_value is not None else ""
filter_str = str(filter_val).lower() if filter_val is not None else ""
if filter_str not in record_str:
matches = False
break
elif operator == "startsWith":
record_str = str(record_value).lower() if record_value is not None else ""
filter_str = str(filter_val).lower() if filter_val is not None else ""
if not record_str.startswith(filter_str):
matches = False
break
elif operator == "endsWith":
record_str = str(record_value).lower() if record_value is not None else ""
filter_str = str(filter_val).lower() if filter_val is not None else ""
if not record_str.endswith(filter_str):
matches = False
break
elif operator == "gt":
try:
record_num = float(record_value) if record_value is not None else float('-inf')
filter_num = float(filter_val) if filter_val is not None else float('-inf')
if record_num <= filter_num:
matches = False
break
except (ValueError, TypeError):
matches = False
break
elif operator == "gte":
try:
record_num = float(record_value) if record_value is not None else float('-inf')
filter_num = float(filter_val) if filter_val is not None else float('-inf')
if record_num < filter_num:
matches = False
break
except (ValueError, TypeError):
matches = False
break
elif operator == "lt":
try:
record_num = float(record_value) if record_value is not None else float('inf')
filter_num = float(filter_val) if filter_val is not None else float('inf')
if record_num >= filter_num:
matches = False
break
except (ValueError, TypeError):
matches = False
break
elif operator == "lte":
try:
record_num = float(record_value) if record_value is not None else float('inf')
filter_num = float(filter_val) if filter_val is not None else float('inf')
if record_num > filter_num:
matches = False
break
except (ValueError, TypeError):
matches = False
break
elif operator == "in":
if not isinstance(filter_val, list):
filter_val = [filter_val]
if record_value not in filter_val:
matches = False
break
elif operator == "notIn":
if not isinstance(filter_val, list):
filter_val = [filter_val]
if record_value in filter_val:
matches = False
break
else:
# Unknown operator - default to equals
if record_value != filter_val:
matches = False
break
if matches:
filtered.append(record)
return filtered
def _applySorting(self, records: List[Dict[str, Any]], sortFields: List[Any]) -> List[Dict[str, Any]]:
"""Apply multi-level sorting to records using stable sort (sorts from least to most significant field)."""