krisk / Fuse

Lightweight fuzzy-search, in JavaScript
https://fusejs.io/
Apache License 2.0
18.05k stars 766 forks source link

Should-be perfect match has non-zero score #481

Closed Emroni closed 3 years ago

Emroni commented 4 years ago

Describe the bug

When searching for a keyword in a list of objects, the search works and finds matches. However, the should-be perfect matches return a non-zero score like 2.220446049250313e-16. This should be 0 as it is a perfect match, but due to not being exactly 0, it continues the search.

The Minimal Production below gives this as its result:

0: {item: {…}, refIndex: 0, matches: Array(1), score: 2.220446049250313e-16}
1: {item: {…}, refIndex: 1, matches: Array(1), score: 0.2}
2: {item: {…}, refIndex: 2, matches: Array(1), score: 0.2}
3: {item: {…}, refIndex: 3, matches: Array(1), score: 0.2}
4: {item: {…}, refIndex: 4, matches: Array(1), score: 0.2}

Version

6.4.1

Is this a regression?

Yes, the previous version in which this bug was not present was: 3.6.1 (Im using it in another project, there it works fine)

🔬Minimal Reproduction

  const items = [
    {
      value : 'test1',
    },
    {
      value : 'test2',
    },
    {
      value : 'test3',
    },
    {
      value : 'test4',
    },
    {
      value : 'test5',
    },
  ];

  const fuse = new Fuse(items, {
    ignoreLocation : true,
    includeMatches : true,
    includeScore   : true,
    keys           : ['value'],
  });

  console.log(fuse.search('test1'));
smasala commented 3 years ago

Seeing this too, perfect matches are scoring above 1.4 (v6.4.1)

Only happens when using nested values though

Following works as expected:

const items = ['test1', 'test2', 'test3'];

const fuse = new Fuse(items, {
  includeScore: true,
});

console.log(fuse.search('test2')); // score => 0
smasala commented 3 years ago

The score value is 2.220446049250313e-16 for example or aka 0.0000000000000002 So a work around would be to iterate over the results and force it to 2 decimal spaces (or more as needed)

fuse.search('test2').forEach((it) => it.score = Number(it.score.toFixed(2)));
Emroni commented 3 years ago
fuse.search('test2').forEach((it) => it.score = Number(it.score.toFixed(2)));

I've used that workaround too, although I would recommend using Math.round((score * 100) / 100) as its much slower to use toFixed() and then casting to `Number. Especially if there are thousands of records, it would slow it down significantly.

github-actions[bot] commented 3 years ago

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days

smasala commented 3 years ago

Bump