@zanreal/search
Version:
A powerful TypeScript fuzzy search library with intelligent scoring, exact match prioritization, and automatic field detection for any object structure
2 lines (1 loc) • 3.92 kB
JavaScript
var G={fieldWeights:{},fuzzyThreshold:0.7,minFuzzyLength:3,limit:100,caseSensitive:!1};function C(J,$){if(J.length>$.length)[J,$]=[$,J];let Y=J.length,B=$.length,K=new Array(Y+1),V=new Array(Y+1);for(let Z=0;Z<=Y;Z++)K[Z]=Z;for(let Z=1;Z<=B;Z++){V[0]=Z;for(let X=1;X<=Y;X++){let w=J[X-1]===$[Z-1]?0:1;V[X]=Math.min(K[X]+1,V[X-1]+1,K[X-1]+w)}[K,V]=[V,K]}return K[Y]}function p(J,$){return $.split(".").reduce((Y,B)=>{if(Y&&typeof Y==="object"&&B in Y)return Y[B];return""},J)??""}var E=new WeakMap,q=new Map,P=500,y=new WeakMap;function T(J,$="",Y=3){if(Y<=0||!J||typeof J!=="object")return[];if($===""&&E.has(J))return E.get(J);let B=[];for(let[K,V]of Object.entries(J)){let Z=$?`${$}.${K}`:K;if(typeof V==="string"&&V.length>0)B.push(Z);else if(typeof V==="object"&&V!==null&&!Array.isArray(V))B.push(...T(V,Z,Y-1))}if($==="")E.set(J,B);return B}function _(J,$){if($)return J;let Y=`lc:${J}`;if(q.has(Y))return q.get(Y);let B=J.toLowerCase();if(q.size>=P)Array.from(q.keys()).slice(0,P/2).forEach((V)=>q.delete(V));return q.set(Y,B),B}function f(J,$,Y,B){if(!J||!$)return null;let K=_(J,B.caseSensitive),V=_($,B.caseSensitive);if(K.startsWith(V))return{field:"",value:J,score:Y*20,type:"exact-start",position:0};let Z=K.indexOf(V);if(Z!==-1){let X=Math.max(1,100/J.length),w=Z*0.1,M=Y*(10+X-w);return{field:"",value:J,score:Math.max(M,Y),type:"exact-contain",position:Z}}if(V.length>=B.minFuzzyLength){let X=null,w=0;for(let M=0;M<=K.length;M++){let b=K[M];if(M===K.length||b&&/\s/.test(b)){if(M-w>=3){let L=K.slice(w,M),F=C(L,V),H=Math.max(L.length,V.length),O=(H-F)/H;if(O>=B.fuzzyThreshold){let Q=Math.max(1,50/J.length),z=Y*O*(2+Q);if(!X||z>X.score)X={field:"",value:J,score:z,type:"fuzzy"}}}w=M+1}}return X}return null}var A=0;function x(J,$,Y={}){if(A++,A%100===0){if(q.size>P/2)Array.from(q.keys()).slice(0,P/4).forEach((z)=>q.delete(z))}if(!$.trim())return J.map((Q)=>({item:Q,score:0,matches:[]}));let{fields:B,fieldWeights:K=G.fieldWeights,fuzzyThreshold:V=G.fuzzyThreshold,minFuzzyLength:Z=G.minFuzzyLength,limit:X=G.limit,caseSensitive:w=G.caseSensitive}=Y,M=$.trim(),b={fuzzyThreshold:V,minFuzzyLength:Z,caseSensitive:w},L=B||(J.length>0?T(J[0]):[]),F;if(y.has(J))F=y.get(J);else{F=new Map;let Q=(z)=>{let W=0,j=0,U=Math.min(J.length,100);for(let v=0;v<U;v++){let m=J[v],R=p(m,z);if(R&&typeof R==="string"&&R.length>0)W+=R.length,j++}let k=j>0?W/j:0,I=z.split(".").pop()?.toLowerCase()??"",N=1;if(["title","name","heading"].includes(I))N=5;else if(["description","summary","subtitle"].includes(I))N=3;else if(["content","body","text"].includes(I))N=1;let D=1;if(k<50)D=2;else if(k<100)D=1.5;else if(k<300)D=1.2;else D=1;return{avgLength:k,weight:N*D}};for(let z of L)F.set(z,Q(z));y.set(J,F)}let H=[],O=X?X*3:J.length;for(let Q=0;Q<J.length;Q++){let z=J[Q];if(!z)continue;let W=[],j=0;for(let U of L){let k=p(z,U);if(!k)continue;let I=K[U],N=F.get(U),D=I??N?.weight??1,v=f(k,M,D,b);if(v)v.field=U,W.push(v),j+=v.score}if(W.length>0)H.push({item:z,score:j,matches:W});if(H.length>=O)break}return H.sort((Q,z)=>{if(Q.score!==z.score)return z.score-Q.score;let W=Q.matches.reduce((U,k)=>U+k.value.length,0),j=z.matches.reduce((U,k)=>U+k.value.length,0);return W-j}),X?H.slice(0,X):H}function S(J,$,Y={}){return x(J,$,Y).map((B)=>B.item)}function u(J){return($,Y,B={})=>{return x($,Y,{...J,...B})}}function g(){return u({fieldWeights:G.fieldWeights,fuzzyThreshold:G.fuzzyThreshold,minFuzzyLength:G.minFuzzyLength,limit:G.limit,caseSensitive:G.caseSensitive})}function d(J,$,Y){return S(J,$,{fields:Y,fieldWeights:G.fieldWeights,fuzzyThreshold:G.fuzzyThreshold,minFuzzyLength:G.minFuzzyLength,limit:G.limit,caseSensitive:G.caseSensitive})}function n(){q.clear(),A=0}function l(){return{stringProcessingCacheSize:q.size,searchCallCount:A}}export{S as searchItems,x as search,d as quickSearch,l as getCacheStats,u as createSearcher,g as createDocumentSearcher,n as clearSearchCaches,G as DEFAULT_SEARCH_OPTIONS};