From e1ae647f6d5739374246199ffb54b82ff2604f44 Mon Sep 17 00:00:00 2001 From: Thomas Wilkerling Date: Fri, 22 Feb 2019 15:21:06 +0100 Subject: [PATCH] v0.6.2 Improve Suggestion, Improve Coverage, Add Tests --- README.md | 4 +- compile.js | 19 +- dist/flexsearch.compact.js | 50 +- dist/flexsearch.es5.js | 86 ++-- dist/flexsearch.light.js | 26 +- dist/flexsearch.min.js | 69 +-- dist/flexsearch.node.js | 60 +-- dist/flexsearch.pre.js | 986 +++++++++++++++++++++++++++++++++++++ flexsearch.js | 255 +++++----- package.json | 14 +- test/matching.html | 10 +- test/test.es6.js | 31 +- test/test.js | 518 ++++++++++++++++++- 13 files changed, 1832 insertions(+), 296 deletions(-) create mode 100644 dist/flexsearch.pre.js diff --git a/README.md b/README.md index afc1b7f..a12ecbc 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,8 @@

Web's fastest and most memory-flexible full-text search library with zero dependencies.

-It is time for a quantum leap in full-text searching algorithms. When it comes to raw search speed FlexSearch outperforms every single searching library out there and also provides flexible search capabilities like multi-field search, phonetic transformations or partial matching. -Depending on the used options it also provides the most memory-efficient index. FlexSearch introduce a new scoring algorithm called "contextual index" based on a high-performance pre-scored lexical dictionary architecture which actually performs queries up to 100,000 times faster than Apache Lucene. +When it comes to raw search speed FlexSearch outperforms every single searching library out there and also provides flexible search capabilities like multi-field search, phonetic transformations or partial matching. +Depending on the used options it also provides the most memory-efficient index. FlexSearch introduce a new scoring algorithm called "contextual index" based on a pre-scored lexical dictionary architecture which actually performs queries up to 100,000 times faster than other libraries. FlexSearch also provides you a non-blocking asynchronous processing model as well as web workers to perform any updates or queries on the index in parallel through dedicated balanced threads. FlexSearch Server is available here: https://github.com/nextapps-de/flexsearch-server. diff --git a/compile.js b/compile.js index d26993b..41dd802 100644 --- a/compile.js +++ b/compile.js @@ -13,6 +13,8 @@ fs.existsSync("log") || fs.mkdirSync("log"); var flag_str = ""; var language_out; +var formatting; +var compilation_level; var options = (function(argv){ @@ -31,6 +33,14 @@ var options = (function(argv){ language_out = val; } + else if(index === "FORMATTING"){ + + formatting = val; + } + else if(index === "COMPILATION_LEVEL"){ + + compilation_level = val; + } else{ flag_str += " --define='" + index + "=" + val + "'"; @@ -50,6 +60,11 @@ var options = (function(argv){ var parameter = (function(opt){ + if(formatting && !opt["formatting"]){ + + opt["formatting"] = formatting; + } + var parameter = ''; for(var index in opt){ @@ -63,7 +78,7 @@ var parameter = (function(opt){ return parameter; })({ - compilation_level: "ADVANCED_OPTIMIZATIONS", //"WHITESPACE" + compilation_level: compilation_level || "ADVANCED_OPTIMIZATIONS", //"WHITESPACE" use_types_for_optimization: true, new_type_inf: true, jscomp_warning: "newCheckTypes", @@ -80,7 +95,7 @@ var parameter = (function(opt){ output_manifest: "log/manifest.log", output_module_dependencies: "log/module_dependencies.log", property_renaming_report: "log/renaming_report.log" - //formatting: "PRETTY_PRINT" + //formatting: formatting || "DEFAULT" //"PRETTY_PRINT" }); var release = options["RELEASE"]; diff --git a/dist/flexsearch.compact.js b/dist/flexsearch.compact.js index 506f41c..1f089f6 100644 --- a/dist/flexsearch.compact.js +++ b/dist/flexsearch.compact.js @@ -1,31 +1,31 @@ /* - FlexSearch v0.6.1 + FlexSearch v0.6.2 Copyright 2019 Nextapps GmbH Author: Thomas Wilkerling Released under the Apache 2.0 Licence https://github.com/nextapps-de/flexsearch */ -'use strict';(function(l,F,G){let x;(x=G.define)&&x.amd?x([],function(){return F}):(x=G.modules)?x[l.toLowerCase()]=F:"object"===typeof exports?module.exports=F:G[l]=F})("FlexSearch",function(){function l(b,a){const c=a?a.id:b&&b.id;this.id=c||0===c?c:aa++;this.init(b,a);x(this,"index",function(){return this.a?Object.keys(this.a.index[this.a.keys[0]].f):Object.keys(this.f)});x(this,"length",function(){return this.index.length})}function F(b,a){const c=b.length,d=H(a),f=[];for(let m=0,g=0;m=g&&(b=b[h-(f+.5>>0)],b=b[c]||(b[c]=[]),b[b.length]=d);return f}function S(b,a){if(b){const c=Object.keys(b);for(let d=0,f=c.length;db?1:b?-1:0}function ca(b,a){b=b[y];a=a[y];return ba?1:0}function ba(b,a){const c=y.length;for(let d=0;da?1:0}function I(b,a,c){return b?{page:b,next:a?""+a:null,result:c}:c}function V(b,a,c,d,f,m,g){let h,p=[],e;const n=b.length;!0===c?(c="0",e=""):e=c&&c.split(":"); -if(1n;c--)u=g.substring(n,c),P(z,m,u,b,a,p,l,t-1)}break;default:if(h=P(z,m,g,b,1,p,l,t-1),r&&1=l)for(h=m._ctx[g]||(m._ctx[g]=A()),g=this.h[g]||(this.h[g]=W(t-(l||0))),p= -a-r,u=a+r+1,0>p&&(p=0),u>k&&(u=k);p=g&&(a=a[p-(f+.5>>0)],a=a[c]||(a[c]=[]),a[a.length]=d);return f}function S(a,b){if(a){const c=Object.keys(a);for(let d=0,f=c.length;da?1:a?-1:0}function ca(a,b){a=a[x];b=b[x];return ab?1:0}function ba(a,b){const c=x.length;for(let d=0;db?1:0}function K(a,b,c){return a?{page:a,next:b?""+b:null,result:c}:c}function V(a,b,c,d,f,k,g){let p,q=[];if(!0===c){c="0";var l=""}else l=c&&c.split(":"); +const e=a.length;if(1g&&(l=0),l=l||0,p=l+b,pr;c--)l=g.substring(r,c),Q(v,m,l,a,b,q,p,n-1)}break;default:if(e=Q(v,m,g,a,1,q,p,n-1),t&&1=p)for(e=m._ctx[g]||(m._ctx[g]=A()),g=this.h[g]||(this.h[g]= +W(n-(p||0))),q=b-t,l=b+t+1,0>q&&(q=0),l>h&&(l=h);q= -h&&(this.D=this.i),this.D===this.i&&(this.cache&&this.v.set(b,this.A),this.H&&this.H(this.A)));return this}function p(a,b){for(var c=a.length,h=R(b),l=[],w=0,e=0;w=e&&(a=a[d-(l+.5>>0)],a=a[c]||(a[c]=[]),a[a.length]=h);return l}function X(a,b){if(a)for(var c=Object.keys(a),h=0,l=c.length;ha?1:a?-1:0}function qa(a,b){a=a[D];b=b[D];return ab?1:0}function ra(a,b){for(var c=D.length,h=0;hb?1:0}function G(a,b,c){return a?{page:a,next:b?""+b:null,result:c}:c}function ha(a,b,c,h,l,e,d){var w=[],g=a.length;if(!0===c){c="0";var f=""}else f=c&&c.split(":");if(1=this.b.length&&(this.w=0),this.b[this.w].postMessage({add:!0,id:a,content:b}),this.f[e]=""+this.w,c&&c(),this;if(!d){if(this.async&&"function"!==typeof importScripts){var l=this;e=new Promise(function(c){setTimeout(function(){l.add(a,b,null,h,!0);l=null;c()})});if(c)e.then(c);else return e;return this}if(c)return this.add(a,b,null,h,!0),c(),this}b=this.encode(b);if(!b.length)return this;c=this.h; -d=R(c)?c(b):b.split(this.split);this.filter&&(d=p(d,this.filter));var f=E();f._ctx=E();for(var g=d.length,k=this.threshold,aa=this.depth,t=this.c,V=this.u,n=this.G,r=0;rA;u--)q=y.substring(A,u),C(V,f,q,a,x,z, -k,t-1);break;default:if(v=C(V,f,y,a,1,z,k,t-1),aa&&1=k)for(v=f._ctx[y]||(f._ctx[y]=E()),y=this.s[y]||(this.s[y]=ia(t-(k||0))),z=r-aa,q=r+aa+1,0>z&&(z=0),q>g&&(q=g);zf;d--)k=g[d-1],g[d]=k,e[k]=d;g[f]=a;e[a]=f}}}return b};return a}();return g}(function(){var d={},m="undefined"!==typeof Blob&& -"undefined"!==typeof URL&&URL.createObjectURL;return function(g,k,p,Q,f){p=m?URL.createObjectURL(new Blob(["("+p.toString()+")()"],{type:"text/javascript"})):g+".es5.js";g+="-"+k;d[g]||(d[g]=[]);d[g][f]=new Worker(p);d[g][f].onmessage=Q;console.log("Register Worker: "+g+"@"+f);return d[g][f]}}()),this); +'use strict';function ca(d){var l=0;return function(){return l= +h&&(this.D=this.i),this.D===this.i&&(this.cache&&this.v.set(b,this.A),this.H&&this.H(this.A)));return this}function n(a,b){for(var c=a.length,h=V(b),t=[],q=0,e=0;q=e&&(a=a[d-(t+.5>>0)],a=a[c]||(a[c]=[]),a[a.length]=h);return t}function Z(a,b){if(a)for(var c=Object.keys(a),h=0,t=c.length;ha?1:a?-1:0}function ra(a,b){a=a[F];b=b[F];return ab?1:0}function sa(a,b){for(var c=F.length,h=0;hb?1:0}function L(a,b,c){return a?{page:a,next:b?""+b:null,result:c}:c}function ia(a,b,c,h,t,q,e){var d=[];if(!0===c){c="0";var f=""}else f=c&&c.split(":");var g=a.length;if(1e&&(f=0);f=f||0;var N=f+b;N=this.b.length&&(this.w=0),this.b[this.w].postMessage({add:!0,id:a,content:b}),this.f[h]=""+this.w,c&&c(),this;if(!e){if(this.async&&"function"!==typeof importScripts){var f=this;h=new Promise(function(c){setTimeout(function(){f.add(a,b,null,d,!0);f=null;c()})});if(c)h.then(c);else return h;return this}if(c)return this.add(a,b,null,d,!0),c(),this}b= +this.encode(b);if(!b.length)return this;c=this.g;e=V(c)?c(b):b.split(this.split);this.filter&&(e=n(e,this.filter));var t=H();t._ctx=H();for(var g=e.length,k=this.threshold,ba=this.depth,r=this.c,A=this.u,m=this.G,p=0;px;B--)u=z.substring(x,B),G(A,t,u,a,w,y,k,r-1);break;default:if(v=G(A,t,z,a,1,y,k,r-1),ba&&1=k)for(v=t._ctx[z]||(t._ctx[z]=H()),z=this.s[z]||(this.s[z]=ja(r-(k||0))),y=p-ba,u=p+ba+1,0>y&&(y=0),u>g&&(u=g);yf;d--)k=g[d-1],g[d]=k,e[k]=d;g[f]=a;e[a]=f}}}return b};return a}();return f}(function(){var d={},l="undefined"!==typeof Blob&&"undefined"!==typeof URL&&URL.createObjectURL;return function(f,k,n,T,g){n=l?URL.createObjectURL(new Blob(["("+n.toString()+")()"],{type:"text/javascript"})):f+".es5.js";f+="-"+k;d[f]||(d[f]=[]);d[f][g]=new Worker(n);d[f][g].onmessage=T;console.log("Register Worker: "+f+"@"+g);return d[f][g]}}()),this); diff --git a/dist/flexsearch.light.js b/dist/flexsearch.light.js index 4c9c1f7..2a23b30 100644 --- a/dist/flexsearch.light.js +++ b/dist/flexsearch.light.js @@ -1,19 +1,19 @@ /* - FlexSearch v0.6.1 + FlexSearch v0.6.2 Copyright 2019 Nextapps GmbH Author: Thomas Wilkerling Released under the Apache 2.0 Licence https://github.com/nextapps-de/flexsearch */ -'use strict';(function(e,z,t){let k;(k=t.define)&&k.amd?k([],function(){return z}):(k=t.modules)?k[e.toLowerCase()]=z:"object"===typeof exports?module.exports=z:t[e]=z})("FlexSearch",function(){function e(a,c){const b=c?c.id:a&&a.id;this.id=b||0===b?b:K++;this.init(a,c);t(this,"index",function(){return Object.keys(this.b)});t(this,"length",function(){return this.index.length})}function z(a,c){const b=a.length,f=A(c),d=[];for(let l=0,g=0;l=g&&(a=a[h-(d+.5>>0)],a=a[b]||(a[b]=[]),a[a.length]=f);return d}function E(a,c){if(a){const b=Object.keys(a);for(let f=0,d=b.length;fa?1:a?-1:0}function F(a,c,b){return a?{page:a,next:c?""+c:null,result:b}:b}function C(a){return"string"===typeof a}function A(a){return"function"===typeof a}function D(a){return"undefined"===typeof a}function H(a){const c=Array(a);for(let b=0;bm;c--)n=l.substring(m,c),B(q,f,n,a,b,h,r,p-1)}break;default:if(g=B(q,f,l,a,1,h,r,p-1),k&&1=r)for(g=f._ctx[l]||(f._ctx[l]=y()),l=this.f[l]||(this.f[l]=H(p-(r||0))),h=e-k,n=e+k+1,0>h&&(h=0),n>d&&(n=d);h=h&&(a=a[g-(d+.5>>0)],a=a[c]||(a[c]=[]),a[a.length]=f);return d}function G(a,b){if(a){const c=Object.keys(a);for(let f=0,d=c.length;fa?1:a?-1:0}function H(a,b,c){return a?{page:a,next:b?""+b:null,result:c}:c}function E(a){return"string"===typeof a}function C(a){return"function"===typeof a}function F(a){return"undefined"===typeof a}function J(a){const b=Array(a);for(let c=0;cl;c--)m=k.substring(l,c),D(p,f,m,a,b,g,r,v-1)}break;default:if(h=D(p,f,k,a,1,g,r,v-1),u&&1=r)for(h=f._ctx[k]||(f._ctx[k]=x()),k=this.f[k]||(this.f[k]=J(v-(r||0))),g=e-u,m=e+u+1,0>g&&(g=0),m>d&&(m=d);gy&&(g=0),z=g||0,k=z+b,k=d&&(this.u=this.g),this.u===this.g&&(this.cache&&this.l.set(b,this.o),this.F&&this.F(this.o)));return this}function P(a,b){const c=a.length,d=J(b),e=[];for(let g=0,f=0;g=f&&(a=a[r-(e+.5>>0)],a=a[c]||(a[c]=[]),a[a.length]=d);return e}function W(a,b){if(a){const c=Object.keys(a);for(let d=0,e=c.length;da?1:a?-1:0}function ha(a,b){a=a[E];b=b[E];return ab?1:0}function fa(a, -b){const c=E.length;for(let d=0;db?1:0}function L(a,b,c){return a?{page:a,next:b?""+b:null,result:c}:c}function Y(a,b,c,d,e,g,f){let r,q=[],n;const p=a.length;!0===c?(c="0",n=""):n=c&&c.split(":");if(1=this.m.length&&(this.C=0),this.m[this.C].postMessage({add:!0,id:a,content:b}),this.c[g]=""+this.C,c&&c(),this;if(!e){if(this.async&&"function"!==typeof importScripts){let e=this;g=new Promise(function(c){setTimeout(function(){e.add(a,b,null,d,!0);e=null;c()})});if(c)g.then(c);else return g;return this}if(c)return this.add(a,b,null,d,!0), -c(),this}b=this.encode(b);if(!b.length)return this;c=this.f;e=J(c)?c(b):b.split(this.split);this.filter&&(e=P(e,this.filter));const m=u();m._ctx=u();const k=e.length,h=this.threshold,l=this.depth,v=this.b,A=this.j,Q=this.D;for(let b=0;bp;c--)n=f.substring(p,c),T(A,m,n,a,b,q,h,v-1)}break;default:if(r=T(A,m,f,a,1,q,h,v-1),l&&1=h)for(r=m._ctx[f]||(m._ctx[f]=u()),f=this.i[f]||(this.i[f]=Z(v-(h||0))),q=b-l,n=b+l+1,0>q&&(q=0),n>k&&(n=k);qd;b--)e=f[b-1],f[b]=e,c[e]=b;f[d]=a;c[a]=d}}}return c};return a}();return l}(function(){const x={},N="undefined"!==typeof Blob&&"undefined"!==typeof URL&&URL.createObjectURL;return function(l,z,P,S,O){P=N?URL.createObjectURL(new Blob(["("+P.toString()+")()"],{type:"text/javascript"})):l+".min.js";l+="-"+z;x[l]||(x[l]=[]);x[l][O]=new Worker(P);x[l][O].onmessage=S;return x[l][O]}}()),this); +'use strict';(function(y,L,m){let B;(B=m.define)&&B.amd?B([],function(){return L}):(B=m.modules)?B[y.toLowerCase()]=L:"object"===typeof exports?module.exports=L:m[y]=L})("FlexSearch",function da(y){function m(a,b){const c=b?b.id:a&&a.id;this.id=c||0===c?c:ea++;this.init(a,b);M(this,"index",function(){return this.a?Object.keys(this.a.index[this.a.keys[0]].c):Object.keys(this.c)});M(this,"length",function(){return this.index.length})}function B(a,b,c,d){this.u!==this.g&&(this.o=this.o.concat(c),this.u++, +d&&this.o.length>=d&&(this.u=this.g),this.u===this.g&&(this.cache&&this.l.set(b,this.o),this.F&&this.F(this.o)));return this}function N(a,b){const c=a.length,d=J(b),e=[];for(let g=0,f=0;g=f&&(a=a[l-(e+.5>>0)],a=a[c]||(a[c]=[]),a[a.length]=d);return e}function W(a,b){if(a){const c=Object.keys(a);for(let d=0,e=c.length;da?1:a?-1:0}function ha(a,b){a=a[A];b=b[A];return ab?1:0}function fa(a, +b){const c=A.length;for(let d=0;db?1:0}function O(a,b,c){return a?{page:a,next:b?""+b:null,result:c}:c}function Y(a,b,c,d,e,g,f){let l,r=[];if(!0===c){c="0";var p=""}else p=c&&c.split(":");const q=a.length;if(1f&&(p=0),p=p||0,l=p+b,l=this.m.length&&(this.C=0),this.m[this.C].postMessage({add:!0,id:a,content:b}),this.c[g]=""+this.C,c&&c(),this;if(!e){if(this.async&&"function"!==typeof importScripts){let e=this;g=new Promise(function(c){setTimeout(function(){e.add(a,b,null,d,!0);e=null;c()})});if(c)g.then(c);else return g;return this}if(c)return this.add(a,b,null,d,!0), +c(),this}b=this.encode(b);if(!b.length)return this;c=this.f;e=J(c)?c(b):b.split(this.split);this.filter&&(e=N(e,this.filter));const n=w();n._ctx=w();const k=e.length,h=this.threshold,m=this.depth,u=this.b,v=this.j,T=this.D;for(let b=0;bq;c--)p=f.substring(q,c),S(v,n,p,a,b,r,h,u-1)}break;default:if(l=S(v,n,f,a,1,r,h,u-1),m&&1=h)for(l=n._ctx[f]||(n._ctx[f]=w()),f=this.i[f]||(this.i[f]=Z(u-(h||0))),r=b-m,p=b+m+1,0>r&&(r=0),p>k&&(p=k);rc;d--)e=f[d-1],f[d]=e,b[e]=d;f[c]=a;b[a]=c}}}return b};return a}();return m}(function(){const y={},L="undefined"!==typeof Blob&&"undefined"!==typeof URL&&URL.createObjectURL;return function(m,B,N,R,M){N=L?URL.createObjectURL(new Blob(["("+N.toString()+")()"],{type:"text/javascript"})):m+".min.js";m+="-"+B;y[m]||(y[m]=[]);y[m][M]=new Worker(N);y[m][M].onmessage=R;return y[m][M]}}()), +this); diff --git a/dist/flexsearch.node.js b/dist/flexsearch.node.js index 46f8dff..dc57691 100644 --- a/dist/flexsearch.node.js +++ b/dist/flexsearch.node.js @@ -1,36 +1,36 @@ /* - FlexSearch v0.6.1 + FlexSearch v0.6.2 Copyright 2019 Nextapps GmbH Author: Thomas Wilkerling Released under the Apache 2.0 Licence https://github.com/nextapps-de/flexsearch */ -'use strict';(function(n,K,L){let H;(H=L.define)&&H.amd?H([],function(){return K}):(H=L.modules)?H[n.toLowerCase()]=K:"object"===typeof exports?module.exports=K:L[n]=K})("FlexSearch",function(){function n(b,a){const c=a?a.id:b&&b.id;this.id=c||0===c?c:aa++;this.init(b,a);H(this,"index",function(){return this.a?Object.keys(this.a.index[this.a.keys[0]].f):Object.keys(this.f)});H(this,"length",function(){return this.index.length})}function K(b,a){const c=b.length,d=I(a),e=[];for(let g=0,f=0;g=f&&(b=b[r-(e+.5>>0)],b=b[c]||(b[c]=[]),b[b.length]=d);return e}function T(b,a){if(b){const c=Object.keys(b);for(let d=0,e=c.length;db?1:b?-1:0}function ca(b,a){b=b[x];a=a[x];return ba?1:0}function ba(b,a){const c=x.length;for(let d=0;da?1:0}function M(b,a,c){return b?{page:b,next:a?""+a:null,result:c}:c}function V(b,a,c,d,e,g,f){let r,q=[],l;const p=b.length; -!0===c?(c="0",l=""):l=c&&c.split(":");if(1p;c--)l=f.substring(p,c),Q(A,k,l,b,a,q,h,n-1)}break;default:if(r=Q(A,k,f,b,1,q,h,n-1),t&&1=h)for(r=k._ctx[f]||(k._ctx[f]=v()),f=this.h[f]||(this.h[f]=W(n-(h||0))),q=a-t,l=a+t+1,0>q&&(q=0),l>m&&(l=m);qc;d--)e=f[d-1],f[d]=e,b[e]=d;f[c]=a;b[a]=c}}}return b};return b}();return n}(!1),this); +'use strict';(function(m,H,I){let z;(z=I.define)&&z.amd?z([],function(){return H}):(z=I.modules)?z[m.toLowerCase()]=H:"object"===typeof exports?module.exports=H:I[m]=H})("FlexSearch",function(){function m(a,b){const c=b?b.id:a&&a.id;this.id=c||0===c?c:aa++;this.init(a,b);z(this,"index",function(){return this.a?Object.keys(this.a.index[this.a.keys[0]].f):Object.keys(this.f)});z(this,"length",function(){return this.index.length})}function H(a,b){const c=a.length,d=J(b),e=[];for(let g=0,f=0;g=f&&(a=a[k-(e+.5>>0)],a=a[c]||(a[c]=[]),a[a.length]=d);return e}function T(a,b){if(a){const c=Object.keys(a);for(let d=0,e=c.length;da?1:a?-1:0}function ca(a,b){a=a[y];b=b[y];return ab?1:0}function ba(a,b){const c=y.length;for(let d=0;db?1:0}function L(a,b,c){return a?{page:a,next:b?""+b:null,result:c}:c}function V(a,b,c,d,e,g,f){let k,r=[];if(!0===c){c="0";var p= +""}else p=c&&c.split(":");const q=a.length;if(1f&&(p=0),p=p||0,k=p+b,kq;c--)p=f.substring(q,c),P(v,n,p,a,b,r,h,m-1)}break;default:if(k=P(v,n,f,a,1,r,h,m-1),t&&1=h)for(k=n._ctx[f]||(n._ctx[f]=u()),f=this.h[f]||(this.h[f]=W(m-(h||0))),r=b-t,p=b+t+1,0>r&&(r=0),p>l&&(p=l);rc;d--)e=f[d-1],f[d]=e,b[e]=d;f[c]=a;b[a]=c}}}return b};return a}();return m}(!1),this); diff --git a/dist/flexsearch.pre.js b/dist/flexsearch.pre.js new file mode 100644 index 0000000..7a3b396 --- /dev/null +++ b/dist/flexsearch.pre.js @@ -0,0 +1,986 @@ +/* + FlexSearch v0.6.2 + Copyright 2019 Nextapps GmbH + Author: Thomas Wilkerling + Released under the Apache 2.0 Licence + https://github.com/nextapps-de/flexsearch +*/ +'use strict'; +(function(m, H, I) { + let z; + (z = I.define) && z.amd ? z([], function() { + return H; + }) : (z = I.modules) ? z[m.toLowerCase()] = H : "object" === typeof exports ? module.exports = H : I[m] = H; +})("FlexSearch", function() { + function m(a, b) { + const c = b ? b.id : a && a.id; + this.id = c || 0 === c ? c : aa++; + this.init(a, b); + z(this, "index", function() { + return this.a ? Object.keys(this.a.index[this.a.keys[0]].f) : Object.keys(this.f); + }); + z(this, "length", function() { + return this.index.length; + }); + } + function H(a, b) { + const c = a.length, d = J(b), e = []; + for (let g = 0, f = 0; g < c; g++) { + const c = a[g]; + if (d && b(c) || !d && !b[c]) { + e[f++] = c; + } + } + return e; + } + function I(a, b, c, d, e, g, f, k, r, p) { + c = V(c, f ? 0 : e, k, g, b, r, p); + let q; + k && (k = c.page, q = c.next, c = c.result); + if (f) { + b = this.where(f, null, e, c); + } else { + b = c; + c = this.g; + e = b.length; + g = Array(e); + for (f = 0; f < e; f++) { + g[f] = c[b[f]]; + } + b = g; + } + c = b; + d && (J(d) || (y = d.split(":"), 1 < y.length ? d = ba : (y = y[0], d = ca)), c.sort(d)); + c = L(k, q, c); + this.cache && this.j.set(a, c); + return c; + } + function z(a, b, c) { + Object.defineProperty(a, b, {get:c}); + } + function h(a) { + return new RegExp(a, "g"); + } + function K(a, b) { + for (let c = 0; c < b.length; c += 2) { + a = a.replace(b[c], b[c + 1]); + } + return a; + } + function P(a, b, c, d, e, g, f, k) { + if (b[c]) { + return b[c]; + } + e = e ? (k - (f || k / 1.5)) * g + (f || k / 1.5) * e : g; + b[c] = e; + e >= f && (a = a[k - (e + 0.5 >> 0)], a = a[c] || (a[c] = []), a[a.length] = d); + return e; + } + function T(a, b) { + if (a) { + const c = Object.keys(a); + for (let d = 0, e = c.length; d < e; d++) { + const e = c[d], f = a[e]; + if (f) { + for (let c = 0, d = f.length; c < d; c++) { + if (f[c] === b) { + 1 === d ? delete a[e] : f.splice(c, 1); + break; + } else { + A(f[c]) && T(f[c], b); + } + } + } + } + } + } + function U(a) { + let b = "", c = ""; + var d = ""; + for (let e = 0; e < a.length; e++) { + const g = a[e]; + if (g !== c) { + if (e && "h" === g) { + if (d = "a" === d || "e" === d || "i" === d || "o" === d || "u" === d || "y" === d, ("a" === c || "e" === c || "i" === c || "o" === c || "u" === c || "y" === c) && d || " " === c) { + b += g; + } + } else { + b += g; + } + } + d = e === a.length - 1 ? "" : a[e + 1]; + c = g; + } + return b; + } + function da(a, b) { + a = a.length - b.length; + return 0 > a ? 1 : a ? -1 : 0; + } + function ca(a, b) { + a = a[y]; + b = b[y]; + return a < b ? -1 : a > b ? 1 : 0; + } + function ba(a, b) { + const c = y.length; + for (let d = 0; d < c; d++) { + a = a[y[d]], b = b[y[d]]; + } + return a < b ? -1 : a > b ? 1 : 0; + } + function L(a, b, c) { + return a ? {page:a, next:b ? "" + b : null, result:c} : c; + } + function V(a, b, c, d, e, g, f) { + let k, r = []; + if (!0 === c) { + c = "0"; + var p = ""; + } else { + p = c && c.split(":"); + } + const q = a.length; + if (1 < q) { + const R = u(); + let B = [], M, G; + var n = 0, l; + let N; + var h = !0; + let D, E = 0, x, w, y, A; + p && (2 === p.length ? (y = p, p = !1) : p = A = parseInt(p[0], 10)); + if (f) { + for (M = u(); n < q; n++) { + if ("not" === e[n]) { + for (G = a[n], N = G.length, l = 0; l < N; l++) { + M["@" + G[l]] = 1; + } + } else { + w = n + 1; + } + } + if (C(w)) { + return L(c, k, r); + } + n = 0; + } else { + x = F(e) && e; + } + let z; + for (; n < q; n++) { + const u = n === (w || q) - 1; + if (!x || !n) { + if ((l = x || e && e[n]) && "and" !== l) { + if ("or" === l) { + z = !1; + } else { + continue; + } + } else { + z = g = !0; + } + } + G = a[n]; + if (N = G.length) { + if (h) { + if (D) { + var t = D.length; + for (l = 0; l < t; l++) { + h = D[l]; + var m = "@" + h; + f && M[m] || (R[m] = 1, g || (r[E++] = h)); + } + D = null; + h = !1; + } else { + D = G; + continue; + } + } + m = !1; + for (l = 0; l < N; l++) { + t = G[l]; + var v = "@" + t; + const a = g ? R[v] || 0 : n; + if (!(!a && !d || f && M[v] || !g && R[v])) { + if (a === n) { + if (u) { + if (!A || --A < E) { + if (r[E++] = t, b && E === b) { + return L(c, E + (p || 0), r); + } + } + } else { + R[v] = n + 1; + } + m = !0; + } else { + d && (v = B[a] || (B[a] = []), v[v.length] = t); + } + } + } + if (z && !m && !d) { + break; + } + } else { + if (z && !d) { + return L(c, k, G); + } + } + } + if (D) { + if (n = D.length, f) { + for (l = p ? parseInt(p, 10) : 0; l < n; l++) { + a = D[l], M["@" + a] || (r[E++] = a); + } + } else { + r = D; + } + } + if (d) { + for (E = r.length, y ? (n = parseInt(y[0], 10) + 1, l = parseInt(y[1], 10) + 1) : (n = B.length, l = 0); n--;) { + if (t = B[n]) { + for (N = t.length; l < N; l++) { + if (d = t[l], !f || !M["@" + d]) { + if (r[E++] = d, b && E === b) { + return L(c, n + ":" + l, r); + } + } + } + l = 0; + } + } + } + } else { + !q || e && "not" === e[0] || (r = a[0], p && (p = parseInt(p[0], 10))); + } + b && (f = r.length, p && p > f && (p = 0), p = p || 0, k = p + b, k < f ? r = r.slice(p, k) : (k = 0, p && (r = r.slice(p)))); + return L(c, k, r); + } + function F(a) { + return "string" === typeof a; + } + function x(a) { + return a.constructor === Array; + } + function J(a) { + return "function" === typeof a; + } + function A(a) { + return "object" === typeof a; + } + function C(a) { + return "undefined" === typeof a; + } + function W(a) { + const b = Array(a); + for (let c = 0; c < a; c++) { + b[c] = u(); + } + return b; + } + function u() { + return Object.create(null); + } + const w = {encode:"icase", c:"forward", split:/\W+/, cache:!1, async:!1, C:!1, v:!1, a:!1, b:9, threshold:0, depth:0}, X = {memory:{encode:"extra", c:"strict", threshold:0, b:1}, speed:{encode:"icase", c:"strict", threshold:1, b:3, depth:2}, match:{encode:"extra", c:"full", threshold:1, b:3}, score:{encode:"extra", c:"strict", threshold:1, b:9, depth:4}, balance:{encode:"balance", c:"strict", threshold:0, b:3, depth:3}, fast:{encode:"icase", c:"strict", threshold:8, b:9, depth:1}}, S = []; + let aa = 0; + const Y = {}, Z = {}; + m.create = function(a, b) { + return new m(a, b); + }; + m.registerMatcher = function(a) { + for (const b in a) { + a.hasOwnProperty(b) && S.push(h(b), a[b]); + } + return this; + }; + m.registerEncoder = function(a, b) { + O[a] = b.bind(O); + return this; + }; + m.registerLanguage = function(a, b) { + Y[a] = b.filter; + Z[a] = b.stemmer; + return this; + }; + m.encode = function(a, b) { + return O[a](b); + }; + m.prototype.init = function(a, b) { + this.m = []; + if (b) { + var c = b.preset; + a = b; + } else { + a || (a = w), c = a.preset; + } + b = {}; + F(a) ? (b = X[a], a = {}) : c && (b = X[c]); + this.c = a.tokenize || b.c || this.c || w.c; + this.split = a.split || this.split || w.split; + this.v = a.rtl || this.v || w.v; + this.async = "undefined" === typeof Promise || C(c = a.async) ? this.async || w.async : c; + this.threshold = C(c = a.threshold) ? b.threshold || this.threshold || w.threshold : c; + this.b = C(c = a.resolution) ? c = b.b || this.b || w.b : c; + c <= this.threshold && (this.b = this.threshold + 1); + this.depth = "strict" !== this.c || C(c = a.depth) ? b.depth || this.depth || w.depth : c; + this.o = (c = C(c = a.encode) ? b.encode || w.encode : c) && O[c] && O[c].bind(O) || (J(c) ? c : this.o || !1); + (c = a.matcher) && this.addMatcher(c); + if (c = (b = a.lang) || a.filter) { + F(c) && (c = Y[c]); + if (x(c)) { + var d = this.o, e = u(); + for (var g = 0; g < c.length; g++) { + var f = d ? d(c[g]) : c[g]; + e[f] = 1; + } + c = e; + } + this.filter = c; + } + if (c = b || a.stemmer) { + var k; + b = F(c) ? Z[c] : c; + d = this.o; + e = []; + for (k in b) { + b.hasOwnProperty(k) && (g = d ? d(k) : k, e.push(h(g + "($|\\W)"), d ? d(b[k]) : b[k])); + } + this.stemmer = k = e; + } + this.a = e = (c = a.doc) ? c : this.a || w.a; + this.i = W(this.b - (this.threshold || 0)); + this.h = u(); + this.f = u(); + if (e) { + this.g = u(); + a.doc = null; + k = e.index = {}; + b = e.keys = []; + d = e.field; + g = e.tag; + x(e.id) || (e.id = e.id.split(":")); + if (g) { + this.w = u(); + f = u(); + if (d) { + if (F(d)) { + f[d] = a; + } else { + if (x(d)) { + for (let b = 0; b < d.length; b++) { + f[d[b]] = a; + } + } else { + A(d) && (f = d); + } + } + } + x(g) || (e.tag = g = [g]); + for (d = 0; d < g.length; d++) { + this.w[g[d]] = u(); + } + this.B = g; + d = f; + } + if (d) { + let c; + x(d) || (A(d) ? (c = d, e.field = d = Object.keys(d)) : e.field = d = [d]); + for (e = 0; e < d.length; e++) { + g = d[e], x(g) || (c && (a = c[g]), b[e] = g, d[e] = g.split(":")), k[g] = new m(a), k[g].g = this.g; + } + } + } + this.u = !0; + this.j = (this.cache = c = C(c = a.cache) ? this.cache || w.cache : c) ? new ea(c) : !1; + return this; + }; + m.prototype.encode = function(a) { + a && S.length && (a = K(a, S)); + a && this.m.length && (a = K(a, this.m)); + a && this.o && (a = this.o(a)); + a && this.stemmer && (a = K(a, this.stemmer)); + return a; + }; + m.prototype.addMatcher = function(a) { + const b = this.m; + for (const c in a) { + a.hasOwnProperty(c) && b.push(h(c), a[c]); + } + return this; + }; + m.prototype.add = function(a, b, c, d, e) { + if (this.a && A(a)) { + return this.s("add", a, b); + } + if (b && F(b) && (a || 0 === a)) { + var g = "@" + a; + if (this.f[g] && !d) { + return this.update(a, b); + } + if (!e) { + if (this.async) { + let e = this; + g = new Promise(function(c) { + setTimeout(function() { + e.add(a, b, null, d, !0); + e = null; + c(); + }); + }); + if (c) { + g.then(c); + } else { + return g; + } + return this; + } + if (c) { + return this.add(a, b, null, d, !0), c(), this; + } + } + b = this.encode(b); + if (!b.length) { + return this; + } + c = this.c; + e = J(c) ? c(b) : b.split(this.split); + this.filter && (e = H(e, this.filter)); + const n = u(); + n._ctx = u(); + const l = e.length, h = this.threshold, t = this.depth, m = this.b, v = this.i, Q = this.v; + for (let b = 0; b < l; b++) { + var f = e[b]; + if (f) { + var k = f.length, r = (Q ? b + 1 : l - b) / l, p = ""; + switch(c) { + case "reverse": + case "both": + for (var q = k; --q;) { + p = f[q] + p, P(v, n, p, a, Q ? 1 : (k - q) / k, r, h, m - 1); + } + p = ""; + case "forward": + for (q = 0; q < k; q++) { + p += f[q], P(v, n, p, a, Q ? (q + 1) / k : 1, r, h, m - 1); + } + break; + case "full": + for (q = 0; q < k; q++) { + const b = (Q ? q + 1 : k - q) / k; + for (let c = k; c > q; c--) { + p = f.substring(q, c), P(v, n, p, a, b, r, h, m - 1); + } + } + break; + default: + if (k = P(v, n, f, a, 1, r, h, m - 1), t && 1 < l && k >= h) { + for (k = n._ctx[f] || (n._ctx[f] = u()), f = this.h[f] || (this.h[f] = W(m - (h || 0))), r = b - t, p = b + t + 1, 0 > r && (r = 0), p > l && (p = l); r < p; r++) { + r !== b && P(f, k, e[r], a, 0, m - (r < b ? b - r : r - b), h, m - 1); + } + } + } + } + } + this.f[g] = 1; + this.u = !1; + } + return this; + }; + m.prototype.s = function(a, b, c) { + if (x(b)) { + for (let d = 0, e = b.length; d < e; d++) { + if (d === e - 1) { + return this.s(a, b[d], c); + } + this.s(a, b[d]); + } + } else { + const h = this.a.index, p = this.a.keys; + var d = this.a.tag, e = this.a.id; + let q; + let n; + for (var g = 0; g < e.length; g++) { + q = (q || b)[e[g]]; + } + if (d) { + for (e = 0; e < d.length; e++) { + var f = d[e]; + var k = f.split(":"); + for (g = 0; g < k.length; g++) { + n = (n || b)[k[g]]; + } + n = "@" + n; + } + k = this.w[f]; + k = k[n] || (k[n] = []); + } + if ("remove" === a) { + delete this.g[q]; + for (let b = 0, a = p.length; b < a; b++) { + if (b === a - 1) { + return h[p[b]].remove(q, c), this; + } + h[p[b]].remove(q); + } + } + e = this.a.field; + k && (k[k.length] = b); + this.g[q] = b; + for (let g = 0, k = e.length; g < k; g++) { + d = e[g]; + let l; + for (f = 0; f < d.length; f++) { + l = (l || b)[d[f]]; + } + d = h[p[g]]; + f = "add" === a ? d.add : d.update; + g === k - 1 ? f.call(d, q, l, c) : f.call(d, q, l); + } + } + return this; + }; + m.prototype.update = function(a, b, c) { + if (this.a && A(a)) { + return this.s("update", a, b); + } + this.f["@" + a] && F(b) && (this.remove(a), this.add(a, b, c, !0)); + return this; + }; + m.prototype.remove = function(a, b, c) { + if (this.a && A(a)) { + return this.s("remove", a, b); + } + var d = "@" + a; + if (this.f[d]) { + if (!c) { + if (this.async && "function" !== typeof importScripts) { + let c = this; + d = new Promise(function(b) { + setTimeout(function() { + c.remove(a, null, !0); + c = null; + b(); + }); + }); + if (b) { + d.then(b); + } else { + return d; + } + return this; + } + if (b) { + return this.remove(a, null, !0), b(), this; + } + } + for (b = 0; b < this.b - (this.threshold || 0); b++) { + T(this.i[b], a); + } + this.depth && T(this.h, a); + delete this.f[d]; + this.u = !1; + } + return this; + }; + let y; + m.prototype.search = function(a, b, c, d) { + if (A(b)) { + if (x(b)) { + for (var e = 0; e < b.length; e++) { + b[e].query = a; + } + } else { + b.query = a; + } + a = b; + b = 1000; + } else { + b && J(b) ? (c = b, b = 1000) : b || 0 === b || (b = 1000); + } + let g = [], f = a; + let k, h, p; + if (A(a) && !x(a)) { + c || (c = a.callback) && (f.callback = null); + h = a.sort; + k = a.page; + b = a.limit; + var q = a.threshold; + p = a.suggest; + a = a.query; + } + if (this.a) { + q = this.a.index; + const r = f.where; + var n = f.bool || "or", l = f.field; + let B = n; + let v, u; + if (l) { + x(l) || (l = [l]); + } else { + if (x(f)) { + var m = f; + l = []; + B = []; + for (var t = 0; t < f.length; t++) { + d = f[t], e = d.bool || n, l[t] = d.field, B[t] = e, "not" === e ? v = !0 : "and" === e && (u = !0); + } + } else { + l = this.a.keys; + } + } + n = l.length; + for (t = 0; t < n; t++) { + m && (f = m[t]), k && !F(f) && (f.page = null, f.limit = 0), g[t] = q[l[t]].search(f, 0); + } + if (c) { + return c(I.call(this, a, B, g, h, b, p, r, k, u, v)); + } + if (this.async) { + const c = this; + return new Promise(function(d) { + Promise.all(g).then(function(e) { + d(I.call(c, a, B, e, h, b, p, r, k, u, v)); + }); + }); + } + return I.call(this, a, B, g, h, b, p, r, k, u, v); + } + q || (q = this.threshold || 0); + if (!d) { + if (this.async && "function" !== typeof importScripts) { + let a = this; + q = new Promise(function(c) { + setTimeout(function() { + c(a.search(f, b, null, !0)); + a = null; + }); + }); + if (c) { + q.then(c); + } else { + return q; + } + return this; + } + if (c) { + return c(this.search(f, b, null, !0)), this; + } + } + if (!a || !F(a)) { + return g; + } + f = a; + if (this.cache) { + if (this.u) { + if (c = this.j.get(a)) { + return c; + } + } else { + this.j.clear(), this.u = !0; + } + } + f = this.encode(f); + if (!f.length) { + return g; + } + c = this.c; + c = J(c) ? c(f) : f.split(this.split); + this.filter && (c = H(c, this.filter)); + m = c.length; + d = !0; + e = []; + const w = u(); + let v = 0; + 1 < m && (this.depth && "strict" === this.c ? n = !0 : c.sort(da)); + if (!n || (t = this.h)) { + const b = this.b; + for (; v < m; v++) { + let a = c[v]; + if (a) { + if (n) { + if (!l) { + if (t[a]) { + l = a, w[a] = 1; + } else { + if (!p) { + return g; + } + } + } + if (p && v === m - 1 && !e.length) { + n = !1, a = l || a, w[a] = 0; + } else { + if (!l) { + continue; + } + } + } + if (!w[a]) { + const c = []; + let f = !1, g = 0; + const k = n ? t[l] : this.i; + if (k) { + let d; + for (let e = 0; e < b - q; e++) { + if (d = k[e][a]) { + c[g++] = d, f = !0; + } + } + } + if (f) { + l = a, e[e.length] = 1 < g ? c.concat.apply([], c) : c[0]; + } else { + if (!p) { + d = !1; + break; + } + } + w[a] = 1; + } + } + } + } else { + d = !1; + } + d && (g = V(e, b, k, p)); + this.cache && this.j.set(a, g); + return g; + }; + m.prototype.find = function(a, b) { + return this.where(a, b, 1)[0] || null; + }; + m.prototype.where = function(a, b, c, d) { + const e = this.g, g = []; + let f = 0; + let k; + var h; + let p; + if (A(a)) { + c || (c = b); + var q = Object.keys(a); + var n = q.length; + k = !1; + if (1 === n && "id" === q[0]) { + return [e[a.id]]; + } + if ((h = this.B) && !d) { + for (var l = 0; l < h.length; l++) { + var m = h[l], t = a[m]; + if (!C(t)) { + p = this.w[m]["@" + t]; + if (0 === --n) { + return p; + } + q.splice(q.indexOf(m), 1); + delete a[m]; + break; + } + } + } + h = Array(n); + for (l = 0; l < n; l++) { + h[l] = q[l].split(":"); + } + } else { + if (J(a)) { + b = d || Object.keys(e); + c = b.length; + for (q = 0; q < c; q++) { + n = e[b[q]], a(n) && (g[f++] = n); + } + return g; + } + if (C(b)) { + return [e[a]]; + } + if ("id" === a) { + return [e[b]]; + } + q = [a]; + n = 1; + h = [a.split(":")]; + k = !0; + } + d = p || d || Object.keys(e); + l = d.length; + for (m = 0; m < l; m++) { + t = p ? d[m] : e[d[m]]; + let l = !0; + for (let c = 0; c < n; c++) { + k || (b = a[q[c]]); + const d = h[c], e = d.length; + let f = t; + if (1 < e) { + for (let a = 0; a < e; a++) { + f = f[d[a]]; + } + } else { + f = f[d[0]]; + } + if (f !== b) { + l = !1; + break; + } + } + if (l && (g[f++] = t, c && f === c)) { + break; + } + } + return g; + }; + m.prototype.info = function() { + return {id:this.id, items:this.length, cache:this.cache && this.cache.l ? this.cache.l.length : !1, matcher:S.length + (this.m ? this.m.length : 0), worker:this.C, threshold:this.threshold, depth:this.depth, resolution:this.b, contextual:this.depth && "strict" === this.c}; + }; + m.prototype.clear = function() { + return this.destroy().init(); + }; + m.prototype.destroy = function() { + this.cache && (this.j.clear(), this.j = null); + this.i = this.h = this.f = null; + if (this.a) { + const a = this.a.keys; + for (let b = 0; b < a.length; b++) { + this.a.index[a[b]].destroy(); + } + this.a = this.g = null; + } + return this; + }; + m.prototype.export = function() { + let a; + if (this.a) { + const b = this.a.keys; + a = Array(b.length + 1); + let c = 0; + for (; c < b.length; c++) { + const d = this.a.index[b[c]]; + a[c] = [d.i, d.h, Object.keys(d.f)]; + } + a[c] = this.g; + } else { + a = [this.i, this.h, Object.keys(this.f)]; + } + return JSON.stringify(a); + }; + m.prototype.import = function(a) { + a = JSON.parse(a); + const b = u(); + if (this.a) { + var c = this.a.keys, d = c.length, e = a[0][2]; + for (var g = 0; g < e.length; g++) { + b[e[g]] = 1; + } + for (e = 0; e < d; e++) { + g = this.a.index[c[e]], g.i = a[e][0], g.h = a[e][1], g.f = b, g.g = a[d]; + } + this.g = a[d]; + } else { + c = a[2]; + for (d = 0; d < c.length; d++) { + b[c[d]] = 1; + } + this.i = a[0]; + this.h = a[1]; + this.f = b; + } + }; + const O = {icase:function(a) { + return a.toLowerCase(); + }, simple:function() { + const a = [h("[\u00e0\u00e1\u00e2\u00e3\u00e4\u00e5]"), "a", h("[\u00e8\u00e9\u00ea\u00eb]"), "e", h("[\u00ec\u00ed\u00ee\u00ef]"), "i", h("[\u00f2\u00f3\u00f4\u00f5\u00f6\u0151]"), "o", h("[\u00f9\u00fa\u00fb\u00fc\u0171]"), "u", h("[\u00fd\u0177\u00ff]"), "y", h("\u00f1"), "n", h("\u00e7"), "c", h("\u00df"), "s", h(" & "), " and ", h("[-/]"), " ", h("[^a-z0-9 ]"), "", h("\\s+"), " "]; + return function(b) { + b = K(b.toLowerCase(), a); + return " " === b ? "" : b; + }; + }(), advanced:function() { + const a = [h("ae"), "a", h("ai"), "ei", h("ay"), "ei", h("ey"), "ei", h("oe"), "o", h("ue"), "u", h("ie"), "i", h("sz"), "s", h("zs"), "s", h("sh"), "s", h("ck"), "k", h("cc"), "k", h("dt"), "t", h("ph"), "f", h("pf"), "f", h("ou"), "o", h("uo"), "u"]; + return function(b, c) { + if (!b) { + return b; + } + b = this.simple(b); + 2 < b.length && (b = K(b, a)); + c || 1 < b.length && (b = U(b)); + return b; + }; + }(), extra:function() { + const a = [h("p"), "b", h("z"), "s", h("[cgq]"), "k", h("n"), "m", h("d"), "t", h("[vw]"), "f", h("[aeiouy]"), ""]; + return function(b) { + if (!b) { + return b; + } + b = this.advanced(b, !0); + if (1 < b.length) { + b = b.split(" "); + for (let c = 0; c < b.length; c++) { + const d = b[c]; + 1 < d.length && (b[c] = d[0] + K(d.substring(1), a)); + } + b = b.join(" "); + b = U(b); + } + return b; + }; + }(), balance:function() { + const a = [h("[-/]"), " ", h("[^a-z0-9 ]"), "", h("\\s+"), " "]; + return function(b) { + return U(K(b.toLowerCase(), a)); + }; + }()}, ea = function() { + function a(a) { + this.clear(); + this.A = !0 !== a && a; + } + a.prototype.clear = function() { + this.cache = u(); + this.count = u(); + this.index = u(); + this.l = []; + }; + a.prototype.set = function(a, c) { + if (this.A && C(this.cache[a])) { + let b = this.l.length; + if (b === this.A) { + b--; + const a = this.l[b]; + delete this.cache[a]; + delete this.count[a]; + delete this.index[a]; + } + this.index[a] = b; + this.l[b] = a; + this.count[a] = -1; + this.cache[a] = c; + this.get(a); + } else { + this.cache[a] = c; + } + }; + a.prototype.get = function(a) { + const b = this.cache[a]; + if (this.A && b) { + var d = ++this.count[a]; + const b = this.index; + let c = b[a]; + if (0 < c) { + const f = this.l; + for (var e = c; this.count[f[--c]] <= d && -1 !== c;) { + } + c++; + if (c !== e) { + for (d = e; d > c; d--) { + e = f[d - 1], f[d] = e, b[e] = d; + } + f[c] = a; + b[a] = c; + } + } + } + return b; + }; + return a; + }(); + return m; +}(!1), this); + diff --git a/flexsearch.js b/flexsearch.js index 213c665..e07bdc4 100644 --- a/flexsearch.js +++ b/flexsearch.js @@ -1,5 +1,5 @@ /**! - * @preserve FlexSearch v0.6.1 + * @preserve FlexSearch v0.6.2 * Copyright 2019 Nextapps GmbH * Author: Thomas Wilkerling * Released under the Apache 2.0 Licence @@ -191,12 +191,13 @@ /** * @param {Object=} options + * @param {Object=} settings * @export */ - FlexSearch.create = function(options){ + FlexSearch.create = function(options, settings){ - return new FlexSearch(options); + return new FlexSearch(options, settings); }; /** @@ -495,20 +496,6 @@ ); */ - // if(SUPPORT_SUGGESTION){ - // - // /** @private */ - // this.suggest = ( - // - // is_undefined(custom = options["suggest"]) ? - // - // this.suggest || - // defaults.suggest - // : - // custom - // ); - // } - custom = is_undefined(custom = options["encode"]) ? preset.encode || @@ -532,7 +519,9 @@ ); } - if((custom = options["filter"])) { + let lang = options["lang"]; + + if((custom = lang || options["filter"])) { if(is_string(custom)){ @@ -548,7 +537,7 @@ this.filter = custom; } - if((custom = options["stemmer"])) { + if((custom = lang || options["stemmer"])) { /** @private */ this.stemmer = init_stemmer( @@ -860,7 +849,7 @@ if(!_recall){ - if(SUPPORT_ASYNC && this.async && (typeof importScripts !== "function")){ + if(SUPPORT_ASYNC && this.async && (!SUPPORT_WORKER || (typeof importScripts !== "function"))){ let self = this; @@ -1171,7 +1160,8 @@ if(z === length - 1){ - return index[keys[z]].remove(id, callback); + index[keys[z]].remove(id, callback); + return this; } else{ @@ -1214,7 +1204,7 @@ if(i === len - 1){ - return fn.call(self, id, content, callback); + fn.call(self, id, content, callback); } else{ @@ -1222,6 +1212,8 @@ } } } + + return this; }; } @@ -1474,7 +1466,16 @@ } query = /** @type {Object} */ (limit); - limit = 0; + limit = 1000; + } + else if(limit && is_function(limit)){ + + callback = /** @type {?Function} */ (limit); + limit = 1000; + } + else{ + + limit || (limit === 0 ) || (limit = 1000); } let result = []; @@ -1490,11 +1491,14 @@ if(SUPPORT_ASYNC){ - callback = query["callback"] || (is_function(limit) && /** @type {?Function} */ (limit)); + if(!callback){ - if(callback) { + callback = query["callback"]; - _query["callback"] = null; + if(callback){ + + _query["callback"] = null; + } } } @@ -1636,16 +1640,6 @@ threshold || (threshold = this.threshold || 0); - if(is_function(limit)){ - - callback = limit; - limit = 1000; - } - else { - - limit || (limit === 0 ) || (limit = 1000); - } - if(SUPPORT_WORKER && this.worker){ this._current_callback = callback; @@ -1781,14 +1775,13 @@ let ctx_root; let use_contextual; + let a = 0; if(length > 1){ - if(this.depth){ + if(this.depth && (this.tokenize === "strict")){ use_contextual = true; - ctx_root = words[0]; // TODO: iterate roots - check_words[ctx_root] = 1; } else{ @@ -1827,7 +1820,7 @@ let ctx_map; - if(!use_contextual || (ctx_map = this._ctx)[ctx_root]){ + if(!use_contextual || (ctx_map = this._ctx)){ const resolution = this.resolution; @@ -1837,12 +1830,41 @@ // threshold = (threshold || 1) / boost; // } - for(let a = (use_contextual ? 1 : 0); a < length; a++){ + for(; a < length; a++){ - const value = words[a]; + let value = words[a]; if(value){ + if(use_contextual){ + + if(!ctx_root){ + + if(ctx_map[value]){ + + ctx_root = value; + check_words[value] = 1; + } + else if(!suggest){ + + return result; + } + } + + if(suggest && (a === length - 1) && !check.length){ + + // fall back to single-term-strategy + + use_contextual = false; + value = ctx_root || value; + check_words[value] = 0; + } + else if(!ctx_root){ + + continue; + } + } + if(!check_words[value]){ const map_check = []; @@ -1871,7 +1893,10 @@ if(map_found){ + ctx_root = value; + // not handled by intersection: + check[check.length] = ( count > 1 ? @@ -1882,7 +1907,7 @@ ); // handled by intersection: - //check[check.length] = map_check; + // check[check.length] = map_check; } else if(!SUPPORT_SUGGESTION || !suggest){ @@ -1892,8 +1917,6 @@ check_words[value] = 1; } - - ctx_root = value; } } } @@ -1902,24 +1925,21 @@ found = false; } - //if(!SUPPORT_DOCUMENT || !this.doc){ - - if(found){ + if(found){ - // Not handled by intersection: - result = /** @type {Array} */ (intersect(check, limit, cursor, SUPPORT_SUGGESTION && suggest)); + // Not handled by intersection: + result = /** @type {Array} */ (intersect(check, limit, cursor, SUPPORT_SUGGESTION && suggest)); - // Handled by intersection: - //result = intersect_3d(check, limit, suggest); - } + // Handled by intersection: + //result = intersect_3d(check, limit, suggest); + } - // store result to cache + // store result to cache - if(SUPPORT_CACHE && this.cache){ + if(SUPPORT_CACHE && this.cache){ - this._cache.set(query, result); - } - //} + this._cache.set(query, result); + } if(PROFILER){ @@ -1959,6 +1979,7 @@ let keys_len; let has_value; let tree; + let tag_results; if(is_object(key)){ @@ -1983,12 +2004,12 @@ if(!is_undefined(current_where)){ - result = this._tag[current_tag]["@" + current_where]; + tag_results = this._tag[current_tag]["@" + current_where]; //result = result.slice(0, limit && (limit < result.length) ? limit : result.length); if(--keys_len === 0){ - return result; + return tag_results; } keys.splice(keys.indexOf(current_tag), 1); @@ -2042,12 +2063,12 @@ has_value = true; } - const ids = result || get_keys(doc); // this._ids; + const ids = tag_results || result || get_keys(doc); // this._ids; const length = ids.length; for(let x = 0; x < length; x++){ - const obj = doc[ids[x]]; + const obj = tag_results ? ids[x] : doc[ids[x]]; let found = true; for(let i = 0; i < keys_len; i++){ @@ -2214,11 +2235,14 @@ FlexSearch.prototype.export = function(){ + let payload; + if(SUPPORT_DOCUMENT && this.doc){ const keys = this.doc.keys; const length = keys.length; - const payload = new Array(length + 1); + + payload = new Array(length + 1); let i = 0; @@ -2233,16 +2257,18 @@ } payload[i] = this._doc; - - return JSON.stringify(payload); } + else{ - return JSON.stringify([ + payload = [ - this._map, - this._ctx, - get_keys(this._ids) - ]); + this._map, + this._ctx, + get_keys(this._ids) + ] + } + + return JSON.stringify(payload); }; /** @@ -3161,7 +3187,6 @@ let page; let result = []; let pointer; - const length_z = arrays.length; if(cursor === true){ @@ -3173,6 +3198,8 @@ pointer = cursor && cursor.split(":"); } + const length_z = arrays.length; + // use complex handler when length > 1 if(length_z > 1){ @@ -3186,7 +3213,7 @@ let suggestions = []; let check_not; let arr; - let z = -1; // start from 0 + let z = 0; let i = 0; let length; let tmp; @@ -3197,6 +3224,7 @@ let last_index; let pointer_suggest; + let pointer_count; if(pointer){ @@ -3207,7 +3235,7 @@ } else{ - pointer = parseInt(pointer[0], 10); + pointer = pointer_count = parseInt(pointer[0], 10); } } @@ -3220,17 +3248,16 @@ check_not = create_object(); - while(++z < length_z){ + for(; z < length_z; z++){ if(bool[z] === "not"){ arr = arrays[z]; length = arr.length; - i = 0; - while(i < length){ + for(i = 0; i < length; i++){ - check_not["@" + arr[i++]] = 1; + check_not["@" + arr[i]] = 1; } } else{ @@ -3250,7 +3277,7 @@ return create_page(cursor, page, result); } - z = -1; + z = 0; } else{ @@ -3263,7 +3290,7 @@ // loop through arrays - while(++z < length_z){ + for(; z < length_z; z++){ const is_final_loop = ( @@ -3322,13 +3349,11 @@ const result_length = first_result.length; - i = 0; - // fill initial map - while(i < result_length){ + for(i = 0; i < result_length; i++){ - const id = first_result[i++]; + const id = first_result[i]; const index = "@" + id; // there is no and, add items @@ -3359,17 +3384,14 @@ let found = false; - i = 0; - //suggestions = []; - - while(i < length){ + for(i = 0; i < length; i++){ - tmp = arr[i++]; + tmp = arr[i]; const index = "@" + tmp; - const check_val = has_and ? check[index] : z; + const check_val = has_and ? check[index] || 0 : z; - if(check_val){ + if(check_val || suggest){ if(has_not && check_not[index]){ @@ -3392,13 +3414,13 @@ // should only happened when at least one bool "and" was set // TODO: check bool and provide complexity reduction - if(!pointer || (--pointer < count)){ + if(!pointer_count || (--pointer_count < count)){ result[count++] = tmp; if(limit && (count === limit)){ - return create_page(cursor, count, result); + return create_page(cursor, count + (pointer || 0), result); } } } @@ -3449,21 +3471,19 @@ i = 0; } - while(i < result_length){ + for(; i < result_length; i++){ - const id = first_result[i++]; + const id = first_result[i]; if(!check_not["@" + id]){ - if(!pointer || (--pointer < count)){ + result[count++] = id; - result[count++] = id; - - if(limit && (count === limit)){ - - return create_page(cursor, i, result); - } - } + // TODO: is actually not covered + // if(limit && (count === limit)){ + // + // return create_page(cursor, (i + 1), result); + // } } } } @@ -3480,7 +3500,7 @@ if(pointer_suggest){ z = parseInt(pointer_suggest[0], 10) + 1; - i = parseInt(pointer_suggest[1], 10); + i = parseInt(pointer_suggest[1], 10) + 1; } else{ @@ -3488,7 +3508,7 @@ i = 0; } - while(z--){ + for(; z--;){ tmp = suggestions[z]; @@ -3520,6 +3540,11 @@ result = arrays[0]; + if(pointer){ + + pointer = parseInt(pointer[0], 10); + } + // TODO: handle references to the original index array // return result.slice(0); } @@ -3527,16 +3552,28 @@ if(limit){ - const start = cursor ? parseInt(cursor, 10) : 0; + const length = result.length; + + if(pointer && (pointer > length)){ + + pointer = 0; + } + + const start = pointer || 0; page = start + limit; - if(page < result.length){ + if(page < length){ result = result.slice(start, page); } - else if(start){ + else { + + page = 0; + + if(start){ - result = result.slice(start); + result = result.slice(start); + } } } @@ -4014,8 +4051,6 @@ function provide(name, factory, root){ - /* istanbul ignore next */ - let prop; // AMD (RequireJS) diff --git a/package.json b/package.json index 46cfaa8..647ba13 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flexsearch", - "version": "0.6.1", + "version": "0.6.2", "description": "Next-Generation full text search library with zero dependencies.", "homepage": "https://github.com/nextapps-de/flexsearch/", "author": "Thomas Wilkerling", @@ -32,13 +32,15 @@ "build-custom": "node compile RELEASE=custom DEBUG=false PROFILER=false SUPPORT_WORKER=false SUPPORT_ENCODER=false SUPPORT_CACHE=false SUPPORT_ASYNC=false SUPPORT_PRESET=false SUPPORT_SUGGESTION=false SUPPORT_SERIALIZE=false SUPPORT_INFO=false SUPPORT_DOCUMENT=false SUPPORT_OPERATOR=false SUPPORT_WHERE=false SUPPORT_PAGINATION=false SUPPORT_LANG_DE=false SUPPORT_LANG_EN=false", "build-es5": "node compile RELEASE=es5 DEBUG=true PROFILER=false SUPPORT_WORKER=true SUPPORT_ENCODER=true SUPPORT_CACHE=true SUPPORT_ASYNC=true SUPPORT_PRESET=true SUPPORT_SUGGESTION=true SUPPORT_SERIALIZE=true SUPPORT_INFO=true SUPPORT_DOCUMENT=true SUPPORT_OPERATOR=true SUPPORT_WHERE=true SUPPORT_PAGINATION=true SUPPORT_LANG_DE=false SUPPORT_LANG_EN=false LANGUAGE_OUT=ECMASCRIPT5_STRICT", "build-node": "node compile RELEASE=node DEBUG=false PROFILER=false SUPPORT_WORKER=false SUPPORT_ENCODER=true SUPPORT_CACHE=true SUPPORT_ASYNC=true SUPPORT_PRESET=true SUPPORT_SUGGESTION=true SUPPORT_SERIALIZE=true SUPPORT_INFO=true SUPPORT_DOCUMENT=true SUPPORT_OPERATOR=true SUPPORT_WHERE=true SUPPORT_PAGINATION=true SUPPORT_LANG_DE=false SUPPORT_LANG_EN=false", + "build-pre": "node compile RELEASE=pre DEBUG=false PROFILER=false SUPPORT_WORKER=false SUPPORT_ENCODER=true SUPPORT_CACHE=true SUPPORT_ASYNC=true SUPPORT_PRESET=true SUPPORT_SUGGESTION=true SUPPORT_SERIALIZE=true SUPPORT_INFO=true SUPPORT_DOCUMENT=true SUPPORT_OPERATOR=true SUPPORT_WHERE=true SUPPORT_PAGINATION=true SUPPORT_LANG_DE=false SUPPORT_LANG_EN=false FORMATTING=PRETTY_PRINT", "build-lang": "node compile RELEASE=lang", - "build-all": "npm run build && npm run build-light && npm run build-compact && npm run build-es5 && npm run build-node", - "test-production": "nyc --reporter=html --reporter=text mocha --timeout=3000 test --exit", - "test-light": "nyc --reporter=html --reporter=text mocha --timeout=3000 test/ --exit", - "test-develop": "nyc --reporter=html --reporter=text mocha --timeout=3000 --exit", + "build-all": "npm run build && npm run build-light && npm run build-compact && npm run build-es5 && npm run build-node && npm run build-pre", + "test-production": "mocha --timeout=3000 test --exit", + "test-light": "mocha --timeout=3000 test/ --exit", + "test-develop": "mocha --timeout=3000 --exit", "test-browser": "mocha-phantomjs test/index.html", - "test": "npm run test-develop && npm run test-production && npm run test-light && npm run test-browser", + "test-coverage": "nyc --reporter=html --reporter=text mocha --timeout=3000 test/test.js --exit", + "test": "npm run test-develop && npm run test-production && npm run test-light && npm run test-browser && npm run test-coverage", "update": "node_modules/.bin/updtr --to non-breaking", "coverage": "nyc report --reporter=lcov --reporter=text-lcov | coveralls" }, diff --git a/test/matching.html b/test/matching.html index c638e83..441b257 100644 --- a/test/matching.html +++ b/test/matching.html @@ -276,8 +276,7 @@

