# Filtering and queries

Source: https://libtmux.org/en/tmux/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 returns zero or more matches. An exactly-one operation
  returns one object or reports 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.

<a id="python-filter-and-get-django-style"></a>

## Filter collections and select one object

[`server.sessions`](<https://libtmux.org/en/py/latest/reference/libtmux-server-sessions/>), [`session.windows`](<https://libtmux.org/en/py/latest/reference/libtmux-session-windows/>), and [`window.panes`](<https://libtmux.org/en/py/latest/reference/libtmux-window-panes/>) are [`QueryList`](<https://libtmux.org/en/py/latest/reference/libtmux-_internal-query_list-querylist/>)
collections. Call [`.filter()`](<https://libtmux.org/en/py/latest/reference/libtmux-_internal-query_list-querylist-filter/>) with field names and optional lookup suffixes:

```python
>>> session.windows.filter(window_name__startswith='api')
[Window(@... ...:api-server, Session($... ...))]

>>> session.windows.filter(window_name__iregex=r'n?vim')
```

Lookups include `exact`, `contains`, `startswith`, `endswith`, [`regex`](<https://libtmux.org/en/py/latest/workspace/reference/tmuxp-cli-search-searchpattern-regex/>), and
their case-insensitive `i`-prefixed variants. Multiple keywords and chained
[`.filter()`](<https://libtmux.org/en/py/latest/reference/libtmux-_internal-query_list-querylist-filter/>) calls combine with AND. `.get()` requires exactly one match. Its
[`default`](<https://docs.python.org/3/library/argparse.html#default>) argument handles an absent result; multiple matches still raise
[`MultipleObjectsReturned`](<https://libtmux.org/en/py/latest/reference/libtmux-exc-multipleobjectsreturned/>).

Server-wide collections ([`server.windows`](<https://libtmux.org/en/py/latest/reference/libtmux-server-windows/>), [`server.panes`](<https://libtmux.org/en/py/latest/reference/libtmux-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`](<https://libtmux.org/en/py/latest/reference/libtmux-window-linked_sessions/>) to
find its sessions. For a known ID, use [`Pane.from_pane_id()`](<https://libtmux.org/en/py/latest/reference/libtmux-pane-from_pane_id/>) or
[`Window.from_window_id()`](<https://libtmux.org/en/py/latest/reference/libtmux-window-from_window_id/>) to resolve the object directly.

For servers with hundreds or thousands of panes, [`.filter()`](<https://libtmux.org/en/py/latest/reference/libtmux-_internal-query_list-querylist-filter/>) still builds
every object before you discard the ones that don't match. [`search_sessions`](<https://libtmux.org/en/py/latest/reference/libtmux-server-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
>>> server.search_sessions(filter='#{==:#{session_name},alpha-1}')
```

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.

<a id="typescript-criteria-as-data"></a>

## TypeScript criteria

The [TypeScript filtering guide](https://libtmux.org/en/ts/latest/concepts/queries/) includes complete
programs for matching names, handling result counts, traversing linked windows,
refreshing snapshots, validating query documents, and filtering live tmux rows.

<a id="go-rust-java-c-typed-fields-that-fail-queries-at-compile-time"></a>

## Typed and local filters

Typed field operations let the compiler reject incompatible comparisons:

[`tmux.PaneFilter`](<https://libtmux.org/en/go/latest/reference/tmux-panefilter/>) describes a typed predicate over captured values. Use
[`tmuxq.Matching`](<https://libtmux.org/en/go/latest/reference/tmuxq-matching/>) or compile its [`PaneFilter.Predicate()`](<https://libtmux.org/en/go/latest/reference/tmux-panefilter-predicate/>) for local queries. To filter a
live tmux listing, pass a [`TmuxFilter`](<https://libtmux.org/en/go/latest/reference/tmux-tmuxfilter/>) expression to [`Server.SearchPanes`](<https://libtmux.org/en/go/latest/reference/tmux-server-searchpanes/>).
Typed fields reject invalid comparisons: `fields.pane_active.eq(true)` is
valid, while `.gt(...)` on a boolean field fails to compile. Compose
expressions with [`.and()`](<https://libtmux.org/en/rs/latest/reference/query-filterexpr-and/>). The `serde` feature supports versioned query
JSON for configuration or MCP.
[`Pane_`](<https://libtmux.org/en/java/latest/reference/io-github-libtmux-pane_-pane_/>), [`Window_`](<https://libtmux.org/en/java/latest/reference/io-github-libtmux-window_-window_/>), and [`Session_`](<https://libtmux.org/en/java/latest/reference/io-github-libtmux-session_-session_/>) expose typed field accessors for stream
predicates. A numeric field has no [`startsWith`](<https://libtmux.org/en/java/latest/reference/io-github-libtmux-query-fields-fields-textfield-startswith/>) operation. Use
[`Selections.exactlyOne`](<https://libtmux.org/en/java/latest/reference/io-github-libtmux-query-selections-selections-exactlyone/>) to reject absent or ambiguous matches.
Compose [`FilterExpr`](<https://libtmux.org/en/cxx/latest/reference/libtmux-filterexpr/>) values with `&&`, `||`, and `!`. For example,
[`pane::command.starts_with("nv") && pane::active`](<https://libtmux.org/en/cxx/latest/reference/libtmux-stringfieldhandle-starts_with/>) is valid, while
`pane::active.starts_with("x")` fails to compile.

Examples of typed and local filters:

```rust
use libtmux::query::{Filterable as _, QueryIteratorExt as _};

let fields = libtmux::Pane::filter_fields();

// `pane_active` is a flag, so `.eq(true)` compiles; `.gt(..)` would not.
let active = fields
    .pane_current_command
    .starts_with("sh")
    .and(fields.pane_active.eq(true));

let panes = server.panes().await?;
let matched: Vec<_> = panes.iter().matching(&active).collect();
```

```go
// Read once, filter in Go: several answers from one read.
snapshot, err := server.Snapshot(ctx)
if err != nil {
	return err
}
predicate, err := tmux.PaneActiveIs(true).Predicate()
if err != nil {
	return err
}
active := tmuxq.Where(snapshot.Panes(), predicate)
fmt.Println("active panes:", len(active))

// Or push the filter down: tmux returns only the matches.
filter := tmux.TmuxFilter("#{==:#{pane_active},1}")
panes, err := server.SearchPanes(ctx, &filter)
if err != nil {
	return err
}
fmt.Println("live matches:", len(panes))
```

```java
List<Window> editors = server.windows().stream()
        .filter(Window_.name().startsWith("edit"))
        .toList();

// Selections.exactlyOne() is the `.get()`-shaped call.
Session build = Selections.exactlyOne(
        server.sessions().stream().filter(Session_.name().is("build")).toList());
```

```csharp
IReadOnlyList<Window> windows = await session.GetWindowsAsync(ct);
IEnumerable<Window> building = windows.Where(
    each => each.Name.StartsWith("build", StringComparison.Ordinal));

// A declarative query is a document, not just a lambda run in place.
IReadOnlyList<Session> sessions = await server.GetSessionsAsync(ct);
IReadOnlyList<Session> matched = sessions.Matching<Session>(
    session => session.Name.StartsWith("build", StringComparison.Ordinal));
```

```cpp
// A filter is a value built from typed fields; `window::active.starts_with(...)`
// would not compile: a flag has no string operations.
const auto interesting =
    libtmux::window::name.starts_with("e") || libtmux::window::name == "logs";

auto matched = *windows | libtmux::matching(interesting);

// "Exactly one, or say why not" is a question the library answers directly.
auto logs = *windows | libtmux::matching(libtmux::window::name == "logs");
if (const auto only = libtmux::exactly_one(logs); only.has_value()) {
  std::printf("exactly one: %s\n", std::string{only->get().id()}.c_str());
}
```

```swift
// Filter locally with the standard library:
let editors = try await server.panes().filter { $0.currentCommand == "nvim" }

// Or build a filter that travels: stored, sent, replayed elsewhere:
let expression = try FilterExpr<Pane>.where(\.currentCommand, .isIn(["nvim", "vim"]))
let matching = try await server.panes().filter(expression)
```

<a id="the-cardinality-contract-side-by-side"></a>

## Result counts

### Python
Use [`QueryList.filter`](<https://libtmux.org/en/py/latest/reference/libtmux-_internal-query_list-querylist-filter/>) to keep matching objects and [`QueryList.get`](<https://libtmux.org/en/py/latest/reference/libtmux-_internal-query_list-querylist-get/>) when
exactly one must match. An empty result raises [`ObjectDoesNotExist`](<https://libtmux.org/en/py/latest/reference/libtmux-exc-objectdoesnotexist/>) unless
you supply `default=`. Several matches raise [`MultipleObjectsReturned`](<https://libtmux.org/en/py/latest/reference/libtmux-exc-multipleobjectsreturned/>).

### TypeScript
Use [`where()`](<https://libtmux.org/en/ts/latest/reference/selection-selection-where/>) or [`filter()`](<https://libtmux.org/en/ts/latest/reference/selection-selection-filter/>) to select matches and [`one()`](<https://libtmux.org/en/ts/latest/reference/selection-selection-one/>) to require exactly
one. No match raises [`NoMatchError`](<https://libtmux.org/en/ts/latest/reference/errors-nomatcherror/>); [`oneOrUndefined()`](<https://libtmux.org/en/ts/latest/reference/selection-selection-oneorundefined/>) accepts that case.
Several matches raise [`MultipleMatchesError`](<https://libtmux.org/en/ts/latest/reference/errors-multiplematcheserror/>).

### Java
Filter with [`Stream.filter`](<https://docs.oracle.com/en/java/javase/21/docs/api/java/util/stream/Stream.html#filter(java.util.function.Predicate)>) and require one match with
[`Selections.exactlyOne`](<https://libtmux.org/en/java/latest/reference/io-github-libtmux-query-selections-selections-exactlyone/>). Empty and multiple results raise
[`CardinalityException.NoMatch`](<https://libtmux.org/en/java/latest/reference/io-github-libtmux-exception-cardinalityexception-cardinalityexception-nomatch/>) and [`CardinalityException.MultipleMatches`](<https://libtmux.org/en/java/latest/reference/io-github-libtmux-exception-cardinalityexception-cardinalityexception-multiplematches/>),
respectively.

[Filtering and querying](https://libtmux.org/en/tmux/guides/querying-and-filtering/) shows exactly-one
lookups and their error handling. Do not index the first result until the
operation has established that a match exists.

## tmux command reference

The tmux [list-panes](https://libtmux.org/en/tmux/latest/manual/list-panes/) and
[list-windows](https://libtmux.org/en/tmux/latest/manual/list-windows/) references describe native
format filters. See [formats](https://libtmux.org/en/tmux/latest/manual/full/#FORMATS) for
expressions and available variables.
