Skip to content

Search Engine

ABCrimson edited this page Mar 11, 2026 · 2 revisions

Search Engine

Default Scorer

The built-in TypeScript scorer uses fuzzy matching with these scoring factors:

  • Exact match — Highest score when query matches item value exactly
  • Prefix match — High score for matches at the start of words
  • Consecutive characters — Bonus for adjacent character matches
  • Word boundary bonus — Higher scores when matches align with word boundaries (spaces, hyphens, camelCase)
  • Position penalty — Matches earlier in the string score higher

Score aggregation uses Math.sumPrecise (ES2026) for accurate floating-point summation.

Incremental Filtering

When the user types additional characters (e.g., "ap" → "app"), the engine only re-scores items that matched the previous query, using Set.difference for efficient candidate pruning.

// First search: scores all 10K items
engine.search('a', items);     // → 6,000 results

// Incremental: only re-scores the 6,000 matches, not all 10K
engine.search('ap', items);    // → 2,000 results (from 6K candidates)
engine.search('app', items);   // → 500 results (from 2K candidates)

Custom Scorer

Provide a custom scorer function:

import { createSearchEngine, type ScorerFn } from 'modern-cmdk';

const myScorer: ScorerFn = (query, item) => {
  if (item.value.toLowerCase().includes(query.toLowerCase())) {
    return { id: item.id, score: 1, matches: [] };
  }
  return null; // No match
};

using engine = createSearchEngine({ scorer: myScorer });

WASM Search

For datasets > 10K items, use the WASM trigram index:

import { createWasmSearchEngine } from 'modern-cmdk-search-wasm';

await using engine = await createWasmSearchEngine();
engine.index(items);
const results = engine.search('query', items).toArray();

See WASM Search for details.

Performance Benchmarks

Operation 1K Items 10K Items 100K Items
JS scorer (full pipeline) ~0.5ms ~5ms ~50ms
JS scorer (incremental) ~0.2ms ~2ms ~20ms
WASM scorer ~0.1ms ~0.8ms ~3ms

Clone this wiki locally