Our engine never sees your warehouse password
datahashi

BlogQuerying

Filters that compose safely

Two filters interacting should narrow a result, not rewrite the metric. How datahashi treats filter composition as a first-class part of every semantic query.

The datahashi team3 min read

Filters look simple until two of them interact. One filter on customer region and another on order status should narrow the result, not rewrite the metric. datahashi treats filter composition as a first-class part of semantic queries so agents and dashboards share the same rules.

Filters belong to entities

A safe filter is attached to a known entity or dimension in the catalog, not to a free-form column string. When you call run_semantic_query, filters reference catalog fields. That lets the compiler place predicates on the correct tables after joins are planned.

If a filter named a raw column that only exists after a join, older ad hoc SQL often applied it too early or too late. Semantic filters avoid that by resolving the field first via the same catalog you browse with list_catalog.

Composition rules that matter

Safe composition means:

  • Conjunction by default. Multiple filters AND together unless the request explicitly uses an OR group the API supports.
  • No silent widening. Adding a filter never expands the row set. If a filter cannot be applied at the requested grain, the query fails instead of dropping the filter.
  • Metric-aware applicability. Some metrics disallow certain filters (for example, a pipeline stage filter on a metric defined only on closed-won facts). describe_metric surfaces those constraints; run_semantic_query enforces them.

When two filters target related entities, the join path from the catalog decides how they meet. You do not hand-merge WHERE clauses.

Time filters versus attribute filters

Time ranges are special only in that they usually bind to a declared time dimension on the metric’s model. Attribute filters (status, segment, channel) bind to their dimensions. Mixing them is normal; the compiler keeps time predicates next to the time column and attribute predicates on their tables.

Do not encode time windows inside metric names (“revenue_last_7_days”) when a time filter on a base revenue metric would compose cleanly. Named windows duplicate logic and fight composition.

How to inspect before you trust

  1. Find filterable fields with list_catalog.
  2. Check each metric with describe_metric for filter restrictions.
  3. Run run_semantic_query with one filter, then with both, and compare.
  4. Read the compiled SQL WHERE clause to confirm both predicates landed where you expect.

If the second filter disappears from the SQL, stop. That is a modeling bug or an unsupported combination, not something to paper over in the client.

Patterns that break composition

Avoid client-side filtering after the query “to finish the job.” That splits the definition of the metric across systems. Avoid OR filters that span unrelated entities without an explicit catalog relationship. Avoid pushing the same business rule as both a metric definition and a recurring filter; pick one home for the rule.

Filters that compose safely are boring in the best way: they narrow, they fail loud when they cannot, and they leave an audit trail in compiled SQL. datahashi keeps that boring path as the default for every run_semantic_query call.

Product notes

An occasional email when we ship something or write something worth reading.