Skip to main content
What this builds. A vendor scorecard with mixed-logic filters — “European customers with priority above 2 or status closed”, “vendors in AS or with priority above 5” — using nested FilterGroups instead of writing the boolean logic by hand. You’ll end up with. A working pattern for three increasingly expressive filter shapes plus an order_by / paginate recipe you can drop straight into a real query.

The mental model

A filter is groups of conditions:
  • Conditions inside one FilterGroup share an operator — AND (default) or OR.
  • The top-level CompoundFilter joins those groups together with its own operator — also AND (default) or OR.
So (region == "EU") AND (priority > 2 OR status == "closed") is naturally two groups: one with the region condition, one with the priority/status conditions joined by OR. The compound filter joins those two groups with AND. When the whole filter is a single AND or single OR, use CompoundFilter.of(...) — it wraps your conditions in a single group for you.

Setup

Recipe 1 — all conditions must match (AND inside a group)

“European vendors that are still open.” Conditions inside a single group default to AND, so CompoundFilter.of(...) is enough:

Recipe 2 — any condition matches (OR inside a group)

“Vendors in EU or US.” Same single-group shape, but flip the group operator to OR:
For a flat IS-IN like this you could also use TableFilterOperator.IS_IN — but the OR shape generalises when the two conditions are on different columns.

Recipe 3 — mix AND and OR with two groups

“European vendors that are either high-priority or already closed.” Two clauses, with OR inside one and AND between groups:

Order and paginate the result

order_by and paginate chain onto any CompoundFilter. They return a new filter (the dataclass is frozen), so call them on the result of .of(...) or the constructor:
order_by is chainable for multi-key sorting — call it once per key, in priority order:
The first key wins ties; the second breaks them. Same filter can be used to drive a scroll(...) if you have more rows than fit in one read_rows page.

Cleanup

read_rows is one page (~1,000 rows by default). For larger tables iterate with scroll, passing the same CompoundFilter — scroll pages through every matching row for you.

See also

Common filter recipes

One snippet per TableFilterOperator, grouped by use case.

CompoundFilter reference

Top-level shape and builder methods.

Rows and filters

Filters in context with insert / update / delete / scroll.