Skip to main content

FilterEvaluator

Struct FilterEvaluator 

Source
pub struct FilterEvaluator { /* private fields */ }
Expand description

A validated Filter paired with the canonical evaluator.

Build one with FilterEvaluator::new; the filter is walked once at construction to enforce MAX_FILTER_DEPTH and MAX_IN_VALUES. After that, FilterEvaluator::evaluate is infallible and may be called per row inside a search loop without revalidation.

§Examples

use iqdb_filter::FilterEvaluator;
use iqdb_types::{Filter, Metadata, Value};

let evaluator = FilterEvaluator::new(Filter::eq("year", Value::Int(2026)))?;

let meta: Metadata =
    [("year".to_string(), Value::Int(2026))].into_iter().collect();

assert!(evaluator.evaluate(Some(&meta)));
assert!(!evaluator.evaluate(None));

Implementations§

Source§

impl FilterEvaluator

Source

pub fn new(filter: Filter) -> Result<Self>

Validates filter and wraps it for evaluation.

The validation walk is iterative — an explicit work-list, not recursion — so new itself cannot stack-overflow on a pathological input. It returns IqdbError::InvalidFilter when:

§Errors

Returns IqdbError::InvalidFilter for filters that violate either cap. The variant carries no extra context — callers that need to distinguish “too deep” from “In too wide” can re-walk the filter themselves or pre-validate against the public consts.

§Examples
use iqdb_filter::FilterEvaluator;
use iqdb_types::{Filter, IqdbError, Value};

// Accepted: a small, well-formed filter.
let ok = FilterEvaluator::new(Filter::eq("k", Value::Int(1)));
assert!(ok.is_ok());

// Rejected: an oversized `In`.
let huge = vec![Value::Int(0); iqdb_filter::MAX_IN_VALUES + 1];
let err = FilterEvaluator::new(Filter::is_in("tag", huge)).unwrap_err();
assert_eq!(err, IqdbError::InvalidFilter);
Source

pub fn evaluate(&self, metadata: Option<&Metadata>) -> bool

Evaluates the validated filter against metadata.

None means the record has no metadata at all — distinct from an empty Metadata only at the API boundary; semantically every leaf over None and every leaf over an empty Metadata evaluates to false (and Not over either evaluates to true).

This call is infallible: validation happened in FilterEvaluator::new, so the recursive descent is bounded by MAX_FILTER_DEPTH and cannot stack-overflow.

§Examples
use iqdb_filter::FilterEvaluator;
use iqdb_types::{Filter, Metadata, Value};

let evaluator =
    FilterEvaluator::new(Filter::not(Filter::eq("author", Value::String("ada".into()))))?;

// No metadata → leaf is false → Not flips it to true. This is the
// documented "records without this field" idiom.
assert!(evaluator.evaluate(None));

let meta: Metadata = [(
    "author".to_string(),
    Value::String("ada".into()),
)]
.into_iter()
.collect();
assert!(!evaluator.evaluate(Some(&meta)));
Source

pub fn prefilter<'a, K, I>( &'a self, candidates: I, ) -> impl Iterator<Item = K> + 'a
where I: IntoIterator<Item = (K, Option<&'a Metadata>)>, I::IntoIter: 'a, K: 'a,

Pre-filter a stream of candidates: yield the key of each candidate whose metadata matches, before any distance is computed.

This is the FilterStrategy::PreFilter shape — reduce the candidate set first, then score only the survivors. It is the pattern an exact index uses to skip the distance computation for rows the predicate already rejects.

The adapter is lazy and allocation-free: it borrows each candidate’s metadata and forwards the key untouched. key is whatever a caller uses to identify a row — a storage index, a iqdb_types::VectorId, a tuple.

§Examples
use iqdb_filter::FilterEvaluator;
use iqdb_types::{Filter, Metadata, Value};

let evaluator = FilterEvaluator::new(Filter::gt("year", Value::Int(2000)))?;

let m2026: Metadata = [("year".to_string(), Value::Int(2026))].into_iter().collect();
let m1999: Metadata = [("year".to_string(), Value::Int(1999))].into_iter().collect();
let rows = [(0_usize, Some(&m2026)), (1, Some(&m1999)), (2, None)];

let kept: Vec<usize> = evaluator.prefilter(rows).collect();
assert_eq!(kept, [0]); // only the 2026 row survives
Source

pub fn postfilter<'a, H, I>(&'a self, scored: I) -> impl Iterator<Item = H> + 'a
where I: IntoIterator<Item = (H, Option<&'a Metadata>)>, I::IntoIter: 'a, H: 'a,

Post-filter a stream of already-scored results: yield each hit whose metadata matches, after the distance scan has ranked candidates.

This is the FilterStrategy::PostFilter shape — score everything, then drop the hits the predicate rejects. It shares the per-row test with prefilter; the difference is purely where in the pipeline it runs. Because it is lazy, a caller refilling a top-k result set can chain .take(k) and stop as soon as k survivors are found.

§Examples
use iqdb_filter::FilterEvaluator;
use iqdb_types::{Filter, Metadata, Value};

let evaluator = FilterEvaluator::new(Filter::eq("lang", Value::String("rust".into())))?;

let rust: Metadata = [("lang".to_string(), Value::String("rust".into()))]
    .into_iter()
    .collect();
let go: Metadata = [("lang".to_string(), Value::String("go".into()))]
    .into_iter()
    .collect();

// Hits arrive sorted by distance; keep the first matching one.
let scored = [("hit-a", Some(&go)), ("hit-b", Some(&rust))];
let best: Vec<&str> = evaluator.postfilter(scored).take(1).collect();
assert_eq!(best, ["hit-b"]);
Source

pub fn filter(&self) -> &Filter

Borrows the inner validated filter.

Useful for adapters that want to introspect the predicate (for logging, pushdown, or statistics) without rebuilding it.

§Examples
use iqdb_filter::FilterEvaluator;
use iqdb_types::{Filter, Value};

let evaluator = FilterEvaluator::new(Filter::eq("k", Value::Int(1)))?;
assert!(matches!(evaluator.filter(), Filter::Eq { .. }));

Trait Implementations§

Source§

impl Clone for FilterEvaluator

Source§

fn clone(&self) -> FilterEvaluator

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for FilterEvaluator

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<E> WithErrorCode<E> for E

Source§

fn with_code(self, code: impl Into<String>) -> CodedError<E>

Attach an error code to an error