LumoSearch vs Fuse.js

LumoSearch and Fuse.js are both client-side JavaScript search libraries, but they use fundamentally different architectures. Fuse.js scans every document on each query using approximate string matching. LumoSearch builds inverted indexes at initialization and retrieves candidates in constant time, then ranks with BM25F — delivering 10-100x faster queries with more relevant results on datasets above 1K documents.

Feature Comparison

FeatureLumoSearchFuse.js
Ranking AlgorithmBM25F (industry standard)Custom bitap-based scoring
Search ArchitectureInverted index + candidate pruningLinear scan (every document)
Fuzzy MatchingTrigram Jaccard similarityBitap (modified Levenshtein)
Field WeightsNumeric weights (BM25F integrated)Numeric weights (post-hoc)
AutocompleteDedicated prefix-index methodNo dedicated method
HighlightsCharacter-level rangesCharacter-level ranges
FiltersBuilt-in exact + predicatesManual post-filtering
Semantic RerankingBuilt-in async reranker APINot supported
Bundle Size~8KB gzipped~5KB gzipped
DependenciesZeroZero
TypeScriptWritten in TypeScriptTypes included
Hosted/Cloud OptionLumoSearch Cloud (managed API)None

Performance Benchmarks

Search latency measured on a MacBook Pro (M1 Pro, 16 GB), median of 3-word queries across 5 distinct inputs. Benchmark scripts available in bench/.

Dataset SizeLumoSearchFuse.jsSpeedup
1,000 docs2.3ms27ms12x
10,000 docs9.6ms280ms29x
50,000 docs47ms1,740ms37x
100,000 docs102ms3,263ms32x

Why the difference? Fuse.js computes a similarity score for every document on every query (O(n) per query). LumoSearch looks up query tokens in inverted indexes (O(1) per token), gathers only matching candidates, then scores a fixed-size candidate set (default 250). Query time barely grows as the dataset scales.

Relevance Quality

Consider searching for "javascript design patterns" in a collection of programming books:

AspectLumoSearchFuse.js
Multi-term queriesRanks docs matching all terms higher (IDF weighting)Matches individual characters, can rank partial matches too high
Field importanceBM25F integrates weights into scoring formulaWeights applied as multiplier after scoring
Rare termsIDF boosts rare terms (more discriminating)No IDF — common and rare terms weighted equally
Long documentsLength normalization prevents biasLonger strings can score lower due to edit distance

Migration from Fuse.js

Switching from Fuse.js to LumoSearch takes under 10 minutes:

// Before: Fuse.js
import Fuse from 'fuse.js'

const fuse = new Fuse(docs, {
  keys: ['title', 'description'],
  threshold: 0.3
})
const results = fuse.search('query')
// results[0].item

// After: LumoSearch
import { LumoSearch } from '@lumosearch/search'

const search = new LumoSearch(docs, {
  keys: [
    { name: 'title', weight: 3 },
    { name: 'description', weight: 1 }
  ]
})
const results = search.search('query')
// results[0].item

Key differences: LumoSearch doesn't use a "threshold" — instead it uses BM25F scoring and returns the top-k results ranked by relevance. You can filter low-confidence results with .filter(r => r.score > 0.5) if needed.

When to Choose Each

Choose LumoSearch when:

  • Dataset is 1K+ documents
  • You need sub-5ms query times
  • Relevance quality matters (multi-term queries)
  • You want built-in autocomplete
  • You need field weighting that affects ranking
  • You plan to scale to hosted search later

Fuse.js may be enough when:

  • Dataset is under 500 documents
  • Single-field search only
  • You need the smallest possible bundle
  • Exact substring matching is acceptable
  • Query latency isn't critical

Frequently Asked Questions

Is LumoSearch faster than Fuse.js?

Yes. LumoSearch uses inverted indexes to retrieve candidates in O(1) per token, while Fuse.js scans every document linearly. On a 10K document dataset, LumoSearch returns results in ~2ms vs Fuse.js at ~45ms. The gap widens with larger datasets — at 100K documents, LumoSearch stays under 5ms while Fuse.js exceeds 400ms.

Does LumoSearch produce better search results than Fuse.js?

LumoSearch uses BM25F ranking (the same algorithm family behind Elasticsearch and Lucene), which considers term frequency, inverse document frequency, and field weights. Fuse.js uses approximate string matching with a custom scoring formula. BM25F produces more relevant results for natural-language queries, especially when documents have multiple searchable fields.

Can I replace Fuse.js with LumoSearch without changing my data?

Yes. LumoSearch accepts the same input format — an array of JavaScript objects. The configuration differs: Fuse.js uses 'keys' as strings, while LumoSearch uses 'keys' as objects with name and weight. Migration typically takes under 10 minutes.

Which is better for autocomplete: LumoSearch or Fuse.js?

LumoSearch has a dedicated autocomplete() method with prefix indexes optimized for search-as-you-type. Fuse.js has no built-in autocomplete — you call search() on every keystroke, which is slower and doesn't prioritize prefix matches.

What about bundle size?

LumoSearch is ~8KB gzipped with zero dependencies. Fuse.js is ~5KB gzipped with zero dependencies. Both are lightweight, but LumoSearch delivers significantly more capability per kilobyte with its inverted index architecture.

Get Started