# Filtering and queries

Source: https://libtmux.org/en/ruby/latest/concepts/queries/

> How you get from every session on the server to the one pane you mean, and what happens when zero or several match.

Use a collection filter to find matching sessions, windows, or panes. Use an
exactly-one lookup when your next operation requires a single target.

- **Filtering returns a collection; exactly-one lookup checks the result
  count.** A `.filter()` or `.where()` call returns zero or more matches. Methods
  such as `.get()`, `.one()`, and `Selections.exactlyOne()` return one object or
  report a missing or ambiguous match.

- **Choose where to filter.** Filter a snapshot in your program when you need
  several queries over the same data. A tmux format filter can reduce the rows
  returned by a live read. The cost depends on the data and queries you need.

## Python: `.filter()` and `.get()`, Django-style

`server.sessions`, `session.windows`, and `window.panes` are `QueryList`
collections. Call `.filter()` with field names and optional lookup suffixes:

Lookups include `exact`, `contains`, `startswith`, `endswith`, `regex`, and
their case-insensitive `i`-prefixed variants. Multiple keywords and chained
`.filter()` calls combine with AND. `.get()` requires exactly one match. Its
`default` argument handles an absent result; multiple matches still raise
`MultipleObjectsReturned`.

Server-wide collections (`server.windows`, `server.panes`) enumerate window
links. A window linked to two sessions appears once per session, so a lookup can
be ambiguous even when the window ID is unique. Use `Window.linked_sessions` to
find its sessions. For a known ID, use `Pane.from_pane_id()` or
`Window.from_window_id()` to resolve the object directly.

For servers with hundreds or thousands of panes, `.filter()` still builds
every object before you discard the ones that don't match. `search_sessions`,
`search_windows`, and `search_panes` push a tmux `-f` filter expression down
to the server instead, so libtmux builds objects only for the matches:

Python-side lookups work with the library's supported tmux versions. The tmux
filter grammar requires tmux 3.2 or newer. An unknown format token expands to an
empty value, so a malformed filter can look like a valid filter with no matches.
If `search_*()` unexpectedly returns no results, try `#{m:*,#{session_name}}` to
check that the session data is available.

## TypeScript: criteria as data

TypeScript's `Selection.where()` accepts structured, serializable criteria that
can be stored in a configuration file or sent through MCP:

`some`, `every`, and `none` test related objects. `{ mode: "insensitive" }`
enables case-insensitive comparison. Use `.where()` for criteria that can be
encoded with `encodeWhereDocument` and decoded with `decodeWhereDocument`; use
`.filter()` for a predicate function. `.one()` throws `NoMatchError` or
`MultipleMatchesError`. `.oneOrUndefined()` permits an absent result.

## Go, Rust, Java, C++: typed fields that fail queries at compile time

These ports use typed fields to reject invalid comparisons at compile time:

- **Go** offers both `tmux.PaneFilter{Active: tmux.Ptr(true), ...}` structs
  that push down into `SearchPanes` (one tmux command, only matches
  returned), and a `snapshot()` read followed by `tmuxq.Where(panes,
  predicate)` when you want several answers from one read.
- **Rust** uses typed fields: `fields.pane_active.eq(true)` is valid, but
  `.gt(...)` on that boolean field is not. Expressions compose with `.and()`.
  With the `serde` feature, a query can be encoded as a versioned JSON document
  for configuration or MCP.
- **Java** exposes each field as a typed accessor (`Pane_.index()`,
  `Session_.name()`) that plugs straight into an ordinary `Stream.filter()`;
  `Pane_.index().startsWith("2")` doesn't compile because the index is a
  number, not a string. `Selections.exactlyOne(...)` is the `.get()`-shaped
  call, throwing `NoMatchException` or `MultipleMatchesException`.
- **C++** composes `FilterExpr` values with `&&`, `||`, and `!`, as in
  `pane::command.starts_with("nv") && pane::active`. Invalid field operations
  such as `pane::active.starts_with("x")` fail to compile.

Examples of typed and local filters:

## The cardinality contract, side by side

| Port | Collection filter | Exactly-one | Empty | Several |
|------|--------------------|--------------|-------|---------|
| Python | `.filter()` | `.get()` | `ObjectDoesNotExist` (or `default=`) | `MultipleObjectsReturned` |
| TypeScript | `.where()` / `.filter()` | `.one()` | `NoMatchError` (or `.oneOrUndefined()`) | `MultipleMatchesError` |
| Java | `Stream.filter()` | `Selections.exactlyOne()` | `NoMatchException` | `MultipleMatchesException` |

See the Go, Rust, C++, .NET, and Swift references for their exactly-one result
types and failure handling.
