Skip to content

Commit e164b61

Browse files
committed
fix(scoring): normalise key weights in object and keyless-logical search
_searchObjectList and the keyless branch of _searchLogical scored off FuseIndex.keys, which holds the raw user-supplied weights. Only KeyStore normalises weights to sum to 1, so large weights pushed Math.pow(Number.EPSILON, weight * norm) to underflow to 0, and absolute scores diverged between plain-string, keyed-logical, and keyless-logical queries over identical data. Add _normalizedKeys(), which resolves each index key to its normalised KeyStore counterpart by id, kept aligned to positional item[keyIndex] so a pre-built index whose key order differs from options.keys still scores correctly. Use it in both scoring paths, matching how the keyed logical path already reads from the KeyStore. Result ordering is unchanged (normalised score = raw^(1/T) for a global constant T); only absolute scores for weights that do not sum to 1 shift, and underflow is eliminated. Closes #833
1 parent dbb98b6 commit e164b61

14 files changed

Lines changed: 185 additions & 15 deletions

dist/fuse.basic.cjs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,9 @@ var Fuse = class {
993993
getIndex() {
994994
return this._myIndex;
995995
}
996+
_normalizedKeys() {
997+
return this._myIndex.keys.map((key) => this._keyStore.get(key.id) || key);
998+
}
996999
search(query, options) {
9971000
const { limit = -1 } = options || {};
9981001
const { includeMatches, includeScore, shouldSort, sortFn, ignoreFieldNorm } = this.options;
@@ -1070,7 +1073,8 @@ var Fuse = class {
10701073
_searchObjectList(query, { heap, ignoreFieldNorm } = {}) {
10711074
const searcher = this._getSearcher(query);
10721075
const requireAllTokens = this.options.useTokenSearch && this.options.tokenMatch === "all";
1073-
const { keys, records } = this._myIndex;
1076+
const { records } = this._myIndex;
1077+
const keys = this._normalizedKeys();
10741078
const results = heap ? null : [];
10751079
records.forEach(({ $: item, i: idx }) => {
10761080
if (!isDefined(item)) return;

dist/fuse.basic.min.cjs

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

dist/fuse.basic.min.mjs

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

dist/fuse.basic.mjs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,9 @@ var Fuse = class {
992992
getIndex() {
993993
return this._myIndex;
994994
}
995+
_normalizedKeys() {
996+
return this._myIndex.keys.map((key) => this._keyStore.get(key.id) || key);
997+
}
995998
search(query, options) {
996999
const { limit = -1 } = options || {};
9971000
const { includeMatches, includeScore, shouldSort, sortFn, ignoreFieldNorm } = this.options;
@@ -1069,7 +1072,8 @@ var Fuse = class {
10691072
_searchObjectList(query, { heap, ignoreFieldNorm } = {}) {
10701073
const searcher = this._getSearcher(query);
10711074
const requireAllTokens = this.options.useTokenSearch && this.options.tokenMatch === "all";
1072-
const { keys, records } = this._myIndex;
1075+
const { records } = this._myIndex;
1076+
const keys = this._normalizedKeys();
10731077
const results = heap ? null : [];
10741078
records.forEach(({ $: item, i: idx }) => {
10751079
if (!isDefined(item)) return;

dist/fuse.cjs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1374,6 +1374,9 @@ var Fuse = class {
13741374
getIndex() {
13751375
return this._myIndex;
13761376
}
1377+
_normalizedKeys() {
1378+
return this._myIndex.keys.map((key) => this._keyStore.get(key.id) || key);
1379+
}
13771380
search(query, options) {
13781381
const { limit = -1 } = options || {};
13791382
const { includeMatches, includeScore, shouldSort, sortFn, ignoreFieldNorm } = this.options;
@@ -1447,13 +1450,14 @@ var Fuse = class {
14471450
}
14481451
_searchLogical(query) {
14491452
const expression = parse(query, this.options);
1453+
const keys = this._normalizedKeys();
14501454
const evaluate = (node, item, idx) => {
14511455
if (!("children" in node)) {
14521456
const { keyId, searcher } = node;
14531457
let matches;
14541458
if (keyId === null) {
14551459
matches = [];
1456-
this._myIndex.keys.forEach((key, keyIndex) => {
1460+
keys.forEach((key, keyIndex) => {
14571461
matches.push(...this._findMatches({
14581462
key,
14591463
value: item[keyIndex],
@@ -1508,7 +1512,8 @@ var Fuse = class {
15081512
_searchObjectList(query, { heap, ignoreFieldNorm } = {}) {
15091513
const searcher = this._getSearcher(query);
15101514
const requireAllTokens = this.options.useTokenSearch && this.options.tokenMatch === "all";
1511-
const { keys, records } = this._myIndex;
1515+
const { records } = this._myIndex;
1516+
const keys = this._normalizedKeys();
15121517
const results = heap ? null : [];
15131518
records.forEach(({ $: item, i: idx }) => {
15141519
if (!isDefined(item)) return;

dist/fuse.d.cts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ declare class Fuse<T> {
335335
removeAt(idx: number): T;
336336
_invalidateSearcherCache(): void;
337337
getIndex(): FuseIndex<T>;
338+
_normalizedKeys(): KeyObject[];
338339
search(query: string | Expression, options?: FuseSearchOptions): FuseResult<T>[];
339340
_searchStringList(query: string, {
340341
heap,

dist/fuse.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ declare class Fuse<T> {
335335
removeAt(idx: number): T;
336336
_invalidateSearcherCache(): void;
337337
getIndex(): FuseIndex<T>;
338+
_normalizedKeys(): KeyObject[];
338339
search(query: string | Expression, options?: FuseSearchOptions): FuseResult<T>[];
339340
_searchStringList(query: string, {
340341
heap,

dist/fuse.min.cjs

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

dist/fuse.min.mjs

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

dist/fuse.mjs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,6 +1373,9 @@ var Fuse = class {
13731373
getIndex() {
13741374
return this._myIndex;
13751375
}
1376+
_normalizedKeys() {
1377+
return this._myIndex.keys.map((key) => this._keyStore.get(key.id) || key);
1378+
}
13761379
search(query, options) {
13771380
const { limit = -1 } = options || {};
13781381
const { includeMatches, includeScore, shouldSort, sortFn, ignoreFieldNorm } = this.options;
@@ -1446,13 +1449,14 @@ var Fuse = class {
14461449
}
14471450
_searchLogical(query) {
14481451
const expression = parse(query, this.options);
1452+
const keys = this._normalizedKeys();
14491453
const evaluate = (node, item, idx) => {
14501454
if (!("children" in node)) {
14511455
const { keyId, searcher } = node;
14521456
let matches;
14531457
if (keyId === null) {
14541458
matches = [];
1455-
this._myIndex.keys.forEach((key, keyIndex) => {
1459+
keys.forEach((key, keyIndex) => {
14561460
matches.push(...this._findMatches({
14571461
key,
14581462
value: item[keyIndex],
@@ -1507,7 +1511,8 @@ var Fuse = class {
15071511
_searchObjectList(query, { heap, ignoreFieldNorm } = {}) {
15081512
const searcher = this._getSearcher(query);
15091513
const requireAllTokens = this.options.useTokenSearch && this.options.tokenMatch === "all";
1510-
const { keys, records } = this._myIndex;
1514+
const { records } = this._myIndex;
1515+
const keys = this._normalizedKeys();
15111516
const results = heap ? null : [];
15121517
records.forEach(({ $: item, i: idx }) => {
15131518
if (!isDefined(item)) return;

0 commit comments

Comments
 (0)