Skip to main content

Filters and segmentation

Filters let you select a subset of your data for analysis. Use them to segment users, pick out specific events, and build complex conditions.

What are filters?​

A filter is a set of conditions that decides which events or users are included in the analysis. Filters run at the SQL query level (but you never need to know SQL), which keeps execution fast and efficient.

Filter structure​

Filters in Metriox have a hierarchical structure:

  • Filter
    • Logic — AND or OR
    • Predicates
      • Predicate 1
      • Predicate 2
      • and so on

Filter logic​

Logic defines how the conditions are combined:

  • AND — every condition must match
  • OR — at least one condition must match

Predicate types​

A predicate is a single filter condition. It consists of:

  1. Type — an event, a property, or a user
  2. Operator — the comparison operation
  3. Value — what you compare against
  4. Negation — NOT (optional)

These four parts form one row in the filter builder. Here is how it looks on the Events page: the NOT toggle, the field, the operator, and the value, with the Add filter type menu adding the next predicate.

The Metriox Events page: section navigation on the left, the bot picker and the "Last 7 days" range on top, and the filter builder holding a single predicate (NOT / User ID / equals / 160266) with a "1 condition" badge and the "Add filter type" menu offering Event, Property, and User

Event predicates​

Filtering by event name:

Event Name equals "button_click"
NOT Event Name equals "button_click"
Event Name contains "purchase"
Event Name startswith "pay"

Operators for events:

  • equals — exact match
  • != — not equal
  • contains — contains a substring
  • startswith — starts with (expression line only)

Examples:

ConditionWhat matches
Event Name equals "purchase_completed"Only events named purchase_completed
Event Name contains "button"Events button_click, button_view, share_button, and any other name containing that substring
NOT Event Name equals "bot_started"Every event except bot_started

Property predicates​

Filtering by event properties:

plan equals "premium"
NOT plan equals "free"
price > "100"

The available operators depend on the property's data type.


User predicates​

These select whole users rather than individual events. In the builder this is one type, User: in the row's first list, choose what to check. The list has two groups, and they answer different questions.

GroupQuestionExample
Profile (now)who this person is"has Telegram Premium", "interface language ru", "arrived through the promo deep link"
Activity in the periodwhat they did in the selected period"pressed buttons on at least 5 different days", "spent more than 10,000"

Until something is chosen in the list, the condition is not applied.

Profile (now)​

Telegram profile attributes: Telegram Premium, interface language, username, first name, last name, Telegram ID and deep-link parameter. Below them, the "Found on this bot" group lists the other profile properties your bot has.

  • The value is today's. The profile keeps only the latest known value, so a "Premium" condition on last month's report selects the people who are Premium now, not then.
  • Operators: equals and not equals. First name, last name, username and deep-link parameter also offer contains, because names carry emoji and mixed case.
  • "Not Premium" and not equals "Premium" are different selections. "Not Premium" selects only people known not to have Premium. not equals "Premium" adds everyone whose flag was never recorded, which on many bots is most of the audience.
  • At most 4 profile conditions in one filter. Once there are four, the "Profile" group is disabled in a new row, and the list says why. The "Activity" group stays available.

In the expression box above the builder, such a condition reads:

profile $tg.is_premium = "true"

Activity in the period​

This condition first computes a figure per user, then keeps the users who clear the threshold. It has four parts:

PartWhat it setsExample
Metricwhat to measure per userEvent count, Active days, Sum of a property
Counting events namedwhich events to countsuccessful_payment
Windowmeasure over the whole period, or on the busiest dayIn total / On the busiest day
Thresholdoperator and number> 10000

Name the event. Without it the condition is not applied: more than 5 of what? Conditions in the main builder do not affect the count. Only events with the name in this field, within the selected period, are counted.

The Sum of a property metric additionally needs a numeric property and a source, for the same reason the Sum metric does: one payment can arrive from two sources, and without pinning to one the total doubles. See Metrics. A Sum threshold may be fractional; the other metrics take whole numbers.

The window separates two different questions. "In total" is the sum across the whole period ("spent more than 10,000 this month"). "On the busiest day" is the maximum over days: with > it means "spent more than 1,000 on at least one day", with < it means "under that every day". It is not available for Active days, which is always 1 within a single day.

This type has no NOT toggle. To find people who never paid, set the threshold to = 0 rather than NOT around > 0. = 0 selects only people, while NOT would also match group and channel events that have no user. In the expression box, NOT before a profile condition is refused, and NOT before an activity condition is accepted but answers that broader question.

Examples:

SegmentMetricCounting events namedWindowThreshold
Regulars (pressed buttons on at least 5 different days)Active dayscallback_queryIn total>= 5
Paying usersSum of a property ($tg.total_amount, Bot API)successful_paymentIn total> 10000
One big purchaseSum of a property ($tg.total_amount, Bot API)successful_paymentOn the busiest day> 5000
Never paidEvent countsuccessful_paymentIn total= 0

In the expression box, such a condition reads:

Event Count where "successful_payment" (Total) > 3

The expression line​

Above the builder there is a line with the same filter written as text. You can copy it, paste one in, or edit it by hand. Changes apply on Enter or when the cursor leaves the line. If the line cannot be parsed, the reason appears under it and the filter stays as it was.

  • An event field is written as Event Name, Event Type, Source, Origin, Text or User ID.
  • A property is its key exactly as the field list shows it: plan, $tg.chat_type.
  • A profile condition starts with the word profile: profile $tg.is_premium = "true".
  • The value is always quoted, even a number: price > "100". Write a quote inside a value as \" and a backslash as \\: plan equals "say \"hi\"".
  • A number uses a dot: "10.5". A boolean is "true" or "false".
  • AND and OR may be written in any case, but one line uses only one of them.
  • NOT goes before the condition: NOT Event Name equals "bot_started".
  • User ID only takes equals and !=: an identifier is compared whole, never with contains.
  • An activity condition must name its event: Event Count where "purchase" (Total) >= 2.

