From 3e89963192935f9ad5088204b88cfa2fca02e7e8 Mon Sep 17 00:00:00 2001 From: Thomas Wilkerling Date: Mon, 17 May 2021 19:02:41 +0200 Subject: [PATCH] added tags to the document --- dist/flexsearch.compact.js | 24 +++ dist/flexsearch.light.js | 17 ++ dist/flexsearch.min.js | 41 ++--- doc/0.7.0.md | 141 +++++++++++++++- package.json | 2 +- src/config.js | 23 +-- src/config/bundle.js | 44 +++++ src/config/compact.js | 44 +++++ src/config/light.js | 44 +++++ src/document.js | 323 +++++++++++++++++++++++++++++-------- src/index.js | 44 +---- src/intersect.js | 41 ++++- 12 files changed, 641 insertions(+), 147 deletions(-) create mode 100644 dist/flexsearch.compact.js create mode 100644 dist/flexsearch.light.js create mode 100644 src/config/bundle.js create mode 100644 src/config/compact.js create mode 100644 src/config/light.js diff --git a/dist/flexsearch.compact.js b/dist/flexsearch.compact.js new file mode 100644 index 0000000..2f18d91 --- /dev/null +++ b/dist/flexsearch.compact.js @@ -0,0 +1,24 @@ +/**! + * FlexSearch.js v0.7.0-pre-alpha (Compact) + * Copyright 2019 Nextapps GmbH + * Author: Thomas Wilkerling + * Licence: Apache-2.0 + * https://github.com/nextapps-de/flexsearch + */ +(function(){var r;function t(a){for(var b=Array(a),c=0;c=b)return e.concat(d.slice(0,b-f));e=e.concat(d);f+=h}return e};var O={memory:{charset:"latin:extra",m:3,j:3,u:!1,l:"memory"},performance:{threshold:8,j:3,context:{depth:1,s:!0}},match:{charset:"latin:extra",D:"full",m:3},score:{charset:"latin:advanced",threshold:1,context:{depth:3,s:!0}},"default":{m:3,threshold:0,depth:3}};function P(a,b){if(!(this instanceof P))return new P(a);var c;if(a){"string"===typeof a?a=O[a]:(c=a.preset)&&(a=Object.assign({},O[c],a));c=a.charset;var d=a.lang;"string"===typeof c&&(-1===c.indexOf(":")&&(c+=":default"),c=F[c]);"string"===typeof d&&(d=E[d])}else a={};var e,h=a.context||{};this.encode=a.encode||c&&c.encode||B;this.register=b||x();var f=a.resolution||9;var g=a.threshold||0;g>=f&&(g=f-1);this.m=f;this.threshold=g;this.v=b=c&&c.D||a.tokenize||"strict";this.depth="strict"===b&&h.depth; +this.s=Q(h.bidirectional,!0);this.l=e="memory"===a.optimize;this.u=Q(a.fastupdate,!0);this.j=a.minlength||1;this.g=e?t(f-g):x();f=h.resolution||f;g=h.threshold||g;g>=f&&(g=f-1);this.h=f;this.o=g;this.i=e?t(f-g):x();this.B=c&&c.B||a.rtl;this.A=(b=a.matcher||d&&d.A)&&D(b,!1);this.C=(b=a.stemmer||d&&d.C)&&D(b,!0);if(a=b=a.filter||d&&d.filter){a=b;c=x();d=0;for(b=a.length;d=this.j&&(e||!f[k])){var m=Math.min(this.m/d*l|0,l);if(mq;v--)p=k.substring(q,v),p.length>=this.j&&R(this,f,p,u,a,c)}break}case "reverse":if(2< +n){for(q=n-1;0=this.j&&R(this,f,p,m,a,c);p=""}case "forward":if(1=this.j&&R(this,f,p,m,a,c);break;default:if(R(this,f,k,m,a,c),e&&1=this.j&&!m[k]){if(m[k]=1,v=Math.min((this.h-q)/d*l+u|0,l+(u-1)),vp;R(this,g,y?p:k,v,a,c,y?k:p)}}else q=Math.min(q+1,d-l)}}}}this.u||(this.register[a]=1)}}return this}; +function R(a,b,c,d,e,h,f){var g=f?a.i:a.g;if(!b[c]||f&&!b[c][f])a.l&&(g=g[d]),f?(b[c]||(b[c]=x()),b[c][f]=1,g=g[f]||(g[f]=x())):b[c]=1,g=g[c]||(g[c]=[]),a.l||(g=g[d]||(g[d]=[])),h&&-1!==g.indexOf(e)||(g[g.length]=e,a.u&&(a=a.register[e]||(a.register[e]=[]),a[a.length]=g))} +r.search=function(a,b,c){"object"===typeof a?(c=a,a=c.query):"object"===typeof b&&(c=b);var d=[],e=this.threshold;if(c){b=c.limit;e=Q(c.threshold,e);var h=c.context;var f=c.suggest}if(a){a=this.encode(a);var g=a.length;if(1=this.j&&!c[m])if(this.l||f||this.g[m])l[n++]=m,c[m]=1;else return d;a=l;g=a.length}}if(!g)return d;b||(b=100);c=this.m-e;e=this.h-e;h=this.depth&&1b?d.slice(0,b):d}}return N(d,b,f)};function T(a,b,c,d){c?(d=d&&b>c,a=(a=a[d?b:c])&&a[d?c:b]):a=a[b];return a} +function S(a,b,c,d,e,h,f,g){var l=[],k=g?a.i:a.g;a.l||(k=T(k,f,g,a.s));if(k){var n=0;d=Math.min(k.length,d);for(var m=0,p=0,q;m=e)));m++);if(n){if(h)return l=1===n?l[0]:[].concat.apply([],l),l.length>e?l.slice(0,e):l;b[b.length]=l;return}}return!c&&l}r.contain=function(a){return!!this.register[a]};r.update=function(a,b){return this.remove(a).add(a,b)}; +r.remove=function(a,b){var c=this.register[a];if(c){if(this.u)for(var d=0,e;d=b)return g.concat(d.slice(0,b-e));g=g.concat(d);e+=k}return g};function G(a,b){if(!(this instanceof G))return new G(a);if(a){var c=a.charset;var d=a.lang;"string"===typeof c&&(-1===c.indexOf(":")&&(c+=":default"),c=E[c]);"string"===typeof d&&(d=D[d])}else a={};var g,k=a.context||{};this.encode=a.encode||c&&c.encode||A;this.register=b||v();var e=a.resolution||9;var f=a.threshold||0;f>=e&&(f=e-1);this.i=e;this.s=f;this.F=b=c&&c.G||a.tokenize||"strict";this.m="strict"===b&&k.depth;this.v=H(k.bidirectional,!0);this.g=g="memory"===a.optimize;this.C=H(a.fastupdate, +!0);this.h=a.minlength||1;this.j=g?r(e-f):v();e=k.resolution||e;f=k.threshold||f;f>=e&&(f=e-1);this.l=e;this.D=f;this.B=g?r(e-f):v();this.u=c&&c.u||a.rtl;this.o=(b=a.matcher||d&&d.o)&&C(b,!1);this.A=(b=a.stemmer||d&&d.A)&&C(b,!0);if(a=b=a.filter||d&&d.filter){a=b;c=v();d=0;for(b=a.length;d=this.h&&(g||!e[l])){var q=Math.min(this.i/d*h|0,h);if(qm;u--)n=l.substring(m,u),n.length>=this.h&&I(this,e,n,t,a,c)}break}case "reverse":if(2< +p){for(m=p-1;0=this.h&&I(this,e,n,q,a,c);n=""}case "forward":if(1=this.h&&I(this,e,n,q,a,c);break;default:if(I(this,e,l,q,a,c),g&&1=this.h&&!q[l]){if(q[l]=1,u=Math.min((this.l-m)/d*h+t|0,h+(t-1)),un;I(this,f,x?n:l,u,a,c,x?l:n)}}else m=Math.min(m+1,d-h)}}}}this.C||(this.register[a]=1)}}return this}; +function I(a,b,c,d,g,k,e){var f=e?a.B:a.j;if(!b[c]||e&&!b[c][e])a.g&&(f=f[d]),e?(b[c]||(b[c]=v()),b[c][e]=1,f=f[e]||(f[e]=v())):b[c]=1,f=f[c]||(f[c]=[]),a.g||(f=f[d]||(f[d]=[])),k&&-1!==f.indexOf(g)||(f[f.length]=g,a.C&&(a=a.register[g]||(a.register[g]=[]),a[a.length]=f))} +G.prototype.search=function(a,b,c){"object"===typeof a?(c=a,a=c.query):"object"===typeof b&&(c=b);var d=[],g=this.s;if(c){b=c.limit;g=H(c.threshold,g);var k=c.context;var e=!1}if(a){a=this.encode(a);var f=a.length;if(1=this.h&&!c[q])if(this.g||e||this.j[q])h[p++]=q,c[q]=1;else return d;a=h;f=a.length}}if(!f)return d;b||(b=100);c=this.i-g;g=this.l-g;k=this.m&&1b?d.slice(0,b):d}}return F(d,b,e)};function K(a,b,c,d){c?(d=d&&b>c,a=(a=a[d?b:c])&&a[d?c:b]):a=a[b];return a} +function J(a,b,c,d,g,k,e,f){var h=[],l=f?a.B:a.j;a.g||(l=K(l,e,f,a.v));if(l){var p=0;d=Math.min(l.length,d);for(var q=0,n=0,m;q=g)));q++);if(p){if(k)return h=1===p?h[0]:[].concat.apply([],h),h.length>g?h.slice(0,g):h;b[b.length]=h;return}}return!c&&h}G.prototype.contain=function(a){return!!this.register[a]};G.prototype.update=function(a,b){return this.remove(a).add(a,b)}; +G.prototype.remove=function(a,b){var c=this.register[a];if(c){if(this.C)for(var d=0,g;d=b)return e.concat(d.slice(0,b-f));e=e.concat(d);f+=h}return e};function Q(a,b){if(!(this instanceof Q))return new Q(a);if(a){a=ca(a);var c=a.charset;var d=a.lang;"string"===typeof c&&(-1===c.indexOf(":")&&(c+=":default"),c=E[c]);"string"===typeof d&&(d=D[d])}else a={};var e,h=a.context||{};this.encode=a.encode||c&&c.encode||aa;this.register=b||x();var f=a.resolution||9;var g=a.threshold||0;g>=f&&(g=f-1);this.m=f;this.threshold=g;this.D=b=c&&c.F||a.tokenize||"strict";this.depth="strict"===b&&h.depth;this.s=R(h.bidirectional,!0);this.l=e="memory"===a.optimize; -this.v=R(a.fastupdate,!0);this.j=a.minlength||1;this.g=e?t(f-g):x();f=h.resolution||f;g=h.threshold||g;g>=f&&(g=f-1);this.h=f;this.o=g;this.i=e?t(f-g):x();this.B=c&&c.B||a.rtl;this.A=(b=a.matcher||d&&d.A)&&C(b,!1);this.C=(b=a.stemmer||d&&d.C)&&C(b,!0);this.filter=(b=a.filter||d&&d.filter)&&ba(b);this.cache=(b=a.cache)&&new N(b);this.u=(b=a.worker)&&new Worker("worker.js",{type:"module"});b&&(this.u.onmessage=function(k){console.log(k.data)},this.u.postMessage({G:"register",options:a}))} -function R(a,b){return"undefined"!==typeof a?a:b}r=Q.prototype;r.append=function(a,b){return this.add(a,b,!0)}; -r.add=function(a,b,c){if(this.register[a]&&!c)return this.update(a,b);if(b&&(a||0===a)){b=this.encode(b);var d=b.length;if(d){for(var e=this.depth,h=this.m-this.threshold,f=x(),g=x(),k=0;k=this.j&&(e||!f[l])){var m=Math.min(this.m/d*k|0,k);if(mq;v--)n=l.substring(q,v),n.length>=this.j&&S(this,f,n,u,a,c)}break}case "reverse":if(2< -p){for(q=p-1;0=this.j&&S(this,f,n,m,a,c);n=""}case "forward":if(1=this.j&&S(this,f,n,m,a,c);break;default:if(S(this,f,l,m,a,c),e&&1=this.j&&!m[l]){if(m[l]=1,v=Math.min((this.h-q)/d*k+u|0,k+(u-1)),vn;S(this,g,y?n:l,v,a,c,y?l:n)}}else q=Math.min(q+1,d-k)}}}}this.v||(this.register[a]=1)}}return this}; -function S(a,b,c,d,e,h,f){var g=f?a.i:a.g;if(!b[c]||f&&!b[c][f])a.l&&(g=g[d]),f?(b[c]||(b[c]=x()),b[c][f]=1,g=g[f]||(g[f]=x())):b[c]=1,g=g[c]||(g[c]=[]),a.l||(g=g[d]||(g[d]=[])),h&&-1!==g.indexOf(e)||(g[g.length]=e,a.v&&((b=a.register[e])?b[b.length]=g:a.register[e]=[g]))} -r.search=function(a,b,c){"object"===typeof a?(c=a,a=c.query):"object"===typeof b&&(c=b);var d=[],e=this.threshold;if(c){b=c.limit;e=R(c.threshold,e);var h=c.context;var f=c.suggest}if(a){a=this.encode(a);var g=a.length;if(1=this.j&&!c[m])if(this.l||f||this.g[m])k[p++]=m,c[m]=1;else return d;a=k;g=a.length}}if(!g)return d;b||(b=100);c=this.m-e;e=this.h-e;h=this.depth&&1b?d.slice(0,b):d}}return da(d,b,f)};function U(a,b,c,d){c?(d=d&&b>c,a=(a=a[d?b:c])&&a[d?c:b]):a=a[b];return a} -function T(a,b,c,d,e,h,f,g){var k=[],l=g?a.i:a.g;a.l||(l=U(l,f,g,a.s));if(l){var p=0;d=Math.min(l.length,d);for(var m=0,n=0,q;m=e)));m++);if(p){if(h)return k=1===p?k[0]:[].concat.apply([],k),k.length>e?k.slice(0,e):k;b[b.length]=k;return}}return!c&&k}r.contain=function(a){return!!this.register[a]};r.update=function(a,b){return this.remove(a).add(a,b)}; -r.remove=function(a,b){var c=this.register[a];if(c){if(this.v)for(var d=0,e;d=b)return f.concat(d.slice(0,b-e));f=f.concat(d);e+=h}return f};function Q(a){this.h=!0!==a&&a;this.cache=B();this.g=[]}function R(a,b,c){"object"===typeof a&&(a=a.query);var d=this.cache.get(a);d||(d=this.search(a,b,c),this.cache.set(a,d));return d}Q.prototype.set=function(a,b){if(!this.cache[a]){var c=this.g.length;c===this.h?delete this.cache[this.g[c-1]]:c++;for(--c;0=e&&(g=e-1);this.o=e;this.threshold=g;this.A=b=c&&c.G||a.tokenize||"strict";this.depth="strict"===b&&h.depth;this.v=T(h.bidirectional,!0);this.m=f="memory"===a.optimize; +this.s=T(a.fastupdate,!0);this.l=a.minlength||1;this.g=f?u(e-g):B();e=h.resolution||e;g=h.threshold||g;g>=e&&(g=e-1);this.h=e;this.j=g;this.i=f?u(e-g):B();this.C=c&&c.C||a.rtl;this.B=(b=a.matcher||d&&d.B)&&E(b,!1);this.D=(b=a.stemmer||d&&d.D)&&E(b,!0);this.filter=(b=a.filter||d&&d.filter)&&ca(b);this.cache=(b=a.cache)&&new Q(b);this.u=(b=a.worker)&&new Worker("worker.js",{type:"module"});b&&(this.u.onmessage=function(k){console.log(k.data)},this.u.postMessage({H:"register",options:a}))} +function T(a,b){return"undefined"!==typeof a?a:b}q=S.prototype;q.append=function(a,b){return this.add(a,b,!0)}; +q.add=function(a,b,c){if(this.register[a]&&!c)return this.update(a,b);if(b&&(a||0===a)){b=this.encode(b);var d=b.length;if(d){for(var f=this.depth,h=this.o-this.threshold,e=B(),g=B(),k=0;k=this.l&&(f||!e[l])){var p=Math.min(this.o/d*k|0,k);if(pm;v--)n=l.substring(m,v),n.length>=this.l&&U(this,e,n,t,a,c)}break}case "reverse":if(2< +r){for(m=r-1;0=this.l&&U(this,e,n,p,a,c);n=""}case "forward":if(1=this.l&&U(this,e,n,p,a,c);break;default:if(U(this,e,l,p,a,c),f&&1=this.l&&!p[l]){if(p[l]=1,v=Math.min((this.h-m)/d*k+t|0,k+(t-1)),vn;U(this,g,x?n:l,v,a,c,x?l:n)}}else m=Math.min(m+1,d-k)}}}}this.s||(this.register[a]=1)}}return this}; +function U(a,b,c,d,f,h,e){var g=e?a.i:a.g;if(!b[c]||e&&!b[c][e])a.m&&(g=g[d]),e?(b[c]||(b[c]=B()),b[c][e]=1,g=g[e]||(g[e]=B())):b[c]=1,g=g[c]||(g[c]=[]),a.m||(g=g[d]||(g[d]=[])),h&&-1!==g.indexOf(f)||(g[g.length]=f,a.s&&(a=a.register[f]||(a.register[f]=[]),a[a.length]=g))} +q.search=function(a,b,c){"object"===typeof a?(c=a,a=c.query):"object"===typeof b&&(c=b);var d=[],f=this.threshold;if(c){b=c.limit;f=T(c.threshold,f);var h=c.context;var e=c.suggest}if(a){a=this.encode(a);var g=a.length;if(1=this.l&&!c[p])if(this.m||e||this.g[p])k[r++]=p,c[p]=1;else return d;a=k;g=a.length}}if(!g)return d;b||(b=100);c=this.o-f;f=this.h-f;h=this.depth&&1b?d.slice(0,b):d}}return P(d,b,e)};function ha(a,b,c,d){c?(d=d&&b>c,a=(a=a[d?b:c])&&a[d?c:b]):a=a[b];return a} +function fa(a,b,c,d,f,h,e,g){var k=[],l=g?a.i:a.g;a.m||(l=ha(l,e,g,a.v));if(l){var r=0;d=Math.min(l.length,d);for(var p=0,n=0,m;p=f)));p++);if(r){if(h)return k=1===r?k[0]:[].concat.apply([],k),k.length>f?k.slice(0,f):k;b[b.length]=k;return}}return!c&&k}q.contain=function(a){return!!this.register[a]};q.update=function(a,b){return this.remove(a).add(a,b)}; +q.remove=function(a,b){var c=this.register[a];if(c){if(this.s)for(var d=0,f;dp&&(a=a.slice(0,p)),k&&this.store&&(a=ja.call(this,a)),e={tag:e,result:a}):e=void 0;e&&(d[d.length]=e, +h++)}return h?d:[]}}e||(e=this.h);l=l&&(1 A field search will apply a query with the boolean "or" logic by default. Each field has its own result to the given query. -There is only one situation where the `bool` property is still supported. When you like to switch the default "or" logic from the field search into "and", e.g.: +There is one situation where the `bool` property is still supported. When you like to switch the default "or" logic from the field search into "and", e.g.: ```js index.search(query, { @@ -754,7 +773,103 @@ index.search({ } } }); -``` +``` + +### Tags + +Like the `key` for the ID just define the path to the tag: + +```js +const index = new Document({ + key: "id", + tag: "tag", + doc: "content" +}); +``` + +```js +index.add({ + id: 0, + tag: "cat", + content: "Some content ..." +},{ + id: 1, + tag: "dog", + content: "Some content ..." +}); +``` + +Your data also can have multiple tags as an array: + +```js +index.add({ + id: 0, + tag: ["animal", "cat"], + content: "Some content ..." +},{ + id: 1, + tag: ["animal", "dog"], + content: "Some content ..." +}); +``` + +You can perform a tag-specific search by: + +```js +index.search(query, { + field: "content", + tag: "animal" +}); +``` + +This just gives you result which was tagged with the given tag. + +Use multiple tags when searching: + +```js +index.search(query, { + field: "content", + tag: ["cat", "dog"] +}); +``` + +This gives you result which are tagged with one of the given tag. + +> Multiple tags will apply as the boolean "or" by default. It just needs one of the tags to be existing. + +This is another situation where the `bool` property is still supported. When you like to switch the default "or" logic from the tag search into "and", e.g.: + +```js +index.search(query, { + field: "content", + tag: ["dog", "animal"], + bool: "and" +}); +``` + +You will just get results which contains both tags (in this example there is just one records which has the tag "dog" and "animal"). + +### Tag Search + +You can also fetch results from one or more tags when no query was passed: + +```js +index.search({ tag: ["cat", "dog"] }); +``` + +In this case the result-set looks like: + +```js +[{ + tag: "cat", + result: [ /* all cats */ ] +},{ + tag: "dog", + result: [ /* all dogs */ ] +}] +``` + +> By default, every query is limited to 100 entries. Unbounded queries leads into issues. You need to set the limit as an option to adjust the size. ## Document Stores @@ -1355,6 +1470,22 @@ FlexSearch provides you many parameters you can use to adjust the optimal balanc 0 + + document index + +3 (per field) + -1 (per field) + 0 + 0 + + + + document tags + +1 (per tag) + -1 (per tag) + 0 + 0 + + store: true +5 (per document) diff --git a/package.json b/package.json index 4d1f146..1e364aa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flexsearch", - "version": "0.7.0", + "version": "0.7.0-pre-alpha", "description": "Next-Generation full text search library with zero dependencies.", "homepage": "https://github.com/nextapps-de/flexsearch/", "author": "Thomas Wilkerling", diff --git a/src/config.js b/src/config.js index 5f75b15..e94c217 100644 --- a/src/config.js +++ b/src/config.js @@ -2,10 +2,7 @@ export const RELEASE = "browser"; /** @define {boolean} */ -export const DEBUG = true; - -/** @define {boolean} */ -export const PROFILER = false; +export const DEBUG = false; /** @define {boolean} */ export const POLYFILL = true; @@ -28,26 +25,20 @@ export const SUPPORT_ASYNC = true; /** @define {boolean} */ export const SUPPORT_PRESET = true; +/** @define {boolean} */ +export const SUPPORT_STORE = true; + +/** @define {boolean} */ +export const SUPPORT_TAGS = true; + /** @define {boolean} */ export const SUPPORT_SUGGESTION = true; /** @define {boolean} */ export const SUPPORT_SERIALIZE = true; -/** @define {boolean} */ -export const SUPPORT_INFO = true; - /** @define {boolean} */ export const SUPPORT_DOCUMENT = true; -/** @define {boolean} */ -export const SUPPORT_WHERE = true; - /** @define {boolean} */ export const SUPPORT_PAGINATION = true; - -/** @define {boolean} */ -export const SUPPORT_OPERATOR = true; - -/** @define {boolean} */ -export const SUPPORT_CALLBACK = true; diff --git a/src/config/bundle.js b/src/config/bundle.js new file mode 100644 index 0000000..e94c217 --- /dev/null +++ b/src/config/bundle.js @@ -0,0 +1,44 @@ +/** @define {string} */ +export const RELEASE = "browser"; + +/** @define {boolean} */ +export const DEBUG = false; + +/** @define {boolean} */ +export const POLYFILL = true; + +/** @define {boolean} */ +export const SUPPORT_WORKER = true; + +/** @define {boolean|string} */ +export const SUPPORT_ENCODER = true; + +/** @define {boolean|string} */ +export const SUPPORT_LANG = true; + +/** @define {boolean} */ +export const SUPPORT_CACHE = true; + +/** @define {boolean} */ +export const SUPPORT_ASYNC = true; + +/** @define {boolean} */ +export const SUPPORT_PRESET = true; + +/** @define {boolean} */ +export const SUPPORT_STORE = true; + +/** @define {boolean} */ +export const SUPPORT_TAGS = true; + +/** @define {boolean} */ +export const SUPPORT_SUGGESTION = true; + +/** @define {boolean} */ +export const SUPPORT_SERIALIZE = true; + +/** @define {boolean} */ +export const SUPPORT_DOCUMENT = true; + +/** @define {boolean} */ +export const SUPPORT_PAGINATION = true; diff --git a/src/config/compact.js b/src/config/compact.js new file mode 100644 index 0000000..64e8fee --- /dev/null +++ b/src/config/compact.js @@ -0,0 +1,44 @@ +/** @define {string} */ +export const RELEASE = "browser"; + +/** @define {boolean} */ +export const DEBUG = false; + +/** @define {boolean} */ +export const POLYFILL = false; + +/** @define {boolean} */ +export const SUPPORT_WORKER = false; + +/** @define {boolean|string} */ +export const SUPPORT_ENCODER = true; + +/** @define {boolean|string} */ +export const SUPPORT_LANG = false; + +/** @define {boolean} */ +export const SUPPORT_CACHE = false; + +/** @define {boolean} */ +export const SUPPORT_ASYNC = true; + +/** @define {boolean} */ +export const SUPPORT_PRESET = true; + +/** @define {boolean} */ +export const SUPPORT_STORE = true; + +/** @define {boolean} */ +export const SUPPORT_TAGS = false; + +/** @define {boolean} */ +export const SUPPORT_SUGGESTION = true; + +/** @define {boolean} */ +export const SUPPORT_SERIALIZE = false; + +/** @define {boolean} */ +export const SUPPORT_DOCUMENT = true; + +/** @define {boolean} */ +export const SUPPORT_PAGINATION = false; diff --git a/src/config/light.js b/src/config/light.js new file mode 100644 index 0000000..e38f9e1 --- /dev/null +++ b/src/config/light.js @@ -0,0 +1,44 @@ +/** @define {string} */ +export const RELEASE = "browser"; + +/** @define {boolean} */ +export const DEBUG = false; + +/** @define {boolean} */ +export const POLYFILL = false; + +/** @define {boolean} */ +export const SUPPORT_WORKER = false; + +/** @define {boolean|string} */ +export const SUPPORT_ENCODER = false; + +/** @define {boolean|string} */ +export const SUPPORT_LANG = false; + +/** @define {boolean} */ +export const SUPPORT_CACHE = false; + +/** @define {boolean} */ +export const SUPPORT_ASYNC = false; + +/** @define {boolean} */ +export const SUPPORT_PRESET = false; + +/** @define {boolean} */ +export const SUPPORT_STORE = false; + +/** @define {boolean} */ +export const SUPPORT_TAGS = false; + +/** @define {boolean} */ +export const SUPPORT_SUGGESTION = false; + +/** @define {boolean} */ +export const SUPPORT_SERIALIZE = false; + +/** @define {boolean} */ +export const SUPPORT_DOCUMENT = false; + +/** @define {boolean} */ +export const SUPPORT_PAGINATION = false; diff --git a/src/document.js b/src/document.js index 961bfb3..839472b 100644 --- a/src/document.js +++ b/src/document.js @@ -6,11 +6,12 @@ * https://github.com/nextapps-de/flexsearch */ -import { SUPPORT_ASYNC, SUPPORT_CACHE } from "./config.js"; -import { create_object } from "./common.js"; +import { SUPPORT_ASYNC, SUPPORT_CACHE, SUPPORT_STORE, SUPPORT_TAGS } from "./config.js"; import Index from "./index.js"; -import { addAsync, appendAsync, removeAsync, searchAsync, updateAsync } from "./async.js"; import Cache, { searchCache } from "./cache.js"; +import { create_object } from "./common.js"; +import { addAsync, appendAsync, removeAsync, searchAsync, updateAsync } from "./async.js"; +import { intersect, intersect_union } from "./intersect.js"; /** * @param {Object=} options @@ -29,13 +30,24 @@ function Document(options){ options || (options = {}); - this.register = create_object(); - this.store = (opt = options["store"]) && create_object(); - this.storetree = opt && (opt !== true) && []; - this.markup = []; - this.key = ((opt = options["key"]) && parse_tree(opt, this.markup)) || "id"; this.tree = []; this.field = []; + this.marker = []; + this.register = create_object(); + this.key = ((opt = options["key"]) && parse_tree(opt, this.marker)) || "id"; + this.fastupdate = parse_option(options["fastupdate"], true); + + if(SUPPORT_STORE){ + + this.store = (opt = options["store"]) && create_object(); + this.storetree = opt && (opt !== true) && []; + } + + if(SUPPORT_TAGS){ + + this.tag = ((opt = options["tag"]) && parse_tree(opt, this.marker)); + this.tagindex = opt && create_object(); + } if(SUPPORT_CACHE){ @@ -49,6 +61,11 @@ function Document(options){ export default Document; +function parse_option(value, default_value){ + + return typeof value !== "undefined" ? value : default_value; +} + /** * @this Document */ @@ -84,11 +101,11 @@ function parse_descriptor(options){ } index[key] = new Index(item, this.register); - this.tree[i] = parse_tree(key, this.markup); + this.tree[i] = parse_tree(key, this.marker); this.field[i] = key; } - if(this.storetree){ + if(SUPPORT_STORE && this.storetree){ let store = options["store"]; @@ -99,14 +116,14 @@ function parse_descriptor(options){ for(let i = 0; i < store.length; i++){ - this.storetree[i] = parse_tree(store[i], this.markup); + this.storetree[i] = parse_tree(store[i], this.marker); } } return index; } -function parse_tree(key, markup){ +function parse_tree(key, marker){ const tree = key.split(":"); let count = 0; @@ -121,7 +138,7 @@ function parse_tree(key, markup){ if(key){ - markup[count] = true; + marker[count] = true; } } @@ -139,7 +156,7 @@ function parse_tree(key, markup){ return count > 1 ? tree : tree[0]; } -function get_id(obj, tree){ +function parse_simple(obj, tree){ if(typeof tree === "string"){ @@ -190,7 +207,7 @@ function store_value(obj, store, tree, pos, key){ } } -function add_index(obj, tree, markup, pos, index, id, key, _append){ +function add_index(obj, tree, marker, pos, index, id, key, _append){ obj = obj[key]; @@ -202,7 +219,7 @@ function add_index(obj, tree, markup, pos, index, id, key, _append){ if(obj.constructor === Array){ - if(markup[pos]){ + if(marker[pos]){ for(let i = 0; i < obj.length; i++){ @@ -224,14 +241,14 @@ function add_index(obj, tree, markup, pos, index, id, key, _append){ for(let i = 0; i < obj.length; i++){ // do not increase index, an array is not a field - add_index(obj, tree, markup, pos, index, id, i, _append); + add_index(obj, tree, marker, pos, index, id, i, _append); } } else{ key = tree[++pos]; - add_index(obj, tree, markup, pos, index, id, key, _append); + add_index(obj, tree, marker, pos, index, id, key, _append); } } } @@ -249,7 +266,7 @@ Document.prototype.add = function(id, content, _append){ if(typeof id === "object"){ content = id; - id = get_id(content, this.key); + id = parse_simple(content, this.key); } if(content && (id || (id === 0))){ @@ -269,10 +286,45 @@ Document.prototype.add = function(id, content, _append){ tree = [tree]; } - add_index(content, tree, this.markup, 0, this.index[field], id, tree[0], _append); + add_index(content, tree, this.marker, 0, this.index[field], id, tree[0], _append); } - if(this.store){ + if(SUPPORT_TAGS && this.tag){ + + let tag = parse_simple(content, this.tag); + let dupes = create_object(); + + if(typeof tag === "string"){ + + tag = [tag]; + } + + for(let i = 0, key, arr; i < tag.length; i++){ + + key = tag[i]; + + if(!dupes[key]){ + + dupes[key] = 1; + arr = this.tagindex[key] || (this.tagindex[key] = []); + + if(!_append || (arr.indexOf(id) === -1)){ + + arr[arr.length] = id; + + // add a reference to the register for fast updates + + if(this.fastupdate){ + + const tmp = this.register[id] || (this.register[id] = []); + tmp[tmp.length] = arr; + } + } + } + } + } + + if(SUPPORT_STORE && this.store){ let store; @@ -297,6 +349,7 @@ Document.prototype.add = function(id, content, _append){ this.store[id] = store || content; } + } return this; @@ -326,7 +379,33 @@ Document.prototype.remove = function(id){ this.index[this.field[i]].remove(id, true); } - if(this.store){ + if(SUPPORT_TAGS && this.tag){ + + // when fastupdate was enabled the id will be already cleanup by the index + + if(!this.fastupdate){ + + for(let key in this.tagindex){ + + const tag = this.tagindex[key]; + const pos = tag.indexOf(id); + + if(pos !== -1){ + + if(tag.length > 1){ + + tag.splice(pos, 1); + } + else{ + + delete this.tagindex[key]; + } + } + } + } + } + + if(SUPPORT_STORE && this.store){ delete this.store[id]; } @@ -351,75 +430,145 @@ Document.prototype.search = function(query, limit, options){ let result = []; let pluck, enrich; - let field, field_options, bool, count = 0; + let field, field_options, tag, bool, count = 0; if(options){ pluck = options["pluck"]; field = pluck || options["field"]; - enrich = options["enrich"]; + tag = SUPPORT_TAGS && options["tag"]; + enrich = SUPPORT_STORE && this.store && options["enrich"]; bool = options["bool"] === "and"; limit = options["limit"]; - } - if(field){ + if(field){ - if(typeof field === "string"){ + if(typeof field === "string"){ - field = [field]; + field = [field]; + } + else if(field.constructor !== Array){ + + field_options = field; + field = Object.keys(field); + } } - else if(field.constructor !== Array){ - field_options = field; - field = Object.keys(field); + if(tag){ + + if(typeof tag === "string"){ + + tag = [tag]; + } + + // when tags is used and no query was set, + // then just return the tag indexes + + if(!query){ + + for(let i = 0, res; i < tag.length; i++){ + + res = get_tag.call(this, tag[i], limit || 100, enrich); + + if(res){ + + result[result.length] = res; + count++; + } + } + + return count ? result : []; + } } } - else{ - field = this.field; - } + field || (field = this.field); + bool = bool && ((field.length > 1) || (tag && (tag.length > 1))); - bool = bool && (field.length > 1); + let found_tag = []; - for(let i = 0, res, key, item; i < field.length; i++){ + // TODO solve this in one loop below + + for(let i = 0, res, key, item, len; i < field.length; i++){ key = field[i]; - if(field_options){ + // if(field_options){ + // + // item = field_options[key]; + // + // // inherit options also when search? it is just for laziness, Object.assign() has a cost + // //item = typeof item === "object" ? Object.assign({}, options, item) : options; + // } + // else{ + // + // item = options; + // } - item = field_options[key]; + res = this.index[key].search(query, limit, field_options ? field_options[key] : options); + len = res.length; - // inherit options? - //item = typeof item === "object" ? Object.assign({}, options, item) : options; - } - else{ + if(tag && len){ - item = options; + const field_tag = found_tag[i] = []; + const arr = []; + let count = 0; + + if(bool){ + + // prepare for intersection + + arr[0] = [res]; + } + + for(let y = 0, key, res; y < tag.length; y++){ + + key = tag[y]; + res = this.tagindex[key]; + len = res && res.length; + + if(len){ + + arr[arr.length] = bool ? [res] : res; + field_tag[count++] = key; + } + } + + if(count){ + + if(bool){ + + res = intersect(arr, limit || 100); + } + else{ + + res = intersect_union(res, arr); + } + + len = res.length; + } } - res = this.index[key].search(query, limit, item); + if(len){ - if(bool){ + result[count++] = res; + } + else if(bool){ - if(!res.length){ - - // fast path optimization + //if(!len){ return []; - } + //} // add a pseudo relevance index for the intersection // used when squash the results on boolean "and" //res = [res]; } - - count += res.length; - result[i] = res; } if(!count){ - // fast path optimization + // fast path "not found" return []; } @@ -450,9 +599,12 @@ Document.prototype.search = function(query, limit, options){ key = field[i]; res = result[i]; - if(enrich && this.store){ + if(res.length){ - res = apply_enrich.call(this, res); + if(enrich){ + + res = apply_enrich.call(this, res); + } } if(pluck){ @@ -460,16 +612,50 @@ Document.prototype.search = function(query, limit, options){ return res; } - result[i] = { + result[i] = res = { "field": key, "result": res }; + + if(tag){ + + res["tag"] = found_tag[i]; + } } return result; }; +/** + * @this Document + */ + +function get_tag(key, limit, enrich){ + + let res = this.tagindex[key]; + let len = res && res.length; + + if(len){ + + if(len > limit){ + + res = res.slice(0, limit); + } + + if(enrich){ + + res = apply_enrich.call(this, res); + } + + return { + + "tag": key, + "result": res + }; + } +} + /** * @this Document */ @@ -492,22 +678,25 @@ function apply_enrich(res){ return arr; } -Document.prototype.get = function(id){ - - return this.store[id]; -}; - -Document.prototype.set = function(id, data){ - - this.store[id] = data; - return this; -}; - Document.prototype.contain = function(id){ return !!this.register[id]; }; +if(SUPPORT_STORE){ + + Document.prototype.get = function(id){ + + return this.store[id]; + }; + + Document.prototype.set = function(id, data){ + + this.store[id] = data; + return this; + }; +} + if(SUPPORT_CACHE){ Document.prototype.searchCache = searchCache; diff --git a/src/index.js b/src/index.js index 0fb8393..fd2bbe0 100644 --- a/src/index.js +++ b/src/index.js @@ -6,15 +6,15 @@ * https://github.com/nextapps-de/flexsearch */ -import { SUPPORT_PRESET, SUPPORT_CACHE, SUPPORT_ASYNC, SUPPORT_WORKER } from "./config.js"; +import { SUPPORT_PRESET, SUPPORT_CACHE, SUPPORT_ASYNC, SUPPORT_WORKER, SUPPORT_SUGGESTION } from "./config.js"; import { encode as default_encoder } from "./lang/latin/default.js"; import { create_object, create_object_array, concat, sort_by_length_down } from "./common.js"; import { pipeline, init_stemmer_or_matcher, init_filter } from "./lang.js"; import { global_lang, global_charset } from "./global.js"; import { addAsync, appendAsync, removeAsync, searchAsync, updateAsync } from "./async.js"; +import { intersect } from "./intersect.js"; import Cache, { searchCache } from "./cache.js"; import apply_preset from "./presets.js"; -import intersect from "./intersect.js"; /** * @param {Object=} options @@ -349,16 +349,8 @@ Index.prototype.push_index = function(dupes, value, score, id, append, keyword){ if(this.fastupdate){ - const tmp = this.register[id]; - - if(tmp){ - - tmp[tmp.length] = arr; - } - else{ - - this.register[id] = [arr]; - } + const tmp = this.register[id] || (this.register[id] = []); + tmp[tmp.length] = arr; } } } @@ -392,7 +384,7 @@ Index.prototype.search = function(query, limit, options){ limit = options["limit"]; threshold = parse_option(options["threshold"], threshold); context = options["context"]; - suggest = options["suggest"]; + suggest = SUPPORT_SUGGESTION && options["suggest"]; } if(query){ @@ -569,18 +561,6 @@ Index.prototype.add_result = function(result, suggest, resolution, limit, just_o if(!this.optimize){ arr = get_array(arr, term, keyword, this.bidirectional); - - // if(keyword){ - // - // const swap = this.bidirectional && (term > keyword); - // - // arr = arr[swap ? term : keyword]; - // arr = arr && arr[swap ? keyword : term]; - // } - // else{ - // - // arr = arr[term]; - // } } if(arr){ @@ -595,18 +575,6 @@ Index.prototype.add_result = function(result, suggest, resolution, limit, just_o if(this.optimize){ tmp = get_array(tmp, term, keyword, this.bidirectional); - - // if(keyword){ - // - // const swap = this.bidirectional && (term > keyword); - // - // tmp = tmp[swap ? term : keyword]; - // tmp = tmp && tmp[swap ? keyword : term]; - // } - // else{ - // - // tmp = tmp[term]; - // } } if(tmp){ @@ -682,7 +650,7 @@ Index.prototype.remove = function(id, _skip_deletion){ if(this.fastupdate){ - // fast updates performs really fast but did not cleanup the key entries + // fast updates performs really fast but did not fully cleanup the key entries for(let i = 0, tmp; i < refs.length; i++){ diff --git a/src/intersect.js b/src/intersect.js index 0aeef07..0dab346 100644 --- a/src/intersect.js +++ b/src/intersect.js @@ -7,7 +7,7 @@ import { create_object, concat } from "./common.js"; * @returns {Array} */ -export default function(arrays, limit, suggest) { +export function intersect(arrays, limit, suggest) { const length = arrays.length; let result = []; @@ -135,3 +135,42 @@ export default function(arrays, limit, suggest) { return result; } + +/** + * @param mandatory + * @param arrays + * @returns {Array} + */ + +export function intersect_union(mandatory, arrays) { + + const check = create_object(); + const union = create_object(); + const result = []; + + for(let x = 0; x < mandatory.length; x++){ + + check[mandatory[x]] = 1; + } + + for(let x = 0, arr; x < arrays.length; x++){ + + arr = arrays[x]; + + for(let y = 0, id; y < arr.length; y++){ + + id = arr[y]; + + if(check[id]){ + + if(!union[id]){ + + union[id] = 1; + result[result.length] = id; + } + } + } + } + + return result; +} \ No newline at end of file