Search
Cheap-to-Expensive Cascades
Ordering algorithms from cheap and exact to expensive and fuzzy, and stopping as soon as a cheap one is confident enough.
Edit distance and n-gram similarity are more expensive per entry than a plain
string comparison, and unnecessary on a query that would have matched
exactly. A cascade puts the cheap, exact algorithms first and the expensive,
fuzzy ones last, with stop-on-match so the expensive ones only run when the
cheap ones actually miss.
new Search()
.register(NaiveSearch()) // cheap, exact
.register(NormalizedNaive()) // cheap, forgiving of ambiguous characters
.register(Levenshtein()) // expensive, fuzzy
.defineDataset(allModels)
.defineBehavior("stop-on-match"); // Levenshtein only runs if the first two miss
runOrder makes this explicit and enforces it. See
Run Order and Type Safety:
const catalog = new Search()
.register(NaiveSearch())
.register(NormalizedNaive())
.register(KMP())
.register(Levenshtein({ threshold: 0.75 }))
.defineDataset(allModels)
.runOrder(["naive", "normalized-naive", "kmp", "levenshtein"])
.defineBehavior("stop-on-match");
catalog.search("iPad Mini"); // exact — only naive runs
catalog.search("MacBook Pr 16"); // naive, normalized-naive, kmp all miss;
// levenshtein catches the typo
Mixing behaviors within one cascade
The global stop-on-match setting is the common case, but per-algorithm
overrides let you build more specific cascades: for instance, keep the whole
pipeline find-all so every algorithm’s matches are available for ranking,
but still skip the expensive ones once a cheap algorithm is confident:
new Search()
.register(NaiveSearch(), { stopOnMatch: true }) // short-circuits the rest
.register(NgramSearch()) // only reached on a naive miss
.defineDataset(allModels)
.defineBehavior("find-all")
.search("MacBook");
Because behavior on a registration wins outright and stopOnMatch wins
over the global default, you can make exactly one algorithm in an otherwise
find-all pipeline the one that ends things early. See
Match Behavior for the full resolution order.
Cascades and culling
Once a cascade includes more than one find-all algorithm — for example
several fuzzy algorithms stacked to rank suggestions rather than resolve one
entry — cullMatchedRows stops later algorithms from re-scoring a row an
earlier, cheaper one already matched. See
Culling Matched Rows.
Choosing the order
Levenshtein and NgramSearch are worth ordering as O(entries × query length) or worse; NaiveSearch, SubstringSearch, PrefixSearch, KMP and
BoyerMoore are cheap by comparison. JaroWinkler and SoundexSearch sit in
between: cheap per comparison, but still more work than a straight string
comparison. See Bundled Algorithms for what each
one actually does and its default threshold.