# Filtered Search

> USearch for Zig

A `Predicate` in the search config admits only the keys it approves, and non-matching entries still route the traversal, so recall does not collapse at low selectivity.
`Predicate.of` takes a `comptime` function rather than a closure, so the trampoline that crosses the boundary is generated per call site and nothing is boxed.
Its context travels as one pointer: pass `{}` for a predicate needing none, and a single-item pointer otherwise.

```zig
const admits = struct {
    fn call(_: void, key: usearch.Key) usearch.Admission {
        const value = key.asInteger() orelse return .reject;
        return if (value % 2 == 0) .admit else .reject;
}
}.call;

const even = try index.on(executor).search(try .of(f32, queries, 768), 10, &arena, .{
    .predicate = .of({}, admits),
});
```

It runs once per scored candidate, far more often than the "k" asked for, and concurrently from the executor's threads, so it belongs on your fast path and must tolerate that.
A VRAM index refuses a predicate with `Error.Device`, since a host callback cannot narrow a device walk.

Every long verb, namely `insert`, `refine`, `search`, `absorb`, `multiply`, `attend`, `maxsim`, `save`, `saveBuffer` and every load, takes a `ProgressCallback` in its config, called with the units finished so far and the units the call finishes if it runs to the end.
Answering `.cancel` stops the call at the next increment boundary, except an `insert`, which ignores it.

```zig
const Deadline = struct {
    io: std.Io,
    stop_at: i64,
    fn reached(self: *const @This(), _: usize, _: usize) usearch.Continuation {
        const now = std.Io.Clock.awake.now(self.io).toMilliseconds();
        return if (now < self.stop_at) .@"continue" else .cancel;
}
};

const deadline: Deadline = .{ .io = io, .stop_at = std.Io.Clock.awake.now(io).toMilliseconds() + 30_000 };
_ = try index.on(executor).refine(.{
    .expansion_factor = 128,
    .progress = .of(&deadline, Deadline.reached),
});
```

Like a predicate, the handler is a `comptime` function whose trampoline is generated per call site, and its context travels as one pointer, `{}` where none is needed.
It arrives on whichever thread advanced the work and never on two at once, so a counter or a deadline needs no synchronization of its own.
Reports are denominated in whatever the verb consumes, such as entries for an insertion, queries for a search, donors for an absorb and shards for a checkpoint, and how often they arrive is the increment's size rather than a guarantee, so read the counts and never the number of calls.

An `insert` cannot be stopped: it claims every entry before its first report and publishes the whole batch at once, so a batch is all-or-nothing with or without a handler.
A stopped `refine` is not a failure but the time budget a whole-graph sweep is meant to run under: re-selection claims no identity, so the graph stays correct and merely less improved, and the next call sweeps again, so calling under a time budget still converges.

`const usearch = @import("usearch");`

`const ProgressCallback = @import("usearch").ProgressCallback;`

`const Predicate = @import("usearch").Predicate;`

## Admission

Whether a filtered search admits one candidate key or passes over it.

```zig
pub const Admission = enum(c_int)
```

Mirrors `usearch_admission_t`.

- `reject` = `0`: Skip the key; the walk still routes through it.
- `admit` = `1`: Report the key among the matches.

## Continuation

Whether the operation a handler is watching carries on or unwinds.

```zig
pub const Continuation = enum(c_int)
```

Mirrors `usearch_continuation_t`.

- `@"continue"` = `0`: Carry on to the next unit of work.
- `cancel` = `1`: Stop; what the finished increments leave behind is each verb's own contract.

## ProgressCallback

A progress handler as the C interface carries it: a `usearch_progress_callback_t` and the state forwarded into it.

```zig
pub const ProgressCallback = struct
```

Build one with [`ProgressCallback.of`](https://www.unum.cloud/docs/usearch/zig/linear-algebra.md#of); the default reports nowhere.

- `callback` `?ProgressFunction`
- `state` `?*anyopaque`

## of

Wraps `reports` in a trampoline generated per call site, so it reads a typed context rather than an `anyopaque`.

```zig
pub fn of(
    context: anytype,
    comptime reports: fn (@TypeOf(context), usize, usize) Continuation,
) ProgressCallback
```

Pass `{}` for a handler needing no context, and a single-item pointer
otherwise.

The handler arrives on whichever thread advanced the work and never on two at once, so it
needs no synchronization of its own; answering `.cancel` stops the call.

## Predicate

Which candidate keys a filtered search may report: a handler and the state forwarded into it.

```zig
pub const Predicate = extern struct
```

Build one with [`Predicate.of`](https://www.unum.cloud/docs/usearch/zig/linear-algebra.md#of); the default admits everything. Mirrors `usearch_predicate_t`.

- `callback` `?PredicateFunction`: The optional handler; null admits every key.
- `state` `?*anyopaque`: Opaque pointer forwarded into every `callback` call.
- `cost` `PredicateCost`: How many keys each `callback` call carries.

## of

Wraps `admits` in a trampoline generated per call site, asking it about each key of a batch.

```zig
pub fn of(
    context: anytype,
    comptime admits: fn (@TypeOf(context), Key) Admission,
) Predicate
```

Pass `{}` for a predicate needing no context, and a single-item pointer otherwise.

It judges every scored candidate, far more than `wanted`, and runs concurrently from the
executor's threads - so it belongs on the caller's fast path and must tolerate that.

