Data types
Metriox supports four terminal data types for event properties. Understanding them matters both for filters to work correctly and for building analytics.
Supported types
String
Text values of any length.
Examples:
{
"button_name": "buy_subscription",
"user_language": "ru",
"screen_name": "onboarding",
"error_message": "Payment failed"
}
Operations:
equalscontainsnot equals
Number
Numeric values: integers and floating-point numbers.
Examples:
{
"price": 299,
"quantity": 5,
"rating": 4.5,
"age": 25,
"balance": 1000.5
}
Operations:
equals(=)not equals(!=)greater than(>)less than(<)greater than or equal(>=)less than or equal(<=)
DateTime
Timestamps and dates.
Examples:
{
"subscription_start": "2026-03-01T10:00:00Z",
"last_login": "2026-03-02T15:30:00Z",
"trial_ends_at": "2026-03-15T23:59:59Z"
}
Format: ISO 8601, with time zone support
Operations:
equals(=)not equals(!=)greater than(>) — after the given dateless than(<) — before the given dategreater than or equal(>=)less than or equal(<=)
Boolean
Logical values: true or false.
Examples:
{
"is_premium": true,
"has_completed_onboarding": false,
"email_verified": true,
"push_notifications_enabled": false
}
Operations:
equals— with the valuetrueorfalsenot equals
Automatic type detection
Metriox detects the data type automatically from the value:
{
"count": 42, // → Number
"name": "Ivan", // → String
"active": true, // → Boolean
"created": "2026-03-02T10:00:00Z" // → DateTime
}
Storage
Internally, Metriox uses ClickHouse to store data. The events table schema:
| Field | ClickHouse type | Description |
|---|---|---|
tenant_id | UUID | Project ID |
plan | LowCardinality(String) | Pricing plan |
event_id | UUID | Event ID |
platform_user_id | String | User ID |
session_id | UUID | Session ID |
event_name | String | Event name |
received_at | DateTime64(3, 'UTC') | Time received |
created_at | DateTime64(6, 'UTC') | Time created |
body | Nullable(String) | Event body |
props | JSON | Properties (JSON) |
The props field lets you store arbitrary nested data structures and query them efficiently.
Working with nested structures
Properties can contain nested objects:
{
"user": {
"id": 123,
"name": "Ivan",
"premium": true
},
"product": {
"id": "premium_month",
"price": 299,
"currency": "RUB"
}
}
You reach nested properties with a dot:
user.id→ Numberuser.name→ Stringuser.premium→ Booleanproduct.price→ Number
Null values
A property can be null or absent altogether:
{
"optional_field": null
// another_field is absent
}
When filtering, keep in mind that null values are handled separately.
Best practices
Naming properties
- Use snake_case:
user_id,created_at,is_premium - Make names descriptive:
subscription_planrather thanplan - Add prefixes to group related fields:
payment_method,payment_amount,payment_status
Choosing types
- String: categories, statuses, names
- Number: counts, prices, ages, ratings
- DateTime: event timestamps
- Boolean: state flags
Consistency
Use the same type for the same property across all events:
// Good — Number everywhere
{ "amount": 299 }
{ "amount": 499 }
// Bad — mixed types
{ "amount": 299 }
{ "amount": "499" }