completed pagination functionality with filter option
This commit is contained in:
parent
b603399690
commit
1810a85c99
4 changed files with 482 additions and 11 deletions
|
|
@ -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]):
|
||||
|
|
|
|||
|
|
@ -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)."""
|
||||
|
|
|
|||
|
|
@ -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)."""
|
||||
|
|
|
|||
|
|
@ -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)."""
|
||||
|
|
|
|||
Loading…
Reference in a new issue