Repository navigation
Releases: krisk/Fuse
Release list
v7.6.0-beta.0
Beta preview of the MongoDB-style object query syntax. Install with npm install fuse.js@beta.
Features
- search: add MongoDB-style object query syntax (#08459a9)
Express match types structurally instead of encoding them in magic characters:
const fuse = new Fuse(books, { keys: ['title', 'author'] })
fuse.search({ title: { $startsWith: 'old' }, author: { $eq: 'Kay' } })Five operators ($fuzzy, $eq, $contains, $startsWith, $endsWith), plus $not for negation and field-local $and / $or.
| Object | String equivalent |
|---|---|
$fuzzy |
term |
$eq |
=term |
$contains |
'term |
$startsWith |
^term |
$endsWith |
term$ |
$not: { $contains } |
!term |
$not: { $startsWith } |
!^term |
$not: { $endsWith } |
!term$ |
Highlights:
- No
useExtendedSearchflag. Object operators are unambiguous, so the flag that exists to parse magic characters in strings is not needed. - Exact parity with string syntax. An object query compiles to the same matchers as its string equivalent, so
scoreandmatchesindices are identical. - Strict validation. Unknown operators, empty or non-string values, and illegal nesting throw immediately instead of silently degrading to a fuzzy search. The published types enforce the same grammar at compile time.
- Composable. Field-local
$and/$orfor a field, and object leaves work anywhere a string leaf does inside logical$and/$or. - Works with
FuseWorker, and basic builds stay lean (the compiler is behind the extended-search build flag).
$not wraps exactly one of $contains / $startsWith / $endsWith. $fuzzy and $eq have no inverse matcher, so negating them throws; $ne is reserved for a future whole-string inequality.
Docs: https://fusejs.io/extended-search#object-syntax
This is a prerelease. Feedback welcome before 7.6.0 goes stable.
v7.5.0
β οΈ Behavior changes
Every change in this release is a bug fix, but each one corrects a scoring or ranking bug. Scores and result ordering will shift for some queries. That is why this ships as a minor rather than a patch: the public API is unchanged and upgrading is a drop-in, but the results you get back can differ, and that should not arrive silently in a patch bump.
If you assert on exact score values or on a specific result order, expect those assertions to need updating. Re-baseline them against 7.5.0 rather than pinning to 7.4.x, since the 7.4.x behavior was wrong in the cases below.
- Field-length normalisation now counts words correctly. Tabs and newlines were not treated as word separators, so a multi-line or tab-delimited field was scored as though it were one long word, making it look far shorter than it is. Fields containing
\t,\n, or\rnow score differently (#830). - Key weights are now normalised in object and keyless-logical search. Weights that did not sum to
1were applied unnormalised, skewing the relative influence of each key. If yourkeysweights do not already sum to1, your relative ranking changes (#833). limitnow returns the correct top-N when scores tie. A tie at the cutoff boundary could evict a result that should have been kept, solimitcould return the wrong items, not merely the right items in a different order (#835).- Bitap respects
minMatchCharLengthin the exact-match shortcut. Matches shorter thanminMatchCharLengthwere still reported via the exact-match fast path, so thematchesarray could contain entries it was configured to exclude (#831).
Bug Fixes
- bitap: respect minMatchCharLength in exact-match shortcut (dbb98b6), closes #831
- fieldNorm: count tabs and newlines as word separators (6fe85b0), closes #830
- fieldNorm: count word-starts instead of space transitions (2946f97)
- scoring: normalise key weights in object and keyless-logical search (e164b61), closes #833
- search: keep the correct top-N under limit when scores tie (437f8f3), closes #835, thanks @spokodev for the report and the fix
Full Changelog: v7.4.2...v7.5.0
v7.4.2
Bug Fixes
- types: ship CommonJS type declarations (
.d.cts) so consumers onmoduleResolution: node16/nodenextno longer hitTS1479("masquerading as ESM") when importing the package from a CommonJS project. The lib entries now resolve to a runtime-accurateexport =declaration and the worker to its named declaration, via therequireexports condition. (#780)
Full Changelog: v7.4.1...v7.4.2
v7.4.1
v7.4.0
First stable on the 7.4 line. Aggregates everything from the 7.4.0-beta.1 β 7.4.0-beta.8 cycle. See CHANGELOG.md for the per-beta breakdown.
Features
- FuseWorker β parallel search via Web Workers (9ba192c). New
FuseWorkerclass shards the collection across workers, runs searches in parallel, and preserves the same result ordering asFuse. See the Web Workers guide. - token-search:
tokenMatch: 'all' | 'any'option for AND/OR semantics (#827, 9f979d0). Default'any'preserves existing behavior;'all'requires every query word to match somewhere in a record (term-centric AND), useful for filter-style queries. - token-search: customizable tokenizer with unicode-aware default (#821, 8e55cae). New
tokenize?: RegExp | (text: string) => string[]option; the default regex now segments CJK, Cyrillic, Greek, Arabic, etc. out of the box.
Bug Fixes
- matches: report array-path keys as dotted strings (acd54e8). Previously
match.keyleaked a rawstring[]for keys declared as path arrays (e.g.keys: [['author', 'firstName']]), contradicting the documentedFuseResultMatch.key: stringtype. Now emits the canonical dotted string ('author.firstName'). Behavior change: consumers relying on the array shape need to update. - bitap: restrict highlight indices to matched window (#792, 622f105). Closes #505, #611, #691, #793.
- index: correct doc-index alignment for
Fuse<string>with blank docs (0b8e3ca). - core: invalidate searcher cache on collection mutation (fcf4228).
- token-search: renumber inverted index after doc removal (ea9356d).
- workers: preserve global
refIndexacross shards (e4217f9), match Fuse ordering and reject non-cloneable options (d571390), rejectuseTokenSearchinFuseWorker(8c6183d). - match: explicitly reject
useTokenSearchinFuse.match(3959d91). - correct
fieldCountaccounting and add reverse lookup in inverted index (54e702c). - guard against empty-string crash in
fieldNorm(e550ab1); skip consecutive spaces infieldNormword counting (5929af6). - types: resolve typecheck errors and align
KeyStoretypes (dbc115d). - docs: reflect data edits in playground demo (#825, 6eff909).
Performance
- reuse bit arrays in Bitap search instead of allocating per error level (ec9b446)
- replace
forEachwithforloops in search hot paths (1945f49) - fast-path
Math.powwhen exponent is 1 incomputeScore(c82de57) - pre-allocate records array and use
forloops inFuseIndex.create(5800036) - replace regex with loop for word counting in
fieldNorm(5517a9b) - use
filterinstead of reverse-splice inremoveAll/remove(c74823b, 8372b0a) - token-search: drop unused postings from inverted index (5ea216f)
Internal
- refactor: replace extended-search matcher classes with plain functions (f8d61c9)
- bench: consolidate
benchmark/intobench/and add npm scripts (3b3a4c6) - ci: update Node matrix to 20, 22, 24 (b906906)
- chore(deps): pin secure versions via npm overrides (3a1b118)
Docs
- Migrated the site from VuePress to VitePress (663ee53)
- New articles: "Using Fuse.js with React" (512bc7d), "Fuse.js vs Semantic Search" (5f93b4d)
- New interactive demos in the fuzzy search article (edit distance, Bitap, threshold, search window)
- Web Workers documentation and demo
- Fuse Cloud landing page with waitlist
Behavior changes worth noting
FuseResultMatch.keynow returns a dotted string for array-path keys (was a rawstring[]in violation of the documented type). If you were doingmatch.key.join('.')to recover the dotted form, drop the.join.
v7.4.0-beta.8
Bug Fixes
- matches: array-path keys (e.g.
keys: [['author', 'firstName']]) are now reported as a dotted string ('author.firstName') inresult.matches[].key, matching the documentedFuseResultMatch.key: stringtype. Previously the rawstring[]leaked through. (acd54e8)
v7.4.0-beta.7
Features
- token-search: add
tokenMatch: 'all' | 'any'for AND/OR (#827).'all'makes token search require every query word to match somewhere in a record (across all fields and array elements) β the filtering behavior, where adding a word narrows results. Default'any'is unchanged OR behavior.
Bug Fixes
- docs: demo playground now reflects data edits on delete (#825)
Full changelog: v7.4.0-beta.6...v7.4.0-beta.7
v7.4.0-beta.6
Bug Fixes
includeMatches: true no longer reports stray highlight ranges far outside the actual match window. Previously, the bitap fuzzy scan marked every text character whose codepoint appeared in the search pattern's alphabet anywhere the scan visited, producing highlights like m, ic, l, o, ic scattered across olympic medical office when searching for olympics.
After the fix, highlight indices are restricted to the bitap-matched window ([bestLocation, bestLocation + patternLen - 1 + bestErrors]) plus exact-match windows from the index-of fast path.
Behavior change. Callers who built UIs around the previous noisier output may see fewer/narrower highlights. A residual tail of up to bestErrors characters can still appear immediately after the match window to preserve correctness in insertion-heavy fuzzy cases (e.g. abc vs abxc still highlights both ab and the matched c). To suppress short residual runs, set minMatchCharLength: N; runs shorter than N are filtered out.
Perf bonus. Removing the inner-loop mask write nets ~15% faster fuzzy search on +matches queries; non-+matches paths also pick up 2-5%.
v7.4.0-beta.5
Bug Fixes
-
index: correct doc-index alignment for
Fuse<string>collections containing blank strings (0b8e3ca)Pre-fix, blank docs in a string collection caused
records.length < _docs.length, and the mutation paths (add,removeAt,remove,removeAll) conflated records-array offset with source-array doc-index. Search results could be silently misaligned with the source array β e.g.removeAt(1)on["", "apple", "banana"]could return"banana"as a match for a search for"apple".Affects any
Fuse<string>collection where one or more docs are""or whitespace-only. Object collections are unaffected.Additionally:
Fuse.removeAt(idx)now validatesidx(integer, in range) atomically before any mutation.Fuse.add("")no longer re-ingests the previous record into the token-search inverted index, and no longer crashes on all-blank collections.FuseIndex.add,removeAt,removeAllgain cheap input guards for direct callers.
API note
FuseIndex.add gained a required second parameter:
// Before
FuseIndex<T>.add(doc: T): void
// After
FuseIndex<T>.add(doc: T, docIndex: number): IndexRecord | nullThe public Fuse.add(doc) signature is unchanged. Only code that calls FuseIndex.add directly on an instance from Fuse.createIndex() needs to update; calling without docIndex now throws INVALID_DOC_INDEX.
Persisted-index migration
If you serialized a Fuse<string> index via getIndex().toJSON() from a pre-fix instance whose collection contained blanks and you ran any add/remove/removeAt/removeAll calls before serialization, those records may misrepresent the source array. Rebuild via new Fuse(docs, options) (drop the persisted-index argument) once after upgrading.
Indexes from clean string collections (no blanks, or blanks but no mutations) and all object-collection indexes are unaffected and load normally.
v7.4.0-beta.4
Performance
- token-search: drop unused postings from inverted index (5ea216f)
The token-search query path only consumes df and fieldCount for IDF weighting. Postings (tf, keyIdx, subIdx) and the docTerms reverse map were built at index time but never read during search. Replacing them with a stats-only shape (df, fieldCount, docFieldCount, docTermFieldHits) preserves identical search behavior while reducing the inverted-index footprint.
Measured on a Zipfian synthetic corpus (3 keys: title/body/tags):
| Corpus | Heap savings | Build speedup |
|---|---|---|
| 1,000 docs | 44.7% | 1.31Γ |
| 10,000 docs | 43.2% | 1.26Γ |
| 50,000 docs | 42.4% | 1.21Γ |
Bundle: fuse.min.cjs gzipped 8.72 KB β 8.44 KB.
Internal InvertedIndexData shape changed; the Posting interface is removed. Both surfaces are private (_invertedIndex is underscored, not part of the documented public API). All token-search behavioral tests pass unchanged.
Full changelog: v7.4.0-beta.3...v7.4.0-beta.4