lucisearch 0.8.0

Embeddable, in-process search engine — the SQLite/DuckDB of Elasticsearch
Documentation
//! PrefixQuery: match documents containing terms with a given prefix.
//!
//! Per-segment rewrite: enumerates matching terms in `scorer_supplier(reader)`,
//! routes through [`ConstantScoreMultiTermSupplier`] (the shared
//! `FilterScorer` + `BufferedUnionScorer` primitive used by all
//! constant-score multi-term queries — prefix, wildcard, fuzzy, regexp).
//!
//! See [[optimization-multiterm-constant-score-rewrite]],
//! [[fix-disjunction-heap-inefficiency]], and
//! [[query-dsl#Term-Level Queries]].

use crate::core::{Result, ScoreMode};

use crate::query::multi_term::ConstantScoreMultiTermSupplier;
use crate::query::{BoundQuery, Query, ScorerSupplier};
use crate::search::searcher::Searcher;
use crate::segment::reader::SegmentReader;

pub struct PrefixQuery {
    pub field: String,
    pub value: String,
}

impl Query for PrefixQuery {
    fn bind(&self, _searcher: &Searcher, _score_mode: ScoreMode) -> Result<Box<dyn BoundQuery>> {
        Ok(Box::new(BoundPrefixQuery {
            field: self.field.clone(),
            prefix: self.value.clone(),
        }))
    }
}

struct BoundPrefixQuery {
    field: String,
    prefix: String,
}

impl BoundQuery for BoundPrefixQuery {
    fn scorer_supplier(&self, reader: &SegmentReader) -> Result<Option<Box<dyn ScorerSupplier>>> {
        let field_id = match reader
            .header()
            .fields
            .iter()
            .find(|f| f.field_name == self.field)
            .map(|f| f.field_id)
        {
            Some(id) => id,
            None => return Ok(None),
        };

        // Enumerate matching prefix terms in this segment.
        let terms = reader.terms_with_prefix(field_id, &self.prefix);
        if terms.is_empty() {
            return Ok(None);
        }

        Ok(Some(Box::new(ConstantScoreMultiTermSupplier::new(
            reader, field_id, terms,
        ))))
    }
}