Indexed Text: "Gulliver's Travels" (Swift Jonathan 1726)

bulksearch = new BulkSearch({ type: 'short', // this type specifies the maximum bitlength of assigned IDs! encode: 'extra', - multi: true, - suggest:true + multi: true }); console.time('bulksearch'); @@ -295,8 +294,7 @@

Indexed Text: "Gulliver's Travels" (Swift Jonathan 1726)

tokenize: 'strict', threshold: 0, resolution: 9, - depth: 3, - suggest: true + depth: 3 }); console.time('flexsearch'); @@ -452,11 +450,11 @@

Indexed Text: "Gulliver's Travels" (Swift Jonathan 1726)

switch(i){ case 1: - results = flexsearch.search(query); + results = flexsearch.search(query, {suggest: true}); break; case 2: - results = bulksearch.search(query); + results = bulksearch.search(query, {suggest: true}); break; case 3: diff --git a/test/test.es6.js b/test/test.es6.js index ccb0fa9..c319405 100644 --- a/test/test.es6.js +++ b/test/test.es6.js @@ -1,4 +1,4 @@ -var env = ""; +var env = global.env; if(typeof module !== "undefined"){ @@ -60,7 +60,6 @@ describe("Index Multi-Field Documents (ES6)", function(){ async: true, doc: { - id: "id", field: [ "data:title", @@ -71,8 +70,11 @@ describe("Index Multi-Field Documents (ES6)", function(){ await index.add(data); - expect(index.doc.index["data:title"].length).to.equal(3); - expect(index.doc.index["data:body"].length).to.equal(3); + if(env === ""){ + + expect(index.doc.index["data:title"].length).to.equal(3); + expect(index.doc.index["data:body"].length).to.equal(3); + } expect(await index.search({field: "data:body", query: "body"})).to.have.members(data); expect(await index.search({field: "data:title", query: "title"})).to.have.members(data); @@ -106,8 +108,11 @@ describe("Index Multi-Field Documents (ES6)", function(){ await index.remove(update); - expect(await index.doc.index["data:title"].length).to.equal(0); - expect(await index.doc.index["data:body"].length).to.equal(0); + if(env === ""){ + + expect(await index.doc.index["data:title"].length).to.equal(0); + expect(await index.doc.index["data:body"].length).to.equal(0); + } }); it("Should have been indexed properly (Worker)", async function(){ @@ -128,8 +133,11 @@ describe("Index Multi-Field Documents (ES6)", function(){ await index.add(data); - expect(index.doc.index["data:title"].length).to.equal(3); - expect(index.doc.index["data:body"].length).to.equal(3); + if(env === ""){ + + expect(index.doc.index["data:title"].length).to.equal(3); + expect(index.doc.index["data:body"].length).to.equal(3); + } expect(await index.search({field: "data:body", query: "body"})).to.have.members(data); expect(await index.search({field: "data:title", query: "title"})).to.have.members(data); @@ -163,7 +171,10 @@ describe("Index Multi-Field Documents (ES6)", function(){ await index.remove(update); - expect(await index.doc.index["data:title"].length).to.equal(0); - expect(await index.doc.index["data:body"].length).to.equal(0); + if(env === ""){ + + expect(await index.doc.index["data:title"].length).to.equal(0); + expect(await index.doc.index["data:body"].length).to.equal(0); + } }); }); diff --git a/test/test.js b/test/test.js index 72cda27..8ac9a31 100644 --- a/test/test.js +++ b/test/test.js @@ -1,6 +1,6 @@ if(typeof module !== "undefined"){ - var env = process.argv[3] === "test" ? "min" : process.argv[3] === "test/" ? "light" : ""; + var env = global.env = process.argv[3] === "test" ? "min" : process.argv[3] === "test/" ? "light" : process.argv[3] === "test/test.js" ? "pre" : ""; var expect = require("chai").expect; var FlexSearch = require("../" + (env ? "dist/": "") + "flexsearch" + (env ? "." + env : "") + ".js"); //require("../lang/en.min.js"); @@ -130,7 +130,7 @@ describe("Initialize", function(){ encode: "icase", tokenize: "reverse", - cache: true + cache: 2 }); it("Should have correct constructors", function(){ @@ -186,7 +186,7 @@ describe("Initialize", function(){ expect(flexsearch_async.async).to.equal(true); } - if((env !== "light") && (env !== "min")){ + if((env !== "light") && (env !== "min") && (env !== "pre")){ expect(flexsearch_default.tokenize).to.equal("forward"); expect(flexsearch_strict.tokenize).to.equal("strict"); @@ -403,15 +403,42 @@ if(env !== "light"){ describe("Add (Async)", function(){ + it("Should have been added to the index", function(done){ + + var index = new FlexSearch(); + + expect(index.length).to.equal(0); + + index.add(0, "foo", function(){ + + expect(index.length).to.equal(1); + + done(); + }); + + expect(index.length).to.equal(0); + }); + it("Should have been added to the index", function(done){ expect(flexsearch_async.length).to.equal(0); - flexsearch_async.add(0, "foo"); + flexsearch_async.add(0, "foo", function(){ + + done(); + }); + + expect(flexsearch_async.length).to.equal(0); + }); + + it("Should have been added to the index", function(done){ + + expect(flexsearch_async.length).to.equal(1); + flexsearch_async.add(2, "bar"); flexsearch_async.add(1, "foobar"); - expect(flexsearch_async.length).to.equal(0); + expect(flexsearch_async.length).to.equal(1); setTimeout(function(){ @@ -448,6 +475,20 @@ if(env !== "light"){ describe("Search (Async)", function(){ + it("Should have been matched from index", function(done){ + + var index = new FlexSearch({doc: {id: "id", field: "title"}}); + var data = {id: 0, title: "foo"}; + + index.add(data); + + index.search("foo", function(result){ + + expect(result).to.have.members([data]); + done(); + }); + }); + it("Should have been matched from index", function(done){ flexsearch_async.search("foo", function(result){ @@ -580,13 +621,44 @@ if(env !== "light"){ it("Should have been removed from the index", function(done){ - flexsearch_async.remove(0); + var index = new FlexSearch(); + + index.add(0, "foo"); + + expect(index.length).to.equal(1); + + index.remove(0, function(){ + + expect(index.length).to.equal(0); + + done(); + }); + + expect(index.length).to.equal(1); + }); + + it("Should have been removed from the index", function(done){ + + expect(flexsearch_async.length).to.equal(3); + + flexsearch_async.remove(0, function(){ + + expect(flexsearch_async.length).to.equal(2); + + done(); + }); + + expect(flexsearch_async.length).to.equal(3); + }); + + it("Should have been removed from the index", function(done){ + flexsearch_async.remove(2); flexsearch_async.remove(1).then(function(){ expect(flexsearch_async.length).to.equal(0); }); - expect(flexsearch_async.length).to.equal(3); + expect(flexsearch_async.length).to.equal(2); flexsearch_async.search("foo", function(result){ expect(result).to.have.lengthOf(0); @@ -794,7 +866,7 @@ if(env !== "light"){ }); }); - if(env !== "light" && env !== "min"){ + if((env !== "light") && (env !== "min")){ it("Should have been debug mode activated", function(){ @@ -820,7 +892,7 @@ if(env !== "light"){ worker: 4 }); - if(env !== "min"){ + if((env !== "min") && (env !== "pre")){ expect(flexsearch_worker.info().worker).to.equal(false); } @@ -1130,6 +1202,50 @@ describe("Stemmer", function(){ }); }); + +describe("Custom Language", function(){ + + it("Should have been applied properly", function(){ + + FlexSearch.registerLanguage("custom", { + filter: ["a", "an"], + stemmer: { + "ization": "ize", + "tional": "tion" + } + }); + + var index = new FlexSearch({ + encode: "icase", + tokenize: "reverse", + filter: "custom", + stemmer: "custom" + }); + + index.add(0, "Just a multinational colonization."); + + expect(index.length).to.equal(1); + expect(index.search("Just a multinational colonization.")).to.include(0); + expect(index.search("Just an multinational colonization.")).to.include(0); + expect(index.search("multinational colonization")).to.include(0); + expect(index.search("tional tion")).to.have.length(0); + + index = new FlexSearch({ + encode: "icase", + tokenize: "reverse", + lang: "custom" + }); + + index.add(0, "Just a multinational colonization."); + + expect(index.length).to.equal(1); + expect(index.search("Just a multinational colonization.")).to.include(0); + expect(index.search("Just an multinational colonization.")).to.include(0); + expect(index.search("multinational colonization")).to.include(0); + expect(index.search("tional tion")).to.have.length(0); + }); +}); + // ------------------------------------------------------------------------ // Relevance Tests // ------------------------------------------------------------------------ @@ -1209,11 +1325,31 @@ if(env !== "light") describe("Suggestions", function(){ expect(index.search("1 3 4 7", { suggest: false })).to.have.lengthOf(0); expect(index.search("1 3 4 7", { suggest: true })).to.have.members([0]); expect(index.search("1 3 9 7", { suggest: true })).to.have.members([0]); + + expect(index.search("foobar one two", { suggest: true })).to.have.members([1, 2]); expect(index.search("one foobar two", { suggest: true })).to.have.members([1, 2]); + expect(index.search("one two foobar", { suggest: true })).to.have.members([1, 2]); expect(index.search("zero one foobar two foobar", { suggest: true })).to.have.members([1, 2]); + }); + + it("Should have been suggested properly by context", function(){ + + var index = new FlexSearch({ + encode: "advanced", + tokenize: "strict", + depth: 3 + }); + + index.add(1, "zero one two three four five six seven eight nine ten"); + index.add(2, "four two zero one three ten five seven eight six nine"); - //TODO - //expect(index.search("zero one foobar two foobar")[0]).to.equal(1); + expect(index.search("foobar one", { suggest: true })).to.have.members([1, 2]); + expect(index.search("foobar foobar foobar one foobar foobar foobar", { suggest: true })).to.have.members([1, 2]); + expect(index.search("foobar one two", { suggest: true })).to.have.members([1, 2]); + expect(index.search("one foobar two", { suggest: true })).to.have.members([1, 2]); + expect(index.search("one two foobar", { suggest: true })).to.have.members([1, 2]); + expect(index.search("foobar one foobar two foobar", { suggest: true })).to.have.members([1, 2]); + expect(index.search("zero one foobar two foobar", { suggest: true })).to.have.members([1, 2]); }); }); @@ -1221,7 +1357,7 @@ if(env !== "light") describe("Suggestions", function(){ // Where Clause // ------------------------------------------------------------------------ -if(env === "" || env === "min") describe("Where/Find", function(){ +if((env === "") || (env === "min") || (env === "pre")) describe("Where/Find", function(){ var data = [{ id: 0, @@ -1286,6 +1422,145 @@ if(env === "" || env === "min") describe("Where/Find", function(){ } })).to.have.members([data[2]]); }); + + it("Should have been tagged properly", function(){ + + var index = new FlexSearch({ + doc: { + id: "id", + field: "title", + tag : "cat" + } + }); + + index.add(data); + + expect(index.length).to.equal(3); + expect(index.index).to.have.members(["@0", "@1", "@2"]); + + expect(index.where({cat: "1"})).to.have.members([data[0], data[2]]); + expect(index.where("cat", "1")).to.have.members([data[0], data[2]]); + expect(index.where("cat", "1", 1)).to.have.members([data[0]]); + + expect(index.where({ + cat: "1", + flag: true + })).to.have.members([data[2]]); + + expect(index.where({ + flag: true, + cat: "1" + })).to.have.members([data[2]]); + + expect(index.search("title", { + where: { + cat: "1" + } + })).to.have.members([data[0], data[2]]); + + expect(index.search("title", { + where: { + cat: "1" + }, + limit: 1 + })).to.have.members([data[0]]); + + expect(index.search("title", { + where: { + cat: "3" + } + })).to.have.lengthOf(0); + + expect(index.search("foobar", { + where: { + cat: "1" + } + })).to.have.lengthOf(0); + + // ----------------------------------------- + + index = new FlexSearch({ + doc: { + id: "id", + field: ["title"], + tag : ["cat"] + } + }); + + index.add(data); + + expect(index.length).to.equal(3); + expect(index.index).to.have.members(["@0", "@1", "@2"]); + + expect(index.where({cat: "1"})).to.have.members([data[0], data[2]]); + + expect(index.search("title", { + where: { + cat: "1" + } + })).to.have.members([data[0], data[2]]); + + expect(index.search("title", { + where: { + cat: "3" + } + })).to.have.lengthOf(0); + + expect(index.search("foobar", { + where: { + cat: "1" + } + })).to.have.lengthOf(0); + + // ------------------------------------ + + index = new FlexSearch({ + doc: { + id: "id", + field: "data:title", + tag : "data:cat" + } + }); + + data = [{ + id: 0, + data: { + title: "Title 1", + cat: "1", + flag: false + } + },{ + id: 1, + data: { + title: "Title 2", + cat: "2", + flag: false + } + },{ + id: 2, + data: { + title: "Title 3", + cat: "1", + flag: true + } + }]; + + index.add(data); + + expect(index.where({"data:cat": "1"})).to.have.members([data[0], data[2]]); + expect(index.where("data:cat", "1")).to.have.members([data[0], data[2]]); + expect(index.where("data:cat", "1", 1)).to.have.members([data[0]]); + + expect(index.where({ + "data:cat": "1", + "data:flag": true + })).to.have.members([data[2]]); + + expect(index.where({ + "data:flag": true, + "data:cat": "1" + })).to.have.members([data[2]]); + }); }); // ------------------------------------------------------------------------ @@ -1777,7 +2052,79 @@ if(env !== "light") describe("Index Multi-Field Documents", function(){ expect(results[2]).to.equal(data[0]); }); - if(env === "" && (typeof require !== "undefined") && !this._phantom){ + it("Should have been sorted suggested and paged", function(){ + + var index = new FlexSearch({ + + doc: { + id: "id", + field: ["data:title", "data:body"] + } + }); + + index.add(data); + + var results = index.search({ + + field: "data:title", + query: "title", + suggest: true, + page: true, + limit: 2 + }); + + expect(results.result).to.have.members([data[0], data[1]]); + + results = index.search({ + + field: "data:title", + query: "title", + suggest: true, + page: results.next, + limit: 2 + }); + + expect(results.result).to.have.members([data[2]]); + + results = index.search({ + + field: "data:title", + query: "foobar title foobar title foobar", + suggest: true, + page: true, + limit: 2 + }); + + expect(results.result).to.have.members([data[0], data[1]]); + + results = index.search({ + + field: "data:title", + query: "foobar title foobar title foobar", + suggest: true, + page: results.next, + limit: 2 + }); + + expect(results.result).to.have.members([data[2]]); + + results = index.search([{ + + field: "data:title", + query: "title undefined", + bool: "and", + suggest: true, + limit: 2 + },{ + field: "data:body", + query: "undefined", + bool: "not" + }]); + + expect(results).to.have.members([data[0], data[1]]); + }); + + if((!env || (env === "pre")) && (typeof require !== "undefined") && !this._phantom){ require("./test.es6.js"); } @@ -1829,6 +2176,66 @@ if(env !== "light") describe("Pagination", function(){ expect(result.result).to.have.members([4]); }); + it("Should have been properly paged (suggestion)", function(){ + + var index = new FlexSearch(); + + index.add(0, "foo bar").add(1, "foo bar").add(2, "foo bar test").add(3, "foo bar").add(4, "foo bar"); + + expect(index.index).to.have.members(["@0", "@1", "@2", "@3", "@4"]); + expect(index.search("foo")).to.have.lengthOf(5); + expect(index.search("foo", 2)).to.have.lengthOf(2); + + var result = index.search("foo undefined bar", { + suggest: true, + page: true, + limit: 2 + }); + + expect(result).to.have.keys(["page", "next", "result"]); + expect(result.result).to.have.members([0, 1]); + + result = index.search("foo bar test", { + suggest: true, + page: true, + limit: 2 + }); + + expect(result.result).to.have.members([2, 0]); + + result = index.search("foo undefined bar", { + suggest: true, + page: true, + limit: 2 + }); + + expect(result.result).to.have.members([0, 1]); + + result = index.search("foo undefined bar", { + suggest: true, + page: result.next, + limit: 2 + }); + + expect(result.result).to.have.members([2, 3]); + + result = index.search("foo undefined bar", { + suggest: true, + page: result.page, + limit: 2 + }); + + expect(result.result).to.have.members([2, 3]); + + result = index.search("foo undefined bar", { + suggest: true, + page: result.next, + limit: 2 + }); + + expect(result.result).to.have.members([4]); + }); + it("Should have been properly paged (documents)", function(){ var data = [{ @@ -2164,6 +2571,20 @@ if(env !== "light") describe("Operators", function(){ query: "x", bool: "not" }])).to.have.length(0); + + expect(index.search([{ + field: "title", + query: "title", + bool: "not" + },{ + field: "body", + query: "body", + bool: "not" + },{ + field: "blacklist", + query: "x", + bool: "not" + }])).to.have.length(0); }); }); @@ -2220,6 +2641,42 @@ if(env !== "light") describe("Export / Import", function(){ it("Should have been exported properly", function(){ + var index = new FlexSearch("match"); + + index.add(0, "foo"); + index.add(1, "bar"); + index.add(2, "foobar"); + + data = index.export(); + + if(env === ""){ + + expect(data).to.equal(JSON.stringify( + [ + index._map, + index._ctx, + Object.keys(index._ids) + ] + )); + } + }); + + it("Should have been imported properly", function(){ + + var index = new FlexSearch("match"); + + index.import(data); + + expect(index.length).to.equal(3); + + expect(index.search("foo")).to.have.lengthOf(2); + expect(index.search("bar")).to.have.lengthOf(2); + expect(index.search("foobar")).to.have.lengthOf(1); + expect(index.search("foobar")[0]).to.equal(2); + }); + + it("Should have been exported properly (documents)", function(){ + var index = new FlexSearch({ tokenize: "reverse", doc: { @@ -2248,9 +2705,10 @@ if(env !== "light") describe("Export / Import", function(){ } }); - it("Should have been imported properly", function(){ + it("Should have been imported properly (documents)", function(){ - var index = new FlexSearch("score", { + var index = new FlexSearch({ + tokenize: "reverse", doc: { id: "id", field: "title" @@ -2286,6 +2744,19 @@ describe("Presets", function(){ expect(FlexSearch.create("balance").length).to.equal(0); expect(FlexSearch.create("fast").length).to.equal(0); }); + + it("Should have been properly extended", function(){ + + var index = FlexSearch.create("fast"); + index.add(0, "foobar"); + expect(index.search("bar")).to.have.lengthOf(0); + + index = FlexSearch.create("fast", {id: "test", tokenize: "reverse"}); + expect(index.id).to.equal("test"); + index.add(0, "foobar"); + expect(index.search("bar")).to.have.lengthOf(1); + expect(index.search("bar")).to.have.members([0]) + }); }); // ------------------------------------------------------------------------ @@ -2418,6 +2889,23 @@ describe("Destroy", function(){ expect(index.search("foo")).to.have.lengthOf(0); expect(index.search("bar")).to.have.lengthOf(0); }); + + if(env !== "light") it("Should have been destroyed properly (documents)", function(){ + + var data = [{id: 0, title: "foo"}, {id: 1, title: "bar"}]; + + var index = FlexSearch.create({doc: {id: "id", field: "title"}}) + .add(data) + .add(data); + + expect(index.search("foo")).to.have.members([data[0]]); + expect(index.search("bar")).to.have.members([data[1]]); + + index.destroy(); + + expect(index.search("foo")).to.have.lengthOf(0); + expect(index.search("bar")).to.have.lengthOf(0); + }); }); // ------------------------------------------------------------------------