The line is a record of what the builder holds, not a separate filter. A condition it cannot write (for example a nested group sent through the API) is shown read-only in the builder, and the line cannot be edited while it is there.


Operators by data type​

String​

Operators:

  • equals — exact match
  • contains — contains a substring
  • startswith — starts with (expression line only)
  • != — not equal. Events that have no such property at all match too, as they do with the NOT toggle.

Examples:

ConditionWhat matches
plan equals "premium"Only events where plan = "premium"
button_name contains "buy"button_name: "buy_now", "buy_premium", "buy_subscription"
NOT language equals "en"Every language except English, and events with no language recorded

Number​

Operators:

  • = — equal to
  • != — not equal to
  • > — greater than
  • < — less than
  • >= — greater than or equal to
  • <= — less than or equal to

A number is quoted and uses a dot: "10.5", not "10,5".

Examples:

ConditionWhat matches
price > "100"Price above 100
quantity = "1"Quantity equal to 1
age >= "18"Age 18 and older
NOT rating < "4.0"Rating 4.0 and higher, and events with no rating at all

Dates and times​

Filters cannot compare dates yet. A date your bot sends in a property, such as "2026-03-01T00:00:00Z", reaches the field list as a string, so only string operators apply to it: equals finds an exact match, but "after 1 March" cannot be asked that way.

For "when" questions use the report period. For "what did this person do in the last N days" use a User → Activity over a period condition.


Boolean​

Operators:

  • equals — equal to (true/false)
  • not equals — not equal to

Examples:

ConditionWhat matches
is_subscriber = "true"Subscribers only (your own event property)
email_verified = "false"Email not confirmed
NOT has_completed_onboarding = "false"Users who completed onboarding (true or null)

Combining conditions​

AND logic​

Every condition must match at the same time.

Example:

  • AND
    • Event Name equals "purchase_completed"
    • amount > "500"
    • currency equals "RUB"

This selects purchases above 500 roubles.

OR logic​

At least one condition must match.

Example:

  • OR
    • Event Name equals "purchase_completed"
    • Event Name equals "subscription_renewed"
    • Event Name equals "trial_started"

This selects any monetization event.

AND and OR together​

In the builder the whole filter is joined one way: every condition with AND, or every condition with OR. An OR group cannot be nested inside AND in the builder. Such a filter can be sent through the API; the builder then shows the nested group read-only and keeps it as it is, but cannot edit it.

A common workaround for "one of several events" is a shared fragment of the name: Event Name contains "click" instead of two conditions joined with OR.


Negation (NOT)​

Add NOT to an event or property predicate to invert its condition. User predicates have no NOT toggle in the builder: use the not equals operator or the = 0 and < thresholds instead (see above).

ConditionWhat matches
NOT Event Name equals "bot_started"Every event except bot_started
NOT plan equals "free"Everyone whose plan is not free, including events with no plan at all
NOT age < "18"Age 18 and older, and events with no age — the inverse of "younger than 18"

Filter examples​

Simple filters​

All purchases:

Event Name equals "purchase_completed"

Users with Telegram Premium:

profile $tg.is_premium = "true"

Premium is a profile attribute, not an event property, so this is a User condition, not a Property one.

Purchases above 1000 roubles:

  • AND
    • Event Name equals "purchase_completed"
    • amount > "1000"

Complex filters​

New premium users:

  • AND
    • Event Name equals "subscription_started"
    • plan equals "premium"
    • is_trial = "false"

Button presses by people, not bots:

  • AND
    • Event Name contains "button"
    • NOT is_bot = "true"

Problem transactions:

  • AND
    • Event Name equals "payment_failed"
    • attempt_number > "2"
    • amount >= "500"

User segmentation​

Young audience from Russia:

  • AND
    • age < "25"
    • country equals "RU"
    • language equals "ru"

High-value users:

  • AND
    • total_spent > "5000"
    • is_subscriber = "true"
    • Event Count where "successful_payment" (Total) >= 3

Applying filters​

Every widget has its own filters:

  • "Premium purchases" widget
    • Type — Series
    • Metric — Events
    • Filter
      • AND
        • Event Name equals "purchase_completed"
        • plan equals "premium"

A filter you need in more than one place can be saved as a segment: apply it in reports and insert it into a broadcast audience without building the conditions again.


Technical details​

Compiling to SQL​

Filters compile into SQL WHERE conditions.

Filter:

  • AND
    • Event Name equals "purchase_completed"
    • amount > "500"

SQL (conceptually):

WHERE event_name = 'purchase_completed'
AND JSONExtractFloat(props, 'amount') > 500
Note

You do not need to know SQL — Metriox turns your filters into optimized queries automatically.

Performance​

Metriox uses ClickHouse to execute queries quickly:

  • Indexing on event_name
  • Efficient handling of JSON properties
  • Parallel query execution

Optimization:

  • Filter by event first
  • Prefer equals over contains where possible
  • Keep the time range narrow

Best practices​

Building filters​

  • Start by filtering on events
  • Then add property conditions
  • Use NOT sparingly (it can be slower)

Testing​

  • Test filters on short time ranges first
  • Compare the results against what you expect
  • Use tables to inspect the details

What's next?​

  • Segments — save a filter and apply it everywhere
  • Metrics — see which metrics you can count with filters
  • Widgets — apply filters inside widgets
  • Events — the structure of events and properties