fuse.js
Version:
Lightweight fuzzy-search
9 lines • 14.5 kB
JavaScript
/**
* Fuse.js v6.2.1 - Lightweight fuzzy-search (http://fusejs.io)
*
* Copyright (c) 2020 Kiro Risk (http://kiro.me)
* All Rights Reserved. Apache Software License 2.0
*
* http://www.apache.org/licenses/LICENSE-2.0
*/
function t(t){return Array.isArray?Array.isArray(t):"[object Array]"===Object.prototype.toString.call(t)}function e(t){return"string"==typeof t}function s(t){return"number"==typeof t}function n(t){return null!=t}function i(t){return!t.trim().length}const r=Object.prototype.hasOwnProperty;class c{constructor(t){this._keys={},this._keyNames=[];let s=0;t.forEach(t=>{let n,i=1;if(e(t))n=t;else{if(!r.call(t,"name"))throw new Error(`Missing ${"name"} property in key`);if(n=t.name,r.call(t,"weight")&&(i=t.weight,i<=0))throw new Error((t=>`Property 'weight' in key '${t}' must be a positive integer`)(n))}this._keyNames.push(n),this._keys[n]={weight:i},s+=i}),this._keyNames.forEach(t=>{this._keys[t].weight/=s})}get(t,e){return this._keys[t]&&this._keys[t][e]}keys(){return this._keyNames}toJSON(){return JSON.stringify(this._keys)}}var o={isCaseSensitive:!1,includeScore:!1,keys:[],shouldSort:!0,sortFn:(t,e)=>t.score===e.score?t.idx<e.idx?-1:1:t.score<e.score?-1:1,includeMatches:!1,findAllMatches:!1,minMatchCharLength:1,location:0,threshold:.6,distance:100,...{useExtendedSearch:!1,getFn:function(i,r){let c=[],o=!1;const h=(i,r)=>{if(r){const a=r.indexOf(".");let l=r,u=null;-1!==a&&(l=r.slice(0,a),u=r.slice(a+1));const d=i[l];if(!n(d))return;if(u||!e(d)&&!s(d))if(t(d)){o=!0;for(let t=0,e=d.length;t<e;t+=1)h(d[t],u)}else u&&h(d,u);else c.push(function(t){return null==t?"":function(t){if("string"==typeof t)return t;let e=t+"";return"0"==e&&1/t==-1/0?"-0":e}(t)}(d))}else c.push(i)};return h(i,r),o?c:c[0]},ignoreLocation:!1,ignoreFieldNorm:!1}};const h=/[^ ]+/g;class a{constructor({getFn:t=o.getFn}={}){this.norm=function(t=3){const e=new Map;return{get(s){const n=s.match(h).length;if(e.has(n))return e.get(n);const i=parseFloat((1/Math.sqrt(n)).toFixed(t));return e.set(n,i),i},clear(){e.clear()}}}(3),this.getFn=t,this.isCreated=!1,this.setIndexRecords()}setSources(t=[]){this.docs=t}setIndexRecords(t=[]){this.records=t}setKeys(t=[]){this.keys=t}create(){!this.isCreated&&this.docs.length&&(this.isCreated=!0,e(this.docs[0])?this.docs.forEach((t,e)=>{this._addString(t,e)}):this.docs.forEach((t,e)=>{this._addObject(t,e)}),this.norm.clear())}add(t){const s=this.size();e(t)?this._addString(t,s):this._addObject(t,s)}removeAt(t){this.records.splice(t,1);for(let e=t,s=this.size();e<s;e+=1)this.records[e].i-=1}size(){return this.records.length}_addString(t,e){if(!n(t)||i(t))return;let s={v:t,i:e,n:this.norm.get(t)};this.records.push(s)}_addObject(s,r){let c={i:r,$:{}};this.keys.forEach((r,o)=>{let h=this.getFn(s,r);if(n(h))if(t(h)){let s=[];const r=[{nestedArrIndex:-1,value:h}];for(;r.length;){const{nestedArrIndex:c,value:o}=r.pop();if(n(o))if(e(o)&&!i(o)){let t={v:o,i:c,n:this.norm.get(o)};s.push(t)}else t(o)&&o.forEach((t,e)=>{r.push({nestedArrIndex:e,value:t})})}c.$[o]=s}else if(!i(h)){let t={v:h,n:this.norm.get(h)};c.$[o]=t}}),this.records.push(c)}toJSON(){return{keys:this.keys,records:this.records}}}function l(t,e,{getFn:s=o.getFn}={}){const n=new a({getFn:s}),i=new c(t);return n.setKeys(i.keys()),n.setSources(e),n.create(),n}function u(t,e){const s=t.matches;e.matches=[],n(s)&&s.forEach(t=>{if(!n(t.indices)||!t.indices.length)return;const{indices:s,value:i}=t;let r={indices:s,value:i};t.key&&(r.key=t.key),t.idx>-1&&(r.refIndex=t.idx),e.matches.push(r)})}function d(t,e){e.score=t.score}function g(t,{errors:e=0,currentLocation:s=0,expectedLocation:n=0,distance:i=o.distance,ignoreLocation:r=o.ignoreLocation}={}){const c=e/t.length;if(r)return c;const h=Math.abs(n-s);return i?c+h/i:h?1:c}function f(t,e,s,{location:n=o.location,distance:i=o.distance,threshold:r=o.threshold,findAllMatches:c=o.findAllMatches,minMatchCharLength:h=o.minMatchCharLength,includeMatches:a=o.includeMatches,ignoreLocation:l=o.ignoreLocation}={}){if(e.length>32)throw new Error(`Pattern length exceeds max of ${32}.`);const u=e.length,d=t.length,f=Math.max(0,Math.min(n,d));let p=r,m=f;const M=h>1||a,x=M?Array(d):[];let y;for(;(y=t.indexOf(e,m))>-1;){let t=g(e,{currentLocation:y,expectedLocation:f,distance:i,ignoreLocation:l});if(p=Math.min(t,p),m=y+u,M){let t=0;for(;t<u;)x[y+t]=1,t+=1}}m=-1;let k=[],L=1,_=u+d;const v=1<<u-1;for(let n=0;n<u;n+=1){let r=0,o=_;for(;r<o;){g(e,{errors:n,currentLocation:f+o,expectedLocation:f,distance:i,ignoreLocation:l})<=p?r=o:_=o,o=Math.floor((_-r)/2+r)}_=o;let h=Math.max(1,f-o+1),a=c?d:Math.min(f+o,d)+u,y=Array(a+2);y[a+1]=(1<<n)-1;for(let r=a;r>=h;r-=1){let c=r-1,o=s[t.charAt(c)];if(M&&(x[c]=+!!o),y[r]=(y[r+1]<<1|1)&o,n&&(y[r]|=(k[r+1]|k[r])<<1|1|k[r+1]),y[r]&v&&(L=g(e,{errors:n,currentLocation:c,expectedLocation:f,distance:i,ignoreLocation:l}),L<=p)){if(p=L,m=c,m<=f)break;h=Math.max(1,2*f-m)}}if(g(e,{errors:n+1,currentLocation:f,expectedLocation:f,distance:i,ignoreLocation:l})>p)break;k=y}const S={isMatch:m>=0,score:Math.max(.001,L)};if(M){const t=function(t=[],e=o.minMatchCharLength){let s=[],n=-1,i=-1,r=0;for(let c=t.length;r<c;r+=1){let c=t[r];c&&-1===n?n=r:c||-1===n||(i=r-1,i-n+1>=e&&s.push([n,i]),n=-1)}return t[r-1]&&r-n>=e&&s.push([n,r-1]),s}(x,h);t.length?a&&(S.indices=t):S.isMatch=!1}return S}function p(t){let e={};for(let s=0,n=t.length;s<n;s+=1){const i=t.charAt(s);e[i]=(e[i]||0)|1<<n-s-1}return e}class m{constructor(t,{location:e=o.location,threshold:s=o.threshold,distance:n=o.distance,includeMatches:i=o.includeMatches,findAllMatches:r=o.findAllMatches,minMatchCharLength:c=o.minMatchCharLength,isCaseSensitive:h=o.isCaseSensitive,ignoreLocation:a=o.ignoreLocation}={}){if(this.options={location:e,threshold:s,distance:n,includeMatches:i,findAllMatches:r,minMatchCharLength:c,isCaseSensitive:h,ignoreLocation:a},this.pattern=h?t:t.toLowerCase(),this.chunks=[],!this.pattern.length)return;const l=(t,e)=>{this.chunks.push({pattern:t,alphabet:p(t),startIndex:e})},u=this.pattern.length;if(u>32){let t=0;const e=u%32,s=u-e;for(;t<s;)l(this.pattern.substr(t,32),t),t+=32;if(e){const t=u-32;l(this.pattern.substr(t),t)}}else l(this.pattern,0)}searchIn(t){const{isCaseSensitive:e,includeMatches:s}=this.options;if(e||(t=t.toLowerCase()),this.pattern===t){let e={isMatch:!0,score:0};return s&&(e.indices=[[0,t.length-1]]),e}const{location:n,distance:i,threshold:r,findAllMatches:c,minMatchCharLength:o,ignoreLocation:h}=this.options;let a=[],l=0,u=!1;this.chunks.forEach(({pattern:e,alphabet:d,startIndex:g})=>{const{isMatch:p,score:m,indices:M}=f(t,e,d,{location:n+g,distance:i,threshold:r,findAllMatches:c,minMatchCharLength:o,includeMatches:s,ignoreLocation:h});p&&(u=!0),l+=m,p&&M&&(a=[...a,...M])});let d={isMatch:u,score:u?l/this.chunks.length:1};return u&&s&&(d.indices=a),d}}class M{constructor(t){this.pattern=t}static isMultiMatch(t){return x(t,this.multiRegex)}static isSingleMatch(t){return x(t,this.singleRegex)}search(){}}function x(t,e){const s=t.match(e);return s?s[1]:null}class y extends M{constructor(t){super(t)}static get type(){return"exact"}static get multiRegex(){return/^'"(.*)"$/}static get singleRegex(){return/^'(.*)$/}search(t){let e,s=0;const n=[],i=this.pattern.length;for(;(e=t.indexOf(this.pattern,s))>-1;)s=e+i,n.push([e,s-1]);const r=!!n.length;return{isMatch:r,score:r?1:0,indices:n}}}class k extends M{constructor(t,{location:e=o.location,threshold:s=o.threshold,distance:n=o.distance,includeMatches:i=o.includeMatches,findAllMatches:r=o.findAllMatches,minMatchCharLength:c=o.minMatchCharLength,isCaseSensitive:h=o.isCaseSensitive}={}){super(t),this._bitapSearch=new m(t,{location:e,threshold:s,distance:n,includeMatches:i,findAllMatches:r,minMatchCharLength:c,isCaseSensitive:h})}static get type(){return"fuzzy"}static get multiRegex(){return/^"(.*)"$/}static get singleRegex(){return/^(.*)$/}search(t){return this._bitapSearch.searchIn(t)}}const L=[y,class extends M{constructor(t){super(t)}static get type(){return"prefix-exact"}static get multiRegex(){return/^\^"(.*)"$/}static get singleRegex(){return/^\^(.*)$/}search(t){const e=t.startsWith(this.pattern);return{isMatch:e,score:e?0:1,indices:[0,this.pattern.length-1]}}},class extends M{constructor(t){super(t)}static get type(){return"inverse-prefix-exact"}static get multiRegex(){return/^!\^"(.*)"$/}static get singleRegex(){return/^!\^(.*)$/}search(t){const e=!t.startsWith(this.pattern);return{isMatch:e,score:e?0:1,indices:[0,t.length-1]}}},class extends M{constructor(t){super(t)}static get type(){return"inverse-suffix-exact"}static get multiRegex(){return/^!"(.*)"\$$/}static get singleRegex(){return/^!(.*)\$$/}search(t){const e=!t.endsWith(this.pattern);return{isMatch:e,score:e?0:1,indices:[0,t.length-1]}}},class extends M{constructor(t){super(t)}static get type(){return"suffix-exact"}static get multiRegex(){return/^"(.*)"\$$/}static get singleRegex(){return/^(.*)\$$/}search(t){const e=t.endsWith(this.pattern);return{isMatch:e,score:e?0:1,indices:[t.length-this.pattern.length,t.length-1]}}},class extends M{constructor(t){super(t)}static get type(){return"inverse-exact"}static get multiRegex(){return/^!"(.*)"$/}static get singleRegex(){return/^!(.*)$/}search(t){const e=-1===t.indexOf(this.pattern);return{isMatch:e,score:e?0:1,indices:[0,t.length-1]}}},k],_=L.length,v=/ +(?=([^\"]*\"[^\"]*\")*[^\"]*$)/;const S=new Set([k.type,y.type]);class C{constructor(t,{isCaseSensitive:e=o.isCaseSensitive,includeMatches:s=o.includeMatches,minMatchCharLength:n=o.minMatchCharLength,findAllMatches:i=o.findAllMatches,location:r=o.location,threshold:c=o.threshold,distance:h=o.distance}={}){this.query=null,this.options={isCaseSensitive:e,includeMatches:s,minMatchCharLength:n,findAllMatches:i,location:r,threshold:c,distance:h},this.pattern=e?t:t.toLowerCase(),this.query=function(t,e={}){return t.split("|").map(t=>{let s=t.trim().split(v).filter(t=>t&&!!t.trim()),n=[];for(let t=0,i=s.length;t<i;t+=1){const i=s[t];let r=!1,c=-1;for(;!r&&++c<_;){const t=L[c];let s=t.isMultiMatch(i);s&&(n.push(new t(s,e)),r=!0)}if(!r)for(c=-1;++c<_;){const t=L[c];let s=t.isSingleMatch(i);if(s){n.push(new t(s,e));break}}}return n})}(this.pattern,this.options)}static condition(t,e){return e.useExtendedSearch}searchIn(t){const e=this.query;if(!e)return{isMatch:!1,score:1};const{includeMatches:s,isCaseSensitive:n}=this.options;t=n?t:t.toLowerCase();let i=0,r=[],c=0;for(let n=0,o=e.length;n<o;n+=1){const o=e[n];r.length=0,i=0;for(let e=0,n=o.length;e<n;e+=1){const n=o[e],{isMatch:h,indices:a,score:l}=n.search(t);if(!h){c=0,i=0,r.length=0;break}if(i+=1,c+=l,s){const t=n.constructor.type;S.has(t)?r=[...r,...a]:r.push(a)}}if(i){let t={isMatch:!0,score:c/i};return s&&(t.indices=r),t}}return{isMatch:!1,score:1}}}const w=[];function A(t,e){for(let s=0,n=w.length;s<n;s+=1){let n=w[s];if(n.condition(t,e))return new n(t,e)}return new m(t,e)}const I="$and",$="$or",E=t=>!(!t[I]&&!t[$]),b=t=>({[I]:Object.keys(t).map(e=>({[e]:t[e]}))});function F(s,n,{auto:i=!0}={}){const r=s=>{let c=Object.keys(s);if(c.length>1&&!E(s))return r(b(s));let o=c[0];if((e=>!t(e)&&"object"==typeof e&&!E(e))(s)){const t=s[o];if(!e(t))throw new Error((t=>"Invalid value for key "+t)(o));const r={key:o,pattern:t};return i&&(r.searcher=A(t,n)),r}let h={children:[],operator:o};return c.forEach(e=>{const n=s[e];t(n)&&n.forEach(t=>{h.children.push(r(t))})}),h};return E(s)||(s=b(s)),r(s)}class O{constructor(t,e={},s){this.options={...o,...e},this.options.useExtendedSearch,this._keyStore=new c(this.options.keys),this.setCollection(t,s)}setCollection(t,e){if(this._docs=t,e&&!(e instanceof a))throw new Error("Incorrect 'index' type");this._myIndex=e||l(this._keyStore.keys(),this._docs,{getFn:this.options.getFn})}add(t){n(t)&&(this._docs.push(t),this._myIndex.add(t))}remove(t=(()=>!1)){const e=[];for(let s=0,n=this._docs.length;s<n;s+=1){const n=this._docs[s];t(n,s)&&(this.removeAt(s),s-=1,e.push(n))}return e}removeAt(t){this._docs.splice(t,1),this._myIndex.removeAt(t)}getIndex(){return this._myIndex}search(t,{limit:n=-1}={}){const{includeMatches:i,includeScore:r,shouldSort:c,sortFn:h,ignoreFieldNorm:a}=this.options;let l=e(t)?e(this._docs[0])?this._searchStringList(t):this._searchObjectList(t):this._searchLogical(t);return function(t,e,{ignoreFieldNorm:s=o.ignoreFieldNorm}){t.forEach(t=>{let n=1;t.matches.forEach(({key:t,norm:i,score:r})=>{const c=e.get(t,"weight");n*=Math.pow(0===r&&c?Number.EPSILON:r,(c||1)*(s?1:i))}),t.score=n})}(l,this._keyStore,{ignoreFieldNorm:a}),c&&l.sort(h),s(n)&&n>-1&&(l=l.slice(0,n)),function(t,e,{includeMatches:s=o.includeMatches,includeScore:n=o.includeScore}={}){const i=[];s&&i.push(u);n&&i.push(d);return t.map(t=>{const{idx:s}=t,n={item:e[s],refIndex:s};return i.length&&i.forEach(e=>{e(t,n)}),n})}(l,this._docs,{includeMatches:i,includeScore:r})}_searchStringList(t){const e=A(t,this.options),{records:s}=this._myIndex,i=[];return s.forEach(({v:t,i:s,n:r})=>{if(!n(t))return;const{isMatch:c,score:o,indices:h}=e.searchIn(t);c&&i.push({item:t,idx:s,matches:[{score:o,value:t,norm:r,indices:h}]})}),i}_searchLogical(t){const e=F(t,this.options),{keys:s,records:i}=this._myIndex,r={},c=[],o=(t,e,n)=>{if(t.children){const s=t.operator;let i=[];for(let r=0;r<t.children.length;r+=1){let c=t.children[r],h=o(c,e,n);if(h&&h.length){if(i.push({idx:n,item:e,matches:h}),s===$)break}else if(s===I){i.length=0;break}}return i}{const{key:n,searcher:i}=t,r=e[s.indexOf(n)];return this._findMatches({key:n,value:r,searcher:i})}};return i.forEach(({$:t,i:s})=>{if(n(t)){let n=o(e,t,s);n.length&&(r[s]||(r[s]={idx:s,item:t,matches:[]},c.push(r[s])),n.forEach(({matches:t})=>{r[s].matches.push(...t)}))}}),c}_searchObjectList(t){const e=A(t,this.options),{keys:s,records:i}=this._myIndex,r=[];return i.forEach(({$:t,i:i})=>{if(!n(t))return;let c=[];s.forEach((s,n)=>{c.push(...this._findMatches({key:s,value:t[n],searcher:e}))}),c.length&&r.push({idx:i,item:t,matches:c})}),r}_findMatches({key:e,value:s,searcher:i}){if(!n(s))return[];let r=[];if(t(s))s.forEach(({v:t,i:s,n:c})=>{if(!n(t))return;const{isMatch:o,score:h,indices:a}=i.searchIn(t);o&&r.push({score:h,key:e,value:t,idx:s,norm:c,indices:a})});else{const{v:t,n:n}=s,{isMatch:c,score:o,indices:h}=i.searchIn(t);c&&r.push({score:o,key:e,value:t,norm:n,indices:h})}return r}}O.version="6.2.1",O.createIndex=l,O.parseIndex=function(t,{getFn:e=o.getFn}={}){const{keys:s,records:n}=t,i=new a({getFn:e});return i.setKeys(s),i.setIndexRecords(n),i},O.config=o,function(...t){w.push(...t)}(C);export default O;