Filtering
Filtering
Section titled “Filtering”Query filter syntax and operators
Overview
Section titled “Overview”Filtering allows clients to request a subset of resources matching specific criteria. Filters are applied server-side before returning results.
Filter Object
Section titled “Filter Object”A filter specifies a single condition:
{ "attribute": "status", "operator": "equals", "value": "pending"}Fields
Section titled “Fields”| Field | Type | Required | Description |
|---|---|---|---|
attribute | string | Yes | Attribute to filter on |
operator | string | Yes | Comparison operator |
value | any | Conditional | Value to compare (type depends on operator) |
boolean | string | No | Boolean combinator (and, or) |
Operators
Section titled “Operators”Equality Operators
Section titled “Equality Operators”| Operator | SQL Equivalent | Value Type | Description |
|---|---|---|---|
equals | = | any | Exact match |
not_equals | != | any | Not equal |
{ "attribute": "status", "operator": "equals", "value": "active" }{ "attribute": "type", "operator": "not_equals", "value": "draft" }Comparison Operators
Section titled “Comparison Operators”| Operator | SQL Equivalent | Value Type | Description |
|---|---|---|---|
greater_than | > | number/string | Greater than |
greater_than_or_equal_to | >= | number/string | Greater than or equal |
less_than | < | number/string | Less than |
less_than_or_equal_to | <= | number/string | Less than or equal |
{ "attribute": "amount", "operator": "greater_than", "value": 100 }{ "attribute": "created_at", "operator": "greater_than_or_equal_to", "value": "2024-01-01T00:00:00Z" }Pattern Operators
Section titled “Pattern Operators”| Operator | SQL Equivalent | Value Type | Description |
|---|---|---|---|
like | LIKE | string | Pattern match (use % wildcard) |
not_like | NOT LIKE | string | Negative pattern match |
{ "attribute": "email", "operator": "like", "value": "%@example.com" }{ "attribute": "name", "operator": "like", "value": "John%" }{ "attribute": "code", "operator": "not_like", "value": "TEST%" }Set Operators
Section titled “Set Operators”| Operator | SQL Equivalent | Value Type | Description |
|---|---|---|---|
in | IN | array | Value in set |
not_in | NOT IN | array | Value not in set |
{ "attribute": "status", "operator": "in", "value": ["pending", "processing", "shipped"] }{ "attribute": "country_code", "operator": "not_in", "value": ["US", "CA"] }Range Operators
Section titled “Range Operators”| Operator | SQL Equivalent | Value Type | Description |
|---|---|---|---|
between | BETWEEN | array[2] | Value in range (inclusive) |
not_between | NOT BETWEEN | array[2] | Value outside range |
{ "attribute": "price", "operator": "between", "value": [10, 100] }{ "attribute": "created_at", "operator": "between", "value": ["2024-01-01", "2024-01-31"] }Null Operators
Section titled “Null Operators”| Operator | SQL Equivalent | Value Type | Description |
|---|---|---|---|
is_null | IS NULL | — | Attribute is null |
is_not_null | IS NOT NULL | — | Attribute is not null |
{ "attribute": "deleted_at", "operator": "is_null" }{ "attribute": "verified_at", "operator": "is_not_null" }Note: value is not required for null operators.
Filter Structure
Section titled “Filter Structure”Filters are organized by resource using an object:
{ "filters": { "self": [ { "attribute": "status", "operator": "equals", "value": "active" }, { "attribute": "created_at", "operator": "greater_than", "value": "2024-01-01T00:00:00Z" } ] }}The self key targets the primary resource. Additional keys target relationships.
Default Behavior
Section titled “Default Behavior”Without explicit boolean, filters are combined with AND:
{ "filters": { "self": [ { "attribute": "status", "operator": "equals", "value": "active" }, { "attribute": "type", "operator": "equals", "value": "premium" } ] }}// SQL: WHERE status = 'active' AND type = 'premium'Boolean Logic
Section titled “Boolean Logic”AND Conditions
Section titled “AND Conditions”Explicit AND (same as default):
{ "filters": { "self": [ { "attribute": "status", "operator": "equals", "value": "active", "boolean": "and" }, { "attribute": "verified", "operator": "equals", "value": true, "boolean": "and" } ] }}// SQL: WHERE status = 'active' AND verified = trueOR Conditions
Section titled “OR Conditions”{ "filters": { "self": [ { "attribute": "status", "operator": "equals", "value": "pending", "boolean": "or" }, { "attribute": "status", "operator": "equals", "value": "processing", "boolean": "or" } ] }}// SQL: WHERE status = 'pending' OR status = 'processing'Note: For simple OR on same attribute, prefer in operator:
{ "attribute": "status", "operator": "in", "value": ["pending", "processing"] }Mixed Boolean
Section titled “Mixed Boolean”Filters are applied sequentially, with each filter’s boolean determining how it connects to the previous condition:
{ "filters": { "self": [ { "attribute": "type", "operator": "equals", "value": "order" }, { "attribute": "status", "operator": "equals", "value": "pending", "boolean": "or" }, { "attribute": "status", "operator": "equals", "value": "failed", "boolean": "or" } ] }}// SQL: WHERE type = 'order' OR status = 'pending' OR status = 'failed'Important: Boolean operators connect filters left-to-right without automatic grouping. For complex grouping logic, use the in operator:
{ "filters": { "self": [ { "attribute": "type", "operator": "equals", "value": "order" }, { "attribute": "status", "operator": "in", "value": ["pending", "failed"] } ] }}// SQL: WHERE type = 'order' AND status IN ('pending', 'failed')Filtering by Resource
Section titled “Filtering by Resource”Filters can target different resources in a query:
{ "filters": { "self": [ { "attribute": "status", "operator": "equals", "value": "active" } ], "customer": [ { "attribute": "country_code", "operator": "equals", "value": "FI" } ] }}Structure
Section titled “Structure”| Key | Description |
|---|---|
self | Filters on the primary resource |
<relationship> | Filters on related resources |
Relationship Filtering
Section titled “Relationship Filtering”Filtering by relationship creates a WHERE EXISTS condition:
{ "filters": { "self": [ { "attribute": "status", "operator": "equals", "value": "pending" } ], "customer": [ { "attribute": "type", "operator": "equals", "value": "vip" } ] }}// SQL: WHERE status = 'pending'// AND EXISTS (SELECT 1 FROM customers WHERE customers.id = orders.customer_id AND type = 'vip')Allowed Filters
Section titled “Allowed Filters”Servers MUST define which attributes are filterable per resource:
{ "filters": { "self": ["id", "status", "created_at", "total_amount"], "customer": ["id", "type", "country_code"] }}Validation
Section titled “Validation”- Filtering on non-allowed attributes MUST return an error
- Servers SHOULD expose allowed filters via
mesh.describe
Error Response
Section titled “Error Response”{ "errors": [{ "code": "INVALID_ARGUMENTS", "message": "Filter attribute not allowed: secret_field", "retryable": false, "source": { "pointer": "/call/arguments/filters/self/0/attribute" }, "details": { "attribute": "secret_field", "allowed": ["id", "status", "created_at", "total_amount"] } }]}Type Coercion
Section titled “Type Coercion”String Values
Section titled “String Values”Most operators accept string values:
{ "attribute": "status", "operator": "equals", "value": "pending" }Numeric Values
Section titled “Numeric Values”Comparison operators work with numbers:
{ "attribute": "quantity", "operator": "greater_than", "value": 10 }{ "attribute": "price", "operator": "between", "value": [10.00, 99.99] }Boolean Values
Section titled “Boolean Values”{ "attribute": "is_active", "operator": "equals", "value": true }{ "attribute": "verified", "operator": "equals", "value": false }Date/Time Values
Section titled “Date/Time Values”Use ISO 8601 format:
{ "attribute": "created_at", "operator": "greater_than", "value": "2024-01-15T10:30:00Z" }{ "attribute": "date", "operator": "between", "value": ["2024-01-01", "2024-12-31"] }Null Values
Section titled “Null Values”Use null operators, not equals with null:
// Correct{ "attribute": "deleted_at", "operator": "is_null" }
// Incorrect{ "attribute": "deleted_at", "operator": "equals", "value": null }Examples
Section titled “Examples”Simple Filter
Section titled “Simple Filter”{ "call": { "function": "orders.list", "version": "1", "arguments": { "filters": { "self": [ { "attribute": "status", "operator": "equals", "value": "pending" } ] } } }}Multiple Filters (AND)
Section titled “Multiple Filters (AND)”{ "arguments": { "filters": { "self": [ { "attribute": "status", "operator": "in", "value": ["pending", "processing"] }, { "attribute": "created_at", "operator": "greater_than_or_equal_to", "value": "2024-01-01T00:00:00Z" }, { "attribute": "total_amount", "operator": "greater_than", "value": 50 } ] } }}OR Conditions
Section titled “OR Conditions”{ "arguments": { "filters": { "self": [ { "attribute": "tracking_number", "operator": "equals", "value": "MH726955185FI", "boolean": "or" }, { "attribute": "parcel_tracking_number", "operator": "equals", "value": "MH726955185FI", "boolean": "or" }, { "attribute": "return_tracking_number", "operator": "equals", "value": "MH726955185FI", "boolean": "or" } ] } }}Pattern Matching
Section titled “Pattern Matching”{ "arguments": { "filters": { "self": [ { "attribute": "email", "operator": "like", "value": "%@company.com" }, { "attribute": "name", "operator": "not_like", "value": "Test%" } ] } }}Date Range
Section titled “Date Range”{ "arguments": { "filters": { "self": [ { "attribute": "created_at", "operator": "between", "value": ["2024-01-01T00:00:00Z", "2024-01-31T23:59:59Z"] } ] } }}Null Checks
Section titled “Null Checks”{ "arguments": { "filters": { "self": [ { "attribute": "deleted_at", "operator": "is_null" }, { "attribute": "verified_at", "operator": "is_not_null" } ] } }}Multi-Resource Filtering
Section titled “Multi-Resource Filtering”{ "arguments": { "filters": { "self": [ { "attribute": "status", "operator": "equals", "value": "active" } ], "vendor": [ { "attribute": "type", "operator": "in", "value": ["premium", "enterprise"] } ], "location": [ { "attribute": "country_code", "operator": "equals", "value": "FI" } ] }, "relationships": ["vendor", "location"] }}Complex Query
Section titled “Complex Query”{ "protocol": { "name": "mesh", "version": "0.1.0" }, "id": "req_complex", "call": { "function": "tracking_events.list", "version": "1", "arguments": { "fields": { "self": ["id", "status", "location", "occurred_at"], "shipment": ["id", "tracking_number"] }, "filters": { "self": [ { "attribute": "occurred_at", "operator": "greater_than", "value": "2024-01-01T00:00:00Z" }, { "attribute": "status", "operator": "in", "value": ["in_transit", "delivered"] } ], "shipment": [ { "attribute": "carrier", "operator": "equals", "value": "posti" } ] }, "sorts": [ { "attribute": "occurred_at", "direction": "desc" } ], "relationships": ["shipment"], "pagination": { "limit": 50 } } }}