krisk / Fuse

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

weighted search : weight >=1 causes error, but the docs say it should be allowed #416

Closed abenhamdine closed 4 years ago

abenhamdine commented 4 years ago

Describe the bug

1°) Docs here say we can set weights >1, they will be normalized : https://fusejs.io/examples.html#weighted-search

But if I search with following options :

isCaseSensitive: false,
    shouldSort: true,
    threshold: 0.4,
    location: 0,
    distance: 100,
    minMatchCharLength: 1,
    useExtendedSearch: true,
    keys : [{ name: 'sal_desc', weight: 2 }, 'var.var_lib', 'vva_periodedebut', 'vva_valeur']

I get that error :

Uncaught Error: "weight" property in key must be in the range of (0, 1)
    at new KeyStore (fuse.esm.js:1043)
    at Fuse._processKeys (fuse.esm.js:1143)
    at new Fuse (fuse.esm.js:1123)

due to that check https://github.com/krisk/Fuse/blob/eff9ad9cf783f5506aacc09496dfc06ea6e3c5bd/src/tools/KeyStore.js#L39

2°) Also the docs say keys wich are string only are normalized with a weight of 1, but according the same code, the type of the keys is determined based on the first key of the array, see https://github.com/krisk/Fuse/blob/eff9ad9cf783f5506aacc09496dfc06ea6e3c5bd/src/tools/KeyStore.js#L12

therefore I fail to see how it could possible to mix string/object keys with weights...

Version

5.2.3

Is this a regression?

I don't know

🔬Minimal Reproduction

search anything with these options :

isCaseSensitive: false,
    shouldSort: true,
    threshold: 0.4,
    location: 0,
    distance: 100,
    minMatchCharLength: 1,
    useExtendedSearch: true,
    keys : [{ name: 'sal_desc', weight: 2 }, 'var.var_lib', 'vva_periodedebut', 'vva_valeur']

Workaround

Normalize keys as following before passing to fuse search :

function normalizeKeys(keys: (Fuse.FuseOptionKeyObject| string)[]): Fuse.FuseOptionKeyObject[] {

    const keysObjectified = keys.map(key => {
        if (typeof key === 'string') {
            return {
                name: key,
                weight: 1
            }
        } else {
            return {
                weight: 1,
                ...key
            }
        }
    })

    const maxWeight = keysObjectified.reduce((max, key) => {
        return key.weight > max ? key.weight : max
    }, 0)

    if (maxWeight < 1) {
        return keysObjectified
    } else {
        const ratio = maxWeight / 0.99
        return keysObjectified.map(key => {
            return {
                ...key,
                weight: key.weight / ratio
            }
        })
    }

} 
krisk commented 4 years ago

Oops. Docs were prematurely updated for v6.x, which isn't out yet in latest but will be soon.

In v6.x you can set weight to any value >= 0, and you can mix and match. Code is in the next branch.

Pull in the beta version: 6.0.0-beta.0.

gabbloquet commented 4 years ago

Oh we have been spoil :p Thanks for the work :)

abenhamdine commented 4 years ago

Oops. Docs were prematurely updated for v6.x, which isn't out yet in latest but will be soon.

In v6.x you can set weight to any value >= 0, and you can mix and match. Code is in the next branch.

Pull in the beta version: 6.0.0-beta.0.

Thx for your answer and your useful module. It's indeed often difficult to synchronize docs and code ;)

krisk commented 4 years ago

Now in the latest version. Will close this.