Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Quick Start

Build an engine once, then query it as many times as you like. The engine is immutable and cheap to share (&FuzzyAhoCorasick) across threads.

use fuzzy_aho_corasick::{FuzzyAhoCorasickBuilder, FuzzyLimits, SearchOptions};

fn main() {
    // Allow up to 1 edit per match, case-insensitive.
    let engine = FuzzyAhoCorasickBuilder::new()
        .fuzzy(FuzzyLimits::new().edits(1))
        .case_insensitive(true)
        .build(["hello", "world"]);

    // "helllo wolrd" has two typos: an extra 'l' (insertion) and swapped 'lr' (transposition).
    let opts = SearchOptions::new().threshold(0.8).sorted().non_overlapping();
    for m in engine.search("helllo wolrd", &opts).unwrap().iter() {
        println!("matched '{}' as '{}' (score {:.2})", m.pattern, m.text, m.similarity);
    }
    // matched 'hello' as 'helllo' (score 0.90)
    // matched 'world' as 'wolrd' (score 0.90)
}

Three things are happening here:

  1. fuzzy(FuzzyLimits::new().edits(1)) — without this the engine only matches exactly. edits(1) lets each match differ from its pattern by at most one edit operation.
  2. .threshold(0.8) — the similarity threshold. A candidate is only returned if its score is at least this high. See Scoring & Thresholds.
  3. .sorted().non_overlapping() — asks for a ranked set of matches whose spans don’t overlap. SearchOptions bundles the order and overlap strategy for the single search entry point.

Inspecting a match

Each FuzzyMatch tells you what was found and how:

use fuzzy_aho_corasick::{FuzzyAhoCorasickBuilder, FuzzyLimits, SearchOptions};
let engine = FuzzyAhoCorasickBuilder::new().fuzzy(FuzzyLimits::new().edits(1)).build(["needle"]);
for m in engine.search("find the neeedle", &SearchOptions::new().threshold(0.8)).unwrap().iter() {
    println!(
        "pattern #{} ({}) matched bytes {}..{} = {:?}",
        m.pattern_index, m.pattern, m.start, m.end, m.text,
    );
    println!(
        "  score {:.2}, edits {} (ins {}, del {}, sub {}, swap {})",
        m.similarity, m.edits, m.insertions, m.deletions, m.substitutions, m.swaps,
    );
}

start/end are byte offsets into the haystack, and text is the matched slice.

Next steps