Filter Operations
Filtering is where OData starts to feel powerful: you describe the rows you want, and Portway translates that into SQL for you. This reference covers every supported filter operation, with examples you can adapt and a few practices that keep queries fast.
Filter Syntax
Basic filter structure:
txt
$filter=expressionComparison Operators
Equality Operators
| Operator | Description | Example |
|---|---|---|
eq |
Equal to | $filter=Status eq 'Active' |
ne |
Not equal to | $filter=Status ne 'Closed' |
Examples
http
# String equality
GET /api/500/Products?$filter=ItemCode eq 'PROD001'
# Numeric equality
GET /api/500/Products?$filter=Price eq 99.99
# Boolean equality
GET /api/500/Products?$filter=IsActive eq true
# Date equality
GET /api/500/Orders?$filter=OrderDate eq 2024-01-15Relational Operators
| Operator | Description | Example |
|---|---|---|
gt |
Greater than | $filter=Price gt 100 |
ge |
Greater than or equal to | $filter=Price ge 100 |
lt |
Less than | $filter=Price lt 100 |
le |
Less than or equal to | $filter=Price le 100 |
Examples
http
# Numeric comparison
GET /api/500/Products?$filter=Price gt 50.00
# Date comparison
GET /api/500/Orders?$filter=OrderDate gt 2024-01-01
# String comparison (alphabetical)
GET /api/500/Customers?$filter=Name gt 'M'Logical Operators
Combining Conditions
| Operator | Description | Example |
|---|---|---|
and |
Logical AND | $filter=Price gt 100 and Status eq 'Active' |
or |
Logical OR | $filter=Status eq 'New' or Status eq 'Pending' |
not |
Logical NOT | $filter=not contains(Description,'test') |
Examples
http
# AND condition
GET /api/500/Products?$filter=Price gt 100 and Category eq 'Electronics'
# OR condition
GET /api/500/Orders?$filter=Status eq 'Pending' or Status eq 'Processing'
# NOT condition
GET /api/500/Products?$filter=not IsDeleted
# Complex combination
GET /api/500/Products?$filter=(Price gt 100 and Price lt 500) or Category eq 'Special'Grouping with Parentheses
Use parentheses to control operator precedence:
http
# Without parentheses (AND has precedence over OR)
GET /api/500/Products?$filter=Price gt 100 and Category eq 'A' or Category eq 'B'
# Evaluates as: (Price gt 100 AND Category eq 'A') OR Category eq 'B'
# With parentheses
GET /api/500/Products?$filter=Price gt 100 and (Category eq 'A' or Category eq 'B')
# Evaluates as: Price gt 100 AND (Category eq 'A' OR Category eq 'B')String Functions
Text Search Functions
| Function | Description | Example |
|---|---|---|
contains(field,value) |
Contains substring | $filter=contains(Description,'widget') |
startswith(field,value) |
Starts with string | $filter=startswith(Name,'A') |
endswith(field,value) |
Ends with string | $filter=endswith(Email,'.com') |
Examples
http
# Contains search (case-sensitive)
GET /api/500/Products?$filter=contains(Description,'book')
# Starts with
GET /api/500/Customers?$filter=startswith(Name,'John')
# Ends with
GET /api/500/Documents?$filter=endswith(FileName,'.pdf')
# Combining string functions
GET /api/500/Products?$filter=contains(Description,'premium') and startswith(ItemCode,'P')All string comparisons are case-sensitive. startswith is typically faster than contains because it can use index range scans. There is no wildcard syntax, use contains, startswith, or endswith instead.
Working with Data Types
String Values
Strings are enclosed in single quotes:
http
# Correct
$filter=Name eq 'John Smith'
# Escape single quotes with another single quote
$filter=Description eq 'It''s a product'
# Multiple words
$filter=Category eq 'Home & Garden'Numeric Values
Numbers don't require quotes:
http
# Integer
$filter=Quantity eq 10
# Decimal
$filter=Price eq 99.99
# Negative numbers
$filter=Balance gt -100.50
# Scientific notation
$filter=Value lt 1.5e6Date and DateTime Values
Use ISO 8601 format:
http
# Date only
$filter=OrderDate eq 2024-01-15
# DateTime
$filter=CreatedAt gt 2024-01-15T14:30:00Z
# Date range
$filter=OrderDate ge 2024-01-01 and OrderDate lt 2024-02-01Boolean Values
Use lowercase true or false:
http
# Boolean true
$filter=IsActive eq true
# Boolean false
$filter=IsDeleted eq false
# Negation
$filter=not IsActiveNull Values
Use null keyword:
http
# Check for null
$filter=AssignedTo eq null
# Check for not null
$filter=CompletedDate ne null
# Combine with other conditions
$filter=Status eq 'Open' and AssignedTo eq nullAdvanced Filter Patterns
Range Queries
http
# Numeric range
GET /api/500/Products?$filter=Price ge 100 and Price le 500
# Date range
GET /api/500/Orders?$filter=OrderDate ge 2024-01-01 and OrderDate lt 2024-02-01
# Excluding boundaries
GET /api/500/Products?$filter=Price gt 100 and Price lt 500Multiple Value Matching
http
# Using OR for multiple values
GET /api/500/Orders?$filter=Status eq 'New' or Status eq 'Pending' or Status eq 'Processing'
# Alternative approach with grouping
GET /api/500/Products?$filter=(Category eq 'A' or Category eq 'B' or Category eq 'C') and Price gt 50Complex Text Searches
http
# Multiple text conditions
GET /api/500/Products?$filter=contains(Description,'premium') and not contains(Description,'refurbished')
# Search in multiple fields
GET /api/500/Products?$filter=contains(Name,'widget') or contains(Description,'widget')Nested Conditions
http
# Complex nested logic
GET /api/500/Orders?$filter=(Status eq 'Open' and Priority eq 1) or (Status eq 'Pending' and DueDate lt 2024-02-01)
# Multiple grouping levels
GET /api/500/Products?$filter=((Price gt 100 and Price lt 500) or Category eq 'Special') and IsActive eq trueFilter Performance Tips
1. Use Indexed Fields
Always filter on indexed fields when possible:
http
# Good - filtering on primary key
GET /api/500/Products?$filter=ItemCode eq 'PROD001'
# Less efficient - filtering on non-indexed field
GET /api/500/Products?$filter=contains(Description,'long text search')2. Avoid Complex String Operations
http
# Efficient - exact match
GET /api/500/Products?$filter=Category eq 'Electronics'
# Less efficient - contains operation
GET /api/500/Products?$filter=contains(Category,'Elec')
# Most efficient for prefix search
GET /api/500/Products?$filter=startswith(ItemCode,'PROD')3. Limit Result Sets Early
http
# Good - filter reduces dataset before sorting
GET /api/500/Orders?$filter=Status eq 'Open'&$orderby=OrderDate desc&$top=10
# Less efficient - sorting entire dataset
GET /api/500/Orders?$orderby=OrderDate desc&$top=104. Use Specific Conditions
http
# Specific date
GET /api/500/Orders?$filter=OrderDate eq 2024-01-15
# Date range (less specific)
GET /api/500/Orders?$filter=OrderDate ge 2024-01-01 and OrderDate lt 2024-02-01
# Very broad (avoid)
GET /api/500/Orders?$filter=OrderDate ne nullCommon Filter Patterns
Active Records
http
# Active items
GET /api/500/Products?$filter=IsActive eq true and IsDeleted eq false
# Non-deleted items
GET /api/500/Customers?$filter=DeletedDate eq nullDate-Based Filters
http
# Today's records
GET /api/500/Orders?$filter=OrderDate eq 2024-01-15
# This month's records
GET /api/500/Orders?$filter=OrderDate ge 2024-01-01 and OrderDate lt 2024-02-01
# Last 30 days
GET /api/500/Orders?$filter=OrderDate gt 2023-12-16Status Filters
http
# Single status
GET /api/500/Tasks?$filter=Status eq 'Open'
# Multiple statuses
GET /api/500/Tasks?$filter=Status eq 'Open' or Status eq 'InProgress'
# Exclude status
GET /api/500/Tasks?$filter=Status ne 'Closed'Search Patterns
http
# Partial match
GET /api/500/Products?$filter=contains(Name,'widget')
# Prefix search
GET /api/500/Customers?$filter=startswith(LastName,'Sm')
# Multiple field search
GET /api/500/Products?$filter=contains(Name,'phone') or contains(Description,'phone')Error Handling
Common Filter Errors
- Invalid Field Name
json
{
"error": "Invalid field name",
"details": "Field 'InvalidField' is not allowed",
"success": false
}- Syntax Error
json
{
"error": "Invalid filter syntax",
"details": "Expected operator at position 15",
"success": false
}- Type Mismatch
json
{
"error": "Type mismatch",
"details": "Cannot compare string field with numeric value",
"success": false
}- Invalid Date Format
json
{
"error": "Invalid date format",
"details": "Date must be in ISO 8601 format",
"success": false
}Filter Limitations
| Limitation | Description | Workaround |
|---|---|---|
| Query length | Maximum 2048 characters | Split into multiple queries |
| Filter complexity | Maximum 10 conditions | Simplify or split filters |
| String functions | Case-sensitive only | Handle case in application |
| No regex support | No pattern matching | Use contains/startswith |
| No arithmetic | No calculations in filters | Pre-calculate values |
Related Topics
- OData Syntax - Complete OData syntax reference
- Sorting & Pagination - Sorting and pagination
- SQL Endpoints - SQL endpoint configuration
- API Overview - General API reference
Portway