minisearch
Advanced tools
Comparing version 0.1.4 to 0.1.5
@@ -1,1 +0,1 @@ | ||
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define("minisearch",[],e):"object"==typeof exports?exports.minisearch=e():t.minisearch=e()}("undefined"!=typeof self?self:this,function(){return function(t){var e={};function n(i){if(e[i])return e[i].exports;var s=e[i]={i:i,l:!1,exports:{}};return t[i].call(s.exports,s,s.exports,n),s.l=!0,s.exports}return n.m=t,n.c=e,n.d=function(t,e,i){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var s in t)n.d(i,s,function(e){return t[e]}.bind(null,s));return i},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=0)}([function(t,e,n){"use strict";n.r(e);class i{constructor(t,e=s){const n=t._tree,i=Object.keys(n);this.set=t,this.type=e,this.path=i.length>0?[{node:n,keys:i}]:[]}next(){const t=this.dive();return this.backtrack(),t}dive(){if(0===this.path.length)return{done:!0};const{node:t,keys:e}=u(this.path);return u(e)===c?{done:!1,value:this.result()}:(this.path.push({node:t[u(e)],keys:Object.keys(t[u(e)])}),this.dive())}backtrack(){0!==this.path.length&&(u(this.path).keys.pop(),u(this.path).keys.length>0||(this.path.pop(),this.backtrack()))}key(){return this.set._prefix+this.path.map(({keys:t})=>u(t)).filter(t=>t!==c).join("")}value(){return u(this.path).node[c]}result(){return this.type===o?this.value():this.type===r?this.key():[this.key(),this.value()]}[Symbol.iterator](){return this}}const s="ENTRIES",r="KEYS",o="VALUES",c="",u=function(t){return t[t.length-1]},h=function(t,e,n,i=0,s=d){const r=[{distance:0,ia:i,ib:0,edit:s}],o=[],c=[];for(;r.length>0;){const{distance:i,ia:s,ib:u,edit:h}=r.pop();if(o[s]=o[s]||[],!(o[s][u]&&o[s][u]<=i))if(o[s][u]=i,u!==e.length)if(t[s]===e[u])r.push({distance:i,ia:s+1,ib:u+1,edit:d});else{if(i>=n)continue;h!==l&&r.push({distance:i+1,ia:s,ib:u+1,edit:a}),s<t.length&&(h!==a&&r.push({distance:i+1,ia:s+1,ib:u,edit:l}),h!==a&&h!==l&&r.push({distance:i+1,ia:s+1,ib:u+1,edit:f}))}else c.push({distance:i,i:s,edit:h})}return c},d=0,f=1,l=2,a=3;var p=function(t,e,n){const i=[{distance:0,i:0,key:"",node:t}],s={};for(;i.length>0;){const{node:t,distance:r,key:o,i:u,edit:d}=i.pop();Object.keys(t).forEach(f=>{if(f===c){const i=r+(e.length-u),[,c]=s[o]||[null,1/0];i<=n&&i<c&&(s[o]=[t[f],i])}else h(e,f,n-r,u,d).forEach(({distance:e,i:n,edit:s})=>{i.push({node:t[f],distance:r+e,key:o+f,i:n,edit:s})})})}return s};class m{constructor(t={},e=""){this._tree=t,this._prefix=e}atPrefix(t){if(!t.startsWith(this._prefix))throw new Error("Mismatched prefix");const[e,n]=_(this._tree,t.slice(this._prefix.length));if(void 0===e){const[e,i]=x(n),s=Object.keys(e).find(t=>t!==c&&t.startsWith(i));if(void 0!==s)return new m({[s.slice(i.length)]:e[s]},t)}return new m(e||{},t)}clear(){delete this._size,this._tree={}}delete(t){return delete this._size,O(this._tree,t)}entries(){return new i(this,s)}forEach(t){for(let[e,n]of this)t(e,n,this)}fuzzyGet(t,e){return p(this._tree,t,e)}get(t){const e=y(this._tree,t);return void 0!==e?e[c]:void 0}has(t){const e=y(this._tree,t);return void 0!==e&&e.hasOwnProperty(c)}keys(){return new i(this,r)}set(t,e){if("string"!=typeof t)throw new Error("key must be a string");return delete this._size,b(this._tree,t)[c]=e,this}get size(){return this._size?this._size:(this._size=0,this.forEach(()=>{this._size+=1}),this._size)}update(t,e){if("string"!=typeof t)throw new Error("key must be a string");delete this._size;const n=b(this._tree,t);return n[c]=e(n[c]),this}values(){return new i(this,o)}[Symbol.iterator](){return this.entries()}}m.from=function(t){const e=new m;for(let[n,i]of t)e.set(n,i);return e},m.fromObject=function(t){return m.from(Object.entries(t))};const _=function(t,e,n=[]){if(0===e.length)return[t,n];const i=Object.keys(t).find(t=>t!==c&&e.startsWith(t));return void 0===i?_(void 0,"",[...n,[t,e]]):_(t[i],e.slice(i.length),[...n,[t,i]])},y=function(t,e){if(0===e.length)return t;const n=Object.keys(t).find(t=>t!==c&&e.startsWith(t));return void 0!==n?y(t[n],e.slice(n.length)):void 0},b=function(t,e){if(0===e.length)return t;const n=Object.keys(t).find(t=>t!==c&&e.startsWith(t));if(void 0===n){const n=Object.keys(t).find(t=>t!==c&&t.startsWith(e[0]));if(void 0!==n){const i=g(e,n);return t[i]={[n.slice(i.length)]:t[n]},delete t[n],b(t[i],e.slice(i.length))}return t[e]={},t[e]}return b(t[n],e.slice(n.length))},g=function(t,e,n=0,i=Math.min(t.length,e.length),s=""){return n>=i?s:t[n]!==e[n]?s:g(t,e,n+1,i,s+t[n])},O=function(t,e){const[n,i]=_(t,e);if(void 0===n)return;delete n[c];const s=Object.keys(n);0===s.length&&k(i),1===s.length&&v(i,s[0],n[s[0]])},k=function(t){if(0===t.length)return;const[e,n]=x(t);delete e[n],0===Object.keys(e).length&&k(t.slice(0,-1))},v=function(t,e,n){if(0===t.length)return;const[i,s]=x(t);i[s+e]=n,delete i[s]},x=function(t){return t[t.length-1]};var j=m;const z="or";class w{constructor(t={}){this._options={...P,...t},this._options.searchOptions={...T,...this._options.searchOptions||{}};const{fields:e}=this._options;if(null==e)throw new Error('Option "fields" must be provided');this._index=new j,this._documentCount=0,this._documentIds={},this._fieldIds={},I(this,e)}add(t){const{tokenize:e,processTerm:n,fields:i,idField:s}=this._options;if(null==t[s])throw new Error(`Document does not have ID field "${s}"`);const r=S(this,t[s]);i.filter(e=>null!=t[e]).forEach(i=>{e(t[i]).forEach(t=>{E(this,this._fieldIds[i],r,n(t))})})}addAll(t){t.forEach(t=>this.add(t))}search(t,e={}){const{tokenize:n,processTerm:i,searchOptions:s}=this._options;e={...s,...e};const r=n(t).map(i).map(e.termToQuery).map(t=>this.executeQuery(t,e)),o=this.combineResults(r,e.combineWith);return Object.entries(o).map(([t,{score:e,match:n}])=>({id:this._documentIds[t],score:e,match:n})).sort(({score:t},{score:e})=>t<e?1:-1)}get documentCount(){return this._documentCount}executeQuery(t,e={}){const n=((e={...this._options.defaultSearchOptions,...e}).fields||this._options.fields).reduce((t,e)=>({...t,[e]:t[e]||1}),e.boost||{});if(!t.fuzzy&&!t.prefix)return C(this,t.term,n,this._index.get(t.term));const i=[];if(t.fuzzy){const e=t.fuzzy<1?Math.round(t.term.length*t.fuzzy):t.fuzzy;Object.entries(this._index.fuzzyGet(t.term,e)).forEach(([t,[e,s]])=>{i.push(C(this,t,n,e,s))})}return t.prefix&&this._index.atPrefix(t.term).forEach((e,s)=>{i.push(C(this,e,n,s,e.length-t.term.length))}),i.reduce(M[z],{})}combineResults(t,e=z){if(0===t.length)return{};const n=e.toLowerCase();return t.reduce(M[n],null)}toJSON(){return{index:this._index,documentCount:this._documentCount,documentIds:this._documentIds,fieldIds:this._fieldIds}}toJS(){this.toJSON()}}w.loadJSON=function(t,e={}){return w.loadJS(JSON.parse(t),e)},w.loadJS=function(t,e={}){const{index:{_tree:n,_prefix:i},documentCount:s,documentIds:r,fieldIds:o}=t,c=new w(e);return c._index=new j(n,i),c._documentCount=s,c._documentIds=r,c._fieldIds=o,c},w.SearchableMap=j;const E=function(t,e,n,i){t._index.update(i,t=>{const i=(t=t||{})[e]||{df:0,ds:{}};return null==i.ds[n]&&(i.df+=1),i.ds[n]=(i.ds[n]||0)+1,{...t,[e]:i}})},S=function(t,e){const n=t._documentCount;return t._documentIds[n]=e,t._documentCount+=1,n},I=function(t,e){e.forEach((e,n)=>{t._fieldIds[e]=n})},C=function(t,e,n,i,s=0){return null==i?{}:Object.entries(n).reduce((n,[r,o])=>{const{df:c,ds:u}=i[t._fieldIds[r]]||{ds:{}};return Object.entries(u).forEach(([i,u])=>{const h=o/(1+.333*o*s);n[i]=n[i]||{score:0,match:{}},n[i].match[e]=n[i].match[e]||[],n[i].score=n[i].score+h*W(u,c,t._documentCount),n[i].match[e].push(r)}),n},{})},M={[z]:function(t,e){return Object.entries(e).reduce((t,[e,{match:n,score:i}])=>(t[e]=t[e]||{score:0,match:{}},t[e].score=t[e].score+i,t[e].match={...t[e].match,...n},t),t||{})},and:function(t,e){return null==t?e:Object.entries(e).reduce((e,[n,{score:i,match:s}])=>void 0===t[n]?e:(e[n]=e[n]||{},e[n].score=Math.min(t[n].score,i),e[n].match={...t[n].match,...s},e),{})}},W=function(t,e,n){return t*Math.log(n/e)},P={idField:"id",tokenize:t=>t.split(/\W+/).filter(t=>t.length>1),processTerm:t=>t.toLowerCase()},T={termToQuery:t=>({term:t}),combineWith:z};var J=w;e.default=J}]).default}); | ||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("minisearch",[],t):"object"==typeof exports?exports.minisearch=t():e.minisearch=t()}("undefined"!=typeof self?self:this,function(){return function(e){var t={};function n(i){if(t[i])return t[i].exports;var s=t[i]={i:i,l:!1,exports:{}};return e[i].call(s.exports,s,s.exports,n),s.l=!0,s.exports}return n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var s in e)n.d(i,s,function(t){return e[t]}.bind(null,s));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";n.r(t);class i{constructor(e,t=s){const n=e._tree,i=Object.keys(n);this.set=e,this.type=t,this.path=i.length>0?[{node:n,keys:i}]:[]}next(){const e=this.dive();return this.backtrack(),e}dive(){if(0===this.path.length)return{done:!0};const{node:e,keys:t}=u(this.path);return u(t)===c?{done:!1,value:this.result()}:(this.path.push({node:e[u(t)],keys:Object.keys(e[u(t)])}),this.dive())}backtrack(){0!==this.path.length&&(u(this.path).keys.pop(),u(this.path).keys.length>0||(this.path.pop(),this.backtrack()))}key(){return this.set._prefix+this.path.map(({keys:e})=>u(e)).filter(e=>e!==c).join("")}value(){return u(this.path).node[c]}result(){return this.type===o?this.value():this.type===r?this.key():[this.key(),this.value()]}[Symbol.iterator](){return this}}const s="ENTRIES",r="KEYS",o="VALUES",c="",u=function(e){return e[e.length-1]},d=function(e,t,n,i=0,s=h){const r=[{distance:0,ia:i,ib:0,edit:s}],o=[],c=[];for(;r.length>0;){const{distance:i,ia:s,ib:u,edit:d}=r.pop();if(o[s]=o[s]||[],!(o[s][u]&&o[s][u]<=i))if(o[s][u]=i,u!==t.length)if(e[s]===t[u])r.push({distance:i,ia:s+1,ib:u+1,edit:h});else{if(i>=n)continue;d!==f&&r.push({distance:i+1,ia:s,ib:u+1,edit:a}),s<e.length&&(d!==a&&r.push({distance:i+1,ia:s+1,ib:u,edit:f}),d!==a&&d!==f&&r.push({distance:i+1,ia:s+1,ib:u+1,edit:l}))}else c.push({distance:i,i:s,edit:d})}return c},h=0,l=1,f=2,a=3;var p=function(e,t,n){const i=[{distance:0,i:0,key:"",node:e}],s={};for(;i.length>0;){const{node:e,distance:r,key:o,i:u,edit:h}=i.pop();Object.keys(e).forEach(l=>{if(l===c){const i=r+(t.length-u),[,c]=s[o]||[null,1/0];i<=n&&i<c&&(s[o]=[e[l],i])}else d(t,l,n-r,u,h).forEach(({distance:t,i:n,edit:s})=>{i.push({node:e[l],distance:r+t,key:o+l,i:n,edit:s})})})}return s};class m{constructor(e={},t=""){this._tree=e,this._prefix=t}atPrefix(e){if(!e.startsWith(this._prefix))throw new Error("Mismatched prefix");const[t,n]=_(this._tree,e.slice(this._prefix.length));if(void 0===t){const[t,i]=k(n),s=Object.keys(t).find(e=>e!==c&&e.startsWith(i));if(void 0!==s)return new m({[s.slice(i.length)]:t[s]},e)}return new m(t||{},e)}clear(){delete this._size,this._tree={}}delete(e){return delete this._size,v(this._tree,e)}entries(){return new i(this,s)}forEach(e){for(let[t,n]of this)e(t,n,this)}fuzzyGet(e,t){return p(this._tree,e,t)}get(e){const t=y(this._tree,e);return void 0!==t?t[c]:void 0}has(e){const t=y(this._tree,e);return void 0!==t&&t.hasOwnProperty(c)}keys(){return new i(this,r)}set(e,t){if("string"!=typeof e)throw new Error("key must be a string");return delete this._size,b(this._tree,e)[c]=t,this}get size(){return this._size?this._size:(this._size=0,this.forEach(()=>{this._size+=1}),this._size)}update(e,t){if("string"!=typeof e)throw new Error("key must be a string");delete this._size;const n=b(this._tree,e);return n[c]=t(n[c]),this}values(){return new i(this,o)}[Symbol.iterator](){return this.entries()}}m.from=function(e){const t=new m;for(let[n,i]of e)t.set(n,i);return t},m.fromObject=function(e){return m.from(Object.entries(e))};const _=function(e,t,n=[]){if(0===t.length)return[e,n];const i=Object.keys(e).find(e=>e!==c&&t.startsWith(e));return void 0===i?_(void 0,"",[...n,[e,t]]):_(e[i],t.slice(i.length),[...n,[e,i]])},y=function(e,t){if(0===t.length)return e;const n=Object.keys(e).find(e=>e!==c&&t.startsWith(e));return void 0!==n?y(e[n],t.slice(n.length)):void 0},b=function(e,t){if(0===t.length)return e;const n=Object.keys(e).find(e=>e!==c&&t.startsWith(e));if(void 0===n){const n=Object.keys(e).find(e=>e!==c&&e.startsWith(t[0]));if(void 0!==n){const i=g(t,n);return e[i]={[n.slice(i.length)]:e[n]},delete e[n],b(e[i],t.slice(i.length))}return e[t]={},e[t]}return b(e[n],t.slice(n.length))},g=function(e,t,n=0,i=Math.min(e.length,t.length),s=""){return n>=i?s:e[n]!==t[n]?s:g(e,t,n+1,i,s+e[n])},v=function(e,t){const[n,i]=_(e,t);if(void 0===n)return;delete n[c];const s=Object.keys(n);0===s.length&&O(i),1===s.length&&x(i,s[0],n[s[0]])},O=function(e){if(0===e.length)return;const[t,n]=k(e);delete t[n],0===Object.keys(t).length&&O(e.slice(0,-1))},x=function(e,t,n){if(0===e.length)return;const[i,s]=k(e);i[s+t]=n,delete i[s]},k=function(e){return e[e.length-1]};var j=m;const w="or";class z{constructor(e={}){this._options={...J,...e},this._options.searchOptions={...D,...this._options.searchOptions||{}};const{fields:t}=this._options;if(null==t)throw new Error('Option "fields" must be provided');this._index=new j,this._documentCount=0,this._documentIds={},this._fieldIds={},M(this,t)}add(e){const{tokenize:t,processTerm:n,fields:i,idField:s}=this._options;if(null==e[s])throw new Error(`Document does not have ID field "${s}"`);const r=C(this,e[s]);i.filter(t=>null!=e[t]).forEach(i=>{t(e[i]).forEach(e=>{E(this,this._fieldIds[i],r,n(e))})})}addAll(e){e.forEach(e=>this.add(e))}remove(e){const{tokenize:t,processTerm:n,fields:i,idField:s}=this._options;if(null==e[s])throw new Error(`Document does not have ID field "${s}"`);const[r]=Object.entries(this._documentIds).find(([t,n])=>e[s]===n)||[];if(null==r)throw new Error(`Cannot remove document with ID ${e[s]}: it is not in the index`);i.filter(t=>null!=e[t]).forEach(i=>{t(e[i]).forEach(e=>{I(this,this._fieldIds[i],r,n(e))})}),this._documentCount-=1}search(e,t={}){const{tokenize:n,processTerm:i,searchOptions:s}=this._options;t={...s,...t};const r=n(e).map(i).map(t.termToQuery).map(e=>this.executeQuery(e,t)),o=this.combineResults(r,t.combineWith);return Object.entries(o).map(([e,{score:t,match:n}])=>({id:this._documentIds[e],score:t,match:n})).sort(({score:e},{score:t})=>e<t?1:-1)}get documentCount(){return this._documentCount}executeQuery(e,t={}){const n=((t={...this._options.defaultSearchOptions,...t}).fields||this._options.fields).reduce((e,t)=>({...e,[t]:e[t]||1}),t.boost||{});if(!e.fuzzy&&!e.prefix)return T(this,e.term,n,this._index.get(e.term));const i=[];if(e.fuzzy){const t=e.fuzzy<1?Math.round(e.term.length*e.fuzzy):e.fuzzy;Object.entries(this._index.fuzzyGet(e.term,t)).forEach(([e,[t,s]])=>{i.push(T(this,e,n,t,s))})}return e.prefix&&this._index.atPrefix(e.term).forEach((t,s)=>{i.push(T(this,t,n,s,t.length-e.term.length))}),i.reduce(W[w],{})}combineResults(e,t=w){if(0===e.length)return{};const n=t.toLowerCase();return e.reduce(W[n],null)}toJSON(){return{index:this._index,documentCount:this._documentCount,documentIds:this._documentIds,fieldIds:this._fieldIds}}toJS(){this.toJSON()}}z.loadJSON=function(e,t={}){return z.loadJS(JSON.parse(e),t)},z.loadJS=function(e,t={}){const{index:{_tree:n,_prefix:i},documentCount:s,documentIds:r,fieldIds:o}=e,c=new z(t);return c._index=new j(n,i),c._documentCount=s,c._documentIds=r,c._fieldIds=o,c},z.SearchableMap=j;const E=function(e,t,n,i){e._index.update(i,e=>{const i=(e=e||{})[t]||{df:0,ds:{}};return null==i.ds[n]&&(i.df+=1),i.ds[n]=(i.ds[n]||0)+1,{...e,[t]:i}})},I=function(e,t,n,i){e._index.has(i)?(e._index.update(i,s=>{const r=s[t];return null==r||null==r.ds[n]?(S(e,n,t,i),s):r.df<=1?(delete s[t],s):(r.df-=1,r.ds[n]<=1?(delete r.ds[n],s):(r.ds[n]-=1,{...s,[t]:r}))}),0===Object.keys(e._index.get(i)).length&&e._index.delete(i)):S(e,n,t,i)},S=function(e,t,n,i){if(null==console||null==console.warn)return;const s=Object.entries(e._fieldIds).find(([e,t])=>t===n)[0];console.warn(`MiniSearch: document with ID ${e._documentIds[t]} has changed before removal: term "${i}" was not present in field "${s}". Removing a document after it has changed can corrupt the index!`)},C=function(e,t){const n=e._documentCount;return e._documentIds[n]=t,e._documentCount+=1,n},M=function(e,t){t.forEach((t,n)=>{e._fieldIds[t]=n})},T=function(e,t,n,i,s=0){return null==i?{}:Object.entries(n).reduce((n,[r,o])=>{const{df:c,ds:u}=i[e._fieldIds[r]]||{ds:{}};return Object.entries(u).forEach(([i,u])=>{const d=o/(1+.333*o*s);n[i]=n[i]||{score:0,match:{}},n[i].match[t]=n[i].match[t]||[],n[i].score=n[i].score+d*P(u,c,e._documentCount),n[i].match[t].push(r)}),n},{})},W={[w]:function(e,t){return Object.entries(t).reduce((e,[t,{match:n,score:i}])=>(e[t]=e[t]||{score:0,match:{}},e[t].score=e[t].score+i,e[t].match={...e[t].match,...n},e),e||{})},and:function(e,t){return null==e?t:Object.entries(t).reduce((t,[n,{score:i,match:s}])=>void 0===e[n]?t:(t[n]=t[n]||{},t[n].score=Math.min(e[n].score,i),t[n].match={...e[n].match,...s},t),{})}},P=function(e,t,n){return e*Math.log(n/t)},J={idField:"id",tokenize:e=>e.split(/\W+/).filter(e=>e.length>1),processTerm:e=>e.toLowerCase()},D={termToQuery:e=>({term:e}),combineWith:w};var $=z;t.default=$}]).default}); |
@@ -327,2 +327,8 @@ window.esdocSearchIndex = [ | ||
[ | ||
"src/minisearch.js~minisearch#remove", | ||
"class/src/MiniSearch.js~MiniSearch.html#instance-method-remove", | ||
"src/MiniSearch.js~MiniSearch#remove", | ||
"method" | ||
], | ||
[ | ||
"src/minisearch.js~minisearch#search", | ||
@@ -329,0 +335,0 @@ "class/src/MiniSearch.js~MiniSearch.html#instance-method-search", |
{ | ||
"name": "minisearch", | ||
"version": "0.1.4", | ||
"version": "0.1.5", | ||
"description": "fun with fulltext search", | ||
@@ -5,0 +5,0 @@ "main": "dist/minisearch.js", |
@@ -70,3 +70,3 @@ # MiniSearch | ||
let results = miniSearch.search('zen art motorcycle') | ||
// => [ { id: 2, score: 2.77258, match: { ... } }, { id: 4, score: 1.38629, { ... } } ] | ||
// => [ { id: 2, score: 2.77258, match: { ... } }, { id: 4, score: 1.38629, match: { ... } } ] | ||
@@ -73,0 +73,0 @@ // Search only specific fields |
@@ -99,2 +99,3 @@ import SearchableMap from './SearchableMap/SearchableMap.js' | ||
* Adds a document to the index | ||
* | ||
* @param {Object} document - the document to be indexed | ||
@@ -117,2 +118,3 @@ */ | ||
* Adds all the given documents to the index | ||
* | ||
* @param {Object[]} documents - an array of documents to be indexed | ||
@@ -125,2 +127,33 @@ */ | ||
/** | ||
* Removes the given document from the index. | ||
* | ||
* The document to delete must NOT have changed between indexing and deletion, | ||
* otherwise the index will be corrupted. Therefore, when reindexing a document | ||
* after a change, the correct order of operations is: | ||
* | ||
* 1. remove old version | ||
* 2. apply changes | ||
* 3. index new version | ||
* | ||
* @param {Object} document - the document to be indexed | ||
*/ | ||
remove (document) { | ||
const { tokenize, processTerm, fields, idField } = this._options | ||
if (document[idField] == null) { | ||
throw new Error(`Document does not have ID field "${idField}"`) | ||
} | ||
const [shortDocumentId] = Object.entries(this._documentIds) | ||
.find(([_, longId]) => document[idField] === longId) || [] | ||
if (shortDocumentId == null) { | ||
throw new Error(`Cannot remove document with ID ${document[idField]}: it is not in the index`) | ||
} | ||
fields.filter(field => document[field] != null).forEach(field => { | ||
tokenize(document[field]).forEach(term => { | ||
removeTerm(this, this._fieldIds[field], shortDocumentId, processTerm(term)) | ||
}) | ||
}) | ||
this._documentCount -= 1 | ||
} | ||
/** | ||
* Search for documents matching the given search query. | ||
@@ -240,2 +273,3 @@ * | ||
* with MiniSearch.fromJSON | ||
* | ||
* @return {string} the JSON-serialized index | ||
@@ -265,2 +299,3 @@ */ | ||
* originally used when serializing the index. | ||
* | ||
* @param {string} json - JSON-serialized index | ||
@@ -299,2 +334,36 @@ * @param {Object} options - configuration options, same as the constructor | ||
const removeTerm = function (self, fieldId, documentId, term) { | ||
if (!self._index.has(term)) { | ||
warnDocumentChanged(self, documentId, fieldId, term) | ||
return | ||
} | ||
self._index.update(term, indexData => { | ||
const fieldIndex = indexData[fieldId] | ||
if (fieldIndex == null || fieldIndex.ds[documentId] == null) { | ||
warnDocumentChanged(self, documentId, fieldId, term) | ||
return indexData | ||
} | ||
if (fieldIndex.df <= 1) { | ||
delete indexData[fieldId] | ||
return indexData | ||
} | ||
fieldIndex.df -= 1 | ||
if (fieldIndex.ds[documentId] <= 1) { | ||
delete fieldIndex.ds[documentId] | ||
return indexData | ||
} | ||
fieldIndex.ds[documentId] -= 1 | ||
return { ...indexData, [fieldId]: fieldIndex } | ||
}) | ||
if (Object.keys(self._index.get(term)).length === 0) { | ||
self._index.delete(term) | ||
} | ||
} | ||
const warnDocumentChanged = function (self, shortDocumentId, fieldId, term) { | ||
if (console == null || console.warn == null) { return } | ||
const fieldName = Object.entries(self._fieldIds).find(([name, id]) => id === fieldId)[0] | ||
console.warn(`MiniSearch: document with ID ${self._documentIds[shortDocumentId]} has changed before removal: term "${term}" was not present in field "${fieldName}". Removing a document after it has changed can corrupt the index!`) | ||
} | ||
const addDocumentId = function (self, documentId) { | ||
@@ -301,0 +370,0 @@ const shortDocumentId = self._documentCount |
@@ -35,2 +35,62 @@ /* eslint-env jest */ | ||
describe('remove', () => { | ||
const documents = [ | ||
{ id: 1, title: 'Divina Commedia', text: 'Nel mezzo del cammin di nostra vita' }, | ||
{ id: 2, title: 'I Promessi Sposi', text: 'Quel ramo del lago di Como' }, | ||
{ id: 3, title: 'Vita Nova', text: 'In quella parte del libro della mia memoria' } | ||
] | ||
let ms | ||
beforeEach(() => { | ||
ms = new MiniSearch({ fields: ['title', 'text'] }) | ||
ms.addAll(documents) | ||
}) | ||
it('removes the document from the index', () => { | ||
expect(ms.documentCount).toEqual(3) | ||
ms.remove(documents[0]) | ||
expect(ms.documentCount).toEqual(2) | ||
expect(ms.search('commedia').length).toEqual(0) | ||
expect(ms.search('vita').map(({ id }) => id)).toEqual([3]) | ||
}) | ||
it('cleans up the index', () => { | ||
ms.remove(documents[0]) | ||
expect(ms._index.has('commedia')).toEqual(false) | ||
expect(Object.keys(ms._index.get('vita'))).toEqual([ms._fieldIds.title.toString()]) | ||
}) | ||
describe('when the document was not in the index', () => { | ||
it('throws an error', () => { | ||
expect(() => ms.remove({ id: 99 })) | ||
.toThrow('Cannot remove document with ID 99: it is not in the index') | ||
}) | ||
}) | ||
describe('when the document has changed', () => { | ||
let _warn | ||
beforeEach(() => { | ||
_warn = console.warn | ||
console.warn = jest.fn() | ||
}) | ||
afterEach(() => { | ||
console.warn = _warn | ||
}) | ||
it('warns of possible index corruption', () => { | ||
expect(() => ms.remove({ id: 1, title: 'Divina Commedia cammin', text: 'something has changed' })) | ||
.not.toThrow() | ||
expect(console.warn).toHaveBeenCalledTimes(4) | ||
;[ | ||
['cammin', 'title'], | ||
['something', 'text'], | ||
['has', 'text'], | ||
['changed', 'text'] | ||
].forEach(([term, field], i) => { | ||
expect(console.warn).toHaveBeenNthCalledWith(i + 1, `MiniSearch: document with ID 1 has changed before removal: term "${term}" was not present in field "${field}". Removing a document after it has changed can corrupt the index!`) | ||
}) | ||
}) | ||
}) | ||
}) | ||
describe('addAll', () => { | ||
@@ -124,7 +184,16 @@ it('adds all the documents to the index', () => { | ||
it('works the same with all combinators', () => { | ||
const resultsOr = ms.search('vita nova', { combineWith: 'OR' }) | ||
const resultsAnd = ms.search('vita nova', { combineWith: 'AND' }) | ||
expect(resultsOr[0].match).toEqual(resultsAnd[0].match) | ||
it('reports correct info when combining terms with AND', () => { | ||
const results = ms.search('vita nova', { combineWith: 'AND' }) | ||
expect(results.map(({ match }) => match)).toEqual([ | ||
{ vita: ['title', 'text'], nova: ['title'] } | ||
]) | ||
}) | ||
it('reports correct info for fuzzy and prefix queries', () => { | ||
const results = ms.search('vi nuova', { termToQuery: term => ({ term, fuzzy: 0.2, prefix: true }) }) | ||
expect(results.map(({ match }) => match)).toEqual([ | ||
{ vita: ['title', 'text'], nova: ['title'] }, | ||
{ vita: ['text'] } | ||
]) | ||
}) | ||
}) | ||
@@ -131,0 +200,0 @@ }) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1404902
9520