slimsearch
Version:
Tiny but powerful full-text search engine for browser and Node
3 lines (2 loc) • 4.19 kB
JavaScript
const F="ENTRIES",b="KEYS",M="VALUES",h="";class v{set;_type;_path;constructor(t,e){const s=t._tree,n=Array.from(s.keys());this.set=t,this._type=e,this._path=n.length>0?[{node:s,keys:n}]:[]}next(){const t=this.dive();return this.backtrack(),t}dive(){if(this._path.length===0)return{done:!0,value:void 0};const{node:t,keys:e}=a(this._path);if(a(e)===h)return{done:!1,value:this.result()};const s=t.get(a(e));return this._path.push({node:s,keys:Array.from(s.keys())}),this.dive()}backtrack(){if(this._path.length===0)return;const t=a(this._path).keys;t.pop(),!(t.length>0)&&(this._path.pop(),this.backtrack())}key(){return this.set._prefix+this._path.map(({keys:t})=>a(t)).filter(t=>t!==h).join("")}value(){return a(this._path).node.get(h)}result(){switch(this._type){case M:return this.value();case b:return this.key();default:return[this.key(),this.value()]}}[Symbol.iterator](){return this}}const a=i=>i[i.length-1],K=(i,t,e)=>{const s=new Map;if(typeof t!="string")return s;const n=t.length+1,o=n+e,c=new Uint8Array(o*n).fill(e+1);for(let r=0;r<n;++r)c[r]=r;for(let r=1;r<o;++r)c[r*n]=r;return A(i,t,e,s,c,1,n,""),s},A=(i,t,e,s,n,o,c,r)=>{const _=o*c;t:for(const f of i.keys())if(f===h){const u=n[_-1];u<=e&&s.set(r,[i.get(f),u])}else{let u=o;for(let d=0;d<f.length;++d,++u){const T=f[d],p=c*u,E=p-c;let k=n[p];const W=Math.max(0,u-e-1),I=Math.min(c-1,u+e);for(let l=W;l<I;++l){const L=T!==t[l],R=n[E+l]+ +L,U=n[E+l+1]+1,V=n[p+l]+1,x=n[p+l+1]=Math.min(R,U,V);x<k&&(k=x)}if(k>e)continue t}A(i.get(f),t,e,s,n,u,c,r+f)}};class g{_tree;_prefix;_size=void 0;constructor(t=new Map,e=""){this._tree=t,this._prefix=e}atPrefix(t){if(!t.startsWith(this._prefix))throw new Error("Mismatched prefix");const[e,s]=y(this._tree,t.slice(this._prefix.length));if(e===void 0){const[n,o]=m(s);for(const c of n.keys())if(c!==h&&c.startsWith(o)){const r=new Map;return r.set(c.slice(o.length),n.get(c)),new g(r,t)}}return new g(e,t)}clear(){this._size=void 0,this._tree.clear()}delete(t){return this._size=void 0,N(this._tree,t)}entries(){return new v(this,F)}forEach(t){for(const[e,s]of this)t(e,s,this)}fuzzyGet(t,e){return K(this._tree,t,e)}get(t){const e=w(this._tree,t);return e!==void 0?e.get(h):void 0}has(t){return w(this._tree,t)?.has(h)??!1}keys(){return new v(this,b)}set(t,e){if(typeof t!="string")throw new Error("key must be a string");return this._size=void 0,z(this._tree,t).set(h,e),this}get size(){if(this._size)return this._size;this._size=0;const t=this.entries();for(;!t.next().done;)this._size+=1;return this._size}update(t,e){if(typeof t!="string")throw new Error("key must be a string");this._size=void 0;const s=z(this._tree,t);return s.set(h,e(s.get(h))),this}fetch(t,e){if(typeof t!="string")throw new Error("key must be a string");this._size=void 0;const s=z(this._tree,t);let n=s.get(h);return n===void 0&&s.set(h,n=e()),n}values(){return new v(this,M)}[Symbol.iterator](){return this.entries()}static from(t){const e=new g;for(const[s,n]of t)e.set(s,n);return e}static fromObject(t){return g.from(Object.entries(t))}}const y=(i,t,e=[])=>{if(t.length===0||i==null)return[i,e];for(const s of i.keys())if(s!==h&&t.startsWith(s))return e.push([i,s]),y(i.get(s),t.slice(s.length),e);return e.push([i,t]),y(void 0,"",e)},w=(i,t)=>{if(t.length===0||!i)return i;for(const e of i.keys())if(e!==h&&t.startsWith(e))return w(i.get(e),t.slice(e.length))},z=(i,t)=>{const e=t.length;t:for(let s=0;i&&s<e;){for(const o of i.keys())if(o!==h&&t[s]===o[0]){const c=Math.min(e-s,o.length);let r=1;for(;r<c&&t[s+r]===o[r];)++r;const _=i.get(o);if(r===o.length)i=_;else{const f=new Map;f.set(o.slice(r),_),i.set(t.slice(s,s+r),f),i.delete(o),i=f}s+=r;continue t}const n=new Map;return i.set(t.slice(s),n),n}return i},N=(i,t)=>{const[e,s]=y(i,t);if(e!==void 0){if(e.delete(h),e.size===0)S(s);else if(e.size===1){const[n,o]=e.entries().next().value;j(s,n,o)}}},S=i=>{if(i.length===0)return;const[t,e]=m(i);if(t.delete(e),t.size===0)S(i.slice(0,-1));else if(t.size===1){const[s,n]=t.entries().next().value;s!==h&&j(i.slice(0,-1),s,n)}},j=(i,t,e)=>{if(i.length===0)return;const[s,n]=m(i);s.set(n+t,e),s.delete(n)},m=i=>i[i.length-1];export{g as SearchableMap};
//# sourceMappingURL=SearchableMap.mjs.map