diff --git a/README.md b/README.md
index 6a84396..82fe9ca 100644
--- a/README.md
+++ b/README.md
@@ -3,23 +3,21 @@
-
-
- | BulkSearch - | FlexSearch + | Type | +Complexity | +
Each single term query: | +1 | +|||
Lexical Pre-Scored Dictionary (Solo): | +TERM_COUNT * TERM_MATCHES | +|||
Lexical Pre-Scored Dictionary + Context-based Map: | +TERM_MATCHES / TERM_COUNT | +
+ | BulkSearch | +FlexSearch | ||
Access | @@ -435,7 +462,7 @@ __Note:__ This feature is actually not enabled by default. ReadWeaks |
|
-
|
+
|
The configuration profile. Choose your preferation. @@ -1629,7 +1660,7 @@ FlexSearch ist highly customizable. Make use of the the right | depth |
false - {number:0-9} + {number} |
Enable/Disable contextual indexing and also sets contextual distance of relevance. | threshold |
false - {number:0-9} + {number} |
Enable/Disable the threshold of minimum relevance all results should have. Note: It is also possible to set a lower threshold for indexing and pass a higher value when calling index.search(options). |
resolution | ++ {number} + | +Sets the scoring resolution (default: 9). | +||
stemmer |
@@ -1664,7 +1703,7 @@ FlexSearch ist highly customizable. Make use of the the right | |||
rtl |
+ rtl |
true false @@ -1966,6 +2005,8 @@ The required memory for the index depends on several options: |
=d)for(g=f._ctx[p]||(f._ctx[p]=t()),p=this.c[p]||(this.c[p]= -C(10-(d||0))),k=e-B,m=e+B+1,0>k&&(k=0),m>q&&(m=q);kc&&(g=g.slice(0,c)));e=g}return e};e.prototype.clear=function(){return this.destroy().init()}; -e.prototype.destroy=function(){this.g=this.c=this.a=null;return this};const w={icase:function(a){return a.toLowerCase()}};return e}(!1),this); +'use strict';(function(d,l,v){let t;(t=v.define)&&t.amd?t([],function(){return l}):(t=v.modules)?t[d.toLowerCase()]=l:"object"===typeof exports?module.exports=l:v[d]=l})("FlexSearch",function(){function d(a,c){const b=c?c.id:a&&a.id;this.id=b||0===b?b:G++;this.init(a,c);l(this,"index",function(){return this.b});l(this,"length",function(){return Object.keys(this.b).length})}function l(a,c,b){Object.defineProperty(a,c,{get:b})}function v(a,c){for(let b=0;b =g&&(a=a[h-(e+.5>>0)],a=a[b]||(a[b]=[]),a[a.length]=f);return e}function A(a,c){if(a){const b=Object.keys(a);for(let f=0,e=b.length;f a?1:a?-1:0}function I(a,c){a=a.length-c.length;return 0>a?-1:a?1:0}function z(a){return"function"=== +typeof a}function y(a){return"undefined"===typeof a}function C(a){const c=Array(a);for(let b=0;b +k;c--)m=p.substring(k,c),t(q,f,m,a,b,h,e,r-1)}break;default:if(g=t(q,f,p,a,1,h,e,r-1),n&&1 =e)for(g=f._ctx[p]||(f._ctx[p]=u()),p=this.f[p]||(this.f[p]=C(r-(e||0))),h=d-n,m=d+n+1,0>h&&(h=0),m>x&&(m=x);h c&&(g=g.slice(0,c)));d=g}return d};d.prototype.clear=function(){return this.destroy().init()};d.prototype.destroy=function(){this.h=this.f=this.b=null;return this};const w={icase:function(a){return a.toLowerCase()}};return d}(!1),this); diff --git a/dist/flexsearch.min.js b/dist/flexsearch.min.js index 5facd5b..df8a329 100644 --- a/dist/flexsearch.min.js +++ b/dist/flexsearch.min.js @@ -1,37 +1,38 @@ /* - FlexSearch v0.5.0 + FlexSearch v0.5.1 Copyright 2019 Nextapps GmbH Author: Thomas Wilkerling Released under the Apache 2.0 Licence https://github.com/nextapps-de/flexsearch */ -'use strict';(function(x,F,f){let y;(y=f.define)&&y.amd?y([],function(){return F}):(y=f.modules)?y[x.toLowerCase()]=F:"object"===typeof exports?module.exports=F:f[x]=F})("FlexSearch",function U(x){function f(a,b){const c=b?b.id:a&&a.id;this.id=c||0===c?c:V++;this.init(a,b);K(this,"index",function(){return this.b});K(this,"length",function(){return Object.keys(this.b).length})}function y(a,b,c,d){this.s!==this.f&&(this.j=this.j.concat(c),this.s++,d&&this.j.length>=d&&(this.s=this.f),this.F&&this.s=== -this.f&&(this.cache&&this.m.set(b,this.j),this.F(this.j),this.j=[]));return this}function A(a,b){a=a.concat.apply([],a);b&&(G(b)||(z=b.split(":"),1 =k&&(a=a[9-(e+.5>>0)],a=a[c]||(a[c]=[]),a[a.length]=d); -return e}function N(a,b){if(a){const c=Object.keys(a);for(let d=0,e=c.length;d a?1:a?-1:0}function Z(a,b){a=a.length-b.length;return 0>a?-1:a?1:0}function X(a,b){a=a[z];b=b[z];return ab?1:0}function W(a,b){const c=z.length;for(let d=0;d b?1:0}function aa(a,b,c,d){let e=[],g;const k=a.length;if(1 b&&(e=e.slice(0,b));return e}function J(a){return"string"===typeof a}function G(a){return"function"===typeof a}function D(a){return"object"=== -typeof a}function B(a){return"undefined"===typeof a}function P(a){const b=Array(a);for(let c=0;c=this.l.length&&(this.C=0),this.l[this.C].postMessage({add:!0,id:a,content:b}),this.b[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.c;e=G(c)?c(b):b.split(R);const m=q();m._ctx=q();const n=this.threshold,r=this.depth,t=this.h,v=e.length,L=this.D;for(let b=0;b f;c--)l=k.substring(f,c),I(t,m,l,a,b,h,n)}break;default:if(p=I(t,m,k,a,1,h,n),r&&1 =n)for(p=m._ctx[k]||(m._ctx[k]=q()),k=this.i[k]||(this.i[k]=P(10-(n||0))),h=b-r,l=b+r+1,0>h&&(h=0),l>v&&(l=v);h r&&(a=m));for(r=n?1:0;r c;d--)e=f[d-1],f[d]=e,b[e]=d;f[c]=a;b[a]=c}}}return b};return a}();return f}(function(){const x={},F="undefined"!==typeof Blob&&"undefined"!==typeof URL&&URL.createObjectURL; -return function(f,y,A,K,h){A=F?URL.createObjectURL(new Blob(["("+A.toString()+")()"],{type:"text/javascript"})):f+".min.js";f+="-"+y;x[f]||(x[f]=[]);x[f][h]=new Worker(A);x[f][h].onmessage=K;return x[f][h]}}()),this); +'use strict';(function(w,F,g){let y;(y=g.define)&&y.amd?y([],function(){return F}):(y=g.modules)?y[w.toLowerCase()]=F:"object"===typeof exports?module.exports=F:g[w]=F})("FlexSearch",function U(w){function g(a,b){const c=b?b.id:a&&a.id;this.id=c||0===c?c:V++;this.init(a,b);K(this,"index",function(){return this.b});K(this,"length",function(){return Object.keys(this.b).length})}function y(a,b,c,d){this.u!==this.g&&(this.l=this.l.concat(c),this.u++,d&&this.l.length>=d&&(this.u=this.g),this.G&&this.u=== +this.g&&(this.cache&&this.o.set(b,this.l),this.G(this.l),this.l=[]));return this}function B(a,b){a=a.concat.apply([],a);b&&(G(b)||(z=b.split(":"),1 =k&&(a=a[h-(e+.5>>0)],a=a[c]||(a[c]=[]),a[a.length]= +d);return e}function N(a,b){if(a){const c=Object.keys(a);for(let d=0,e=c.length;d a?1:a?-1:0}function Z(a,b){a=a.length-b.length;return 0>a?-1:a?1:0}function X(a,b){a=a[z];b=b[z];return ab?1:0}function W(a,b){const c=z.length;for(let d=0;d b?1:0}function aa(a,b,c,d,e){c=[];let l;const k=a.length;if(1 b&&(c=c.slice(0,b));return c}function H(a){return"string"===typeof a}function x(a){return a.constructor=== +Array}function G(a){return"function"===typeof a}function A(a){return"object"===typeof a}function v(a){return"undefined"===typeof a}function P(a){const b=Array(a);for(let c=0;c=this.m.length&&(this.D=0),this.m[this.D].postMessage({add:!0,id:a,content:b}),this.b[l]=""+this.D,c&&c(),this;if(!e){if(this.async&&"function"!==typeof importScripts){let e=this;l=new Promise(function(c){setTimeout(function(){e.add(a,b,null,d,!0);e=null;c()})});if(c)l.then(c);else return l;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=G(c)?c(b):b.split(R);const m=r();m._ctx=r();const p=this.threshold,C=this.depth,t=this.c,q=this.i,D=e.length,L=this.F;for(let b=0;b g;c--)n=k.substring(g,c),J(q,m,n,a,b,f,p,t-1)}break;default:if(h=J(q, +m,k,a,1,f,p,t-1),C&&1 =p)for(h=m._ctx[k]||(m._ctx[k]=r()),k=this.j[k]||(this.j[k]=P(t-(p||0))),f=b-C,n=b+C+1,0>f&&(f=0),n>D&&(n=D);f c;d--)e=f[d-1],f[d]=e,b[e]=d;f[c]=a;b[a]=c}}}return b};return a}();return g}(function(){const w= +{},F="undefined"!==typeof Blob&&"undefined"!==typeof URL&&URL.createObjectURL;return function(g,y,B,K,f){B=F?URL.createObjectURL(new Blob(["("+B.toString()+")()"],{type:"text/javascript"})):g+".min.js";g+="-"+y;w[g]||(w[g]=[]);w[g][f]=new Worker(B);w[g][f].onmessage=K;return w[g][f]}}()),this); diff --git a/dist/flexsearch.node.js b/dist/flexsearch.node.js index 29ef3cd..9ea08db 100644 --- a/dist/flexsearch.node.js +++ b/dist/flexsearch.node.js @@ -1,33 +1,34 @@ /* - FlexSearch v0.5.0 + FlexSearch v0.5.1 Copyright 2019 Nextapps GmbH Author: Thomas Wilkerling Released under the Apache 2.0 Licence https://github.com/nextapps-de/flexsearch */ -'use strict';(function(h,w,C){let f;(f=C.define)&&f.amd?f([],function(){return w}):(f=C.modules)?f[h.toLowerCase()]=w:"object"===typeof exports?module.exports=w:C[h]=w})("FlexSearch",function(){function h(a,b){const c=b?b.id:a&&a.id;this.id=c||0===c?c:R++;this.init(a,b);C(this,"index",function(){return this.b});C(this,"length",function(){return Object.keys(this.b).length})}function w(a,b){a=a.concat.apply([],a);b&&(D(b)||(x=b.split(":"),1 =l&&(a=a[9-(e+.5>>0)],a=a[c]||(a[c]=[]),a[a.length]=d);return e}function K(a,b){if(a){const c=Object.keys(a);for(let d=0,e=c.length;d a?1:a?-1:0}function V(a,b){a=a.length-b.length;return 0>a?-1:a?1:0}function T(a,b){a=a[x];b=b[x];return ab?1:0}function S(a,b){const c=x.length;for(let d= -0;d b?1:0}function W(a,b,c,d){let e=[],g;const l=a.length;if(1 b&&(e=e.slice(0,b));return e}function I(a){return"string"===typeof a}function D(a){return"function"===typeof a}function z(a){return"object"===typeof a}function B(a){return"undefined"===typeof a}function M(a){const b=Array(a);for(let c=0;cm;c--)n=l.substring(m,c),H(r,h,n,a,b,f,p)}break;default:if(k=H(r,h,l,a,1,f,p),E&&1 =p)for(k=h._ctx[l]||(h._ctx[l]=u()),l=this.h[l]||(this.h[l]=M(10-(p||0))),f=b-E,n=b+E+1,0>f&&(f=0),n>t&&(n=t);f h&&(a=m));for(h=p?1:0;h c;d--)e=f[d-1],f[d]=e,b[e]=d;f[c]=a;b[a]=c}}}return b};return a}();return h}(!1),this); +'use strict';(function(h,y,D){let e;(e=D.define)&&e.amd?e([],function(){return y}):(e=D.modules)?e[h.toLowerCase()]=y:"object"===typeof exports?module.exports=y:D[h]=y})("FlexSearch",function(){function h(a,b){const c=b?b.id:a&&a.id;this.id=c||0===c?c:S++;this.init(a,b);D(this,"index",function(){return this.c});D(this,"length",function(){return Object.keys(this.c).length})}function y(a,b){a=a.concat.apply([],a);b&&(E(b)||(z=b.split(":"),1=g&&(a=a[k-(f+.5>>0)],a=a[c]||(a[c]=[]),a[a.length]=d);return f}function L(a,b){if(a){const c=Object.keys(a);for(let d=0,f=c.length;d a?1:a?-1:0}function W(a,b){a=a.length-b.length;return 0>a?-1:a?1:0}function U(a,b){a=a[z];b=b[z];return ab?1:0}function T(a,b){const c=z.length;for(let d= +0;d b?1:0}function X(a,b,c,d,f){c=[];let l;const g=a.length;if(1 b&&(c=c.slice(0,b));return c}function F(a){return"string"===typeof a}function x(a){return a.constructor===Array}function E(a){return"function"===typeof a}function A(a){return"object"===typeof a}function v(a){return"undefined"===typeof a}function N(a){const b=Array(a);for(let c=0;cm;c--)n=g.substring(m,c),H(I,p,n,a,b,e,q,u-1)}break;default:if(k=H(I,p,g,a,1,e,q,u-1),h&&1=q)for(k=p._ctx[g]||(p._ctx[g]=t()),g=this.i[g]||(this.i[g]=N(u-(q||0))),e=b-h,n=b+h+1,0>e&&(e=0),n>B&&(n=B);e c;d--)f=e[d-1],e[d]=f,b[f]=d;e[c]=a;b[a]=c}}}return b};return a}();return h}(!1),this); diff --git a/flexsearch.js b/flexsearch.js index 12f13e0..b9b9d4a 100644 --- a/flexsearch.js +++ b/flexsearch.js @@ -1,5 +1,5 @@ /**! - * @preserve FlexSearch v0.5.0 + * @preserve FlexSearch v0.5.1 * Copyright 2019 Nextapps GmbH * Author: Thomas Wilkerling * Released under the Apache 2.0 Licence @@ -41,15 +41,17 @@ async: false, worker: false, rtl: false, + doc: false, + paging: false, - // minimum scoring (0 - 9) + // maximum scoring + resolution: 9, + + // minimum scoring threshold: 0, // contextual depth - depth: 0, - - // multi-field documents - doc: false + depth: 0 }; /** @@ -62,39 +64,46 @@ "memory": { encode: SUPPORT_ENCODER ? "extra" : "icase", tokenize: "strict", - threshold: 7 + threshold: 0, + resolution: 1 }, "speed": { encode: "icase", tokenize: "strict", - threshold: 7, + threshold: 1, + resolution: 3, depth: 2 }, "match": { encode: SUPPORT_ENCODER ? "extra" : "icase", - tokenize: "full" + tokenize: "full", + threshold: 1, + resolution: 3 }, "score": { encode: SUPPORT_ENCODER ? "extra" : "icase", tokenize: "strict", - threshold: 5, + threshold: 1, + resolution: 9, depth: 4 }, "balance": { encode: SUPPORT_ENCODER ? "balance" : "icase", tokenize: "strict", - threshold: 6, + threshold: 0, + resolution: 3, depth: 3 }, - "fastest": { + "fast": { encode: "icase", tokenize: "strict", - threshold: 9, + threshold: 8, + resolution: 9, depth: 1 } }; @@ -114,19 +123,6 @@ let id_counter = 0; - /** - * @enum {number} - */ - - /* - const enum_task = { - - add: 0, - update: 1, - remove: 2 - }; - */ - /** * NOTE: This could make issues on some languages (chinese, korean, thai) * TODO: extract all language-specific stuff from the core @@ -295,6 +291,14 @@ return this; } + const tag_options = { + + "encode": false, + "tokenize": function(doc){ + return [doc]; + } + }; + /** * @param {Object |string=} options * @param {Object =} settings @@ -432,6 +436,23 @@ custom ); + /** @private */ + this.resolution = ( + + is_undefined(custom = options["resolution"]) ? + + custom = preset.resolution || + this.resolution || + defaults.resolution + : + custom + ); + + if(custom <= this.threshold){ + + this.resolution = this.threshold + 1; + } + /** @private */ this.depth = ( @@ -444,6 +465,27 @@ custom ); + /** @private */ + this.paging = ( + + is_undefined(custom = options["paging"]) ? + + this.paging || + defaults.paging + : + custom + ); + + // TODO: provide boost + + /** @private */ + /* + this.boost = ( + + (custom = options["boost"]) ? custom : 0 + ); + */ + if(SUPPORT_SUGGESTIONS){ /** @private */ @@ -511,7 +553,7 @@ // initialize primary index /** @private */ - this._map = create_object_array(10 - (this.threshold || 0)); + this._map = create_object_array(this.resolution - (this.threshold || 0)); /** @private */ this._ctx = create_object(); /** @private */ @@ -529,12 +571,36 @@ let field = doc["field"]; let tag = doc["tag"]; - doc["id"] = doc["id"].split(":"); + if(!is_array(doc["id"])){ + + doc["id"] = doc["id"].split(":"); + } if(SUPPORT_WHERE && tag){ this._tag = create_object(); + let field_custom = create_object(); + + if(field){ + + if(is_string(field)){ + + field_custom[field] = options; + } + else if(is_array(field)){ + + for(let i = 0; i < field.length; i++){ + + field_custom[field[i]] = options; + } + } + else if(is_object(field)){ + + field_custom = field; + } + } + if(!is_array(tag)){ doc["tag"] = tag = [tag]; @@ -542,30 +608,57 @@ for(let i = 0; i < tag.length; i++){ - this._tag[tag[i]] = create_object(); + //TODO: delegate tag indexes to intersection + //field_custom[tag[i]] = tag_options; - tag[i] = tag[i].split(":"); + this._tag[tag[i]] = create_object(); } + + this._tags = tag; + + field = field_custom; } if(field){ + let has_custom; + if(!is_array(field)){ - doc["field"] = field = [field]; + if(is_object(field)){ + + has_custom = field; + doc["field"] = field = get_keys(field); + } + else{ + + doc["field"] = field = [field]; + } } for(let i = 0; i < field.length; i++){ - ref[field[i]] = i; - field[i] = field[i].split(":"); + if(!is_array(field[i])){ + + if(has_custom){ + + options = has_custom[field[i]]; + } + + ref[field[i]] = i; + field[i] = field[i].split(":"); + } + + // TODO: move fields to main index to provide pagination + index[i] = new FlexSearch(options); index[i]._doc = this._doc; - if(SUPPORT_WHERE && tag){ - - index[i]._tag = this._tag; - } + // if(SUPPORT_WHERE && tag){ + // + // index[i]._tag = this._tag; + // index[i]._tags = tag; + // } } } } @@ -826,6 +919,7 @@ const threshold = this.threshold; const depth = this.depth; + const resolution = this.resolution; const map = this._map; const word_length = words.length; const rtl = this.rtl; @@ -863,7 +957,8 @@ id, rtl ? 1 : (length - a) / length, context_score, - threshold + threshold, + resolution - 1 ); } @@ -885,7 +980,8 @@ id, rtl ? (a + 1) / length : 1, context_score, - threshold + threshold, + resolution - 1 ); } @@ -909,7 +1005,8 @@ id, partial_score, context_score, - threshold + threshold, + resolution - 1 ); } } @@ -930,13 +1027,14 @@ // TODO compute and pass distance of ngram sequences as the initial score for each word 1, context_score, - threshold + threshold, + resolution - 1 ); if(depth && (word_length > 1) && (score >= threshold)){ const ctxDupes = dupes["_ctx"][value] || (dupes["_ctx"][value] = create_object()); - const ctxTmp = this._ctx[value] || (this._ctx[value] = create_object_array(10 - (threshold || 0))); + const ctxTmp = this._ctx[value] || (this._ctx[value] = create_object_array(resolution - (threshold || 0))); let x = i - depth; let y = i + depth + 1; @@ -953,8 +1051,9 @@ words[x], id, 0, - 10 - (x < i ? i - x : x - i), - threshold + resolution - (x < i ? i - x : x - i), + threshold, + resolution - 1 ); } } @@ -986,24 +1085,24 @@ /** * @param {!string} job - * @param docs + * @param doc * @param {Function=} callback * @returns {*} */ - FlexSearch.prototype.handle_docs = function(job, docs, callback){ + FlexSearch.prototype.handle_docs = function(job, doc, callback){ - if(is_array(docs)){ + if(is_array(doc)){ - for(let i = 0, len = docs.length; i < len; i++){ + for(let i = 0, len = doc.length; i < len; i++){ if(i === len - 1){ - return this.handle_docs(job, docs[i], callback); + return this.handle_docs(job, doc[i], callback); } else{ - this.handle_docs(job, docs[i]); + this.handle_docs(job, doc[i]); } } } @@ -1014,23 +1113,37 @@ let tree = this.doc["id"]; let id; let tag; + let tag_key; + let tag_value; for(let i = 0; i < tree.length; i++){ - id = (id || docs)[tree[i]]; + id = (id || doc)[tree[i]]; } if(tags){ for(let i = 0; i < tags.length; i++){ - tag = (tag || docs)[tags[i]]; + tag_key = tags[i]; + + const tag_split = tag_key.split(":"); + + for(let i = 0; i < tag_split.length; i++){ + + tag_value = (tag_value || doc)[tag_split[i]]; + } + + tag_value = "@" + tag_value; } + + tag = this._tag[tag_key]; + tag = tag[tag_value] || (tag[tag_value] = []); } if(job === "remove"){ - delete this._doc["@" + id]; + delete this._doc[id]; for(let z = 0, length = index.length; z < length; z++){ @@ -1047,6 +1160,11 @@ tree = this.doc["field"]; + if(tag){ + + tag[tag.length] = doc; // tag[tag.length] = id; + } + for(let i = 0, len = tree.length; i < len; i++){ const branch = tree[i]; @@ -1054,20 +1172,23 @@ for(let x = 0; x < branch.length; x++){ - content = (content || docs)[branch[x]]; + content = (content || doc)[branch[x]]; } - this._doc["@" + id] = docs; + this._doc[id] = doc; const self = index[i]; - const fn = ( - job === "add" ? + let fn; - self.add - : - self.update - ); + if(job === "add"){ + + fn = self.add; + } + else{ + + fn = self.update; + } if(i === len - 1){ @@ -1199,7 +1320,7 @@ profile_start("remove"); } - for(let z = 0; z < (10 - (this.threshold || 0)); z++){ + for(let z = 0; z < (this.resolution - (this.threshold || 0)); z++){ remove_index(this._map[z], id); } @@ -1257,6 +1378,8 @@ } /** + * TODO: move fields to main index to provide pagination + * * @param {!string|Object|Array