Documentation Index Fetch the complete documentation index at: https://docs.grainql.com/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Filters allow you to narrow down your query results by specifying conditions that events must meet. You can filter on event properties, metadata, and use various comparison operators to match your exact needs.
Filter Structure
All filters follow this structure:
{
"property" : "string" ,
"comparison" : "string" ,
"value" : "any"
}
Filter Fields
Field Type Required Description propertystring Yes Property path to filter on comparisonstring Yes Comparison operator valueany Yes Value to compare against
Property Paths
Event Properties
Access custom properties you included when tracking events:
{
"property" : "properties.price" ,
"comparison" : "GREATER_THAN" ,
"value" : 100
}
{
"property" : "properties.category" ,
"comparison" : "EQUALS" ,
"value" : "electronics"
}
Filter on built-in event fields:
{
"property" : "eventName" ,
"comparison" : "EQUALS" ,
"value" : "purchase_completed"
}
{
"property" : "userId" ,
"comparison" : "EQUALS" ,
"value" : "user_123"
}
{
"property" : "eventTs" ,
"comparison" : "GREATER_THAN" ,
"value" : "2024-01-01T00:00:00Z"
}
Nested Properties
Access nested object properties:
{
"property" : "properties.user.plan" ,
"comparison" : "EQUALS" ,
"value" : "premium"
}
{
"property" : "properties.product.details.color" ,
"comparison" : "EQUALS" ,
"value" : "blue"
}
Comparison Operators
Equality Operators
EQUALS
Exact match:
{
"property" : "properties.status" ,
"comparison" : "EQUALS" ,
"value" : "completed"
}
NOT_EQUALS
Not equal to:
{
"property" : "properties.device" ,
"comparison" : "NOT_EQUALS" ,
"value" : "mobile"
}
Numeric Operators
GREATER_THAN
Greater than (for numbers):
{
"property" : "properties.price" ,
"comparison" : "GREATER_THAN" ,
"value" : 100
}
LESS_THAN
Less than (for numbers):
{
"property" : "properties.duration" ,
"comparison" : "LESS_THAN" ,
"value" : 300
}
GREATER_THAN_OR_EQUALS
Greater than or equal to:
{
"property" : "properties.score" ,
"comparison" : "GREATER_THAN_OR_EQUALS" ,
"value" : 80
}
LESS_THAN_OR_EQUALS
Less than or equal to:
{
"property" : "properties.age" ,
"comparison" : "LESS_THAN_OR_EQUALS" ,
"value" : 65
}
String Operators
CONTAINS
String contains substring:
{
"property" : "properties.page" ,
"comparison" : "CONTAINS" ,
"value" : "/product"
}
NOT_CONTAINS
String does not contain substring:
{
"property" : "properties.referrer" ,
"comparison" : "NOT_CONTAINS" ,
"value" : "spam-site.com"
}
List Operators
Value is in a list:
{
"property" : "properties.category" ,
"comparison" : "IN" ,
"value" : [ "electronics" , "books" , "clothing" ]
}
{
"property" : "userId" ,
"comparison" : "IN" ,
"value" : [ "user_123" , "user_456" , "user_789" ]
}
NOT_IN
Value is not in a list:
{
"property" : "properties.country" ,
"comparison" : "NOT_IN" ,
"value" : [ "US" , "CA" , "GB" ]
}
Complex Filter Examples
Multiple Filters
Combine multiple filters with AND logic:
{
"event" : "purchase_completed" ,
"filterSet" : [
{
"property" : "properties.price" ,
"comparison" : "GREATER_THAN" ,
"value" : 100
},
{
"property" : "properties.category" ,
"comparison" : "EQUALS" ,
"value" : "electronics"
},
{
"property" : "properties.device" ,
"comparison" : "EQUALS" ,
"value" : "mobile"
}
]
}
User Segmentation
Filter by user properties:
{
"filterSet" : [
{
"property" : "properties.user.plan" ,
"comparison" : "EQUALS" ,
"value" : "premium"
},
{
"property" : "properties.user.signup_date" ,
"comparison" : "GREATER_THAN" ,
"value" : "2024-01-01"
}
]
}
Geographic Filtering
Filter by location:
{
"filterSet" : [
{
"property" : "properties.country" ,
"comparison" : "EQUALS" ,
"value" : "US"
},
{
"property" : "properties.state" ,
"comparison" : "IN" ,
"value" : [ "CA" , "NY" , "TX" ]
}
]
}
Time-based Filtering
Filter by time ranges:
{
"filterSet" : [
{
"property" : "eventTs" ,
"comparison" : "GREATER_THAN" ,
"value" : "2024-01-01T00:00:00Z"
},
{
"property" : "eventTs" ,
"comparison" : "LESS_THAN" ,
"value" : "2024-01-31T23:59:59Z"
}
]
}
A/B Testing
Filter by experiment variants:
{
"event" : "conversion_completed" ,
"filterSet" : [
{
"property" : "properties.experiment" ,
"comparison" : "EQUALS" ,
"value" : "homepage_hero"
},
{
"property" : "properties.variant" ,
"comparison" : "EQUALS" ,
"value" : "A"
}
]
}
Data Type Handling
String Values
{
"property" : "properties.name" ,
"comparison" : "EQUALS" ,
"value" : "John Doe"
}
{
"property" : "properties.email" ,
"comparison" : "CONTAINS" ,
"value" : "@gmail.com"
}
Numeric Values
{
"property" : "properties.price" ,
"comparison" : "GREATER_THAN" ,
"value" : 99.99
}
{
"property" : "properties.quantity" ,
"comparison" : "EQUALS" ,
"value" : 5
}
Boolean Values
{
"property" : "properties.is_premium" ,
"comparison" : "EQUALS" ,
"value" : true
}
Array Values
{
"property" : "properties.tags" ,
"comparison" : "IN" ,
"value" : [ "featured" , "sale" , "new" ]
}
Null Values
{
"property" : "properties.optional_field" ,
"comparison" : "EQUALS" ,
"value" : null
}
Common Filter Patterns
E-commerce Filters
{
"event" : "purchase_completed" ,
"filterSet" : [
{
"property" : "properties.price" ,
"comparison" : "GREATER_THAN" ,
"value" : 50
},
{
"property" : "properties.payment_method" ,
"comparison" : "EQUALS" ,
"value" : "credit_card"
},
{
"property" : "properties.shipping_country" ,
"comparison" : "NOT_IN" ,
"value" : [ "XX" , "YY" ]
}
]
}
User Engagement Filters
{
"filterSet" : [
{
"property" : "properties.session_duration" ,
"comparison" : "GREATER_THAN" ,
"value" : 300
},
{
"property" : "properties.pages_viewed" ,
"comparison" : "GREATER_THAN_OR_EQUALS" ,
"value" : 5
},
{
"property" : "properties.bounce_rate" ,
"comparison" : "LESS_THAN" ,
"value" : 0.3
}
]
}
Error Tracking Filters
{
"event" : "error_occurred" ,
"filterSet" : [
{
"property" : "properties.error_type" ,
"comparison" : "EQUALS" ,
"value" : "javascript_error"
},
{
"property" : "properties.severity" ,
"comparison" : "IN" ,
"value" : [ "high" , "critical" ]
},
{
"property" : "properties.page" ,
"comparison" : "NOT_CONTAINS" ,
"value" : "/admin"
}
]
}
Feature Usage Filters
{
"event" : "feature_used" ,
"filterSet" : [
{
"property" : "properties.feature_name" ,
"comparison" : "EQUALS" ,
"value" : "advanced_search"
},
{
"property" : "properties.user.plan" ,
"comparison" : "EQUALS" ,
"value" : "premium"
},
{
"property" : "properties.usage_count" ,
"comparison" : "GREATER_THAN" ,
"value" : 10
}
]
}
1. Use Specific Property Paths
Be specific with your property paths to avoid ambiguity:
// ✅ Good: Specific property path
{
"property" : "properties.user.plan" ,
"comparison" : "EQUALS" ,
"value" : "premium"
}
// ❌ Avoid: Ambiguous property path
{
"property" : "plan" ,
"comparison" : "EQUALS" ,
"value" : "premium"
}
2. Use Date Ranges
Always include date ranges to limit the data scanned:
{
"after" : "2024-01-01" ,
"before" : "2024-01-31" ,
"filterSet" : [
{
"property" : "properties.price" ,
"comparison" : "GREATER_THAN" ,
"value" : 100
}
]
}
3. Order Filters by Selectivity
Put the most selective filters first:
{
"filterSet" : [
{
"property" : "userId" ,
"comparison" : "EQUALS" ,
"value" : "user_123"
},
{
"property" : "properties.category" ,
"comparison" : "EQUALS" ,
"value" : "electronics"
},
{
"property" : "properties.price" ,
"comparison" : "GREATER_THAN" ,
"value" : 100
}
]
}
4. Use IN Instead of Multiple EQUALS
When filtering for multiple values, use IN instead of multiple EQUALS filters:
// ✅ Good: Single IN filter
{
"property" : "properties.category" ,
"comparison" : "IN" ,
"value" : [ "electronics" , "books" , "clothing" ]
}
// ❌ Avoid: Multiple EQUALS filters
{
"filterSet" : [
{ "property" : "properties.category" , "comparison" : "EQUALS" , "value" : "electronics" },
{ "property" : "properties.category" , "comparison" : "EQUALS" , "value" : "books" },
{ "property" : "properties.category" , "comparison" : "EQUALS" , "value" : "clothing" }
]
}
Error Handling
Invalid Property Paths
{
"error" : "Invalid property path: 'invalid.property'"
}
Invalid Comparison Operators
{
"error" : "Invalid comparison operator: 'INVALID_OP'"
}
Type Mismatches
{
"error" : "Type mismatch: expected number, got string for property 'price'"
}
Handle Filter Errors
async function queryWithFilters ( tenantId : string , apiKey : string , filters : any ) {
try {
const response = await fetch ( `https://queryapis.grainql.com/v1/api/query/ ${ tenantId } ` , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'X-API-Key' : apiKey
},
body: JSON . stringify ( filters )
});
if ( ! response . ok ) {
const error = await response . json ();
throw new Error ( `Filter error: ${ error . error } ` );
}
return response . json ();
} catch ( error ) {
console . error ( 'Filter query failed:' , error );
throw error ;
}
}
Testing Filters
Validate Filter Syntax
function validateFilter ( filter : any ) : boolean {
if ( ! filter . property || ! filter . comparison || filter . value === undefined ) {
return false ;
}
const validComparisons = [
'EQUALS' , 'NOT_EQUALS' , 'GREATER_THAN' , 'LESS_THAN' ,
'GREATER_THAN_OR_EQUALS' , 'LESS_THAN_OR_EQUALS' ,
'CONTAINS' , 'NOT_CONTAINS' , 'IN' , 'NOT_IN'
];
return validComparisons . includes ( filter . comparison );
}
function validateFilters ( filters : any []) : boolean {
return filters . every ( validateFilter );
}
Test Filter Results
async function testFilter ( tenantId : string , apiKey : string , filter : any ) {
// Test with filter
const filteredResponse = await fetch ( `https://queryapis.grainql.com/v1/api/query/count/ ${ tenantId } ` , {
method: 'POST' ,
headers: { 'X-API-Key' : apiKey },
body: JSON . stringify ({ filterSet: [ filter ] })
});
const filteredCount = ( await filteredResponse . json ()). count ;
// Test without filter
const totalResponse = await fetch ( `https://queryapis.grainql.com/v1/api/query/count/ ${ tenantId } ` , {
method: 'POST' ,
headers: { 'X-API-Key' : apiKey },
body: JSON . stringify ({})
});
const totalCount = ( await totalResponse . json ()). count ;
console . log ( `Filter: ${ filter . property } ${ filter . comparison } ${ filter . value } ` );
console . log ( `Filtered count: ${ filteredCount } ` );
console . log ( `Total count: ${ totalCount } ` );
console . log ( `Filter effectiveness: ${ ( filteredCount / totalCount * 100 ). toFixed ( 1 ) } %` );
}
Next Steps
Query Events Learn how to query events with filters
Count Events Get event counts for aggregations
List Events Discover available event types
Custom Dashboard Build a custom analytics dashboard