Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions dist/fuse-worker.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const FUSE_WORKER_UNSUPPORTED_FN_OPTION = (option) => `FuseWorker does not support function-valued option '${option}': functions cannot be transferred to Web Workers via postMessage. Remove this option or fall back to Fuse.`;
const FUSE_WORKER_TOKEN_SEARCH_UNSUPPORTED = "FuseWorker does not support useTokenSearch: token search depends on corpus-level statistics (df, fieldCount) that are computed per shard, so per-shard scores would diverge from single-thread Fuse. Use Fuse on the main thread for token search.";

//#endregion
//#region src/helpers/typeGuards.ts
function isDefined(value) {
return value !== void 0 && value !== null;
}

//#endregion
//#region src/workers/FuseWorker.ts
const DEFAULT_MAX_WORKERS = 8;
Expand Down Expand Up @@ -159,6 +165,7 @@ var FuseWorker = class FuseWorker {
return merged;
}
async add(doc) {
if (!isDefined(doc)) return;
await this._ensureInit();
const shards = this._shards;
const shard = shards[this._addCursor % shards.length];
Expand Down
7 changes: 7 additions & 0 deletions dist/fuse-worker.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@
const FUSE_WORKER_UNSUPPORTED_FN_OPTION = (option) => `FuseWorker does not support function-valued option '${option}': functions cannot be transferred to Web Workers via postMessage. Remove this option or fall back to Fuse.`;
const FUSE_WORKER_TOKEN_SEARCH_UNSUPPORTED = "FuseWorker does not support useTokenSearch: token search depends on corpus-level statistics (df, fieldCount) that are computed per shard, so per-shard scores would diverge from single-thread Fuse. Use Fuse on the main thread for token search.";

//#endregion
//#region src/helpers/typeGuards.ts
function isDefined(value) {
return value !== void 0 && value !== null;
}

//#endregion
//#region src/workers/FuseWorker.ts
const DEFAULT_MAX_WORKERS = 8;
Expand Down Expand Up @@ -156,6 +162,7 @@ var FuseWorker = class FuseWorker {
return merged;
}
async add(doc) {
if (!isDefined(doc)) return;
await this._ensureInit();
const shards = this._shards;
const shard = shards[this._addCursor % shards.length];
Expand Down
6 changes: 6 additions & 0 deletions src/workers/FuseWorker.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/// <reference lib="dom" />

import * as ErrorMsg from '../core/errorMessages'
import { isDefined } from '../helpers/typeGuards'
import type {
IFuseOptions,
FuseOptionKey,
Expand Down Expand Up @@ -278,6 +279,11 @@ export default class FuseWorker<T> {
}

async add(doc: T): Promise<void> {
// Match Fuse.add's no-op before advancing either global or shard indices.
if (!isDefined(doc)) {
return
}

await this._ensureInit()

const shards = this._shards!
Expand Down
45 changes: 45 additions & 0 deletions test/fuse-worker.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,51 @@ describe('FuseWorker sharding', () => {
fw.terminate()
})

test.each([null, undefined])(
'add(%s) before initialization does not spawn workers',
async (doc) => {
MockWorker.instances = []
const fw = new FuseWorker(['alpha'], {}, { numWorkers: 2 })
await fw.add(doc)
expect(MockWorker.instances).toHaveLength(0)
expect(await fw.search('')).toEqual([{ item: 'alpha', refIndex: 0 }])
fw.terminate()
}
)

test.each([1, 3])(
'nullish additions preserve single-thread results with %s workers',
async (numWorkers) => {
const docs = ['alpha', 'beta', 'gamma']
const options = { includeMatches: true, includeScore: true, threshold: 0 }
const fw = new FuseWorker(docs, options, { numWorkers })
const fuse = new Fuse(docs.slice(), options)
await fw.search('alpha')
for (const doc of [null, undefined, 'delta', null, 'epsilon']) {
await fw.add(doc)
fuse.add(doc)
}
for (const query of ['', 'delta', 'epsilon', 'alpha']) {
expect(await fw.search(query)).toEqual(fuse.search(query))
}
fw.terminate()
}
)

test('falsy but defined additions are not skipped', async () => {
const docs = [{ name: 'alpha' }]
const options = { keys: ['name'], threshold: 0 }
const fw = new FuseWorker(docs, options, { numWorkers: 3 })
const fuse = new Fuse(docs.slice(), options)
for (const doc of [null, '', 0, false, { name: 'beta' }]) {
await fw.add(doc)
fuse.add(doc)
}
expect(await fw.search('')).toEqual(fuse.search(''))
expect(await fw.search('beta')).toEqual(fuse.search('beta'))
fw.terminate()
})

test('interleaved search/add/setCollection keeps refIndex consistent', async () => {
MockWorker.instances = []
const initial = Books.slice(0, 4)
Expand Down