diff --git a/README.md b/README.md index 42de606..6c60fbd 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ npm install git+https://github.com/nextapps-de/flexsearch/tree/v0.8-preview - Custom Score Function - Added French language preset (stop-word filter, stemmer) - Enhanced Worker Support -- Improved Build System + Bundler (Supported: CommonJS, ESM, Global Namespace) +- Improved Build System + Bundler (Supported: CommonJS, ESM, Global Namespace), also the import of language packs are now supported for Node.js - Full covering index.d.ts type definitions Compare Benchmark: [0.7.0](https://nextapps-de.github.io/flexsearch/test/flexsearch-0.7.0/) vs. [0.8.0](https://nextapps-de.github.io/flexsearch/test/flexsearch-0.8.0/) @@ -269,6 +269,32 @@ const encoder = new Encoder({ }); ``` +You can use an `include` __instead__ of an `exclude` definition: + +```js +const encoder = new Encoder({ + exclude: { + letter: false, + number: false, + symbol: true, + punctuation: true, + control: true + } +}); +``` + +Instead of using `include` or `exclude` you can pass a regular expression to the field `split`: + +```js +const encoder = new Encoder({ + split: /\s+/ +}); +``` + +> The definitions `include` and `exclude` is a replacement for `split`. You can just define one of those 3. + +Adding custom functions to the encoder pipeline: + ```js const encoder = new Encoder({ normalize: function(str){ @@ -277,16 +303,20 @@ const encoder = new Encoder({ prepare: function(str){ return str.replace(/&/g, " and "); }, - exclude: { - letter: false, - number: false, - symbol: true, - punctuation: true, - control: true + finalize: function(arr){ + return arr.filter(term => term.length > 2); } }); ``` +Assign encoder to an index: + +```js +const index = new Index({ + encoder: encoder +}); +``` + Define language specific transformations: ```js @@ -309,12 +339,11 @@ const encoder = new Encoder({ }); ``` -Or use predefined language and add custom options: +Or use predefined language and extend it with custom options: ```js import EnglishBookPreset from "./lang/en.js"; -const encoder = new Encoder({ - assign: EnglishBookPreset, +const encoder = new Encoder(EnglishBookPreset, { filter: false }); ``` @@ -330,22 +359,24 @@ encoder.assign({ filter: false }); Assign extensions to the encoder instance: ```js -import LatinEncoder from "./lang/latin/simple.js"; +import LatinEncoderPreset from "./charset/latin/simple.js"; import EnglishBookPreset from "./lang/en.js"; // stack definitions to the encoder instance const encoder = new Encoder() - .assign(LatinEncoder) + .assign(LatinEncoderPreset) .assign(EnglishBookPreset) // override preset options ... .assign({ minlength: 3 }); // assign further presets ... ``` +> When adding extension to the encoder every previously assigned configuration is still intact, very much like Mixins, also when assigning custom functions. + Add custom transformations to an existing index: ```js -import LatinEncoder from "./lang/latin/default.js"; -const encoder = new Encoder(LatinEncoder); +import LatinEncoderPreset from "./charset/latin/default.js"; +const encoder = new Encoder(LatinEncoderPreset); encoder.addReplacer(/[´`’ʼ]/g, "'"); encoder.addFilter("and"); encoder.addMatcher("xvi", "16"); @@ -353,6 +384,15 @@ encoder.addStemmer("ly", ""); encoder.addMapper("é", "e"); ``` +Shortcut for just assigning one encoder configuration to an index: + +```js +import LatinEncoderPreset from "./charset/latin/default.js"; +const index = new Index({ + encoder: LatinEncoderPreset +}); +``` + ## Resolver Retrieve an unresolved result: @@ -363,6 +403,7 @@ const raw = index.search("a short query", { }); ``` +<<<<<<< HEAD ```js const raw = index.search("a short query", { resolve: false @@ -863,31 +897,6 @@ const result = raw.resolve(); The basic concept explained: - ```js // 1. get one or multiple unresolved results const raw1 = index.search("a short query", { @@ -914,39 +923,6 @@ console.log("The final result is:", result) Use inline queries: - ```js const result = index.search("further query", { // set resolve to false on the first query @@ -971,43 +947,6 @@ const result = index.search("further query", { }); ``` - -Or use a fully declarative style (also recommended when run in parallel): - - ```js import Resolver from "./resolver.js"; const result = new Resolver({ @@ -1049,6 +988,7 @@ const result = new Resolver({ .resolve(100); ``` + ### Custom Resolver @@ -1127,46 +1068,6 @@ The internal ID arrays scales automatically when limit of 2^31 has reached by us > Persistent storages has no keystore limit by default. You should not enable keystore when using persistent indexes, as long as you do not stress the buffer too hard before calling `index.commit()`. - - ## Multi-Tag-Search Assume this document schema (a dataset from IMDB): @@ -1189,10 +1090,10 @@ Assume this document schema (a dataset from IMDB): An appropriate document descriptor could look like: ```js -import LatinEncoder from "./lang/latin/simple.js"; +import LatinEncoder from "./charset/latin/simple.js"; const flexsearch = new Document({ - encoder: new Encoder(LatinEncoder), + encoder: LatinEncoder, resolution: 3, document: { id: "tconst", @@ -1213,16 +1114,6 @@ const flexsearch = new Document({ ``` The field contents of `primaryTitle` and `originalTitle` are encoded by the forward tokenizer. The field contents of `startYear` and `genres` are added as tags. - - Get all entries of a specific tag: ```js const result = flexsearch.search({ @@ -1400,7 +1291,7 @@ const index = new FlexSearchIndex({ A common situation is you have some predefined labels which are related to some kind of order, e.g. the importance or priority. A priority label could be `high`, `moderate`, `low` so you can derive the scoring from those properties. Another example is when you have something already ordered and you would like to keep this order as relevance. -The parameters from the score function explained: +Probably you won't need the parameters passed to the score function. But when needed here are the parameters from the score function explained: 1. `content` is the whole content as an array of terms (encoded) 2. `term` is the current term which is actually processed (encoded) @@ -1448,7 +1339,7 @@ By default, the result set of Field-Search has a structure grouped by field name }] ``` -By passing the search option `merge: true` the result set will be merged into: +By passing the search option `merge: true` the result set will be merged into (group by id): ```js [{ id: 1001, @@ -1530,10 +1421,10 @@ You're welcome to make some suggestions how to improve the handling of extern co An extern configuration for one WorkerIndex, let's assume it is located in `./custom_field.js`: ```js const { Charset } = require("flexsearch"); -const EncoderPreset = Charset["latin:simple"]; +const { LatinSimple } = Charset; // it requires a default export: module.exports = { - encoder: EncoderPreset, + encoder: LatinSimple, tokenize: "forward", // custom function: custom: function(data){ @@ -1563,10 +1454,10 @@ const flexsearch = new Document({ An extern configuration for one WorkerIndex, let's assume it is located in `./custom_field.js`: ```js import { Charset } from "./dist/flexsearch.bundle.module.min.js"; -const EncoderPreset = Charset["latin:simple"]; +const { LatinSimple } = Charset; // it requires a default export: export default { - encoder: EncoderPreset, + encoder: LatinSimple, tokenize: "forward", // custom function: custom: function(data){ @@ -1616,7 +1507,7 @@ Fuzzysearch describes a basic concept of how making queries more tolerant. Somet 3. Use one of the language specific presets e.g. `/lang/en.js` for en-US specific content 4. Enable suggestions by passing the search option `suggest: true` -Additionally, you can apply custom `Mapper`, `Replacer`, `Stemmer`, `Filter` or by assigning a custom `normalize` or `prepare` function to the Encoder. +Additionally, you can apply custom `Mapper`, `Replacer`, `Stemmer`, `Filter` or by assigning a custom `normalize(str)`, `prepare(str)` or `finalize(arr)` function to the Encoder. ### Compare Fuzzy-Search Encoding @@ -1944,3 +1835,5 @@ When you are using ESM in Node.js then just use the Modules explained one sectio - Every charset collection (files in folder `/lang/**.js`) is now exported as a config object (instead of a function). This config needs to be created by passing to the constructor `new Encoder(config)` or can be added to an existing instance via `encoder.assign(config)`. The reason was to keep the default encoder configuration when having multiple document indexes. - The property `bool` from DocumentOptions was removed (replaced by `Resolver`) - The static methods `FlexSearch.registerCharset()` and `FlexSearch.registerLanguage()` was removed, those collections are now exported to `FlexSearch.Charset` which can be accessed as module `import { Charset } from "flexsearch"` and language packs are now applied by `encoder.assign()` +- Instead of e.g. "latin:simple" the Charset collection is exported as a module and has to be imported by e.g. `import LatinSimple from "./charset.js"` and then assigned to an existing Encoder by `encoder.assign(LatinSimple)` or by creation `encoder = new Encoder(LatinSimple)` +- You can import language packs by `dist/module/lang/*` when using ESM and by `const EnglishPreset = require("flexsearch/lang/en");` when using CommonJS (Node.js) \ No newline at end of file diff --git a/dist/db/clickhouse/index.cjs b/dist/db/clickhouse/index.cjs new file mode 100644 index 0000000..9c8f576 --- /dev/null +++ b/dist/db/clickhouse/index.cjs @@ -0,0 +1,6424 @@ +'use strict'; + +var clickhouse = require('clickhouse'); + +/** @define {string} */ + +/** @define {boolean} */ +const SUPPORT_PERSISTENT = true; + +/** @define {boolean} */ +const SUPPORT_KEYSTORE = true; + +/** + * @param {*} value + * @param {*} default_value + * @param {*=} merge_value + * @return {*} + */ + +function parse_option(value, default_value, merge_value){ + + const type_merge = typeof merge_value; + const type_value = typeof value; + + if(type_merge !== "undefined"){ + if(type_value !== "undefined"){ + + if(merge_value){ + if(type_value === "function" && + type_merge === type_value){ + return function(str){ + return /** @type Function */ (value)( + /** @type Function */ (merge_value)(str) + ); + } + } + + const constructor_value = value.constructor; + const constructor_merge = merge_value.constructor; + + if(constructor_value === constructor_merge){ + + if(constructor_value === Array){ + return merge_value.concat(value); + } + + if(constructor_value === Map){ + const map = new Map(/** @type !Map */ (merge_value)); + for(const item of /** @type !Map */ (value)){ + const key = item[0]; + const val = item[1]; + map.set(key, val); + } + return map; + } + + if(constructor_value === Set){ + const set = new Set(/** @type !Set */ (merge_value)); + for(const val of /** @type !Set */ (value).values()){ + set.add(val); + } + return set; + } + } + } + + return value; + } + else { + return merge_value; + } + } + + return type_value === "undefined" + ? default_value + : value; +} + +function create_object(){ + return Object.create(null); +} + +function concat(arrays){ + return [].concat.apply([], arrays); +} + +function sort_by_length_down(a, b){ + return b.length - a.length; +} + +function is_array(val){ + return val.constructor === Array; +} + +function is_string(val){ + return typeof val === "string"; +} + +function is_object(val){ + return typeof val === "object"; +} + +function is_function(val){ + return typeof val === "function"; +} + +/** + * @param {Map|Set} val + * @param {boolean=} stringify + * @return {Array} + */ + +function toArray(val, stringify){ + const result = []; + for(const key of val.keys()){ + result.push(key); + } + return result; +} + +// TODO support generic function created from string when tree depth > 1 +function parse_simple(obj, tree){ + + if(is_string(tree)){ + obj = obj[tree]; + } + else for(let i = 0; obj && (i < tree.length); i++){ + obj = obj[tree[i]]; + } + + return obj; +} + +function get_max_len(arr){ + let len = 0; + for(let i = 0, res; i < arr.length; i++){ + if((res = arr[i])){ + if(len < res.length){ + len = res.length; + } + } + } + return len; +} + +var normalize_polyfill = [ + + // Charset Normalization + + ["ª","a"], + ["²","2"], + ["³","3"], + ["¹","1"], + ["º","o"], + ["¼","1⁄4"], + ["½","1⁄2"], + ["¾","3⁄4"], + ["à","a"], + ["á","a"], + ["â","a"], + ["ã","a"], + ["ä","a"], + ["å","a"], + ["ç","c"], + ["è","e"], + ["é","e"], + ["ê","e"], + ["ë","e"], + ["ì","i"], + ["í","i"], + ["î","i"], + ["ï","i"], + ["ñ","n"], + ["ò","o"], + ["ó","o"], + ["ô","o"], + ["õ","o"], + ["ö","o"], + ["ù","u"], + ["ú","u"], + ["û","u"], + ["ü","u"], + ["ý","y"], + ["ÿ","y"], + ["ā","a"], + ["ă","a"], + ["ą","a"], + ["ć","c"], + ["ĉ","c"], + ["ċ","c"], + ["č","c"], + ["ď","d"], + ["ē","e"], + ["ĕ","e"], + ["ė","e"], + ["ę","e"], + ["ě","e"], + ["ĝ","g"], + ["ğ","g"], + ["ġ","g"], + ["ģ","g"], + ["ĥ","h"], + ["ĩ","i"], + ["ī","i"], + ["ĭ","i"], + ["į","i"], + ["ij","ij"], + ["ĵ","j"], + ["ķ","k"], + ["ĺ","l"], + ["ļ","l"], + ["ľ","l"], + ["ŀ","l"], + ["ń","n"], + ["ņ","n"], + ["ň","n"], + ["ʼn","n"], + ["ō","o"], + ["ŏ","o"], + ["ő","o"], + ["ŕ","r"], + ["ŗ","r"], + ["ř","r"], + ["ś","s"], + ["ŝ","s"], + ["ş","s"], + ["š","s"], + ["ţ","t"], + ["ť","t"], + ["ũ","u"], + ["ū","u"], + ["ŭ","u"], + ["ů","u"], + ["ű","u"], + ["ų","u"], + ["ŵ","w"], + ["ŷ","y"], + ["ź","z"], + ["ż","z"], + ["ž","z"], + ["ſ","s"], + ["ơ","o"], + ["ư","u"], + ["dž","dz"], + ["lj","lj"], + ["nj","nj"], + ["ǎ","a"], + ["ǐ","i"], + ["ǒ","o"], + ["ǔ","u"], + ["ǖ","u"], + ["ǘ","u"], + ["ǚ","u"], + ["ǜ","u"], + ["ǟ","a"], + ["ǡ","a"], + ["ǣ","ae"], + ["æ","ae"], + ["ǽ","ae"], + ["ǧ","g"], + ["ǩ","k"], + ["ǫ","o"], + ["ǭ","o"], + ["ǯ","ʒ"], + ["ǰ","j"], + ["dz","dz"], + ["ǵ","g"], + ["ǹ","n"], + ["ǻ","a"], + ["ǿ","ø"], + ["ȁ","a"], + ["ȃ","a"], + ["ȅ","e"], + ["ȇ","e"], + ["ȉ","i"], + ["ȋ","i"], + ["ȍ","o"], + ["ȏ","o"], + ["ȑ","r"], + ["ȓ","r"], + ["ȕ","u"], + ["ȗ","u"], + ["ș","s"], + ["ț","t"], + ["ȟ","h"], + ["ȧ","a"], + ["ȩ","e"], + ["ȫ","o"], + ["ȭ","o"], + ["ȯ","o"], + ["ȱ","o"], + ["ȳ","y"], + ["ʰ","h"], + ["ʱ","h"], + ["ɦ","h"], + ["ʲ","j"], + ["ʳ","r"], + ["ʴ","ɹ"], + ["ʵ","ɻ"], + ["ʶ","ʁ"], + ["ʷ","w"], + ["ʸ","y"], + ["ˠ","ɣ"], + ["ˡ","l"], + ["ˢ","s"], + ["ˣ","x"], + ["ˤ","ʕ"], + ["ΐ","ι"], + ["ά","α"], + ["έ","ε"], + ["ή","η"], + ["ί","ι"], + ["ΰ","υ"], + ["ϊ","ι"], + ["ϋ","υ"], + ["ό","ο"], + ["ύ","υ"], + ["ώ","ω"], + ["ϐ","β"], + ["ϑ","θ"], + ["ϒ","Υ"], + ["ϓ","Υ"], + ["ϔ","Υ"], + ["ϕ","φ"], + ["ϖ","π"], + ["ϰ","κ"], + ["ϱ","ρ"], + ["ϲ","ς"], + ["ϵ","ε"], + ["й","и"], + ["ѐ","е"], + ["ё","е"], + ["ѓ","г"], + ["ї","і"], + ["ќ","к"], + ["ѝ","и"], + ["ў","у"], + ["ѷ","ѵ"], + ["ӂ","ж"], + ["ӑ","а"], + ["ӓ","а"], + ["ӗ","е"], + ["ӛ","ә"], + ["ӝ","ж"], + ["ӟ","з"], + ["ӣ","и"], + ["ӥ","и"], + ["ӧ","о"], + ["ӫ","ө"], + ["ӭ","э"], + ["ӯ","у"], + ["ӱ","у"], + ["ӳ","у"], + ["ӵ","ч"] + + // Term Separators + + // ["'", ""], // it's -> its + // ["´", ""], + // ["`", ""], + // ["’", ""], + // ["ʼ", ""], + + // Numeric-Separators Chars Removal + + // [",", ""], + // [".", ""] + + // Non-Whitespace Separators + + // already was split by default via p{P} + // ["-", " "], + // [":", " "], + // ["_", " "], + // ["|", " "], + // ["/", " "], + // ["\\", " "] +]; + +// COMPILER BLOCK --> + +/* + +Custom Encoder +---------------- + +// Split a passed string into an Array of words: +function englishEncoder(string){ + return string.toLowerCase().split(/[^a-z]+/) +} + +// For CJK split a passed string into an Array of chars: +function chineseEncoder(string){ + return string.replace(/\s+/, "").split("") +} + +// Alternatively do not split the input: +function fixedEncoder(string){ + return [string] +} + +Built-in Encoder (Workflow) +---------------------------- +Pipeline: + 1. apply this.normalize: charset normalization: + applied on the whole input string e.g. lowercase, + will also apply on: filter, matcher, stemmer, mapper + 2. apply this.split: split input into terms (includes/excludes) + 3. apply this.filter (pre-filter) + 4. apply this.matcher (replace terms) + 5. apply this.stemmer (replace term endings) + 6. apply this.filter (post-filter) + 7. apply this.mapper (replace chars) + 8. apply this.replacer (custom regex) + 9. apply this.dedupe (letter deduplication) + 10. apply this.finalize +*/ + +const whitespace = /[^\p{L}\p{N}]+/u; // /[\p{Z}\p{S}\p{P}\p{C}]+/u; +//const numeric_split = /(\d{3})/g; +const numeric_split_length = /(\d{3})/g; +const numeric_split_prev_char = /(\D)(\d{3})/g; +const numeric_split_next_char = /(\d{3})(\D)/g; +//.replace(/(\d{3})/g, "$1 ") +//.replace(/([^\d])([\d])/g, "$1 $2") +//.replace(/([\d])([^\d])/g, "$1 $2") +const normalize = "".normalize && /[\u0300-\u036f]/g; // '´`’ʼ., +//const normalize_mapper = SUPPORT_CHARSET && !normalize && normalize_polyfill; + +/** + * @param {EncoderOptions=} options + * @constructor + */ + +function Encoder(options){ + + if(!this){ + return new Encoder(...arguments); + } + + for(let i = 0; i < arguments.length; i++){ + this.assign(arguments[i]); + } +} +/** + * @param {!EncoderOptions} options + */ +Encoder.prototype.assign = function(options){ + + /** + * pre-processing string input + * @type {Function|boolean} + */ + this.normalize = /** @type {Function|boolean} */ ( + parse_option(options.normalize, true, this.normalize) + ); + + // { + // letter: true, + // number: true, + // whitespace: true, + // symbol: true, + // punctuation: true, + // control: true, + // char: "" + // } + + let include = options.include; + let tmp = include || options.exclude || options.split; + + if(typeof tmp === "object"){ + let numeric = !include; + let regex = ""; + // split on whitespace by default + options.include || ( + regex += "\\p{Z}" + ); + if(tmp.letter){ + regex += "\\p{L}"; + } + if(tmp.number){ + regex += "\\p{N}"; + numeric = !!include; + } + if(tmp.symbol){ + regex += "\\p{S}"; + } + if(tmp.punctuation){ + regex += "\\p{P}"; + } + if(tmp.control){ + regex += "\\p{C}"; + } + if((tmp = tmp.char)){ + regex += typeof tmp === "object" ? tmp.join("") : tmp; + } + + this.split = new RegExp("[" + (include ? "^" : "") + regex + "]+", "u"); + this.numeric = numeric; + } + else { + + /** + * split string input into terms + * @type {string|RegExp|boolean|null} + */ + this.split = /** @type {string|RegExp|boolean} */ (parse_option(tmp, whitespace, this.split)); + this.numeric = parse_option(this.numeric, true); + } + + /** + * post-processing terms + * @type {Function|null} + */ + this.prepare = /** @type {Function|null} */ ( + parse_option(options.prepare, null, this.prepare) + ); + /** + * final processing + * @type {Function|null} + */ + this.finalize = /** @type {Function|null} */ ( + parse_option(options.finalize, null, this.finalize) + ); + + // assign the normalization fallback to the mapper + if(!normalize){ + this.mapper = new Map( + /** @type {Array>} */ ( + normalize_polyfill + ) + ); + } + + // options + + this.rtl = options.rtl || false; + this.dedupe = parse_option(options.dedupe, true, this.dedupe); + this.filter = parse_option((tmp = options.filter) && new Set(tmp), null, this.filter); + this.matcher = parse_option((tmp = options.matcher) && new Map(tmp), null, this.matcher); + this.mapper = parse_option((tmp = options.mapper) && new Map(tmp), null, this.mapper); + this.stemmer = parse_option((tmp = options.stemmer) && new Map(tmp), null, this.stemmer); + this.replacer = parse_option(options.replacer, null, this.replacer); + this.minlength = parse_option(options.minlength, 1, this.minlength); + this.maxlength = parse_option(options.maxlength, 0, this.maxlength); + + // minimum required tokenizer by this encoder + //this.tokenize = options["tokenize"] || ""; + + // auto-balanced cache + { + this.cache = tmp = parse_option(options.cache, true, this.cache); + if(tmp){ + this.timer = null; + this.cache_size = typeof tmp === "number" ? tmp : 2e5; + this.cache_enc = new Map(); + this.cache_prt = new Map(); + this.cache_enc_length = 128; + this.cache_prt_length = 128; + } + } + + // regex temporary state + this.matcher_str = ""; + this.matcher_test = null; + this.stemmer_str = ""; + this.stemmer_test = null; + + // prebuilt + // if(this.filter && this.split){ + // for(const key of this.filter){ + // const tmp = key.replace(this.split, ""); + // if(key !== tmp){ + // this.filter.delete(key); + // this.filter.add(tmp); + // } + // } + // } + if(this.matcher){ + for(const key of this.matcher.keys()){ + this.matcher_str += (this.matcher_str ? "|" : "") + key; + } + } + if(this.stemmer){ + for(const key of this.stemmer.keys()){ + this.stemmer_str += (this.stemmer_str ? "|" : "") + key; + } + } + + // if(SUPPORT_COMPRESSION){ + // this.compression = parse_option(options.compress || options.compression, 0, this.compression); + // if(this.compression && !table){ + // table = new Array(radix); + // for(let i = 0; i < radix; i++) table[i] = i + 33; + // table = String.fromCharCode.apply(null, table); + // } + // } + + return this; +}; + +Encoder.prototype.addMatcher = function(match, replace){ + // regex: + if(typeof match === "object"){ + return this.addReplacer(match, replace); + } + // a single char: + if(match.length < 2){ + return this.addMapper(match, replace); + } + this.matcher || (this.matcher = new Map()); + this.matcher.set(match , replace); + this.matcher_str += (this.matcher_str ? "|" : "") + match; + this.matcher_test = null; //new RegExp("(" + this.matcher_str + ")"); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addStemmer = function(match, replace){ + this.stemmer || (this.stemmer = new Map()); + this.stemmer.set(match, replace); + this.stemmer_str += (this.stemmer_str ? "|" : "") + match; + this.stemmer_test = null; //new RegExp("(" + this.stemmer_str + ")"); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addFilter = function(str){ + this.filter || (this.filter = new Set()); + this.filter.add(str); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addMapper = function(char_match, char_replace){ + // regex: + if(typeof char_match === "object"){ + return this.addReplacer(char_match, char_replace); + } + // not a char: + if(char_match.length > 1){ + return this.addMatcher(char_match, char_replace); + } + this.mapper || (this.mapper = new Map()); + this.mapper.set(char_match, char_replace); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addReplacer = function(match, replace){ + if(typeof match === "string") match = new RegExp(match, "g"); + this.replacer || (this.replacer = []); + this.replacer.push(match, replace || ""); + this.cache && this.invalidate(); + return this; +}; + +{ + Encoder.prototype.invalidate = function(){ + this.cache_enc.clear(); + this.cache_prt.clear(); + }; +} + +Encoder.prototype.encode = function(str){ + + //if(!str) return str; + // todo remove dupe terms + + if(this.cache && str.length <= this.cache_enc_length){ + if(this.timer){ + if(this.cache_enc.has(str)){ + return this.cache_enc.get(str); + } + } + else { + this.timer = setTimeout(clear$1, 0, this); + } + } + + // 1. apply charset normalization + if(this.normalize){ + if(typeof this.normalize === "function"){ + str = this.normalize(str); + } + else if(normalize){ + str = str.normalize("NFKD").replace(normalize, "").toLowerCase(); + } + else { + str = str.toLowerCase(); + // if(SUPPORT_CHARSET){ + // this.mapper = this.mapper + // // todo replace spread + // ? new Map([.../** @type {!Iterable} */(normalize_mapper), ...this.mapper]) + // : new Map(/** @type {Map} */ (normalize_mapper)); + // } + } + //if(!str) return str; + } + + // 2. apply custom encoder (can replace split) + if(this.prepare){ + str = this.prepare(str); + } + + // 3. split numbers into triplets + if(this.numeric && str.length > 3){ + str = str.replace(numeric_split_prev_char, "$1 $2") + .replace(numeric_split_next_char, "$1 $2") + .replace(numeric_split_length, "$1 "); + } + + // if(this.matcher && (str.length > 1)){ + // this.matcher_test || ( + // this.matcher_test = new RegExp("(" + this.matcher_str + ")", "g") + // ); + // str = str.replace(this.matcher_test, match => this.matcher.get(match)); + // } + // if(this.stemmer){ + // this.stemmer_test || ( + // this.stemmer_test = new RegExp("(?!\\b)(" + this.stemmer_str + ")(\\b|_)", "g") + // ); + // str = str.replace(this.stemmer_test, match => this.stemmer.get(match)); + // } + + const skip = !(this.dedupe || this.mapper || this.filter || this.matcher || this.stemmer || this.replacer); + let final = []; + let words = this.split || this.split === "" + ? str.split(/** @type {string|RegExp} */ (this.split)) + : str; //[str]; + + for(let i = 0, word, base; i < words.length; i++){ + // filter empty entries + if(!(word = base = words[i])){ + continue; + } + if(word.length < this.minlength){ + continue; + } + if(skip) { + final.push(word); + continue; + } + + // 1. pre-filter before cache + if(this.filter && this.filter.has(word)){ + continue; + } + + if(this.cache && word.length <= this.cache_prt_length){ + if(this.timer){ + const tmp = this.cache_prt.get(word); + //if(this.cache_prt.has(word)){ + if(tmp || tmp === ""){ + //word = this.cache_prt.get(word); + tmp && final.push(tmp); + //word ? words[i] = word : words.splice(i--, 1); + continue; + } + } + else { + this.timer = setTimeout(clear$1, 0, this); + } + } + + let postfilter; + + // if(this.normalize === true && normalize){ + // word = word.normalize("NFKD").replace(normalize, ""); + // postfilter = 1; + // } + + // if(this.normalize){ + // if(typeof this.normalize === "function"){ + // word = this.normalize(word); + // } + // else if(normalize){ + // word = word.normalize("NFKD").replace(normalize, "").toLowerCase(); + // } + // else{ + // word = word.toLowerCase(); + // this.mapper = this.mapper + // ? new Map([...normalize_mapper, ...this.mapper]) + // : new Map(/** @type {Map} */ normalize_mapper); + // } + // postfilter = 1; + // //if(!str) return str; + // } + + // 2. apply stemmer after matcher + if(this.stemmer && (word.length > 2)){ + // for(const item of this.stemmer){ + // const key = item[0]; + // const value = item[1]; + // + // if(word.length > key.length && word.endsWith(key)){ + // word = word.substring(0, word.length - key.length) + value; + // break; + // } + // + // // const position = word.length - key.length; + // // if(position > 0 && word.substring(position) === key){ + // // word = word.substring(0, position) + value; + // // break; + // // } + // } + this.stemmer_test || ( + this.stemmer_test = new RegExp("(?!^)(" + this.stemmer_str + ")$") + ); + word = word.replace(this.stemmer_test, match => this.stemmer.get(match)); + postfilter = 1; + } + + // 3. apply matcher + if(this.matcher && (word.length > 1)){ + this.matcher_test || ( + this.matcher_test = new RegExp("(" + this.matcher_str + ")", "g") + ); + word = word.replace(this.matcher_test, match => this.matcher.get(match)); + postfilter = 1; + } + + // 4. post-filter after matcher and stemmer was applied + if(word && postfilter && (word.length < this.minlength || (this.filter && this.filter.has(word)))){ + word = ""; + } + + // 5. apply mapper and collapsing + if(word && (this.mapper || (this.dedupe && word.length > 1))){ + //word = this.replace_dedupe(word); + //word = replace_deduped(word, this.mapper, true); + let final = ""; + for(let i = 0, prev = "", char, tmp; i < word.length; i++){ + char = word.charAt(i); + if(char !== prev || !this.dedupe){ + tmp = this.mapper && this.mapper.get(char); + if(!tmp && tmp !== "") + final += (prev = char); + else if((tmp !== prev || !this.dedupe) && (prev = tmp)) + final += tmp; + } + } + word = final; + } + + // apply custom regex + if(word && this.replacer){ + for(let i = 0; word && (i < this.replacer.length); i+=2){ + word = word.replace(this.replacer[i], this.replacer[i+1]); + } + } + + // slower variants for removing same chars in a row: + //word = word.replace(/([^0-9])\1+/g, "$1"); + //word = word.replace(/(.)\1+/g, "$1"); + //word = word.replace(/(?<=(.))\1+/g, ""); + + // if(word){ + // words[i] = word; + // } + + if(this.cache && base.length <= this.cache_prt_length){ + this.cache_prt.set(base, word); + if(this.cache_prt.size > this.cache_size){ + this.cache_prt.clear(); + this.cache_prt_length = this.cache_prt_length / 1.1 | 0; + } + } + + //word || words.splice(i--, 1); + word && final.push(word); + } + + //words = final; + // else if(this.filter){ + // for(let i = 0, word; i < words.length; i++){ + // if((word = words[i]) && !this.filter.has(word)){ + // //filtered.push(word); + // words.splice(i--, 1); + // } + // } + // } + + if(this.finalize){ + final = this.finalize(final) || final; + } + + if(this.cache && str.length <= this.cache_enc_length){ + this.cache_enc.set(str, final); + if(this.cache_enc.size > this.cache_size){ + this.cache_enc.clear(); + this.cache_enc_length = this.cache_enc_length / 1.1 | 0; + } + } + + return final; +}; + +// Encoder.prototype.compress = function(str) { +// +// //return str; +// //if(!str) return str; +// +// if(SUPPORT_CACHE && this.cache && str.length <= this.cache_prt_length){ +// if(this.timer){ +// if(this.cache_cmp.has(str)){ +// return this.cache_cmp.get(str); +// } +// } +// else{ +// this.timer = setTimeout(clear, 0, this); +// } +// } +// +// const result = typeof this.compression === "function" +// ? this.compression(str) +// : hash(str); //window.hash(str); +// +// if(SUPPORT_CACHE && this.cache && str.length <= this.cache_prt_length){ +// this.cache_cmp.set(str, result); +// this.cache_cmp.size > this.cache_size && +// this.cache_cmp.clear(); +// } +// +// return result; +// }; + +// function hash(str){ +// return str; +// } + +function clear$1(self){ + self.timer = null; + self.cache_enc.clear(); + self.cache_prt.clear(); +} + +/** + * @param {string|Object} query + * @param {number|Object=} limit + * @param {Object=} options + * @this {Index|Document} + * @returns {Array|Promise} + */ + +function searchCache(query, limit, options){ + + query = (typeof query === "object" + ? "" + query.query + : "" + query + ).toLowerCase(); + + //let encoded = this.encoder.encode(query).join(" "); + let cache = this.cache.get(query); + if(!cache){ + cache = this.search(query, limit, options); + if(cache.then){ + const self = this; + cache.then(function(cache){ + self.cache.set(query, cache); + return cache; + }); + } + this.cache.set(query, cache); + } + return cache; +} + +/** + * @param {boolean|number=} limit + * @constructor + */ + +function CacheClass(limit){ + /** @private */ + this.limit = (!limit || limit === true) ? 1000 : limit; + /** @private */ + this.cache = new Map(); + /** @private */ + this.last = ""; +} + +CacheClass.prototype.set = function(key, value){ + //if(!this.cache.has(key)){ + this.cache.set(this.last = key, value); + if(this.cache.size > this.limit){ + this.cache.delete(this.cache.keys().next().value); + } + //} +}; + +CacheClass.prototype.get = function(key){ + const cache = this.cache.get(key); + if(cache && this.last !== key){ + this.cache.delete(key); + this.cache.set(this.last = key, cache); + } + return cache; +}; + +CacheClass.prototype.remove = function(id){ + for(const item of this.cache){ + const key = item[0]; + const value = item[1]; + if(value.includes(id)){ + this.cache.delete(key); + } + } +}; + +CacheClass.prototype.clear = function(){ + this.cache.clear(); + this.last = ""; +}; + +/** @type EncoderOptions */ +const options = { + normalize: function(str){ + return str.toLowerCase(); + }, + dedupe: false +}; + +// import { pipeline } from "../../lang.js"; +// +// const whitespace = /[\p{Z}\p{S}\p{P}\p{C}]+/u; +// +// export const rtl = false; +// export const tokenize = ""; +// export default { +// encode: encode, +// rtl: rtl, +// tokenize: tokenize +// } +// +// /** +// * @param {string|number} str +// */ +// +// export function encode(str){ +// +// return pipeline.call( +// +// this, +// /* string: */ ("" + str).toLowerCase(), +// /* normalize: */ false, +// /* split: */ whitespace, +// /* collapse: */ false +// ); +// } + +// KeystoreObj.prototype.destroy = function(){ +// this.index = null; +// this.keys = null; +// this.proxy = null; +// }; + +function _slice(self, start, end, splice){ + let arr = []; + for(let i = 0, index; i < self.index.length; i++){ + index = self.index[i]; + if(start >= index.length){ + start -= index.length; + } + else { + const tmp = index[splice ? "splice" : "slice"](start, end); + const length = tmp.length; + if(length){ + arr = arr.length + ? arr.concat(tmp) + : tmp; + end -= length; + if(splice) self.length -= length; + if(!end) break; + } + start = 0; + } + } + return arr; +} + +/** + * @param arr + * @constructor + */ + +function KeystoreArray(arr){ + + if(!this){ + return new KeystoreArray(arr); + } + + this.index = arr ? [arr] : []; + this.length = arr ? arr.length : 0; + const self = this; + + return /*this.proxy =*/ new Proxy([], { + get(target, key) { + if(key === "length"){ + return self.length; + } + if(key === "push"){ + return function(value){ + self.index[self.index.length - 1].push(value); + self.length++; + } + } + if(key === "pop"){ + return function(){ + if(self.length){ + self.length--; + return self.index[self.index.length - 1].pop(); + } + } + } + if(key === "indexOf"){ + return function(key){ + let index = 0; + for(let i = 0, arr, tmp; i < self.index.length; i++){ + arr = self.index[i]; + //if(!arr.includes(key)) continue; + tmp = arr.indexOf(key); + if(tmp >= 0) return index + tmp; + index += arr.length; + } + return -1; + } + } + if(key === "includes"){ + return function(key){ + for(let i = 0; i < self.index.length; i++){ + if(self.index[i].includes(key)){ + return true; + } + } + return false; + } + } + if(key === "slice"){ + return function(start, end){ + return _slice( + self, + start || 0, + end || self.length, + false + ); + } + } + if(key === "splice"){ + return function(start, end){ + return _slice( + self, + start || 0, + end || self.length, + // splice: + true + ); + } + } + if(key === "constructor"){ + return Array; + } + if(typeof key === "symbol" /*|| isNaN(key)*/){ + // not supported + return; + } + const index = key / (2**31) | 0; + const arr = self.index[index]; + return arr && arr[key]; + }, + set(target, key, value){ + const index = key / (2**31) | 0; + const arr = self.index[index] || (self.index[index] = []); + arr[key] = value; + self.length++; + return true; + } + }); +} + +KeystoreArray.prototype.clear = function(){ + this.index.length = 0; +}; + +KeystoreArray.prototype.destroy = function(){ + this.index = null; + this.proxy = null; +}; + +KeystoreArray.prototype.push = function(val){}; + +/** + * @param bitlength + * @constructor + */ + +function KeystoreMap(bitlength = 8){ + + if(!this){ + return new KeystoreMap(bitlength); + } + + this.index = create_object(); + this.refs = []; + this.size = 0; + + if(bitlength > 32){ + this.crc = lcg64; + this.bit = BigInt(bitlength); + } + else { + this.crc = lcg$1; + this.bit = bitlength; + } +} + +KeystoreMap.prototype.get = function(key) { + const address = this.crc(key); + const map = this.index[address]; + return map && map.get(key); +}; + +KeystoreMap.prototype.set = function(key, value){ + const address = this.crc(key); + let map = this.index[address]; + if(map){ + let size = map.size; + map.set(key, value); + size -= map.size; + size && this.size++; + } + else { + this.index[address] = map = new Map([[key, value]]); + this.refs.push(map); + } +}; + +/** + * @param bitlength + * @constructor + */ + +function KeystoreSet(bitlength = 8){ + + if(!this){ + return new KeystoreSet(bitlength); + } + + // using plain Object with numeric key access + // just for max performance + this.index = create_object(); + this.refs = []; + + if(bitlength > 32){ + this.crc = lcg64; + this.bit = BigInt(bitlength); + } + else { + this.crc = lcg$1; + this.bit = bitlength; + } +} + +KeystoreSet.prototype.add = function(key){ + const address = this.crc(key); + let set = this.index[address]; + if(set){ + let size = set.size; + set.add(key); + size -= set.size; + size && this.size++; + } + else { + this.index[address] = set = new Set([key]); + this.refs.push(set); + } +}; + +KeystoreMap.prototype.has = +KeystoreSet.prototype.has = function(key) { + const address = this.crc(key); + const map_or_set = this.index[address]; + return map_or_set && map_or_set.has(key); +}; + +/* +KeystoreMap.prototype.size = +KeystoreSet.prototype.size = function(){ + let size = 0; + const values = Object.values(this.index); + for(let i = 0; i < values.length; i++){ + size += values[i].size; + } + return size; +}; +*/ + +KeystoreMap.prototype.delete = +KeystoreSet.prototype.delete = function(key){ + const address = this.crc(key); + const map_or_set = this.index[address]; + // set && (set.size === 1 + // ? this.index.delete(address) + // : set.delete(key)); + map_or_set && + map_or_set.delete(key) && + this.size--; +}; + +KeystoreMap.prototype.clear = +KeystoreSet.prototype.clear = function(){ + this.index = create_object(); + this.refs = []; + this.size = 0; +}; + +// KeystoreMap.prototype.destroy = +// KeystoreSet.prototype.destroy = function(){ +// this.index = null; +// this.refs = null; +// this.proxy = null; +// }; + +KeystoreMap.prototype.values = +KeystoreSet.prototype.values = function*(){ + // alternatively iterate through this.keys[] + //const refs = Object.values(this.index); + for(let i = 0; i < this.refs.length; i++){ + for(let value of this.refs[i].values()){ + yield value; + } + } +}; + +KeystoreMap.prototype.keys = +KeystoreSet.prototype.keys = function*(){ + //const values = Object.values(this.index); + for(let i = 0; i < this.refs.length; i++){ + for(let key of this.refs[i].keys()){ + yield key; + } + } +}; + +KeystoreMap.prototype.entries = +KeystoreSet.prototype.entries = function*(){ + //const values = Object.values(this.index); + for(let i = 0; i < this.refs.length; i++){ + for(let entry of this.refs[i].entries()){ + yield entry; + } + } +}; + +/** + * Linear Congruential Generator (LCG) + * @param str + * @this {KeystoreMap|KeystoreSet} + */ + +function lcg$1(str) { + let range = 2 ** this.bit - 1; + if(typeof str == "number"){ + return str & range; + } + let crc = 0, bit = this.bit + 1; + for(let i = 0; i < str.length; i++) { + crc = (crc * bit ^ str.charCodeAt(i)) & range; + } + // shift Int32 to UInt32 because negative numbers + // extremely slows down key lookup + return this.bit === 32 + ? crc + 2 ** 31 + : crc;// & 0xFFFF; +} + +/** + * @param str + * @this {KeystoreMap|KeystoreSet} + */ + +function lcg64(str) { + let range = BigInt(2) ** /** @type {!BigInt} */ (this.bit) - BigInt(1); + let type = typeof str; + if(type === "bigint"){ + return /** @type {!BigInt} */ (str) & range; + } + if(type === "number"){ + return BigInt(str) & range; + } + let crc = BigInt(0), bit = /** @type {!BigInt} */ (this.bit) + BigInt(1); + for(let i = 0; i < str.length; i++){ + crc = (crc * bit ^ BigInt(str.charCodeAt(i))) & range; + } + return crc;// & 0xFFFFFFFFFFFFFFFF; +} + +// TODO return promises instead of inner await + + +function async(callback, self, field, key, index_doc, index, data, on_done){ + + //setTimeout(function(){ + + const res = callback(field ? field + "." + key : key, JSON.stringify(data)); + + // await isn't supported by ES5 + + if(res && res["then"]){ + + res["then"](function(){ + + self.export(callback, self, field, index_doc, index + 1, on_done); + }); + } + else { + + self.export(callback, self, field, index_doc, index + 1, on_done); + } + //}); +} + +/** + * @param callback + * @param self + * @param field + * @param index_doc + * @param index + * @param on_done + * @this {Index|Document} + */ + +function exportIndex(callback, self, field, index_doc, index, on_done){ + + let return_value = true; + if (typeof on_done === 'undefined') { + return_value = new Promise((resolve) => { + on_done = resolve; + }); + } + + let key, data; + + switch(index || (index = 0)){ + + case 0: + + key = "reg"; + + // fastupdate isn't supported by export + + if(this.fastupdate){ + + data = create_object(); + + for(let key of this.reg.keys()){ + + data[key] = 1; + } + } + else { + + data = this.reg; + } + + break; + + case 1: + + key = "cfg"; + data = { + "doc": 0, + "opt": this.optimize ? 1 : 0 + }; + + break; + + case 2: + + key = "map"; + data = this.map; + break; + + case 3: + + key = "ctx"; + data = this.ctx; + break; + + default: + + if (typeof field === 'undefined' && on_done) { + + on_done(); + } + + return; + } + + async(callback, self || this, field, key, index_doc, index, data, on_done); + + return return_value; +} + +/** + * @this Index + */ + +function importIndex(key, data){ + + if(!data){ + + return; + } + + if(is_string(data)){ + + data = JSON.parse(data); + } + + switch(key){ + + case "cfg": + + this.optimize = !!data["opt"]; + break; + + case "reg": + + // fastupdate isn't supported by import + + this.fastupdate = false; + this.reg = data; + break; + + case "map": + + this.map = data; + break; + + case "ctx": + + this.ctx = data; + break; + } +} + +/** + * @this Document + */ + +function exportDocument(callback, self, field, index_doc, index, on_done){ + + let return_value; + if (typeof on_done === 'undefined') { + return_value = new Promise((resolve) => { + on_done = resolve; + }); + } + + index || (index = 0); + index_doc || (index_doc = 0); + + if(index_doc < this.field.length){ + + const field = this.field[index_doc]; + const idx = this.index[field]; + + self = this; + + //setTimeout(function(){ + + if(!idx.export(callback, self, index ? field/*.replace(":", "-")*/ : "", index_doc, index++, on_done)){ + + index_doc++; + index = 1; + + self.export(callback, self, field, index_doc, index, on_done); + } + //}); + } + else { + + let key, data; + + switch(index){ + + case 1: + + key = "tag"; + data = this.tagindex; + field = null; + break; + + case 2: + + key = "store"; + data = this.store; + field = null; + break; + + // case 3: + // + // key = "reg"; + // data = this.register; + // break; + + default: + + on_done(); + return; + } + + async(callback, this, field, key, index_doc, index, data, on_done); + } + + return return_value +} + +/** + * @this Document + */ + +function importDocument(key, data){ + + if(!data){ + + return; + } + + if(is_string(data)){ + + data = JSON.parse(data); + } + + switch(key){ + + case "tag": + + this.tagindex = data; + break; + + case "reg": + + // fastupdate isn't supported by import + + this.fastupdate = false; + this.reg = data; + + for(let i = 0, index; i < this.field.length; i++){ + + index = this.index[this.field[i]]; + index.reg = data; + index.fastupdate = false; + } + + break; + + case "store": + + this.store = data; + break; + + default: + + key = key.split("."); + const field = key[0]; + key = key[1]; + + if(field && key){ + + this.index[field].import(key, data); + } + } +} + +// COMPILER BLOCK --> + +/** + * @enum {Object} + * @const + */ + +const presets = { + + "memory": { + resolution: 1 + }, + + "performance": { + resolution: 6, + fastupdate: true, + context: { + depth: 1, + resolution: 3 + } + }, + + "match": { + tokenize: "forward" + }, + + "score": { + resolution: 9, + context: { + depth: 2, + resolution: 9 + } + } +}; + +/** + * + * @param {!IndexOptions|string} options + * @return {IndexOptions} + */ + +function apply_preset(options){ + + const preset = is_string(options) + ? options + : options["preset"]; + + if(preset){ + if(!presets[preset]){ + console.warn("Preset not found: " + preset); + } + options = Object.assign({}, presets[preset], /** @type {Object} */ (options)); + } + + return options; +} + +function apply_async(prototype){ + register$1.call(prototype, "add"); + register$1.call(prototype, "append"); + register$1.call(prototype, "search"); + register$1.call(prototype, "update"); + register$1.call(prototype, "remove"); +} + +// let cycle; +// let budget = 0; +// +// function tick(resolve){ +// cycle = null; +// budget = 0; +// resolve(); +// } + +/** + * @param {!string} key + * @this {Index|Document} + */ + +function register$1(key){ + this[key + "Async"] = function(){ + + // // prevent stack overflow of adding too much tasks to the same event loop + // // actually limit stack to 1,000,000 tasks every ~4ms + // cycle || ( + // cycle = new Promise(resolve => setTimeout(tick, 0, resolve)) + // ); + // + // // apply different performance budgets + // if(key === "update" || key === "remove" && this.fastupdate === false){ + // budget += 1000 * this.resolution; + // if(this.depth) + // budget += 1000 * this.resolution_ctx; + // } + // else if(key === "search"){ + // budget++; + // } + // else{ + // budget += 20 * this.resolution; + // if(this.depth) + // budget += 20 * this.resolution_ctx; + // } + // + // // wait for the event loop cycle + // if(budget >= 1e6){ + // await cycle; + // } + + const args = /*[].slice.call*/(arguments); + const arg = args[args.length - 1]; + let callback; + + if(typeof arg === "function"){ + callback = arg; + delete args[args.length - 1]; + } + + this.async = true; + const res = this[key].apply(this, args); + this.async = false; + res.then ? res.then(callback) + : callback(res); + return res; + }; +} + +const data = create_object(); + +/** + * @param {!string} name + */ + +function tick(name){ + + /** @type {!Object} */ + const profiler = data["profiler"] || (data["profiler"] = {}); + + profiler[name] || (profiler[name] = 0); + profiler[name]++; +} + +// COMPILER BLOCK --> +// <-- COMPILER BLOCK + +let table;// = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; +let timer; +const cache = new Map(); + +function toRadix(number, radix = 255) { + + if(!table){ + table = new Array(radix); + // the char code 0 could be a special marker + for(let i = 0; i < radix; i++) table[i] = i + 1; + table = String.fromCharCode.apply(null, table); + } + + let rixit; + let residual = number; + let result = ""; + + while(true){ + rixit = residual % radix; + result = table.charAt(rixit) + result; + residual = residual / radix | 0; + if(!residual) + break; + } + + return result; +} + +function default_compress(str){ + + { + if(timer){ + if(cache.has(str)){ + return cache.get(str); + } + } + else { + timer = setTimeout(clear); + } + } + + /* 2 ** ((level + 1.5) * 1.6 | 0) */ + + const result = toRadix( + typeof str == "number" + ? str + : lcg(str) + ); + + { + cache.size > 2e5 && cache.clear(); + cache.set(str, result); + } + + return result; +} + +function lcg(str) { + let range = 2 ** 32 - 1; + if(typeof str == "number"){ + return str & range; + } + let crc = 0, bit = 32 + 1; + for(let i = 0; i < str.length; i++) { + crc = (crc * bit ^ str.charCodeAt(i)) & range; + } + // shift up from Int32 to UInt32 range 0xFFFFFFFF + return crc + 2 ** 31; +} + +function clear(){ + timer = null; + cache.clear(); +} + +// COMPILER BLOCK --> + +// TODO: +// string + number as text +// boolean, null, undefined as ? + + +/** + * @param {!number|string} id + * @param {!string} content + * @param {boolean=} _append + * @param {boolean=} _skip_update + */ + +Index.prototype.add = function(id, content, _append, _skip_update){ + + if(content && (id || (id === 0))){ + + // todo check skip_update + //_skip_update = false; + + if(!_skip_update && !_append){ + if(this.reg.has(id)){ + return this.update(id, content); + } + } + + // do not force a string as input + // https://github.com/nextapps-de/flexsearch/issues/432 + content = this.encoder.encode(content); + const word_length = content.length; + + if(word_length){ + + // check context dupes to skip all contextual redundancy along a document + + const dupes_ctx = create_object(); + const dupes = create_object(); + const depth = this.depth; + const resolution = this.resolution; + + for(let i = 0; i < word_length; i++){ + + let term = content[this.rtl ? word_length - 1 - i : i]; + let term_length = term.length; + + // skip dupes will break the context chain + + if(term_length /*&& (term_length >= this.minlength)*/ && (depth || !dupes[term])){ + + let score = this.score + ? this.score(content, term, i, null, 0) + : get_score(resolution, word_length, i); + let token = ""; + + switch(this.tokenize){ + + case "full": + if(term_length > 2){ + for(let x = 0; x < term_length; x++){ + for(let y = term_length; y > x; y--){ + + //if((y - x) >= this.minlength){ + token = term.substring(x, y); + const partial_score = this.score + ? this.score(content, term, i, token, x) + : get_score(resolution, word_length, i, term_length, x); + this.push_index(dupes, token, partial_score, id, _append); + //} + } + } + break; + } + // fallthrough to next case when term length < 3 + case "reverse": + // skip last round (this token exist already in "forward") + if(term_length > 1){ + for(let x = term_length - 1; x > 0; x--){ + token = term[x] + token; + //if(token.length >= this.minlength){ + const partial_score = this.score + ? this.score(content, term, i, token, x) + : get_score(resolution, word_length, i, term_length, x); + this.push_index(dupes, token, partial_score, id, _append); + //} + } + token = ""; + } + + // fallthrough to next case to apply forward also + case "forward": + if(term_length > 1){ + for(let x = 0; x < term_length; x++){ + token += term[x]; + //if(token.length >= this.minlength){ + this.push_index(dupes, token, score, id, _append); + //} + } + break; + } + + // fallthrough to next case when token has a length of 1 + default: + // case "strict": + + // todo move boost to search + // if(this.boost){ + // score = Math.min((score / this.boost(content, term, i)) | 0, resolution - 1); + // } + + this.push_index(dupes, term, score, id, _append); + + // context is just supported by tokenizer "strict" + if(depth){ + + if((word_length > 1) && (i < (word_length - 1))){ + + // check inner dupes to skip repeating words in the current context + const dupes_inner = create_object(); + const resolution = this.resolution_ctx; + const keyword = term; + const size = Math.min(depth + 1, word_length - i); + + dupes_inner[keyword] = 1; + + for(let x = 1; x < size; x++){ + + term = content[this.rtl ? word_length - 1 - i - x : i + x]; + + if(term /*&& (term.length >= this.minlength)*/ && !dupes_inner[term]){ + + dupes_inner[term] = 1; + + const context_score = this.score + ? this.score(content, keyword, i, term, x) + : get_score(resolution + ((word_length / 2) > resolution ? 0 : 1), word_length, i, size - 1, x - 1); + const swap = this.bidirectional && (term > keyword); + this.push_index(dupes_ctx, swap ? keyword : term, context_score, id, _append, swap ? term : keyword); + } + } + } + } + } + } + } + + this.fastupdate || this.reg.add(id); + } + else { + content = ""; + } + } + + if(this.db){ + // when the term has no valid content (e.g. empty), + // then it was not added to the ID registry for removal + content || this.commit_task.push({ "del": id }); + this.commit_auto && autoCommit(this); + } + + return this; +}; + +/** + * @private + * @param dupes + * @param term + * @param score + * @param id + * @param {boolean=} append + * @param {string=} keyword + */ + +Index.prototype.push_index = function(dupes, term, score, id, append, keyword){ + + let arr = keyword ? this.ctx : this.map; + let tmp; + + if(!dupes[term] || !keyword || !(tmp = dupes[term])[keyword]){ + + if(keyword){ + + dupes = tmp || (dupes[term] = create_object()); + dupes[keyword] = 1; + + if(this.compress){ + keyword = default_compress(keyword); + } + + tmp = arr.get(keyword); + tmp ? arr = tmp + : arr.set(keyword, arr = new Map()); + } + else { + + dupes[term] = 1; + } + + if(this.compress){ + term = default_compress(term); + } + + tmp = arr.get(term); + tmp ? arr = tmp : arr.set(term, arr = tmp = []); + // the ID array will be upgraded dynamically + arr = arr[score] || (arr[score] = []); + + if(!append || !arr.includes(id)){ + + // auto-upgrade to keystore array if max size exceeded + { + if(arr.length === 2**31-1 /*|| !(arr instanceof KeystoreArray)*/){ + const keystore = new KeystoreArray(arr); + if(this.fastupdate){ + for(let value of this.reg.values()){ + if(value.includes(arr)){ + value[value.indexOf(arr)] = keystore; + } + } + } + tmp[score] = arr = keystore; + } + } + + arr.push(id); + + // add a reference to the register for fast updates + if(this.fastupdate){ + const tmp = this.reg.get(id); + tmp ? tmp.push(arr) + : this.reg.set(id, [arr]); + } + } + } +}; + +/** + * @param {number} resolution + * @param {number} length + * @param {number} i + * @param {number=} term_length + * @param {number=} x + * @returns {number} + */ + +function get_score(resolution, length, i, term_length, x){ + + // console.log("resolution", resolution); + // console.log("length", length); + // console.log("term_length", term_length); + // console.log("i", i); + // console.log("x", x); + // console.log((resolution - 1) / (length + (term_length || 0)) * (i + (x || 0)) + 1); + + // the first resolution slot is reserved for the best match, + // when a query matches the first word(s). + + // also to stretch score to the whole range of resolution, the + // calculation is shift by one and cut the floating point. + // this needs the resolution "1" to be handled additionally. + + // do not stretch the resolution more than the term length will + // improve performance and memory, also it improves scoring in + // most cases between a short document and a long document + + return i && (resolution > 1) ? ( + + (length + (term_length || 0)) <= resolution ? + + i + (x || 0) + : + ((resolution - 1) / (length + (term_length || 0)) * (i + (x || 0)) + 1) | 0 + ): + 0; +} + +/* + from -> res[score][id] + to -> [id] +*/ + +/** + * Aggregate the union of a single raw result + * @param {!Array} result + * @param {!number} limit + * @param {number=} offset + * @param {boolean=} enrich + * @return Array + */ + +function resolve_default(result, limit, offset, enrich){ + + // fast path: when there is just one slot in the result + if(result.length === 1){ + result = result[0]; + result = offset || (result.length > limit) + ? (limit + ? result.slice(offset, offset + limit) + : result.slice(offset) + ) + : result; + return enrich + ? enrich_result(result) + : result; + } + + // this is an optimized workaround instead of + // just doing result = concat(result) + + let final = []; + + for(let i = 0, arr, len; i < result.length; i++){ + if(!(arr = result[i]) || !(len = arr.length)) continue; + + if(offset){ + // forward offset pointer + if(offset >= len){ + offset -= len; + continue; + } + // apply offset pointer when length differs + if(offset < len){ + arr = limit + ? arr.slice(offset, offset + limit) + : arr.slice(offset); + len = arr.length; + offset = 0; + } + } + + if(!final.length){ + // fast path: when limit was reached in first slot + if(len >= limit){ + if(len > limit){ + arr = arr.slice(0, limit); + } + return enrich + ? enrich_result(arr) + : arr; + } + final = [arr]; + } + else { + if(len > limit){ + arr = arr.slice(0, limit); + len = arr.length; + } + final.push(arr); + } + + // reduce limit + limit -= len; + + // todo remove + // if(limit < 0){ + // throw new Error("Impl.Error"); + // } + + // break if limit was reached + if(!limit){ + break; + } + } + + // todo remove + if(!final.length){ + //throw new Error("No results found"); + return final; + } + + final = final.length > 1 + ? concat(final) + : final[0]; + + return enrich + ? enrich_result(final) + : final; +} + +function enrich_result(ids){ + for(let i = 0; i < ids.length; i++){ + ids[i] = { + score: i, + id: ids[i] + }; + } + return ids; +} + +// import xor from "./xor.js"; +// import and from "./and.js"; +// import not from "./not.js"; + +Resolver.prototype.or = function(){ + + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.or.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.or.apply(this, first_argument); + } + } + + // for(let i = 0; i < args.length; i++){ + // if(args[i].result instanceof Promise){ + // return; + // } + // } + + // if(args.length < 2){ + // if(first_argument.index){ + // first_argument.resolve = false; + // const result = first_argument.index.search(first_argument); + // if(result instanceof Promise){ + // result.then(function(result){ + // final = self.result.concat(result); + // self.result = resolver(final, limit, offset, enrich, !resolve); + // return resolve ? self.result : self; + // }); + // } + // else{ + // final = this.result.concat(result); + // this.result = resolver(final, limit, offset, enrich, !resolve); + // return resolve ? this.result : this; + // } + // } + // } + + let final = []; + let promises = []; + let limit = 0, offset = 0, enrich, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.and){ + result = this.and(query.and); + } + else if(query.xor){ + result = this.xor(query.xor); + } + else if(query.not){ + result = this.not(query.not); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + enrich = query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + self.result.length && (final = [self.result].concat(final)); + self.result = resolver(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? self.result : self; + }); + } + + this.result.length && (final = [this.result].concat(final)); + this.result = resolver(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? this.result : this; +}; + +/** + * Aggregate the union of N raw results + * @param result + * @param limit + * @param offset + * @param enrich + * @param resolve + * @param boost + * @return {Array} + */ + +function resolver(result, limit, offset, enrich, resolve, boost){ + + if(!result.length){ + // todo remove + //console.log("Empty Result") + return result; + } + + if(typeof limit === "object"){ + offset = limit.offset || 0; + enrich = limit.enrich || false; + limit = limit.limit || 0; + } + + if(result.length < 2){ + // todo remove + //console.log("Single Result") + if(resolve){ + return resolve_default(result[0], limit, offset, enrich); + } + else { + return result[0]; + } + } + + let final = []; + let count = 0; + let dupe = create_object(); + let maxres = get_max_len(result); + + for(let j = 0, ids; j < maxres; j++){ + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res) continue; + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + if(!dupe[id]){ + dupe[id] = 1; + if(offset){ + offset--; + continue; + } + if(resolve){ + final.push(id); + } + else { + // shift resolution by boost (inverse) + const index = j + (i ? boost : 0); + final[index] || (final[index] = []); + final[index].push(id); + } + if(limit && ++count === limit){ + //this.boost = 0; + return final; + } + } + } + } + } + + //this.boost = 0; + return final; +} + +// import xor from "./xor.js"; +// import or from "./or.js"; +// import not from "./not.js"; + +Resolver.prototype.and = function(){ + if(this.result.length){ + + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.and.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.and.apply(this, first_argument); + } + } + + // for(let i = 0; i < args.length; i++){ + // if(args[i].result instanceof Promise){ + // return; + // } + // } + + // if(args.length < 2){ + // if(first_argument.index){ + // first_argument.resolve = false; + // return first_argument.index.search(first_argument); + // } + // } + + let final = []; + let promises = []; + let limit = 0, offset = 0, enrich, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.or){ + result = this.or(query.or); + } + else if(query.xor){ + result = this.xor(query.xor); + } + else if(query.not){ + result = this.not(query.not); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + enrich = query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + final = [self.result].concat(final); + self.result = intersect$1(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? self.result : self; + }); + } + + final = [this.result].concat(final); + this.result = intersect$1(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? this.result : this; + } + return this; +}; + +/** + * Aggregate the intersection of N raw results + * @param result + * @param limit + * @param offset + * @param enrich + * @param resolve + * @param boost + * @return {Array} + */ + +function intersect$1(result, limit, offset, enrich, resolve, boost){ + + // if(!result.length){ + // // todo remove + // console.log("Empty Result") + // return result; + // } + + if(result.length < 2){ + // todo remove + //console.log("Single Result") + return []; + // if(resolve){ + // return default_resolver(result[0], limit, offset, enrich); + // } + // else{ + // return result[0]; + // } + } + + let final = []; + let count = 0; + + // fast path single slot + // if(result.length < 2){ + // if(limit || offset){ + // let res = result[0]; + // for(let j = 0, ids; j < res.length; j++){ + // ids = res[j]; + // if(!ids) continue; + // for(let k = 0, id; k < ids.length; k++){ + // id = ids[k]; + // if(offset){ + // offset--; + // continue; + // } + // if(resolve){ + // final.push(id); + // } + // else{ + // final[j + this.boost] || (final[j + this.boost] = []); + // final[j + this.boost].push(id); + // } + // if(limit && ++count === limit){ + // this.boost = 0; + // return final; + // } + // } + // } + // } + // this.boost = 0; + // return result[0]; + // } + + let contain = create_object(); + let maxres = get_max_len(result); + if(!maxres) return final; + + // for(let j = 0, ids, res = result[0]; j < res.length; j++){ + // ids = res[j]; + // for(let k = 0; k < ids.length; k++){ + // contain[ids[k]] = 1; + // } + // } + + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res || !res.length) return []; + let contain_new = create_object(); + let match = 0; + let last_round = i === result.length - 1; + + for(let j = 0, ids; j < maxres; j++){ + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id, min; k < ids.length; k++){ + id = ids[k]; + // fill in first round + if(!i){ + // shift resolution +1 + // shift resolution by boost (inverse) + contain_new[id] = j + 1 + (i ? boost : 0); + match = 1; + } + // result in last round + else if(last_round){ + if((min = contain[id])){ + match = 1; + //if(!contain_new[id]){ + if(offset){ + offset--; + continue; + } + if(resolve){ + final.push(id); + } + else { + // reduce resolution -1 + min--; + if(j < min) min = j; + final[min] || (final[min] = []); + final[min].push(id); + } + if(limit && ++count === limit){ + //this.boost = 0; + return final; + } + // shift resolution +1 + //contain_new[id] = min + 1; + //} + } + } + // check for intersection + else if((min = contain[id])){ + // shift resolution +1 + if(j + 1 < min) min = j + 1; + contain_new[id] = min; + match = 1; + } + } + } + + if(!match){ + //this.boost = 0; + return []; + } + + contain = contain_new; + } + + //this.boost = 0; + return final; +} + +// import or from "./or.js"; +// import and from "./and.js"; +// import not from "./not.js"; + +Resolver.prototype.xor = function(){ + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.xor.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.xor.apply(this, first_argument); + } + } + + let final = []; + let promises = []; + let limit = 0, offset = 0, enrich, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.or){ + result = this.or(query.or); + } + else if(query.and){ + result = this.and(query.and); + } + else if(query.not){ + result = this.not(query.not); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + enrich = query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + self.result.length && (final = [self.result].concat(final)); + self.result = exclusive(final, limit, offset, enrich, !resolve, self.boostval); + return resolve ? self.result : self; + }); + } + + this.result.length && (final = [this.result].concat(final)); + this.result = exclusive(final, limit, offset, enrich, !resolve, self.boostval); + return resolve ? this.result : this; +}; + +/** + * @param result + * @param limit + * @param offset + * @param enrich + * @param resolve + * @param boost + * @return {Array} + */ + +function exclusive(result, limit, offset, enrich, resolve, boost){ + + if(!result.length){ + // todo remove + //console.log("Empty Result") + return result; + } + + if(result.length < 2){ + // todo remove + //console.log("Single Result") + if(resolve){ + return resolve_default(result[0], limit, offset, enrich); + } + else { + return result[0]; + } + } + + const final = []; + const check = create_object(); + + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res) continue; + + for(let j = 0, ids; j < res.length; j++){ + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + check[id] + ? check[id]++ + : check[id] = 1; + } + } + } + + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res) continue; + + for(let j = 0, ids; j < res.length; j++){ + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + if(check[id] === 1){ + if(resolve){ + final.push(id); + } + else { + // shift resolution by boost (inverse) + const index = j + (i ? boost : 0); + final[index] || (final[index] = []); + final[index].push(id); + } + } + } + } + } + + //this.boost = 0; + return final; +} + +// import or from "./or.js"; +// import and from "./and.js"; +// import xor from "./xor.js"; + +Resolver.prototype.not = function(){ + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.not.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.not.apply(this, first_argument); + } + } + + let final = []; + let promises = []; + let limit = 0, offset = 0, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.or){ + result = this.or(query.or); + } + else if(query.and){ + result = this.and(query.and); + } + else if(query.xor){ + result = this.xor(query.xor); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + self.result = exclusion.call(self, final, limit, offset, resolve); + return resolve ? self.result : self; + }); + } + + this.result = exclusion.call(this, final, limit, offset, resolve); + return resolve ? this.result : this; +}; + +/** + * @param result + * @param limit + * @param offset + * @param resolve + * @this Resolver + * @return {Array} + */ + +function exclusion(result, limit, offset, resolve){ + + if(!result.length){ + return this.result; + } + + const final = []; + const exclude = new Set(result.flat().flat()); + + for(let j = 0, ids; j < this.result.length; j++){ + ids = this.result[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + if(!exclude.has(id)){ + if(resolve){ + final.push(id); + } + else { + final[j] || (final[j] = []); + final[j].push(id); + } + } + } + } + + return final; +} + +/** + * @param result + * @constructor + */ + +function Resolver(result){ + if(!this){ + return new Resolver(result); + } + if(result && result.index){ + result.resolve = false; + this.index = result.index; + return result.index.search(result); + } + if(result.constructor === Resolver){ + // todo test this branch + //console.log("Resolver Loopback") + return result; + } + this.index = null; + this.result = result || []; + this.boostval = 0; +} + +Resolver.prototype.limit = function(limit){ + if(this.result.length){ + const final = []; + let count = 0; + for(let j = 0, ids; j < this.result.length; j++){ + ids = this.result[j]; + if(ids.length + count < limit){ + final[j] = ids; + count += ids.length; + } + else { + final[j] = ids.slice(0, limit - count); + this.result = final; + break; + } + } + } + return this; +}; + +Resolver.prototype.offset = function(offset){ + if(this.result.length){ + const final = []; + let count = 0; + for(let j = 0, ids; j < this.result.length; j++){ + ids = this.result[j]; + if(ids.length + count < offset){ + count += ids.length; + } + else { + final[j] = ids.slice(offset - count); + count = offset; + } + } + this.result = final; + } + return this; +}; + +Resolver.prototype.boost = function(boost){ + this.boostval += boost; + return this; +}; + +Resolver.prototype.resolve = function(limit, offset, enrich){ + set_resolve(1); + const result = this.result; + this.index = null; + this.result = null; + + if(result.length){ + if(typeof limit === "object"){ + enrich = limit.enrich; + offset = limit.offset; + limit = limit.limit; + } + return resolve_default(result, limit || 100, offset, enrich); + } + + return result; +}; + +/* + + from -> result[ + res[score][id], + res[score][id], + ] + + to -> [id] + + */ + +/** + * Implementation based on Object[key] provides better suggestions + * capabilities and has less performance scaling issues on large indexes. + * + * @param arrays + * @param limit + * @param offset + * @param {boolean|Array=} suggest + * @param {boolean=} resolve + * @returns {Array} + */ + +function intersect(arrays, limit, offset, suggest, resolve) { + + const length = arrays.length; + + // todo remove + // if(length < 2){ + // throw new Error("Not optimized intersect"); + // } + + let result = []; + let size = 0; + let check; + let check_suggest; + let check_new; + let res_arr; + + if(suggest){ + suggest = []; + } + + // 1. a reversed order prioritize the order of words from a query + // because it ends with the first term. + // 2. process terms in reversed order often has advantage for + // the fast path "end reached". + + // alternatively the results could be sorted by length up + //arrays.sort(sort_by_length_up); + + // todo the outer loop should be the res array instead of term array, + // this isn't just simple because the intersection calculation needs to reflect this + //const maxlen = get_max_len(arrays); + + for(let x = length - 1, found; x >= 0; x--){ + //for(let x = 0, found; x < length; x++){ + + res_arr = arrays[x]; + check_new = create_object(); + found = !check; + + // process relevance in forward order (direction is + // important for adding IDs during the last round) + + for(let y = 0, ids; y < res_arr.length; y++){ + + ids = res_arr[y]; + if(!ids || !ids.length) continue; + + for(let z = 0, id; z < ids.length; z++){ + + id = ids[z]; + + // check exists starting from the 2nd slot + if(check){ + if(check[id]){ + + // check if in last round + if(!x){ + //if(x === length - 1){ + + if(offset){ + offset--; + } + else { + + result[size++] = id; + + if(size === limit){ + // fast path "end reached" + return result /*resolve === false + ? { result, suggest } + :*/ + } + } + } + + if(x || suggest){ + //if((x < length - 1) || suggest){ + check_new[id] = 1; + } + + found = true; + } + + if(suggest){ + + if(!check_suggest[id]){ + check_suggest[id] = 1; + const arr = suggest[y] || (suggest[y] = []); + arr.push(id); + } + + // OLD: + // + // check_idx = (check_suggest[id] || 0) + 1; + // check_suggest[id] = check_idx; + // + // // do not adding IDs which are already included in the result (saves one loop) + // // the first intersection match has the check index 2, so shift by -2 + // + // if(check_idx < length){ + // + // const tmp = suggest[check_idx - 2] || (suggest[check_idx - 2] = []); + // tmp[tmp.length] = id; + // } + } + } + else { + + // pre-fill in first round + check_new[id] = 1; + } + } + } + + if(suggest){ + + // re-use the first pre-filled check for suggestions + check || (check_suggest = check_new); + } + else if(!found){ + + return []; + } + + check = check_new; + } + + // return intermediate result + // if(resolve === false){ + // return { result, suggest }; + // } + + if(suggest){ + + // needs to iterate in reverse direction + for(let x = suggest.length - 1, ids, len; x >= 0; x--){ + + ids = suggest[x]; + len = ids.length; + + for(let y = 0, id; y < len; y++){ + + id = ids[y]; + + if(!check[id]){ + + if(offset){ + offset--; + } + else { + + result[size++] = id; + + if(size === limit){ + // fast path "end reached" + return result; + } + } + + check[id] = 1; + } + } + } + } + + return result; +} + +/** + * @param mandatory + * @param arrays + * @returns {Array} + */ + +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.push(id); + } + } + } + } + + return result; +} + + +/** + * Implementation based on Array.includes() provides better performance, + * but it needs at least one word in the query which is less frequent. + * Also on large indexes it does not scale well performance-wise. + * This strategy also lacks of suggestion capabilities (matching & sorting). + * + * @param arrays + * @param limit + * @param offset + * @param {boolean|Array=} suggest + * @returns {Array} + */ + +// export function intersect(arrays, limit, offset, suggest) { +// +// const length = arrays.length; +// let result = []; +// let check; +// +// // determine shortest array and collect results +// // from the sparse relevance arrays +// +// let smallest_size; +// let smallest_arr; +// let smallest_index; +// +// for(let x = 0; x < length; x++){ +// +// const arr = arrays[x]; +// const len = arr.length; +// +// let size = 0; +// +// for(let y = 0, tmp; y < len; y++){ +// +// tmp = arr[y]; +// +// if(tmp){ +// +// size += tmp.length; +// } +// } +// +// if(!smallest_size || (size < smallest_size)){ +// +// smallest_size = size; +// smallest_arr = arr; +// smallest_index = x; +// } +// } +// +// smallest_arr = smallest_arr.length === 1 ? +// +// smallest_arr[0] +// : +// concat(smallest_arr); +// +// if(suggest){ +// +// suggest = [smallest_arr]; +// check = create_object(); +// } +// +// let size = 0; +// let steps = 0; +// +// // process terms in reversed order often results in better performance. +// // the outer loop must be the words array, using the +// // smallest array here disables the "fast fail" optimization. +// +// for(let x = length - 1; x >= 0; x--){ +// +// if(x !== smallest_index){ +// +// steps++; +// +// const word_arr = arrays[x]; +// const word_arr_len = word_arr.length; +// const new_arr = []; +// +// let count = 0; +// +// for(let z = 0, id; z < smallest_arr.length; z++){ +// +// id = smallest_arr[z]; +// +// let found; +// +// // process relevance in forward order (direction is +// // important for adding IDs during the last round) +// +// for(let y = 0; y < word_arr_len; y++){ +// +// const arr = word_arr[y]; +// +// if(arr.length){ +// +// found = arr.includes(id); +// +// if(found){ +// +// // check if in last round +// +// if(steps === length - 1){ +// +// if(offset){ +// +// offset--; +// } +// else{ +// +// result[size++] = id; +// +// if(size === limit){ +// +// // fast path "end reached" +// +// return result; +// } +// } +// +// if(suggest){ +// +// check[id] = 1; +// } +// } +// +// break; +// } +// } +// } +// +// if(found){ +// +// new_arr[count++] = id; +// } +// } +// +// if(suggest){ +// +// suggest[steps] = new_arr; +// } +// else if(!count){ +// +// return []; +// } +// +// smallest_arr = new_arr; +// } +// } +// +// if(suggest){ +// +// // needs to iterate in reverse direction +// +// for(let x = suggest.length - 1, arr, len; x >= 0; x--){ +// +// arr = suggest[x]; +// len = arr && arr.length; +// +// if(len){ +// +// for(let y = 0, id; y < len; y++){ +// +// id = arr[y]; +// +// if(!check[id]){ +// +// check[id] = 1; +// +// if(offset){ +// +// offset--; +// } +// else{ +// +// result[size++] = id; +// +// if(size === limit){ +// +// // fast path "end reached" +// +// return result; +// } +// } +// } +// } +// } +// } +// } +// +// return result; +// } + +// COMPILER BLOCK --> + +let global_resolve = 1; +function set_resolve(resolve){ + global_resolve = resolve; +} + +/** + * @param {string|SearchOptions} query + * @param {number|SearchOptions=} limit + * @param {SearchOptions=} options + * @returns {Array|Resolver|Promise} + */ + +Index.prototype.search = function(query, limit, options){ + + if(!options){ + if(!limit && is_object(query)){ + options = /** @type {!SearchOptions} */ (query); + query = ""; + } + else if(is_object(limit)){ + options = /** @type {!SearchOptions} */ (limit); + limit = 0; + } + } + + let result = []; + let length; + let context, suggest, offset = 0, resolve, enrich, tag; + + if(options){ + query = options.query || query; + limit = options.limit || limit; + offset = options.offset || 0; + context = options.context; + suggest = options.suggest; + resolve = (global_resolve && options.resolve !== false); + resolve || (global_resolve = 0); + enrich = resolve && options.enrich; + tag = this.db && options.tag; + } + else { + resolve = this.resolve || global_resolve; + } + + // todo: term deduplication during encoding when context is disabled + + // do not force a string as input + // https://github.com/nextapps-de/flexsearch/issues/432 + query = /** @type {Array} */ (this.encoder.encode(query)); + length = query.length; + limit || !resolve || (limit = 100); + + // fast path single term + if(length === 1){ + return single_term_query.call( + this, + query[0], // term + "", // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + // TODO: dedupe terms within encoder? + // TODO: deduplication will break the context chain + + context = this.depth && context !== false; + + // fast path single context + if(length === 2 && context && !suggest){ + return single_term_query.call( + this, + query[0], // term + query[1], // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + let maxlength = 0; + let minlength = 0; + + if(length > 1){ + + // term deduplication will break the context chain + // todo add context to dupe check + const dupes = create_object(); + const query_new = []; + + // if(context){ + // keyword = query[0]; + // dupes[keyword] = 1; + // query_new.push(keyword); + // maxlength = minlength = keyword.length; + // i = 1; + // } + + for(let i = 0, term; i < length; i++){ + + term = query[i]; + + if(term && !dupes[term]){ + + // todo add keyword check + // this fast path can't apply to persistent indexes + if(!suggest && !(this.db) && !this.get_array(term/*, keyword*/)){ + + // fast path "not found" + return resolve + ? result + : new Resolver(result); + } + else { + + query_new.push(term); + dupes[term] = 1; + } + + const term_length = term.length; + maxlength = Math.max(maxlength, term_length); + minlength = minlength ? Math.min(minlength, term_length) : term_length; + } + // else if(term && (!this.depth || context === false)){ + // query_new.push(term); + // } + } + + query = query_new; + length = query.length; + } + + // the term length could be changed after deduplication + + if(!length){ + return resolve + ? result + : new Resolver(result); + } + + let index = 0, keyword; + + // fast path single term + if(length === 1){ + return single_term_query.call( + this, + query[0], // term + "", // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + // fast path single context + if(length === 2 && context && !suggest){ + return single_term_query.call( + this, + query[0], // term + query[1], // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + if(length > 1){ + if(context){ + // start with context right away + keyword = query[0]; + index = 1; + } + // todo + else if(maxlength > 9 && (maxlength / minlength) > 3){ + // sorting terms will break the context chain + // bigger terms has less occurrence + // this might also reduce the intersection task + // todo check intersection order + query.sort(sort_by_length_down); + } + } + + if(this.db){ + + if(this.db.search){ + // when the configuration is not supported it returns false + const result = this.db.search(this, query, limit, offset, suggest, resolve, enrich, tag); + if(result !== false) return result; + } + + const self = this; + return (async function(){ + + for(let arr, term; index < length; index++){ + + term = query[index]; + + if(keyword){ + + arr = await self.get_array(term, keyword); + arr = add_result( + arr, + result, + suggest, + self.resolution_ctx, + /** @type {!number} */ (limit), + offset, + length === 2 + /*, term, keyword*/ + ); + + // the context is a moving window where the keyword is going forward like a cursor + // 1. when suggestion enabled just forward keyword if term was found + // 2. as long as the result is empty forward the pointer also + if(!suggest || (arr !== false) || !result.length){ + keyword = term; + } + } + else { + + arr = await self.get_array(term); + arr = add_result( + arr, + result, + suggest, + self.resolution, + /** @type {!number} */ (limit), + offset, + length === 1 + /*, term*/ + ); + } + + // limit reached + if(arr){ + return arr; + } + + // apply suggestions on last loop + if(suggest && (index === length - 1)){ + let length = result.length; + if(!length){ + // fallback to non-contextual search when no result was found + if(keyword){ + keyword = ""; + index = -1; + continue; + } + return result; + } + else if(length === 1){ + return resolve + ? resolve_default(result[0], /** @type {number} */ (limit), offset) + : new Resolver(result[0]); + } + } + } + + return resolve + ? intersect(result, /** @type {number} */ (limit), offset, suggest) + : new Resolver(result[0]) + }()); + } + + for(let arr, term; index < length; index++){ + + term = query[index]; + + if(keyword){ + + arr = this.get_array(term, keyword); + arr = /*this.*/add_result( + arr, + result, + suggest, + this.resolution_ctx, + /** @type {!number} */ (limit), + offset, + length === 2 + /*, term, keyword*/ + ); + + // 1. when suggestion enabled just forward keyword if term was found + // 2. as long as the result is empty forward the pointer also + if(!suggest || (arr !== false) || !result.length){ + keyword = term; + } + } + else { + + arr = this.get_array(term); + arr = /*this.*/add_result( + arr, + result, + suggest, + this.resolution, + /** @type {!number} */ (limit), + offset, + length === 1 + /*, term*/ + ); + } + + // limit reached + if(arr){ + return /** @type {Array} */ (arr); + } + + // apply suggestions on last loop + if(suggest && (index === length - 1)){ + const length = result.length; + if(!length){ + // fallback to non-contextual search when no result was found + if(keyword){ + keyword = ""; + index = -1; + continue; + } + return result; + } + else if(length === 1){ + return resolve + ? resolve_default(result[0], limit, offset) + : new Resolver(result[0]); + } + } + } + + return resolve + ? intersect(result, limit, offset, suggest) + : new Resolver(result[0]); +}; + +/** + * @param term + * @param keyword + * @param limit + * @param offset + * @param resolve + * @param enrich + * @param tag + * @this Index + * @return {Array|Resolver} + */ + +function single_term_query(term, keyword, limit, offset, resolve, enrich, tag){ + + const result = this.get_array(term, keyword, limit, offset, resolve, enrich, tag); + + if(this.db){ + return result.then(function(result){ + if(resolve) return result; + return result && result.length + ? (resolve ? resolve_default(result, limit, offset): new Resolver(result)) + : resolve ? [] : new Resolver([]); + }); + } + + return result && result.length + ? (resolve ? resolve_default(result, limit, offset) : new Resolver(result)) + : resolve ? [] : new Resolver([]); +} + +/** + * Returns a 1-dimensional finalized array when the result is done (fast path return), + * returns false when suggestions is enabled and no result was found, + * or returns nothing when a set was pushed successfully to the results + * + * @private + * @param {Array} arr + * @param {Array} result + * @param {Array} suggest + * @param {number} resolution + * @param {number} limit + * @param {number} offset + * @param {boolean} single_term + * @return {Array|boolean|undefined} + */ + +function add_result(arr, result, suggest, resolution, limit, offset, single_term/*, term, keyword*/){ + + let word_arr = []; + //let arr;// = keyword ? this.ctx : this.map; + //arr = this.get_array(term, keyword); + + if(arr){ + + //const resolution = Math.min(arr.length, keyword ? this.resolution_ctx : this.resolution); + // apply reduced resolution for queries + resolution = Math.min(arr.length, resolution); + + for(let x = 0, size = 0, tmp; x < resolution; x++){ + if((tmp = arr[x])){ + + if(offset){ + // apply offset right here on single terms + if(tmp && single_term){ + if(tmp.length <= offset){ + offset -= tmp.length; + tmp = null; + } + else { + tmp = tmp.slice(offset); + offset = 0; + } + } + } + + if(tmp){ + + // keep score (sparse array): + word_arr[x] = tmp; + // simplified score order: + //word_arr.push(tmp); + + if(single_term){ + size += tmp.length; + if(size >= limit){ + // fast path: + // a single term does not need to pre-collect results + break; + } + } + } + } + } + + if(word_arr.length){ + if(single_term){ + // fast path optimization + // offset was already applied at this point + // return an array will stop the query process immediately + return resolve_default(word_arr, limit, 0); + } + + result.push(word_arr); + // return nothing will continue the query + return; + } + } + + // 1. return an empty array will stop the loop + // 2. return a false value to prevent stop when using suggestions + return !suggest && word_arr; +} + +Index.prototype.get_array = function(term, keyword, limit, offset, resolve, enrich, tag){ + + let arr, swap; + + if(keyword){ + swap = this.bidirectional && (term > keyword); + } + + if(this.compress){ + term = default_compress(term); + keyword && (keyword = default_compress(keyword)); + } + + if(this.db){ + return keyword + ? this.db.get( + swap ? keyword : term, // term + swap ? term : keyword, // ctx + limit, + offset, + resolve, + enrich, + tag + ) + : this.db.get( + term, + "", // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + if(keyword){ + // the frequency of the starting letter is slightly less + // on the last half of the alphabet (m-z) in almost every latin language, + // so we sort downwards (https://en.wikipedia.org/wiki/Letter_frequency) + arr = this.ctx.get(swap ? term : keyword); + arr = arr && arr.get(swap ? keyword : term); + } + else { + arr = this.map.get(term); + } + + return arr; +}; + +// COMPILER BLOCK --> + +/** + * @param {boolean=} _skip_deletion + */ + +Index.prototype.remove = function(id, _skip_deletion){ + + const refs = this.reg.size && ( + this.fastupdate + ? this.reg.get(id) + : this.reg.has(id) + ); + + if(refs){ + + if(this.fastupdate){ + + // fast updates did not fully cleanup the key entries + + for(let i = 0, tmp; i < refs.length; i++){ + if((tmp = refs[i])){ + // todo check + //if(tmp.length < 1) throw new Error("invalid length"); + //if(tmp.indexOf(id) < 0) throw new Error("invalid id"); + if(tmp.length < 2){ + tmp.pop(); + } + else { + const index = tmp.indexOf(id); + index === refs.length - 1 + ? tmp.pop() + : tmp.splice(index, 1); + } + } + } + + // todo variation which cleans up, requires to push [ctx, key] instead of arr to the index.reg + // for(let i = 0, arr, term, keyword; i < refs.length; i++){ + // arr = refs[i]; + // if(typeof arr === "string"){ + // arr = this.map.get(term = arr); + // } + // else{ + // arr = this.ctx.get(keyword = arr[0]); + // arr && (arr = arr.get(arr[1])); + // } + // let counter = 0, found; + // if(arr && arr.length){ + // for(let j = 0, tmp; j < arr.length; j++){ + // if((tmp = arr[j])){ + // if(!found && tmp.length){ + // const index = tmp.indexOf(id); + // if(index >= 0){ + // tmp.splice(index, 1); + // // the index [ctx, key]:[res, id] is unique + // found = 1; + // } + // } + // if(tmp.length){ + // counter++; + // if(found){ + // break; + // } + // } + // else{ + // delete arr[j]; + // } + // } + // } + // } + // if(!counter){ + // keyword + // ? this.ctx.delete(keyword) + // : this.map.delete(term); + // } + // } + } + else { + + remove_index(this.map, id/*, this.resolution*/); + this.depth && + remove_index(this.ctx, id/*, this.resolution_ctx*/); + } + + _skip_deletion || this.reg.delete(id); + } + + if(this.db){ + this.commit_task.push({ "del": id }); + this.commit_auto && autoCommit(this); + //return this.db.remove(id); + } + + // the cache could be used outside the InMemory store + if(this.cache){ + this.cache.remove(id); + } + + return this; +}; + +/** + * @param map + * @param id + * @return {number} + */ + +function remove_index(map, id){ + + // a check counter of filled resolution slots + // to prevent removing the field + let count = 0; + + if(is_array(map)){ + for(let x = 0, arr, index; x < map.length; x++){ + if((arr = map[x]) && arr.length){ + index = arr.indexOf(id); + if(index >= 0){ + if(arr.length > 1){ + arr.splice(index, 1); + count++; + } + else { + // remove resolution slot + delete map[x]; + } + // the index key:[res, id] is unique + break; + } + else { + count++; + } + } + } + } + else for(let item of map){ + const key = item[0]; + const value = item[1]; + const tmp = remove_index(value, id); + tmp ? count += tmp + : map.delete(key); + } + + return count; +} + +/**! + * FlexSearch.js + * Author and Copyright: Thomas Wilkerling + * Licence: Apache-2.0 + * Hosted by Nextapps GmbH + * https://github.com/nextapps-de/flexsearch + */ + + +/** + * @constructor + * @param {IndexOptions|string=} options Options or preset as string + * @param {Map|Set|KeystoreSet|KeystoreMap=} _register + */ + +function Index(options$1, _register){ + + if(!this){ + return new Index(options$1); + } + + tick("Index.create"); + + options$1 = options$1 + ? apply_preset(options$1) + : {}; + + /** @type ContextOptions */ + const context = options$1.context || {}; + const encoder = options$1.encode || options$1.encoder || ( + options + ); + /** @type Encoder */ + this.encoder = encoder.encode + ? encoder + : typeof encoder === "object" + ? (new Encoder(encoder) + + ) + : { encode: encoder }; + + { + this.compress = options$1.compress || options$1.compression || false; + } + + let tmp; + this.resolution = options$1.resolution || 9; + this.tokenize = tmp = options$1.tokenize || "strict"; + this.depth = (tmp === "strict" && context.depth) || 0; + this.bidirectional = context.bidirectional !== false; + this.fastupdate = !!options$1.fastupdate; + this.score = options$1.score || null; + + tmp = (options$1.keystore || 0); + tmp && (this.keystore = tmp); + + this.map = tmp && SUPPORT_KEYSTORE ? new KeystoreMap(tmp) : new Map(); + this.ctx = tmp && SUPPORT_KEYSTORE ? new KeystoreMap(tmp) : new Map(); + this.reg = _register || ( + this.fastupdate + ? (tmp && SUPPORT_KEYSTORE ? new KeystoreMap(tmp) : new Map()) + : (tmp && SUPPORT_KEYSTORE ? new KeystoreSet(tmp) : new Set()) + ); + this.resolution_ctx = context.resolution || 1; + this.rtl = (encoder.rtl) || options$1.rtl || false; + + { + this.cache = (tmp = options$1.cache || null) && new CacheClass(tmp); + } + + { + this.resolve = options$1.resolve !== false; + } + + { + if((tmp = options$1.db)){ + this.db = tmp.mount(this); + } + this.commit_auto = options$1.commit !== false; + this.commit_task = []; + this.commit_timer = null; + } +} + +{ + Index.prototype.mount = function(db){ + if(this.commit_timer){ + clearTimeout(this.commit_timer); + this.commit_timer = null; + } + return db.mount(this); + }; + Index.prototype.commit = function(replace, append){ + if(this.commit_timer){ + clearTimeout(this.commit_timer); + this.commit_timer = null; + } + return this.db.commit(this, replace, append); + }; +} + +// if(SUPPORT_RESOLVER){ +// Index.prototype.resolve = function(params){ +// return new Resolver(params); +// }; +// } + +/** + * @param {!Index} self + * @param {boolean=} replace + * @param {boolean=} append + */ + +function autoCommit(self, replace, append){ + if(!self.commit_timer){ + self.commit_timer = setTimeout(function(){ + self.commit_timer = null; + self.db.commit(self, replace, append); + }, 0); + } +} + +Index.prototype.clear = function(){ + + //this.map = new Map(); + //this.ctx = new Map(); + //this.reg = this.fastupdate ? new Map() : new Set(); + this.map.clear(); + this.ctx.clear(); + this.reg.clear(); + + { + this.cache && + this.cache.clear(); + } + + if(this.db){ + this.commit_timer && clearTimeout(this.commit_timer); + this.commit_timer = null; + this.commit_task = [{ "clear": true }]; + //return this.db.clear(); + } + + return this; +}; + +//Index.prototype.pipeline = pipeline; + +/** + * @param {!number|string} id + * @param {!string} content + */ + +Index.prototype.append = function(id, content){ + return this.add(id, content, true); +}; + +Index.prototype.contain = function(id){ + + if(this.db){ + return this.db.has(id); + } + + return this.reg.has(id); +}; + +Index.prototype.update = function(id, content){ + + // todo check the async part + if(this.async /*|| (SUPPORT_PERSISTENT && this.db)*/){ + const self = this; + const res = this.remove(id); + return res.then ? res.then( + () => self.add(id, content) + ) : this.add(id, content); + } + + return this.remove(id).add(id, content); +}; + +/** + * @param map + * @return {number} + */ + +function cleanup_index(map){ + + let count = 0; + + if(is_array(map)){ + for(let i = 0, arr; i < map.length; i++){ + (arr = map[i]) && + (count += arr.length); + } + } + else for(const item of map){ + const key = item[0]; + const value = item[1]; + const tmp = cleanup_index(value); + tmp ? count += tmp + : map.delete(key); + } + + return count; +} + +Index.prototype.cleanup = function(){ + + if(!this.fastupdate){ + console.info("Cleanup the index isn't required when not using \"fastupdate\"."); + return this; + } + + cleanup_index(this.map); + this.depth && + cleanup_index(this.ctx); + + return this; +}; + +{ + + Index.prototype.searchCache = searchCache; +} + +{ + + Index.prototype.export = exportIndex; + Index.prototype.import = importIndex; +} + +{ + + apply_async(Index.prototype); +} + +async function handler(data) { + + data = data["data"]; + + /** @type Index */ + const index = self["_index"]; + const args = data["args"]; + const task = data["task"]; + + switch(task){ + + case "init": + + /** @type IndexOptions */ + let options = data["options"] || {}; + let filepath = options.config; + if(filepath){ + options = filepath; + // will be replaced after build with the line below because + // there is an issue with closure compiler dynamic import + //options = await import(filepath); + } + + // deprecated: + // const encode = options.encode; + // if(encode && (encode.indexOf("function") === 0)){ + // options.encode = Function("return " + encode)(); + // } + + const factory = data["factory"]; + + if(factory){ + + // export the FlexSearch global payload to "self" + Function("return " + factory)()(self); + + /** @type Index */ + self["_index"] = new self["FlexSearch"]["Index"](options); + + // destroy the exported payload + delete self["FlexSearch"]; + } + else { + + self["_index"] = new Index(options); + } + + postMessage({ "id": data["id"] }); + break; + + default: + + const id = data["id"]; + const message = index[task].apply(index, args); + postMessage(task === "search" ? { "id": id, "msg": message } : { "id": id }); + } +} + +//import { promise as Promise } from "../polyfill.js"; + +let pid = 0; + +/** + * @param {IndexOptions=} options + * @constructor + */ + +function WorkerIndex(options){ + + if(!this) { + return new WorkerIndex(options); + } + + if(options); + else { + options = {}; + } + + // the factory is the outer wrapper from the build + // we use "self" as a trap for node.js + + let factory = (self||window)["_factory"]; + if(factory){ + factory = factory.toString(); + } + + const is_node_js = typeof window === "undefined" && self["exports"]; + const _self = this; + + this.worker = create(factory, is_node_js, options.worker); + this.resolver = create_object(); + + if(!this.worker){ + + return; + } + + function onmessage(msg){ + msg = msg["data"] || msg; + const id = msg["id"]; + const res = id && _self.resolver[id]; + if(res){ + res(msg["msg"]); + delete _self.resolver[id]; + } + } + + is_node_js + ? this.worker["on"]("message", onmessage) + : this.worker.onmessage = onmessage; + + if(options["config"]){ + + // when extern configuration needs to be loaded + // it needs to return a promise to await for + return new Promise(function(resolve){ + _self.resolver[++pid] = function(){ + resolve(_self); + }; + _self.worker.postMessage({ + "id": pid, + "task": "init", + "factory": factory, + "options": options + }); + }); + } + + this.worker.postMessage({ + "task": "init", + "factory": factory, + "options": options + }); +} + +register("add"); +register("append"); +register("search"); +register("update"); +register("remove"); + +function register(key){ + + WorkerIndex.prototype[key] = + WorkerIndex.prototype[key + "Async"] = function(){ + + const self = this; + const args = [].slice.call(arguments); + const arg = args[args.length - 1]; + let callback; + + if(is_function(arg)){ + callback = arg; + args.splice(args.length - 1, 1); + } + + const promise = new Promise(function(resolve){ + //setTimeout(function(){ + self.resolver[++pid] = resolve; + self.worker.postMessage({ + "task": key, + "id": pid, + "args": args + }); + //}); + }); + + if(callback){ + promise.then(callback); + return this; + } + else { + + return promise; + } + }; +} + +function create(factory, is_node_js, worker_path){ + + let worker; + + worker = is_node_js ? + // This eval will be removed when compiling, it isn't there in final build + (0, eval)('new (require("worker_threads")["Worker"])(__dirname + "/node/node.js")') + //eval('new (require("worker_threads")["Worker"])(__dirname + "/node/node.js")') + :( + factory ? + new window.Worker(URL.createObjectURL( + new Blob( + ["onmessage=" + handler.toString()], + { "type": "text/javascript" } + ) + )) + : + new window.Worker(is_string(worker_path) ? worker_path : "worker/worker.js", { type: "module" }) + ); + + return worker; +} + +// COMPILER BLOCK --> + +/** + * + * @param id + * @param content + * @param {boolean=} _append + * @returns {Document|Promise} + */ + +Document.prototype.add = function(id, content, _append){ + + if(is_object(id)){ + + content = id; + id = parse_simple(content, this.key); + } + + if(content && (id || (id === 0))){ + + if(!_append && this.reg.has(id)){ + return this.update(id, content); + } + + // this.field does not include db tag indexes + for(let i = 0, tree; i < this.field.length; i++){ + + tree = this.tree[i]; + + const index = this.index.get(this.field[i]); + if(typeof tree === "function"){ + const tmp = tree(content); + if(tmp){ + index.add(id, tmp, /* append: */ false, /* skip update: */ true); + } + } + else { + const filter = tree._filter; + if(filter && !filter(content)){ + continue; + } + if(tree.constructor === String){ + tree = ["" + tree]; + } + else if(is_string(tree)){ + tree = [tree]; + } + add_index(content, tree, this.marker, 0, index, id, tree[0], _append); + } + } + + if(this.tag){ + + //console.log(this.tag, this.tagtree) + + for(let x = 0; x < this.tagtree.length; x++){ + + let tree = this.tagtree[x]; + let field = this.tagfield[x]; + let ref = this.tag.get(field); + let dupes = create_object(); + let tags; + + if(typeof tree === "function"){ + tags = tree(content); + if(!tags) continue; + } + else { + const filter = tree._filter; + if(filter && !filter(content)){ + continue; + } + if(tree.constructor === String){ + tree = "" + tree; + } + tags = parse_simple(content, tree); + } + + if(!ref || !tags){ + ref || (console.warn("Tag '" + field + "' was not found")); + continue; + } + + if(is_string(tags)){ + tags = [tags]; + } + + for(let i = 0, tag, arr; i < tags.length; i++){ + + tag = tags[i]; + //console.log(this.tag, tag, key, field) + + if(!dupes[tag]){ + dupes[tag] = 1; + + let tmp; + tmp = ref.get(tag); + tmp ? arr = tmp : ref.set(tag, arr = []); + + if(!_append || ! /** @type {!Array|KeystoreArray} */(arr).includes(id)){ + + // auto-upgrade to keystore array if max size exceeded + { + if(arr.length === 2**31-1 /*|| !(arr instanceof KeystoreArray)*/){ + const keystore = new KeystoreArray(arr); + if(this.fastupdate){ + for(let value of this.reg.values()){ + if(value.includes(arr)){ + value[value.indexOf(arr)] = keystore; + } + } + } + ref.set(tag, arr = keystore); + } + } + + arr.push(id); + + // add a reference to the register for fast updates + if(this.fastupdate){ + const tmp = this.reg.get(id); + tmp ? tmp.push(arr) + : this.reg.set(id, [arr]); + } + } + } + } + } + } + + if(this.store && (!_append || !this.store.has(id))){ + + let payload; + + if(this.storetree){ + + payload = create_object(); + + for(let i = 0, tree; i < this.storetree.length; i++){ + tree = this.storetree[i]; + + const filter = tree._filter; + if(filter && !filter(content)){ + continue; + } + let custom; + if(typeof tree === "function"){ + custom = tree(content); + if(!custom) continue; + tree = [tree._field]; + } + else if(is_string(tree) || tree.constructor === String){ + payload[tree] = content[tree]; + continue; + } + + store_value(content, payload, tree, 0, tree[0], custom); + } + } + + this.store.set(id, payload || content); + } + } + + return this; +}; + +// TODO support generic function created from string when tree depth > 1 + +/** + * @param obj + * @param store + * @param tree + * @param pos + * @param key + * @param {*=} custom + */ + +function store_value(obj, store, tree, pos, key, custom){ + + obj = obj[key]; + + // reached target field + if(pos === (tree.length - 1)){ + + // store target value + store[key] = custom || obj; + } + else if(obj){ + + if(is_array(obj)){ + + store = store[key] = new Array(obj.length); + + for(let i = 0; i < obj.length; i++){ + // do not increase pos (an array is not a field) + store_value(obj, store, tree, pos, i); + } + } + else { + + store = store[key] || (store[key] = create_object()); + key = tree[++pos]; + store_value(obj, store, tree, pos, key); + } + } +} + +function add_index(obj, tree, marker, pos, index, id, key, _append){ + + if((obj = obj[key])){ + + // reached target field + if(pos === (tree.length - 1)){ + + // handle target value + if(is_array(obj)){ + + // append array contents so each entry gets a new scoring context + if(marker[pos]){ + for(let i = 0; i < obj.length; i++){ + index.add(id, obj[i], /* append: */ true, /* skip update: */ true); + } + return; + } + + // or join array contents and use one scoring context + obj = obj.join(" "); + } + + index.add(id, obj, _append, /* skip_update: */ true); + } + else { + + if(is_array(obj)){ + for(let i = 0; i < obj.length; i++){ + // do not increase index, an array is not a field + add_index(obj, tree, marker, pos, index, id, i, _append); + } + } + else { + key = tree[++pos]; + add_index(obj, tree, marker, pos, index, id, key, _append); + } + } + } + else { + if(index.db){ + index.remove(id); + } + } +} + +// COMPILER BLOCK --> + +/** + * @param {!string|DocumentSearchOptions} query + * @param {number|DocumentSearchOptions=} limit + * @param {DocumentSearchOptions=} options + * @param {Array=} _resolve For internal use only. + * @returns {Promise|Array} + */ + +Document.prototype.search = function(query, limit, options, _resolve){ + + if(!options){ + if(!limit && is_object(query)){ + options = /** @type {DocumentSearchOptions} */ (query); + query = ""; + } + else if(is_object(limit)){ + options = /** @type {DocumentSearchOptions} */ (limit); + limit = 0; + } + } + + let result = [], result_field = []; + let pluck, enrich, merge, suggest; + let field, tag, offset, count = 0; + + if(options){ + + // todo remove support? + if(is_array(options)){ + field = options; + options = null; + } + else { + + query = options.query || query; + pluck = options.pluck; + merge = options.merge; + field = pluck || options.field || options.index; + tag = this.tag && options.tag; + enrich = this.store && options.enrich; + suggest = options.suggest; + limit = options.limit || limit; + offset = options.offset || 0; + limit || (limit = 100); + + if(tag && (!this.db || !_resolve)){ + + if(tag.constructor !== Array){ + tag = [tag]; + } + + let pairs = []; + + for(let i = 0, field; i < tag.length; i++){ + field = tag[i]; + if(is_string(field)){ + throw new Error("A tag option can't be a string, instead it needs a { field: tag } format."); + } + // default array notation + if(field.field && field.tag){ + const value = field.tag; + if(value.constructor === Array){ + for(let k = 0; k < value.length; k++){ + pairs.push(field.field, value[k]); + } + } + else { + pairs.push(field.field, value); + } + } + // shorter object notation + else { + const keys = Object.keys(field); + for(let j = 0, key, value; j < keys.length; j++){ + key = keys[j]; + value = field[key]; + if(value.constructor === Array){ + for(let k = 0; k < value.length; k++){ + pairs.push(key, value[k]); + } + } + else { + pairs.push(key, value); + } + } + } + } + + if(!pairs.length){ + throw new Error("Your tag definition within the search options is probably wrong. No valid tags found."); + } + + // tag used as pairs from this point + tag = pairs; + + // when tags is used and no query was set, + // then just return the tag indexes + if(!query){ + + let promises = []; + if(pairs.length) for(let j = 0; j < pairs.length; j+=2){ + let ids; + if(this.db){ + const index = this.index.get(pairs[j]); + if(!index){ + { + console.warn("Tag '" + pairs[j] + ":" + pairs[j + 1] + "' will be skipped because there is no field '" + pairs[j] + "'."); + } + continue; + } + promises.push(ids = index.db.tag(pairs[j + 1], limit, offset, enrich)); + } + else { + ids = get_tag.call(this, pairs[j], pairs[j + 1], limit, offset, enrich); + } + result.push({ + "field": pairs[j], + "tag": pairs[j + 1], + "result": ids + }); + } + + if(promises.length){ + return Promise.all(promises).then(function(promises){ + for(let j = 0; j < promises.length; j++){ + result[j].result = promises[j]; + } + return result; + }); + } + + return result; + } + } + + // extend to multi field search by default + if(is_string(field)){ + field = [field]; + } + } + } + + field || (field = this.field); + let promises = !_resolve && (this.worker || this.async) && []; + let db_tag_search; + + // multi field search + // field could be a custom set of selected fields by this query + // db tag indexes are also included in this field list + for(let i = 0, res, key, len; i < field.length; i++){ + + key = field[i]; + + if(this.db && this.tag){ + // tree is missing when it is a tag-only index (db) + if(!this.tree[i]){ + continue; + } + } + + let field_options; + + if(!is_string(key)){ + field_options = key; + key = field_options.field; + query = field_options.query || query; + limit = field_options.limit || limit; + //offset = field_options.offset || offset; + suggest = (field_options.suggest || suggest); + //enrich = SUPPORT_STORE && this.store && (field_options.enrich || enrich); + } + + if(_resolve){ + res = _resolve[i]; + } + else { + let opt = field_options || options; + let index = this.index.get(key); + + if(tag){ + if(this.db){ + opt.tag = tag; + db_tag_search = index.db.support_tag_search; + opt.field = field; + } + if(!db_tag_search){ + opt.enrich = false; + } + } + if(promises){ + promises[i] = index.searchAsync(query, limit, opt); + // restore enrich state + opt && enrich && (opt.enrich = enrich); + // just collect and continue + continue; + } + else { + res = index.search(query, limit, opt); + // restore enrich state + opt && enrich && (opt.enrich = enrich); + } + } + + len = res && res.length; + + // todo when no term was matched but tag was retrieved extend suggestion to tags + // every field has to intersect against all selected tag fields + if(tag && len){ + + const arr = []; + let count = 0; + + // tags are only applied in resolve phase when it's a db + if(this.db && _resolve){ + if(!db_tag_search){ + + // retrieve tag results assigned to it's field + for(let y = field.length; y < _resolve.length; y++){ + + let ids = _resolve[y]; + let len = ids && ids.length; + + if(len){ + count++; + arr.push(ids); + } + else if(!suggest){ + // no tags found + return result; + } + } + } + } + else { + + // tag[] are pairs at this line + for(let y = 0, ids, len; y < tag.length; y+=2){ + ids = this.tag.get(tag[y]); + + if(!ids){ + { + console.warn("Tag '" + tag[y] + ":" + tag[y + 1] + "' will be skipped because there is no field '" + tag[y] + "'."); + } + if(suggest){ + continue; + } + else { + return result; + } + } + + ids = ids && ids.get(tag[y + 1]); + len = ids && ids.length; + + if(len){ + count++; + arr.push(ids); + } + else if(!suggest){ + // no tags found + return result; + } + } + } + + if(count){ + res = intersect_union(res, arr); // intersect(arr, limit, offset) + len = res.length; + if(!len && !suggest){ + // nothing matched + return result; + } + // move counter back by 1 + count--; + } + } + + if(len){ + result_field[count] = key; + result.push(res); + count++; + } + else if(field.length === 1){ + // fast path: nothing matched + return result; + } + } + + if(promises){ + if(this.db){ + // todo when a tag index is never a search index this could be extracted + // push tag promises to the end + if(tag && tag.length && !db_tag_search){ + for(let y = 0; y < tag.length; y += 2){ + // it needs to retrieve data from tag pairs + const index = this.index.get(tag[y]); + if(!index){ + { + console.warn("Tag '" + tag[y] + ":" + tag[y + 1] + "' was not found because there is no field '" + tag[y] + "'."); + } + if(suggest){ + continue; + } + else { + return result; + } + } + promises.push(index.db.tag(tag[y + 1], limit, offset, /* enrich */ false)); + } + } + } + + const self = this; + + // TODO unroll this recursion + return Promise.all(promises).then(function(result){ + return result.length + ? self.search(query, limit, options, /* resolve: */ result) + : result; + }); + } + + if(!count){ + return result; + } + if(pluck && (!enrich || !this.store)){ + return result[0]; + } + + promises = []; + + for(let i = 0, res; i < result_field.length; i++){ + + res = result[i]; + + if(enrich && res.length && !res[0].doc){ + if(!this.db){ + if(res.length){ + res = apply_enrich.call(this, res); + } + } + else { + promises.push(res = this.index.get(this.field[0]).db.enrich(res)); + } + } + + if(pluck){ + return res; + } + + result[i] = { + "field": result_field[i], + "result": res + }; + } + + if(enrich && SUPPORT_PERSISTENT && this.db && promises.length){ + return Promise.all(promises).then(function(promises){ + for(let j = 0; j < promises.length; j++){ + result[j].result = promises[j]; + } + return merge + ? merge_fields(result, limit) + : result; + }); + } + + return merge + ? merge_fields(result, limit) + : result; +}; + +// todo support Resolver +// todo when searching through multiple fields each term should +// be found at least by one field to get a valid match without +// using suggestion explicitly + +function merge_fields(fields, limit, offset){ + const final = []; + const set = create_object(); + for(let i = 0, field, res; i < fields.length; i++){ + field = fields[i]; + res = field.result; + for(let j = 0, id, entry, tmp; j < res.length; j++){ + entry = res[j]; + id = entry.id; + tmp = set[id]; + if(!tmp){ + // offset was already applied on field indexes + // if(offset){ + // offset--; + // continue; + // } + // apply limit from last round, because just fields could + // be pushed without adding new results + if(final.length === limit){ + return final; + } + entry.field = set[id] = [field.field]; + final.push(entry); + } + else { + tmp.push(field.field); + } + } + } + return final; +} + +/** + * @this Document + */ + +function get_tag(tag, key, limit, offset, enrich){ + let res = this.tag.get(tag); + if(!res){ + console.warn("Tag '" + tag + "' was not found"); + return []; + } + res = res && res.get(key); + let len = res && (res.length - offset); + + if(len && (len > 0)){ + if((len > limit) || offset){ + res = res.slice(offset, offset + limit); + } + if(enrich){ + res = apply_enrich.call(this, res); + } + return res; + } +} + +/** + * @this Document + */ + +function apply_enrich(res){ + + const arr = new Array(res.length); + + for(let x = 0, id; x < res.length; x++){ + id = res[x]; + arr[x] = { + "id": id, + "doc": this.store.get(id) + }; + } + + return arr; +} + +/**! + * FlexSearch.js + * Author and Copyright: Thomas Wilkerling + * Licence: Apache-2.0 + * Hosted by Nextapps GmbH + * https://github.com/nextapps-de/flexsearch + */ + + +/** + * @constructor + * @param {!DocumentOptions} options + */ + +function Document(options){ + + if(!this) { + return new Document(options); + } + + /** @type DocumentDescriptor */ + const document = options.document || options.doc || options; + let tmp, keystore; + + this.tree = []; + this.field = []; + this.marker = []; + this.key = ((tmp = document.key || document.id) && parse_tree(tmp, this.marker)) || "id"; + + keystore = (options.keystore || 0); + keystore && (this.keystore = keystore); + this.fastupdate = !!options.fastupdate; + this.reg = this.fastupdate + ? (keystore && SUPPORT_KEYSTORE ? new KeystoreMap(keystore) : new Map()) + : (keystore && SUPPORT_KEYSTORE ? new KeystoreSet(keystore) : new Set()); + + { + // todo support custom filter function + this.storetree = (tmp = document.store || null) && tmp !== true && []; + this.store = tmp && ( + keystore && SUPPORT_KEYSTORE + ? new KeystoreMap(keystore) + : new Map() + ); + } + + { + this.cache = (tmp = options.cache || null) && new CacheClass(tmp); + // do not apply cache again for the indexes since .searchCache() + // is just a wrapper over .search() + options.cache = false; + } + + { + this.worker = options.worker; + } + + { + // this switch is used by recall of promise callbacks + this.async = false; + } + + /** @export */ + this.index = parse_descriptor.call(this, options, document); + + { + this.tag = null; + // TODO case-insensitive tags? + if((tmp = document.tag)){ + if(typeof tmp === "string"){ + tmp = [tmp]; + } + if(tmp.length){ + this.tag = new Map(); + this.tagtree = []; + this.tagfield = []; + for(let i = 0, params, field; i < tmp.length; i++){ + params = tmp[i]; + field = params.field || params; + if(!field){ + throw new Error("The tag field from the document descriptor is undefined."); + } + if(params.custom){ + this.tagtree[i] = params.custom; + } + else { + this.tagtree[i] = parse_tree(field, this.marker); + if(params.filter){ + if(typeof this.tagtree[i] === "string"){ + // it needs an object to put a property to it + this.tagtree[i] = new String(this.tagtree[i]); + } + this.tagtree[i]._filter = params.filter; + } + } + // the tag fields needs to be hold by indices + this.tagfield[i] = field; + this.tag.set(field, new Map()); + } + } + } + } + + { + options.db && this.mount(options.db); + } +} + +{ + + Document.prototype.mount = function(db){ + + let fields = this.field; + + if(this.tag){ + // tag indexes are referenced by field + // move tags to their field indexes respectively + for(let i = 0, field; i < this.tagfield.length; i++){ + field = this.tagfield[i]; + let index = this.index.get(field); + if(!index){ + // create raw index when not exists + this.index.set(field, index = new Index({}, this.reg)); + // copy and push to the field selection + if(fields === this.field){ + fields = fields.slice(0); + } + // tag indexes also needs to be upgraded to db instances + fields.push(field); + } + // assign reference + index.tag = this.tag.get(field); + } + } + + const promises = []; + const config = { + db: db.db, + type: db.type, + fastupdate: db.fastupdate + }; + + // upgrade all indexes to db instances + for(let i = 0, index, field; i < fields.length; i++){ + config.field = field = fields[i]; + index = this.index.get(field); + const dbi = new db.constructor(db.id, config); + // take over the storage id + dbi.id = db.id; + promises[i] = dbi.mount(index); + // add an identification property + index.document = true; + if(i){ + // the register has to export just one time + // also it's needed by the index for ID contain check + index.bypass = true; + } + else { + // the datastore has to export one time + index.store = this.store; + } + } + + this.async = true; + this.db = true; + return Promise.all(promises); + }; + + Document.prototype.commit = async function(replace, append){ + // parallel: + const promises = []; + for(const index of this.index.values()){ + promises.push(index.db.commit(index, replace, append)); + } + await Promise.all(promises); + this.reg.clear(); + // queued: + // for(const index of this.index.values()){ + // await index.db.commit(index, replace, append); + // } + // this.reg.clear(); + }; +} + +/** + * @this Document + */ + +function parse_descriptor(options, document){ + + const index = new Map(); + let field = document.index || document.field || document; + + if(is_string(field)){ + field = [field]; + } + + for(let i = 0, key, opt; i < field.length; i++){ + + key = field[i]; + + if(!is_string(key)){ + opt = key; + key = key.field; + } + + opt = /** @type DocumentIndexOptions */ ( + is_object(opt) + ? Object.assign({}, options, opt) + : options + ); + + if(this.worker){ + const worker = new WorkerIndex(opt); + index.set(key, worker); + if(!worker.worker){ + // fallback when not supported + this.worker = false; + } + } + + if(!this.worker){ + index.set(key, new Index(opt, this.reg)); + } + + if(opt.custom){ + this.tree[i] = opt.custom; + } + else { + this.tree[i] = parse_tree(key, this.marker); + if(opt.filter){ + if(typeof this.tree[i] === "string"){ + // it needs an object to put a property to it + this.tree[i] = new String(this.tree[i]); + } + this.tree[i]._filter = opt.filter; + } + } + + this.field[i] = key; + } + + if(this.storetree){ + + let stores = document.store; + if(is_string(stores)) stores = [stores]; + + for(let i = 0, store, field; i < stores.length; i++){ + store = /** @type Array */ (stores[i]); + field = store.field || store; + if(store.custom){ + this.storetree[i] = store.custom; + store.custom._field = field; + } + else { + this.storetree[i] = parse_tree(field, this.marker); + if(store.filter){ + if(typeof this.storetree[i] === "string"){ + // it needs an object to put a property to it + this.storetree[i] = new String(this.storetree[i]); + } + this.storetree[i]._filter = store.filter; + } + } + } + } + + return index; +} + +function parse_tree(key, marker){ + + const tree = key.split(":"); + let count = 0; + + for(let i = 0; i < tree.length; i++){ + key = tree[i]; + // todo apply some indexes e.g. [0], [-1], [0-2] + if(key[key.length - 1] === "]"){ + key = key.substring(0, key.length - 2); + if(key){ + marker[count] = true; + } + } + if(key){ + tree[count++] = key; + } + } + + if(count < tree.length){ + tree.length = count; + } + + return count > 1 ? tree : tree[0]; +} + +Document.prototype.append = function(id, content){ + return this.add(id, content, true); +}; + +Document.prototype.update = function(id, content){ + return this.remove(id).add(id, content); +}; + +Document.prototype.remove = function(id){ + + if(is_object(id)){ + id = parse_simple(id, this.key); + } + + for(const index of this.index.values()){ + index.remove(id, /* skip deletion */ true); + } + + if(this.reg.has(id)){ + + if(this.tag){ + // when fastupdate was enabled all ids are already removed + if(!this.fastupdate){ + for(let field of this.tag.values()){ + for(let item of field){ + const tag = item[0]; + const ids = item[1]; + const pos = ids.indexOf(id); + if(pos > -1){ + ids.length > 1 + ? ids.splice(pos, 1) + : field.delete(tag); + } + } + } + } + } + + if(this.store){ + this.store.delete(id); + } + + this.reg.delete(id); + } + + // the cache could be used outside the InMemory store + if(this.cache){ + this.cache.remove(id); + } + + return this; +}; + +Document.prototype.clear = function(){ + + //const promises = []; + + for(const index of this.index.values()){ + // db index will add clear task + index.clear(); + // const promise = index.clear(); + // if(promise instanceof Promise){ + // promises.push(promise); + // } + } + + if(this.tag){ + for(const tags of this.tag.values()){ + tags.clear(); + } + } + + if(this.store){ + this.store.clear(); + } + + return this; /*promises.length + ? Promise.all(promises) + :*/ +}; + +Document.prototype.contain = function(id){ + + if(this.db){ + return this.index.get(this.field[0]).db.has(id); + } + + return this.reg.has(id); +}; + +Document.prototype.cleanup = function(){ + + for(const index of this.index.values()){ + index.cleanup(); + } + + return this; +}; + +{ + + Document.prototype.get = function(id){ + + if(this.db){ + return this.index.get(this.field[0]).db.enrich(id).then(function(result){ + return result[0] && result[0].doc; + }); + } + + return this.store.get(id); + }; + + Document.prototype.set = function(id, store){ + + this.store.set(id, store); + return this; + }; +} + +{ + // todo mo + Document.prototype.searchCache = searchCache; +} + +{ + + Document.prototype.export = exportDocument; + Document.prototype.import = importDocument; +} + +{ + + apply_async(Document.prototype); +} + +// COMPILER BLOCK --> +const defaults = { + host: "http://localhost", + port: "8123", + debug: false, + basicAuth: null, + isUseGzip: false, + trimQuery: false, + usePost: false, + format: "json", + raw: false, + config: { + output_format_json_quote_64bit_integers: 0, + enable_http_compression: 0, + database: "default" + } +}; +const fields = ["map", "ctx", "tag", "reg", "cfg"]; +const types = { + "text": "String", + "char": "String", + "varchar": "String", + "string": "String", + "number": "Int32", + "numeric": "Int32", + "integer": "Int32", + "smallint": "Int16", + "tinyint": "Int8", + "mediumint": "Int32", + "int": "Int32", + "int8": "Int8", + "uint8": "UInt8", + "int16": "Int16", + "uint16": "UInt16", + "int32": "Int32", + "uint32": "UInt32", + "int64": "Int64", + "uint64": "UInt64", + "bigint": "Int64" +}; + +function sanitize(str) { + return str.toLowerCase().replace(/[^a-z0-9_]/g, ""); +} + +let DB; + +/** + * @constructor + * @implements StorageInterface + */ + +function ClickhouseDB(name, config = {}){ + if(!this){ + return new ClickhouseDB(name, config); + } + if(typeof name === "object"){ + name = name.name; + config = name; + } + if(!name){ + console.info("Default storage space was used, because a name was not passed."); + } + //field = "Test-456"; + this.id = "flexsearch" + (name ? "_" + sanitize(name) : ""); + this.field = config.field ? "_" + sanitize(config.field) : ""; + // Clickhouse does not support ALTER TABLE to upgrade + // the type of the ID when it is a part of the merge key + this.type = config.type ? types[config.type.toLowerCase()] : "String"; + if(!this.type) throw new Error("Unknown type of ID '" + config.type + "'"); + //this.trx = false; + this.support_tag_search = true; + this.db = DB || (DB = config.db || null); + Object.assign(defaults, config); + config.database && (defaults.config.database = config.database); + this.db && delete defaults.db; +} +ClickhouseDB.prototype.mount = function(flexsearch){ + if(flexsearch.constructor === Document){ + return flexsearch.mount(this); + } + defaults.resolution = Math.max(flexsearch.resolution, flexsearch.resolution_ctx); + flexsearch.db = this; + return this.open(); +}; + +ClickhouseDB.prototype.open = async function(){ + + if(!this.db) { + this.db = DB || ( + DB = new clickhouse.ClickHouse(defaults) + ); + } + + const exists = await this.db.query(` + SELECT 1 FROM system.databases WHERE name = '${this.id}'; + `).toPromise(); + + if(!exists || !exists.length){ + await this.db.query(` + CREATE DATABASE IF NOT EXISTS ${this.id}; + `).toPromise(); + } + + for(let i = 0; i < fields.length; i++){ + switch(fields[i]){ + case "map": + await this.db.query(` + CREATE TABLE IF NOT EXISTS ${this.id}.map${this.field}( + key String, + res ${defaults.resolution <= 255 ? "UInt8" : "UInt16"}, + id ${this.type} + ) + ENGINE = MergeTree + /*PRIMARY KEY (key)*/ + ORDER BY (key, id); + `, { params: { name: this.id + ".map" + this.field }}).toPromise(); + break; + + case "ctx": + await this.db.query(` + CREATE TABLE IF NOT EXISTS ${this.id}.ctx${this.field}( + ctx String, + key String, + res ${defaults.resolution <= 255 ? "UInt8" : "UInt16"}, + id ${this.type} + ) + ENGINE = MergeTree + /*PRIMARY KEY (ctx, key)*/ + ORDER BY (ctx, key, id); + `).toPromise(); + break; + + case "tag": + await this.db.query(` + CREATE TABLE IF NOT EXISTS ${this.id}.tag${this.field}( + tag String, + id ${this.type} + ) + ENGINE = MergeTree + /*PRIMARY KEY (ctx, key)*/ + ORDER BY (tag, id); + `).toPromise(); + break; + + case "reg": + await this.db.query(` + CREATE TABLE IF NOT EXISTS ${this.id}.reg( + id ${this.type}, + doc Nullable(String) + ) + ENGINE = MergeTree + ORDER BY (id); + `).toPromise(); + break; + + case "cfg": + await this.db.query(` + CREATE TABLE IF NOT EXISTS ${this.id}.cfg${this.field}( + cfg String + ) + ENGINE = TinyLog; + `).toPromise(); + break; + } + } + + return this.db; +}; + +ClickhouseDB.prototype.close = function(){ + this.db.close(); + this.db = null; + return this; +}; + +ClickhouseDB.prototype.destroy = async function(){ + await Promise.all([ + this.db.query(`DROP TABLE ${this.id}.map${this.field};`).toPromise(), + this.db.query(`DROP TABLE ${this.id}.ctx${this.field};`).toPromise(), + this.db.query(`DROP TABLE ${this.id}.tag${this.field};`).toPromise(), + this.db.query(`DROP TABLE ${this.id}.cfg${this.field};`).toPromise(), + this.db.query(`DROP TABLE ${this.id}.reg;`).toPromise() + ]); + this.close(); +}; + +ClickhouseDB.prototype.clear = function(){ + return Promise.all([ + this.db.query(`TRUNCATE TABLE ${this.id}.map${this.field};`).toPromise(), + this.db.query(`TRUNCATE TABLE ${this.id}.ctx${this.field};`).toPromise(), + this.db.query(`TRUNCATE TABLE ${this.id}.tag${this.field};`).toPromise(), + this.db.query(`TRUNCATE TABLE ${this.id}.cfg${this.field};`).toPromise(), + this.db.query(`TRUNCATE TABLE ${this.id}.reg;`).toPromise() + ]); +}; + +function create_result(rows, resolve, enrich){ + if(resolve){ + for(let i = 0; i < rows.length; i++){ + if(enrich){ + if(rows[i].doc){ + rows[i].doc = JSON.parse(rows[i].doc); + } + } + else { + rows[i] = rows[i].id; + } + } + return rows; + } + else { + const arr = []; + for(let i = 0, row; i < rows.length; i++){ + row = rows[i]; + arr[row.res] || (arr[row.res] = []); + arr[row.res].push(enrich + ? row + : row.id + ); + } + return arr; + } +} + +ClickhouseDB.prototype.get = function(key, ctx, limit = 0, offset = 0, resolve = true, enrich = false, tags){ + let rows; + let stmt = ''; + let params = ctx ? { ctx, key } : { key }; + let table = this.id + (ctx ? ".ctx" : ".map") + this.field; + if(tags){ + for(let i = 0, count = 1; i < tags.length; i+=2){ + stmt += ` AND ${ table }.id IN (SELECT id FROM ${ this.id }.tag_${ sanitize(tags[i]) } WHERE tag = {tag${ count }:String})`; + params["tag" + count] = tags[i + 1]; + count++; + } + } + if(ctx){ + rows = this.db.query(` + SELECT ${ table }.id + ${ resolve ? "" : ", res" } + ${ enrich ? ", doc" : "" } + FROM ${ table } + ${ enrich ? ` + LEFT OUTER JOIN ${ this.id }.reg ON ${ this.id }.reg.id = ${ table }.id + ` : "" } + WHERE ctx = {ctx:String} AND key = {key:String} + ORDER BY res + ${ limit ? "LIMIT " + limit : "" } + ${ offset ? "OFFSET " + offset : "" }`, + { params } + ).toPromise(); + } + else { + rows = this.db.query(` + SELECT ${ table }.id + ${ resolve ? "" : ", res" } + ${ enrich ? ", doc" : "" } + FROM ${ table } + ${ enrich ? ` + LEFT OUTER JOIN ${ this.id }.reg ON ${ this.id }.reg.id = ${ table }.id + ` : "" } + WHERE key = {key:String} + ORDER BY res + ${ limit ? "LIMIT " + limit : "" } + ${ offset ? "OFFSET " + offset : "" }`, + { params } + ).toPromise(); + } + return rows.then(function(rows){ + return create_result(rows, resolve, enrich); + }); +}; + +ClickhouseDB.prototype.tag = function(tag, limit = 0, offset = 0, enrich = false){ + const table = this.id + ".tag" + this.field; + const promise = this.db.query(` + SELECT ${ table }.id + ${ enrich ? ", doc" : "" } + FROM ${ table } + ${ enrich ? ` + LEFT OUTER JOIN ${ this.id }.reg ON ${ this.id }.reg.id = ${ table }.id + ` : "" } + WHERE tag = {tag:String} + ${ limit ? "LIMIT " + limit : "" } + ${ offset ? "OFFSET " + offset : "" }`, + { params: { tag } } + ).toPromise(); + enrich || promise.then(function(rows){ + return create_result(rows, true, false); + }); + return promise; +}; + +ClickhouseDB.prototype.enrich = async function(ids){ + let MAXIMUM_QUERY_VARS = 1e5; + let result = []; + if(typeof ids !== "object"){ + ids = [ids]; + } + for(let count = 0; count < ids.length;){ + const chunk = ids.length - count > MAXIMUM_QUERY_VARS + ? ids.slice(count, count + MAXIMUM_QUERY_VARS) + : count ? ids.slice(count) : ids; + count += chunk.length; + let params = {}; + let stmt = ""; + for(let i = 0; i < chunk.length; i++){ + stmt += (stmt ? "," : "") + "{id" + (i + 1) + ":String}"; + params["id" + (i + 1)] = chunk[i]; + } + const res = await this.db.query(` + SELECT id, doc + FROM ${ this.id }.reg + WHERE id IN (${ stmt })`, + { params } + ).toPromise(); + if(res && res.length){ + for(let i = 0, doc; i < res.length; i++){ + if((doc = res[i].doc)){ + res[i].doc = JSON.parse(doc); + } + } + result.push(res); + } + } + return result.length === 1 + ? result[0] + : result.length > 1 + ? concat(result) + : result; +}; + +ClickhouseDB.prototype.has = function(id){ + return this.db.query(` + SELECT EXISTS( + SELECT 1 + FROM ${this.id}.reg + WHERE id = {id:${this.type /*=== "number" ? "Int32" : "String"*/}} + LIMIT 1 + )`, + { params: { id }} + ).toPromise(); +}; + +ClickhouseDB.prototype.search = function(flexsearch, query, limit = 100, offset = 0, suggest = false, resolve = true, enrich = true, tags){ + let rows; + if(query.length > 1 && flexsearch.depth){ + + let where = ""; + let params = {}; + let keyword = query[0]; + let term; + + for(let i = 1; i < query.length; i++){ + term = query[i]; + const swap = flexsearch.bidirectional && (term > keyword); + where += (where ? " OR " : "") + `(ctx = {ctx${ i }:String} AND key = {key${ i }:String})`; + params["ctx" + i] = swap ? term : keyword; + params["key" + i] = swap ? keyword : term; + keyword = term; + } + + if(tags){ + where = "(" + where + ")"; + for(let i = 0, count = 1; i < tags.length; i+=2){ + where += ` AND id IN (SELECT id FROM ${ this.id }.tag_${ sanitize(tags[i]) } WHERE tag = {tag${ count }:String})`; + params["tag" + count] = tags[i + 1]; + count++; + } + } + + rows = this.db.query(` + SELECT r.id + ${ resolve ? "" : ", res" } + ${ enrich ? ", doc" : "" } + FROM ( + SELECT id, count(*) as count, + ${ suggest ? "SUM" : "MIN" }(res) as res + FROM ${ this.id }.ctx${ this.field } + WHERE ${ where } + GROUP BY id + ) as r + ${ enrich ? ` + LEFT OUTER JOIN ${ this.id }.reg ON ${ this.id }.reg.id = r.id + ` : "" } + ${ suggest ? "" : "WHERE count = " + (query.length - 1) } + ORDER BY ${ suggest ? "count DESC, res" : "res" } + ${ limit ? "LIMIT " + limit : "" } + ${ offset ? "OFFSET " + offset : "" } + `, { params }).toPromise(); + + // for(let i = 1; i < query.length; i++){ + // where += (where ? " UNION ALL " : "") + ` + // SELECT id, res + // FROM ${this.id}.ctx${this.field} + // WHERE ctx = {ctx${i}:String} AND key = {key${i}:String} + // `; + // term = query[i]; + // const swap = flexsearch.bidirectional && (term > keyword); + // params["ctx" + i] = swap ? term : keyword; + // params["key" + i] = swap ? keyword : term; + // keyword = term; + // } + // + // rows = await this.db.query(` + // SELECT id, res + // FROM ( + // SELECT id, ${suggest ? "SUM" : "MIN"}(res) as res, count(*) as count + // FROM (${where}) as t + // GROUP BY id + // ORDER BY ${suggest ? "count DESC, res" : "res"} + // LIMIT ${limit} + // OFFSET ${offset} + // ) as r + // ${suggest ? "" : "WHERE count = " + (query.length - 1)} + // `, { params }).toPromise(); + } + else { + + let where = ""; + let params = {}; + + for(let i = 0; i < query.length; i++){ + where += (where ? "," : "") + `{key${i}:String}`; + params["key" + i] = query[i]; + } + where = "key " + (query.length > 1 ? "IN (" + where + ")" : "= " + where ); + + if(tags){ + where = "(" + where + ")"; + for(let i = 0, count = 1; i < tags.length; i+=2){ + where += ` AND id IN (SELECT id FROM ${ this.id }.tag_${ sanitize(tags[i]) } WHERE tag = {tag${ count }:String})`; + params["tag" + count] = tags[i + 1]; + count++; + } + } + + rows = this.db.query(` + SELECT r.id + ${ resolve ? "" : ", res" } + ${ enrich ? ", doc" : "" } + FROM ( + SELECT id, count(*) as count, + ${ suggest ? "SUM" : "MIN" }(res) as res + FROM ${ this.id }.map${ this.field } + WHERE ${ where } + GROUP BY id + ) as r + ${ enrich ? ` + LEFT OUTER JOIN ${ this.id }.reg ON ${ this.id }.reg.id = r.id + ` : "" } + ${ suggest ? "" : "WHERE count = " + query.length } + ORDER BY ${ suggest ? "count DESC, res" : "res" } + ${ limit ? "LIMIT " + limit : "" } + ${ offset ? "OFFSET " + offset : "" } + `, { params }).toPromise(); + + // for(let i = 0; i < query.length; i++){ + // params["key" + i] = query[i]; + // where += (where ? " UNION ALL " : "") + ` + // SELECT id, res + // FROM ${ this.id }.map${ this.field } + // WHERE key = {key${i}:String} + // `; + // } + // rows = await this.db.query(` + // SELECT id, res + // FROM ( + // SELECT id, ${suggest ? "SUM" : "MIN"}(res) as res, count(*) as count + // FROM (${where}) as t + // GROUP BY id + // ORDER BY ${suggest ? "count DESC, res" : "res"} + // LIMIT ${limit} + // OFFSET ${offset} + // ) as r + // ${ suggest ? "" : "WHERE count = " + query.length } + // `, { params }).toPromise(); + } + return rows.then(function(rows){ + return create_result(rows, resolve, enrich); + }); +}; + +ClickhouseDB.prototype.info = function(){ + // todo +}; + +ClickhouseDB.prototype.transaction = function(task){ + + // not supported + return task.call(this); +}; + +ClickhouseDB.prototype.commit = async function(flexsearch, _replace, _append){ + + // process cleanup tasks + if(_replace){ + await this.clear(); + // there are just removals in the task queue + flexsearch.commit_task = []; + } + else { + let tasks = flexsearch.commit_task; + flexsearch.commit_task = []; + for(let i = 0, task; i < tasks.length; i++){ + task = tasks[i]; + // there are just removals in the task queue + if(task.clear){ + await this.clear(); + _replace = true; + break; + } + else { + tasks[i] = task.del; + } + } + if(!_replace){ + if(!_append){ + tasks = tasks.concat(toArray(flexsearch.reg)); + } + tasks.length && await this.remove(tasks); + } + } + if(!flexsearch.reg.size){ + return; + } + + if(flexsearch.map.size){ + let data = []; + for(const item of flexsearch.map){ + const key = item[0]; + const arr = item[1]; + for(let i = 0, ids; i < arr.length; i++){ + if((ids = arr[i]) && ids.length){ + //this.type || (this.type = typeof ids[0]); + for(let j = 0; j < ids.length; j++){ + data.push({ + key: key, + res: i, + id: /*this.type === "number" + ? parseInt(ids[j], 10) + :*/ ids[j] + }); + } + } + } + } + if(data.length){ + await this.db.insert( + `INSERT INTO ${ this.id }.map${ this.field } (key, res, id)`, data + ).toPromise(); + } + } + + if(flexsearch.ctx.size){ + let data = []; + for(const ctx of flexsearch.ctx){ + const ctx_key = ctx[0]; + const ctx_value = ctx[1]; + for(const item of ctx_value){ + const key = item[0]; + const arr = item[1]; + for(let i = 0, ids; i < arr.length; i++){ + if((ids = arr[i]) && ids.length){ + for(let j = 0; j < ids.length; j++){ + data.push({ + ctx: ctx_key, + key: key, + res: i, + id: /*this.type === "number" + ? parseInt(ids[j], 10) + :*/ ids[j] + }); + } + } + } + } + } + if(data.length){ + await this.db.insert( + `INSERT INTO ${ this.id }.ctx${ this.field } (ctx, key, res, id)`, data + ).toPromise(); + } + } + + if(flexsearch.tag){ + let data = []; + for(const item of flexsearch.tag){ + const tag = item[0]; + const ids = item[1]; + if(!ids.length) continue; + for(let j = 0; j < ids.length; j++){ + data.push({ tag, id: ids[j] }); + } + } + if(data.length){ + await this.db.insert( + `INSERT INTO ${this.id}.tag${ this.field } (tag, id)`, data + ).toPromise(); + } + } + + if(flexsearch.store){ + let data = []; + for(const item of flexsearch.store.entries()){ + const id = item[0]; + const doc = item[1]; + data.push({ id, doc: doc && JSON.stringify(doc) }); + } + if(data.length){ + await this.db.insert( + `INSERT INTO ${this.id}.reg (id, doc)`, data + ).toPromise(); + } + } + else if(!flexsearch.bypass){ + let data = toArray(flexsearch.reg); + for(let i = 0; i < data.length; i++){ + data[i] = { id: data[i] }; + } + if(data.length){ + await this.db.insert( + `INSERT INTO ${this.id}.reg (id)`, data + ).toPromise(); + } + } + + // TODO + // await this.db.insert(`INSERT INTO ${this.id}.cfg${this.field} (cfg)`, [{ + // cfg: JSON.stringify({ + // "encode": typeof flexsearch.encode === "string" ? flexsearch.encode : "", + // "charset": typeof flexsearch.charset === "string" ? flexsearch.charset : "", + // "tokenize": flexsearch.tokenize, + // "resolution": flexsearch.resolution, + // "minlength": flexsearch.minlength, + // "optimize": flexsearch.optimize, + // "fastupdate": flexsearch.fastupdate, + // "encoder": flexsearch.encoder, + // "context": { + // "depth": flexsearch.depth, + // "bidirectional": flexsearch.bidirectional, + // "resolution": flexsearch.resolution_ctx + // } + // }) + // }]).toPromise(); + + flexsearch.map.clear(); + flexsearch.ctx.clear(); + flexsearch.tag && + flexsearch.tag.clear(); + flexsearch.store && + flexsearch.store.clear(); + flexsearch.document || + flexsearch.reg.clear(); + + await Promise.all([ + this.db.query(`OPTIMIZE TABLE ${this.id}.map${this.field} FINAL`).toPromise(), + this.db.query(`OPTIMIZE TABLE ${this.id}.ctx${this.field} FINAL`).toPromise(), + this.db.query(`OPTIMIZE TABLE ${this.id}.tag${this.field} FINAL`).toPromise(), + this.db.query(`OPTIMIZE TABLE ${this.id}.reg FINAL`).toPromise() + ]); +}; + +ClickhouseDB.prototype.remove = async function(ids){ + + if(typeof ids !== "object"){ + ids = [ids]; + } + + while(ids.length){ + + let chunk = ids.slice(0, 1e5); + ids = ids.slice(1e5); + chunk = this.type === "String" + ? "'" + chunk.join("','") + "'" + : chunk.join(","); + + await Promise.all([ + this.db.query(` + ALTER TABLE ${this.id}.map${this.field} + DELETE WHERE id IN (${chunk}) + SETTINGS mutations_sync = 1;` + ).toPromise(), + + this.db.query(` + ALTER TABLE ${this.id}.ctx${this.field} + DELETE WHERE id IN (${chunk}) + SETTINGS mutations_sync = 1;` + ).toPromise(), + + this.db.query(` + ALTER TABLE ${this.id}.tag${this.field} + DELETE WHERE id IN (${chunk}) + SETTINGS mutations_sync = 1;` + ).toPromise(), + + this.db.query(` + ALTER TABLE ${this.id}.reg + DELETE WHERE id IN (${chunk}) + SETTINGS mutations_sync = 1;` + ).toPromise() + ]); + } +}; + +module.exports = ClickhouseDB; diff --git a/dist/db/indexeddb/index.cjs b/dist/db/indexeddb/index.cjs new file mode 100644 index 0000000..c45c43c --- /dev/null +++ b/dist/db/indexeddb/index.cjs @@ -0,0 +1,6326 @@ +'use strict'; + +/** @define {string} */ + +/** @define {boolean} */ +const SUPPORT_PERSISTENT = true; + +/** @define {boolean} */ +const SUPPORT_KEYSTORE = true; + +/** + * @param {*} value + * @param {*} default_value + * @param {*=} merge_value + * @return {*} + */ + +function parse_option(value, default_value, merge_value){ + + const type_merge = typeof merge_value; + const type_value = typeof value; + + if(type_merge !== "undefined"){ + if(type_value !== "undefined"){ + + if(merge_value){ + if(type_value === "function" && + type_merge === type_value){ + return function(str){ + return /** @type Function */ (value)( + /** @type Function */ (merge_value)(str) + ); + } + } + + const constructor_value = value.constructor; + const constructor_merge = merge_value.constructor; + + if(constructor_value === constructor_merge){ + + if(constructor_value === Array){ + return merge_value.concat(value); + } + + if(constructor_value === Map){ + const map = new Map(/** @type !Map */ (merge_value)); + for(const item of /** @type !Map */ (value)){ + const key = item[0]; + const val = item[1]; + map.set(key, val); + } + return map; + } + + if(constructor_value === Set){ + const set = new Set(/** @type !Set */ (merge_value)); + for(const val of /** @type !Set */ (value).values()){ + set.add(val); + } + return set; + } + } + } + + return value; + } + else { + return merge_value; + } + } + + return type_value === "undefined" + ? default_value + : value; +} + +function create_object(){ + return Object.create(null); +} + +function concat(arrays){ + return [].concat.apply([], arrays); +} + +function sort_by_length_down(a, b){ + return b.length - a.length; +} + +function is_array(val){ + return val.constructor === Array; +} + +function is_string(val){ + return typeof val === "string"; +} + +function is_object(val){ + return typeof val === "object"; +} + +function is_function(val){ + return typeof val === "function"; +} + +/** + * @param {Map|Set} val + * @param {boolean=} stringify + * @return {Array} + */ + +function toArray(val, stringify){ + const result = []; + for(const key of val.keys()){ + result.push(key); + } + return result; +} + +// TODO support generic function created from string when tree depth > 1 +function parse_simple(obj, tree){ + + if(is_string(tree)){ + obj = obj[tree]; + } + else for(let i = 0; obj && (i < tree.length); i++){ + obj = obj[tree[i]]; + } + + return obj; +} + +function get_max_len(arr){ + let len = 0; + for(let i = 0, res; i < arr.length; i++){ + if((res = arr[i])){ + if(len < res.length){ + len = res.length; + } + } + } + return len; +} + +var normalize_polyfill = [ + + // Charset Normalization + + ["ª","a"], + ["²","2"], + ["³","3"], + ["¹","1"], + ["º","o"], + ["¼","1⁄4"], + ["½","1⁄2"], + ["¾","3⁄4"], + ["à","a"], + ["á","a"], + ["â","a"], + ["ã","a"], + ["ä","a"], + ["å","a"], + ["ç","c"], + ["è","e"], + ["é","e"], + ["ê","e"], + ["ë","e"], + ["ì","i"], + ["í","i"], + ["î","i"], + ["ï","i"], + ["ñ","n"], + ["ò","o"], + ["ó","o"], + ["ô","o"], + ["õ","o"], + ["ö","o"], + ["ù","u"], + ["ú","u"], + ["û","u"], + ["ü","u"], + ["ý","y"], + ["ÿ","y"], + ["ā","a"], + ["ă","a"], + ["ą","a"], + ["ć","c"], + ["ĉ","c"], + ["ċ","c"], + ["č","c"], + ["ď","d"], + ["ē","e"], + ["ĕ","e"], + ["ė","e"], + ["ę","e"], + ["ě","e"], + ["ĝ","g"], + ["ğ","g"], + ["ġ","g"], + ["ģ","g"], + ["ĥ","h"], + ["ĩ","i"], + ["ī","i"], + ["ĭ","i"], + ["į","i"], + ["ij","ij"], + ["ĵ","j"], + ["ķ","k"], + ["ĺ","l"], + ["ļ","l"], + ["ľ","l"], + ["ŀ","l"], + ["ń","n"], + ["ņ","n"], + ["ň","n"], + ["ʼn","n"], + ["ō","o"], + ["ŏ","o"], + ["ő","o"], + ["ŕ","r"], + ["ŗ","r"], + ["ř","r"], + ["ś","s"], + ["ŝ","s"], + ["ş","s"], + ["š","s"], + ["ţ","t"], + ["ť","t"], + ["ũ","u"], + ["ū","u"], + ["ŭ","u"], + ["ů","u"], + ["ű","u"], + ["ų","u"], + ["ŵ","w"], + ["ŷ","y"], + ["ź","z"], + ["ż","z"], + ["ž","z"], + ["ſ","s"], + ["ơ","o"], + ["ư","u"], + ["dž","dz"], + ["lj","lj"], + ["nj","nj"], + ["ǎ","a"], + ["ǐ","i"], + ["ǒ","o"], + ["ǔ","u"], + ["ǖ","u"], + ["ǘ","u"], + ["ǚ","u"], + ["ǜ","u"], + ["ǟ","a"], + ["ǡ","a"], + ["ǣ","ae"], + ["æ","ae"], + ["ǽ","ae"], + ["ǧ","g"], + ["ǩ","k"], + ["ǫ","o"], + ["ǭ","o"], + ["ǯ","ʒ"], + ["ǰ","j"], + ["dz","dz"], + ["ǵ","g"], + ["ǹ","n"], + ["ǻ","a"], + ["ǿ","ø"], + ["ȁ","a"], + ["ȃ","a"], + ["ȅ","e"], + ["ȇ","e"], + ["ȉ","i"], + ["ȋ","i"], + ["ȍ","o"], + ["ȏ","o"], + ["ȑ","r"], + ["ȓ","r"], + ["ȕ","u"], + ["ȗ","u"], + ["ș","s"], + ["ț","t"], + ["ȟ","h"], + ["ȧ","a"], + ["ȩ","e"], + ["ȫ","o"], + ["ȭ","o"], + ["ȯ","o"], + ["ȱ","o"], + ["ȳ","y"], + ["ʰ","h"], + ["ʱ","h"], + ["ɦ","h"], + ["ʲ","j"], + ["ʳ","r"], + ["ʴ","ɹ"], + ["ʵ","ɻ"], + ["ʶ","ʁ"], + ["ʷ","w"], + ["ʸ","y"], + ["ˠ","ɣ"], + ["ˡ","l"], + ["ˢ","s"], + ["ˣ","x"], + ["ˤ","ʕ"], + ["ΐ","ι"], + ["ά","α"], + ["έ","ε"], + ["ή","η"], + ["ί","ι"], + ["ΰ","υ"], + ["ϊ","ι"], + ["ϋ","υ"], + ["ό","ο"], + ["ύ","υ"], + ["ώ","ω"], + ["ϐ","β"], + ["ϑ","θ"], + ["ϒ","Υ"], + ["ϓ","Υ"], + ["ϔ","Υ"], + ["ϕ","φ"], + ["ϖ","π"], + ["ϰ","κ"], + ["ϱ","ρ"], + ["ϲ","ς"], + ["ϵ","ε"], + ["й","и"], + ["ѐ","е"], + ["ё","е"], + ["ѓ","г"], + ["ї","і"], + ["ќ","к"], + ["ѝ","и"], + ["ў","у"], + ["ѷ","ѵ"], + ["ӂ","ж"], + ["ӑ","а"], + ["ӓ","а"], + ["ӗ","е"], + ["ӛ","ә"], + ["ӝ","ж"], + ["ӟ","з"], + ["ӣ","и"], + ["ӥ","и"], + ["ӧ","о"], + ["ӫ","ө"], + ["ӭ","э"], + ["ӯ","у"], + ["ӱ","у"], + ["ӳ","у"], + ["ӵ","ч"] + + // Term Separators + + // ["'", ""], // it's -> its + // ["´", ""], + // ["`", ""], + // ["’", ""], + // ["ʼ", ""], + + // Numeric-Separators Chars Removal + + // [",", ""], + // [".", ""] + + // Non-Whitespace Separators + + // already was split by default via p{P} + // ["-", " "], + // [":", " "], + // ["_", " "], + // ["|", " "], + // ["/", " "], + // ["\\", " "] +]; + +// COMPILER BLOCK --> + +/* + +Custom Encoder +---------------- + +// Split a passed string into an Array of words: +function englishEncoder(string){ + return string.toLowerCase().split(/[^a-z]+/) +} + +// For CJK split a passed string into an Array of chars: +function chineseEncoder(string){ + return string.replace(/\s+/, "").split("") +} + +// Alternatively do not split the input: +function fixedEncoder(string){ + return [string] +} + +Built-in Encoder (Workflow) +---------------------------- +Pipeline: + 1. apply this.normalize: charset normalization: + applied on the whole input string e.g. lowercase, + will also apply on: filter, matcher, stemmer, mapper + 2. apply this.split: split input into terms (includes/excludes) + 3. apply this.filter (pre-filter) + 4. apply this.matcher (replace terms) + 5. apply this.stemmer (replace term endings) + 6. apply this.filter (post-filter) + 7. apply this.mapper (replace chars) + 8. apply this.replacer (custom regex) + 9. apply this.dedupe (letter deduplication) + 10. apply this.finalize +*/ + +const whitespace = /[^\p{L}\p{N}]+/u; // /[\p{Z}\p{S}\p{P}\p{C}]+/u; +//const numeric_split = /(\d{3})/g; +const numeric_split_length = /(\d{3})/g; +const numeric_split_prev_char = /(\D)(\d{3})/g; +const numeric_split_next_char = /(\d{3})(\D)/g; +//.replace(/(\d{3})/g, "$1 ") +//.replace(/([^\d])([\d])/g, "$1 $2") +//.replace(/([\d])([^\d])/g, "$1 $2") +const normalize = "".normalize && /[\u0300-\u036f]/g; // '´`’ʼ., +//const normalize_mapper = SUPPORT_CHARSET && !normalize && normalize_polyfill; + +/** + * @param {EncoderOptions=} options + * @constructor + */ + +function Encoder(options){ + + if(!this){ + return new Encoder(...arguments); + } + + for(let i = 0; i < arguments.length; i++){ + this.assign(arguments[i]); + } +} +/** + * @param {!EncoderOptions} options + */ +Encoder.prototype.assign = function(options){ + + /** + * pre-processing string input + * @type {Function|boolean} + */ + this.normalize = /** @type {Function|boolean} */ ( + parse_option(options.normalize, true, this.normalize) + ); + + // { + // letter: true, + // number: true, + // whitespace: true, + // symbol: true, + // punctuation: true, + // control: true, + // char: "" + // } + + let include = options.include; + let tmp = include || options.exclude || options.split; + + if(typeof tmp === "object"){ + let numeric = !include; + let regex = ""; + // split on whitespace by default + options.include || ( + regex += "\\p{Z}" + ); + if(tmp.letter){ + regex += "\\p{L}"; + } + if(tmp.number){ + regex += "\\p{N}"; + numeric = !!include; + } + if(tmp.symbol){ + regex += "\\p{S}"; + } + if(tmp.punctuation){ + regex += "\\p{P}"; + } + if(tmp.control){ + regex += "\\p{C}"; + } + if((tmp = tmp.char)){ + regex += typeof tmp === "object" ? tmp.join("") : tmp; + } + + this.split = new RegExp("[" + (include ? "^" : "") + regex + "]+", "u"); + this.numeric = numeric; + } + else { + + /** + * split string input into terms + * @type {string|RegExp|boolean|null} + */ + this.split = /** @type {string|RegExp|boolean} */ (parse_option(tmp, whitespace, this.split)); + this.numeric = parse_option(this.numeric, true); + } + + /** + * post-processing terms + * @type {Function|null} + */ + this.prepare = /** @type {Function|null} */ ( + parse_option(options.prepare, null, this.prepare) + ); + /** + * final processing + * @type {Function|null} + */ + this.finalize = /** @type {Function|null} */ ( + parse_option(options.finalize, null, this.finalize) + ); + + // assign the normalization fallback to the mapper + if(!normalize){ + this.mapper = new Map( + /** @type {Array>} */ ( + normalize_polyfill + ) + ); + } + + // options + + this.rtl = options.rtl || false; + this.dedupe = parse_option(options.dedupe, true, this.dedupe); + this.filter = parse_option((tmp = options.filter) && new Set(tmp), null, this.filter); + this.matcher = parse_option((tmp = options.matcher) && new Map(tmp), null, this.matcher); + this.mapper = parse_option((tmp = options.mapper) && new Map(tmp), null, this.mapper); + this.stemmer = parse_option((tmp = options.stemmer) && new Map(tmp), null, this.stemmer); + this.replacer = parse_option(options.replacer, null, this.replacer); + this.minlength = parse_option(options.minlength, 1, this.minlength); + this.maxlength = parse_option(options.maxlength, 0, this.maxlength); + + // minimum required tokenizer by this encoder + //this.tokenize = options["tokenize"] || ""; + + // auto-balanced cache + { + this.cache = tmp = parse_option(options.cache, true, this.cache); + if(tmp){ + this.timer = null; + this.cache_size = typeof tmp === "number" ? tmp : 2e5; + this.cache_enc = new Map(); + this.cache_prt = new Map(); + this.cache_enc_length = 128; + this.cache_prt_length = 128; + } + } + + // regex temporary state + this.matcher_str = ""; + this.matcher_test = null; + this.stemmer_str = ""; + this.stemmer_test = null; + + // prebuilt + // if(this.filter && this.split){ + // for(const key of this.filter){ + // const tmp = key.replace(this.split, ""); + // if(key !== tmp){ + // this.filter.delete(key); + // this.filter.add(tmp); + // } + // } + // } + if(this.matcher){ + for(const key of this.matcher.keys()){ + this.matcher_str += (this.matcher_str ? "|" : "") + key; + } + } + if(this.stemmer){ + for(const key of this.stemmer.keys()){ + this.stemmer_str += (this.stemmer_str ? "|" : "") + key; + } + } + + // if(SUPPORT_COMPRESSION){ + // this.compression = parse_option(options.compress || options.compression, 0, this.compression); + // if(this.compression && !table){ + // table = new Array(radix); + // for(let i = 0; i < radix; i++) table[i] = i + 33; + // table = String.fromCharCode.apply(null, table); + // } + // } + + return this; +}; + +Encoder.prototype.addMatcher = function(match, replace){ + // regex: + if(typeof match === "object"){ + return this.addReplacer(match, replace); + } + // a single char: + if(match.length < 2){ + return this.addMapper(match, replace); + } + this.matcher || (this.matcher = new Map()); + this.matcher.set(match , replace); + this.matcher_str += (this.matcher_str ? "|" : "") + match; + this.matcher_test = null; //new RegExp("(" + this.matcher_str + ")"); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addStemmer = function(match, replace){ + this.stemmer || (this.stemmer = new Map()); + this.stemmer.set(match, replace); + this.stemmer_str += (this.stemmer_str ? "|" : "") + match; + this.stemmer_test = null; //new RegExp("(" + this.stemmer_str + ")"); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addFilter = function(str){ + this.filter || (this.filter = new Set()); + this.filter.add(str); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addMapper = function(char_match, char_replace){ + // regex: + if(typeof char_match === "object"){ + return this.addReplacer(char_match, char_replace); + } + // not a char: + if(char_match.length > 1){ + return this.addMatcher(char_match, char_replace); + } + this.mapper || (this.mapper = new Map()); + this.mapper.set(char_match, char_replace); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addReplacer = function(match, replace){ + if(typeof match === "string") match = new RegExp(match, "g"); + this.replacer || (this.replacer = []); + this.replacer.push(match, replace || ""); + this.cache && this.invalidate(); + return this; +}; + +{ + Encoder.prototype.invalidate = function(){ + this.cache_enc.clear(); + this.cache_prt.clear(); + }; +} + +Encoder.prototype.encode = function(str){ + + //if(!str) return str; + // todo remove dupe terms + + if(this.cache && str.length <= this.cache_enc_length){ + if(this.timer){ + if(this.cache_enc.has(str)){ + return this.cache_enc.get(str); + } + } + else { + this.timer = setTimeout(clear$1, 0, this); + } + } + + // 1. apply charset normalization + if(this.normalize){ + if(typeof this.normalize === "function"){ + str = this.normalize(str); + } + else if(normalize){ + str = str.normalize("NFKD").replace(normalize, "").toLowerCase(); + } + else { + str = str.toLowerCase(); + // if(SUPPORT_CHARSET){ + // this.mapper = this.mapper + // // todo replace spread + // ? new Map([.../** @type {!Iterable} */(normalize_mapper), ...this.mapper]) + // : new Map(/** @type {Map} */ (normalize_mapper)); + // } + } + //if(!str) return str; + } + + // 2. apply custom encoder (can replace split) + if(this.prepare){ + str = this.prepare(str); + } + + // 3. split numbers into triplets + if(this.numeric && str.length > 3){ + str = str.replace(numeric_split_prev_char, "$1 $2") + .replace(numeric_split_next_char, "$1 $2") + .replace(numeric_split_length, "$1 "); + } + + // if(this.matcher && (str.length > 1)){ + // this.matcher_test || ( + // this.matcher_test = new RegExp("(" + this.matcher_str + ")", "g") + // ); + // str = str.replace(this.matcher_test, match => this.matcher.get(match)); + // } + // if(this.stemmer){ + // this.stemmer_test || ( + // this.stemmer_test = new RegExp("(?!\\b)(" + this.stemmer_str + ")(\\b|_)", "g") + // ); + // str = str.replace(this.stemmer_test, match => this.stemmer.get(match)); + // } + + const skip = !(this.dedupe || this.mapper || this.filter || this.matcher || this.stemmer || this.replacer); + let final = []; + let words = this.split || this.split === "" + ? str.split(/** @type {string|RegExp} */ (this.split)) + : str; //[str]; + + for(let i = 0, word, base; i < words.length; i++){ + // filter empty entries + if(!(word = base = words[i])){ + continue; + } + if(word.length < this.minlength){ + continue; + } + if(skip) { + final.push(word); + continue; + } + + // 1. pre-filter before cache + if(this.filter && this.filter.has(word)){ + continue; + } + + if(this.cache && word.length <= this.cache_prt_length){ + if(this.timer){ + const tmp = this.cache_prt.get(word); + //if(this.cache_prt.has(word)){ + if(tmp || tmp === ""){ + //word = this.cache_prt.get(word); + tmp && final.push(tmp); + //word ? words[i] = word : words.splice(i--, 1); + continue; + } + } + else { + this.timer = setTimeout(clear$1, 0, this); + } + } + + let postfilter; + + // if(this.normalize === true && normalize){ + // word = word.normalize("NFKD").replace(normalize, ""); + // postfilter = 1; + // } + + // if(this.normalize){ + // if(typeof this.normalize === "function"){ + // word = this.normalize(word); + // } + // else if(normalize){ + // word = word.normalize("NFKD").replace(normalize, "").toLowerCase(); + // } + // else{ + // word = word.toLowerCase(); + // this.mapper = this.mapper + // ? new Map([...normalize_mapper, ...this.mapper]) + // : new Map(/** @type {Map} */ normalize_mapper); + // } + // postfilter = 1; + // //if(!str) return str; + // } + + // 2. apply stemmer after matcher + if(this.stemmer && (word.length > 2)){ + // for(const item of this.stemmer){ + // const key = item[0]; + // const value = item[1]; + // + // if(word.length > key.length && word.endsWith(key)){ + // word = word.substring(0, word.length - key.length) + value; + // break; + // } + // + // // const position = word.length - key.length; + // // if(position > 0 && word.substring(position) === key){ + // // word = word.substring(0, position) + value; + // // break; + // // } + // } + this.stemmer_test || ( + this.stemmer_test = new RegExp("(?!^)(" + this.stemmer_str + ")$") + ); + word = word.replace(this.stemmer_test, match => this.stemmer.get(match)); + postfilter = 1; + } + + // 3. apply matcher + if(this.matcher && (word.length > 1)){ + this.matcher_test || ( + this.matcher_test = new RegExp("(" + this.matcher_str + ")", "g") + ); + word = word.replace(this.matcher_test, match => this.matcher.get(match)); + postfilter = 1; + } + + // 4. post-filter after matcher and stemmer was applied + if(word && postfilter && (word.length < this.minlength || (this.filter && this.filter.has(word)))){ + word = ""; + } + + // 5. apply mapper and collapsing + if(word && (this.mapper || (this.dedupe && word.length > 1))){ + //word = this.replace_dedupe(word); + //word = replace_deduped(word, this.mapper, true); + let final = ""; + for(let i = 0, prev = "", char, tmp; i < word.length; i++){ + char = word.charAt(i); + if(char !== prev || !this.dedupe){ + tmp = this.mapper && this.mapper.get(char); + if(!tmp && tmp !== "") + final += (prev = char); + else if((tmp !== prev || !this.dedupe) && (prev = tmp)) + final += tmp; + } + } + word = final; + } + + // apply custom regex + if(word && this.replacer){ + for(let i = 0; word && (i < this.replacer.length); i+=2){ + word = word.replace(this.replacer[i], this.replacer[i+1]); + } + } + + // slower variants for removing same chars in a row: + //word = word.replace(/([^0-9])\1+/g, "$1"); + //word = word.replace(/(.)\1+/g, "$1"); + //word = word.replace(/(?<=(.))\1+/g, ""); + + // if(word){ + // words[i] = word; + // } + + if(this.cache && base.length <= this.cache_prt_length){ + this.cache_prt.set(base, word); + if(this.cache_prt.size > this.cache_size){ + this.cache_prt.clear(); + this.cache_prt_length = this.cache_prt_length / 1.1 | 0; + } + } + + //word || words.splice(i--, 1); + word && final.push(word); + } + + //words = final; + // else if(this.filter){ + // for(let i = 0, word; i < words.length; i++){ + // if((word = words[i]) && !this.filter.has(word)){ + // //filtered.push(word); + // words.splice(i--, 1); + // } + // } + // } + + if(this.finalize){ + final = this.finalize(final) || final; + } + + if(this.cache && str.length <= this.cache_enc_length){ + this.cache_enc.set(str, final); + if(this.cache_enc.size > this.cache_size){ + this.cache_enc.clear(); + this.cache_enc_length = this.cache_enc_length / 1.1 | 0; + } + } + + return final; +}; + +// Encoder.prototype.compress = function(str) { +// +// //return str; +// //if(!str) return str; +// +// if(SUPPORT_CACHE && this.cache && str.length <= this.cache_prt_length){ +// if(this.timer){ +// if(this.cache_cmp.has(str)){ +// return this.cache_cmp.get(str); +// } +// } +// else{ +// this.timer = setTimeout(clear, 0, this); +// } +// } +// +// const result = typeof this.compression === "function" +// ? this.compression(str) +// : hash(str); //window.hash(str); +// +// if(SUPPORT_CACHE && this.cache && str.length <= this.cache_prt_length){ +// this.cache_cmp.set(str, result); +// this.cache_cmp.size > this.cache_size && +// this.cache_cmp.clear(); +// } +// +// return result; +// }; + +// function hash(str){ +// return str; +// } + +function clear$1(self){ + self.timer = null; + self.cache_enc.clear(); + self.cache_prt.clear(); +} + +/** + * @param {string|Object} query + * @param {number|Object=} limit + * @param {Object=} options + * @this {Index|Document} + * @returns {Array|Promise} + */ + +function searchCache(query, limit, options){ + + query = (typeof query === "object" + ? "" + query.query + : "" + query + ).toLowerCase(); + + //let encoded = this.encoder.encode(query).join(" "); + let cache = this.cache.get(query); + if(!cache){ + cache = this.search(query, limit, options); + if(cache.then){ + const self = this; + cache.then(function(cache){ + self.cache.set(query, cache); + return cache; + }); + } + this.cache.set(query, cache); + } + return cache; +} + +/** + * @param {boolean|number=} limit + * @constructor + */ + +function CacheClass(limit){ + /** @private */ + this.limit = (!limit || limit === true) ? 1000 : limit; + /** @private */ + this.cache = new Map(); + /** @private */ + this.last = ""; +} + +CacheClass.prototype.set = function(key, value){ + //if(!this.cache.has(key)){ + this.cache.set(this.last = key, value); + if(this.cache.size > this.limit){ + this.cache.delete(this.cache.keys().next().value); + } + //} +}; + +CacheClass.prototype.get = function(key){ + const cache = this.cache.get(key); + if(cache && this.last !== key){ + this.cache.delete(key); + this.cache.set(this.last = key, cache); + } + return cache; +}; + +CacheClass.prototype.remove = function(id){ + for(const item of this.cache){ + const key = item[0]; + const value = item[1]; + if(value.includes(id)){ + this.cache.delete(key); + } + } +}; + +CacheClass.prototype.clear = function(){ + this.cache.clear(); + this.last = ""; +}; + +/** @type EncoderOptions */ +const options = { + normalize: function(str){ + return str.toLowerCase(); + }, + dedupe: false +}; + +// import { pipeline } from "../../lang.js"; +// +// const whitespace = /[\p{Z}\p{S}\p{P}\p{C}]+/u; +// +// export const rtl = false; +// export const tokenize = ""; +// export default { +// encode: encode, +// rtl: rtl, +// tokenize: tokenize +// } +// +// /** +// * @param {string|number} str +// */ +// +// export function encode(str){ +// +// return pipeline.call( +// +// this, +// /* string: */ ("" + str).toLowerCase(), +// /* normalize: */ false, +// /* split: */ whitespace, +// /* collapse: */ false +// ); +// } + +// KeystoreObj.prototype.destroy = function(){ +// this.index = null; +// this.keys = null; +// this.proxy = null; +// }; + +function _slice(self, start, end, splice){ + let arr = []; + for(let i = 0, index; i < self.index.length; i++){ + index = self.index[i]; + if(start >= index.length){ + start -= index.length; + } + else { + const tmp = index[splice ? "splice" : "slice"](start, end); + const length = tmp.length; + if(length){ + arr = arr.length + ? arr.concat(tmp) + : tmp; + end -= length; + if(splice) self.length -= length; + if(!end) break; + } + start = 0; + } + } + return arr; +} + +/** + * @param arr + * @constructor + */ + +function KeystoreArray(arr){ + + if(!this){ + return new KeystoreArray(arr); + } + + this.index = arr ? [arr] : []; + this.length = arr ? arr.length : 0; + const self = this; + + return /*this.proxy =*/ new Proxy([], { + get(target, key) { + if(key === "length"){ + return self.length; + } + if(key === "push"){ + return function(value){ + self.index[self.index.length - 1].push(value); + self.length++; + } + } + if(key === "pop"){ + return function(){ + if(self.length){ + self.length--; + return self.index[self.index.length - 1].pop(); + } + } + } + if(key === "indexOf"){ + return function(key){ + let index = 0; + for(let i = 0, arr, tmp; i < self.index.length; i++){ + arr = self.index[i]; + //if(!arr.includes(key)) continue; + tmp = arr.indexOf(key); + if(tmp >= 0) return index + tmp; + index += arr.length; + } + return -1; + } + } + if(key === "includes"){ + return function(key){ + for(let i = 0; i < self.index.length; i++){ + if(self.index[i].includes(key)){ + return true; + } + } + return false; + } + } + if(key === "slice"){ + return function(start, end){ + return _slice( + self, + start || 0, + end || self.length, + false + ); + } + } + if(key === "splice"){ + return function(start, end){ + return _slice( + self, + start || 0, + end || self.length, + // splice: + true + ); + } + } + if(key === "constructor"){ + return Array; + } + if(typeof key === "symbol" /*|| isNaN(key)*/){ + // not supported + return; + } + const index = key / (2**31) | 0; + const arr = self.index[index]; + return arr && arr[key]; + }, + set(target, key, value){ + const index = key / (2**31) | 0; + const arr = self.index[index] || (self.index[index] = []); + arr[key] = value; + self.length++; + return true; + } + }); +} + +KeystoreArray.prototype.clear = function(){ + this.index.length = 0; +}; + +KeystoreArray.prototype.destroy = function(){ + this.index = null; + this.proxy = null; +}; + +KeystoreArray.prototype.push = function(val){}; + +/** + * @param bitlength + * @constructor + */ + +function KeystoreMap(bitlength = 8){ + + if(!this){ + return new KeystoreMap(bitlength); + } + + this.index = create_object(); + this.refs = []; + this.size = 0; + + if(bitlength > 32){ + this.crc = lcg64; + this.bit = BigInt(bitlength); + } + else { + this.crc = lcg$1; + this.bit = bitlength; + } +} + +KeystoreMap.prototype.get = function(key) { + const address = this.crc(key); + const map = this.index[address]; + return map && map.get(key); +}; + +KeystoreMap.prototype.set = function(key, value){ + const address = this.crc(key); + let map = this.index[address]; + if(map){ + let size = map.size; + map.set(key, value); + size -= map.size; + size && this.size++; + } + else { + this.index[address] = map = new Map([[key, value]]); + this.refs.push(map); + } +}; + +/** + * @param bitlength + * @constructor + */ + +function KeystoreSet(bitlength = 8){ + + if(!this){ + return new KeystoreSet(bitlength); + } + + // using plain Object with numeric key access + // just for max performance + this.index = create_object(); + this.refs = []; + + if(bitlength > 32){ + this.crc = lcg64; + this.bit = BigInt(bitlength); + } + else { + this.crc = lcg$1; + this.bit = bitlength; + } +} + +KeystoreSet.prototype.add = function(key){ + const address = this.crc(key); + let set = this.index[address]; + if(set){ + let size = set.size; + set.add(key); + size -= set.size; + size && this.size++; + } + else { + this.index[address] = set = new Set([key]); + this.refs.push(set); + } +}; + +KeystoreMap.prototype.has = +KeystoreSet.prototype.has = function(key) { + const address = this.crc(key); + const map_or_set = this.index[address]; + return map_or_set && map_or_set.has(key); +}; + +/* +KeystoreMap.prototype.size = +KeystoreSet.prototype.size = function(){ + let size = 0; + const values = Object.values(this.index); + for(let i = 0; i < values.length; i++){ + size += values[i].size; + } + return size; +}; +*/ + +KeystoreMap.prototype.delete = +KeystoreSet.prototype.delete = function(key){ + const address = this.crc(key); + const map_or_set = this.index[address]; + // set && (set.size === 1 + // ? this.index.delete(address) + // : set.delete(key)); + map_or_set && + map_or_set.delete(key) && + this.size--; +}; + +KeystoreMap.prototype.clear = +KeystoreSet.prototype.clear = function(){ + this.index = create_object(); + this.refs = []; + this.size = 0; +}; + +// KeystoreMap.prototype.destroy = +// KeystoreSet.prototype.destroy = function(){ +// this.index = null; +// this.refs = null; +// this.proxy = null; +// }; + +KeystoreMap.prototype.values = +KeystoreSet.prototype.values = function*(){ + // alternatively iterate through this.keys[] + //const refs = Object.values(this.index); + for(let i = 0; i < this.refs.length; i++){ + for(let value of this.refs[i].values()){ + yield value; + } + } +}; + +KeystoreMap.prototype.keys = +KeystoreSet.prototype.keys = function*(){ + //const values = Object.values(this.index); + for(let i = 0; i < this.refs.length; i++){ + for(let key of this.refs[i].keys()){ + yield key; + } + } +}; + +KeystoreMap.prototype.entries = +KeystoreSet.prototype.entries = function*(){ + //const values = Object.values(this.index); + for(let i = 0; i < this.refs.length; i++){ + for(let entry of this.refs[i].entries()){ + yield entry; + } + } +}; + +/** + * Linear Congruential Generator (LCG) + * @param str + * @this {KeystoreMap|KeystoreSet} + */ + +function lcg$1(str) { + let range = 2 ** this.bit - 1; + if(typeof str == "number"){ + return str & range; + } + let crc = 0, bit = this.bit + 1; + for(let i = 0; i < str.length; i++) { + crc = (crc * bit ^ str.charCodeAt(i)) & range; + } + // shift Int32 to UInt32 because negative numbers + // extremely slows down key lookup + return this.bit === 32 + ? crc + 2 ** 31 + : crc;// & 0xFFFF; +} + +/** + * @param str + * @this {KeystoreMap|KeystoreSet} + */ + +function lcg64(str) { + let range = BigInt(2) ** /** @type {!BigInt} */ (this.bit) - BigInt(1); + let type = typeof str; + if(type === "bigint"){ + return /** @type {!BigInt} */ (str) & range; + } + if(type === "number"){ + return BigInt(str) & range; + } + let crc = BigInt(0), bit = /** @type {!BigInt} */ (this.bit) + BigInt(1); + for(let i = 0; i < str.length; i++){ + crc = (crc * bit ^ BigInt(str.charCodeAt(i))) & range; + } + return crc;// & 0xFFFFFFFFFFFFFFFF; +} + +// TODO return promises instead of inner await + + +function async(callback, self, field, key, index_doc, index, data, on_done){ + + //setTimeout(function(){ + + const res = callback(field ? field + "." + key : key, JSON.stringify(data)); + + // await isn't supported by ES5 + + if(res && res["then"]){ + + res["then"](function(){ + + self.export(callback, self, field, index_doc, index + 1, on_done); + }); + } + else { + + self.export(callback, self, field, index_doc, index + 1, on_done); + } + //}); +} + +/** + * @param callback + * @param self + * @param field + * @param index_doc + * @param index + * @param on_done + * @this {Index|Document} + */ + +function exportIndex(callback, self, field, index_doc, index, on_done){ + + let return_value = true; + if (typeof on_done === 'undefined') { + return_value = new Promise((resolve) => { + on_done = resolve; + }); + } + + let key, data; + + switch(index || (index = 0)){ + + case 0: + + key = "reg"; + + // fastupdate isn't supported by export + + if(this.fastupdate){ + + data = create_object(); + + for(let key of this.reg.keys()){ + + data[key] = 1; + } + } + else { + + data = this.reg; + } + + break; + + case 1: + + key = "cfg"; + data = { + "doc": 0, + "opt": this.optimize ? 1 : 0 + }; + + break; + + case 2: + + key = "map"; + data = this.map; + break; + + case 3: + + key = "ctx"; + data = this.ctx; + break; + + default: + + if (typeof field === 'undefined' && on_done) { + + on_done(); + } + + return; + } + + async(callback, self || this, field, key, index_doc, index, data, on_done); + + return return_value; +} + +/** + * @this Index + */ + +function importIndex(key, data){ + + if(!data){ + + return; + } + + if(is_string(data)){ + + data = JSON.parse(data); + } + + switch(key){ + + case "cfg": + + this.optimize = !!data["opt"]; + break; + + case "reg": + + // fastupdate isn't supported by import + + this.fastupdate = false; + this.reg = data; + break; + + case "map": + + this.map = data; + break; + + case "ctx": + + this.ctx = data; + break; + } +} + +/** + * @this Document + */ + +function exportDocument(callback, self, field, index_doc, index, on_done){ + + let return_value; + if (typeof on_done === 'undefined') { + return_value = new Promise((resolve) => { + on_done = resolve; + }); + } + + index || (index = 0); + index_doc || (index_doc = 0); + + if(index_doc < this.field.length){ + + const field = this.field[index_doc]; + const idx = this.index[field]; + + self = this; + + //setTimeout(function(){ + + if(!idx.export(callback, self, index ? field/*.replace(":", "-")*/ : "", index_doc, index++, on_done)){ + + index_doc++; + index = 1; + + self.export(callback, self, field, index_doc, index, on_done); + } + //}); + } + else { + + let key, data; + + switch(index){ + + case 1: + + key = "tag"; + data = this.tagindex; + field = null; + break; + + case 2: + + key = "store"; + data = this.store; + field = null; + break; + + // case 3: + // + // key = "reg"; + // data = this.register; + // break; + + default: + + on_done(); + return; + } + + async(callback, this, field, key, index_doc, index, data, on_done); + } + + return return_value +} + +/** + * @this Document + */ + +function importDocument(key, data){ + + if(!data){ + + return; + } + + if(is_string(data)){ + + data = JSON.parse(data); + } + + switch(key){ + + case "tag": + + this.tagindex = data; + break; + + case "reg": + + // fastupdate isn't supported by import + + this.fastupdate = false; + this.reg = data; + + for(let i = 0, index; i < this.field.length; i++){ + + index = this.index[this.field[i]]; + index.reg = data; + index.fastupdate = false; + } + + break; + + case "store": + + this.store = data; + break; + + default: + + key = key.split("."); + const field = key[0]; + key = key[1]; + + if(field && key){ + + this.index[field].import(key, data); + } + } +} + +// COMPILER BLOCK --> + +/** + * @enum {Object} + * @const + */ + +const presets = { + + "memory": { + resolution: 1 + }, + + "performance": { + resolution: 6, + fastupdate: true, + context: { + depth: 1, + resolution: 3 + } + }, + + "match": { + tokenize: "forward" + }, + + "score": { + resolution: 9, + context: { + depth: 2, + resolution: 9 + } + } +}; + +/** + * + * @param {!IndexOptions|string} options + * @return {IndexOptions} + */ + +function apply_preset(options){ + + const preset = is_string(options) + ? options + : options["preset"]; + + if(preset){ + if(!presets[preset]){ + console.warn("Preset not found: " + preset); + } + options = Object.assign({}, presets[preset], /** @type {Object} */ (options)); + } + + return options; +} + +function apply_async(prototype){ + register$1.call(prototype, "add"); + register$1.call(prototype, "append"); + register$1.call(prototype, "search"); + register$1.call(prototype, "update"); + register$1.call(prototype, "remove"); +} + +// let cycle; +// let budget = 0; +// +// function tick(resolve){ +// cycle = null; +// budget = 0; +// resolve(); +// } + +/** + * @param {!string} key + * @this {Index|Document} + */ + +function register$1(key){ + this[key + "Async"] = function(){ + + // // prevent stack overflow of adding too much tasks to the same event loop + // // actually limit stack to 1,000,000 tasks every ~4ms + // cycle || ( + // cycle = new Promise(resolve => setTimeout(tick, 0, resolve)) + // ); + // + // // apply different performance budgets + // if(key === "update" || key === "remove" && this.fastupdate === false){ + // budget += 1000 * this.resolution; + // if(this.depth) + // budget += 1000 * this.resolution_ctx; + // } + // else if(key === "search"){ + // budget++; + // } + // else{ + // budget += 20 * this.resolution; + // if(this.depth) + // budget += 20 * this.resolution_ctx; + // } + // + // // wait for the event loop cycle + // if(budget >= 1e6){ + // await cycle; + // } + + const args = /*[].slice.call*/(arguments); + const arg = args[args.length - 1]; + let callback; + + if(typeof arg === "function"){ + callback = arg; + delete args[args.length - 1]; + } + + this.async = true; + const res = this[key].apply(this, args); + this.async = false; + res.then ? res.then(callback) + : callback(res); + return res; + }; +} + +const data = create_object(); + +/** + * @param {!string} name + */ + +function tick(name){ + + /** @type {!Object} */ + const profiler = data["profiler"] || (data["profiler"] = {}); + + profiler[name] || (profiler[name] = 0); + profiler[name]++; +} + +// COMPILER BLOCK --> +// <-- COMPILER BLOCK + +let table;// = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; +let timer; +const cache = new Map(); + +function toRadix(number, radix = 255) { + + if(!table){ + table = new Array(radix); + // the char code 0 could be a special marker + for(let i = 0; i < radix; i++) table[i] = i + 1; + table = String.fromCharCode.apply(null, table); + } + + let rixit; + let residual = number; + let result = ""; + + while(true){ + rixit = residual % radix; + result = table.charAt(rixit) + result; + residual = residual / radix | 0; + if(!residual) + break; + } + + return result; +} + +function default_compress(str){ + + { + if(timer){ + if(cache.has(str)){ + return cache.get(str); + } + } + else { + timer = setTimeout(clear); + } + } + + /* 2 ** ((level + 1.5) * 1.6 | 0) */ + + const result = toRadix( + typeof str == "number" + ? str + : lcg(str) + ); + + { + cache.size > 2e5 && cache.clear(); + cache.set(str, result); + } + + return result; +} + +function lcg(str) { + let range = 2 ** 32 - 1; + if(typeof str == "number"){ + return str & range; + } + let crc = 0, bit = 32 + 1; + for(let i = 0; i < str.length; i++) { + crc = (crc * bit ^ str.charCodeAt(i)) & range; + } + // shift up from Int32 to UInt32 range 0xFFFFFFFF + return crc + 2 ** 31; +} + +function clear(){ + timer = null; + cache.clear(); +} + +// COMPILER BLOCK --> + +// TODO: +// string + number as text +// boolean, null, undefined as ? + + +/** + * @param {!number|string} id + * @param {!string} content + * @param {boolean=} _append + * @param {boolean=} _skip_update + */ + +Index.prototype.add = function(id, content, _append, _skip_update){ + + if(content && (id || (id === 0))){ + + // todo check skip_update + //_skip_update = false; + + if(!_skip_update && !_append){ + if(this.reg.has(id)){ + return this.update(id, content); + } + } + + // do not force a string as input + // https://github.com/nextapps-de/flexsearch/issues/432 + content = this.encoder.encode(content); + const word_length = content.length; + + if(word_length){ + + // check context dupes to skip all contextual redundancy along a document + + const dupes_ctx = create_object(); + const dupes = create_object(); + const depth = this.depth; + const resolution = this.resolution; + + for(let i = 0; i < word_length; i++){ + + let term = content[this.rtl ? word_length - 1 - i : i]; + let term_length = term.length; + + // skip dupes will break the context chain + + if(term_length /*&& (term_length >= this.minlength)*/ && (depth || !dupes[term])){ + + let score = this.score + ? this.score(content, term, i, null, 0) + : get_score(resolution, word_length, i); + let token = ""; + + switch(this.tokenize){ + + case "full": + if(term_length > 2){ + for(let x = 0; x < term_length; x++){ + for(let y = term_length; y > x; y--){ + + //if((y - x) >= this.minlength){ + token = term.substring(x, y); + const partial_score = this.score + ? this.score(content, term, i, token, x) + : get_score(resolution, word_length, i, term_length, x); + this.push_index(dupes, token, partial_score, id, _append); + //} + } + } + break; + } + // fallthrough to next case when term length < 3 + case "reverse": + // skip last round (this token exist already in "forward") + if(term_length > 1){ + for(let x = term_length - 1; x > 0; x--){ + token = term[x] + token; + //if(token.length >= this.minlength){ + const partial_score = this.score + ? this.score(content, term, i, token, x) + : get_score(resolution, word_length, i, term_length, x); + this.push_index(dupes, token, partial_score, id, _append); + //} + } + token = ""; + } + + // fallthrough to next case to apply forward also + case "forward": + if(term_length > 1){ + for(let x = 0; x < term_length; x++){ + token += term[x]; + //if(token.length >= this.minlength){ + this.push_index(dupes, token, score, id, _append); + //} + } + break; + } + + // fallthrough to next case when token has a length of 1 + default: + // case "strict": + + // todo move boost to search + // if(this.boost){ + // score = Math.min((score / this.boost(content, term, i)) | 0, resolution - 1); + // } + + this.push_index(dupes, term, score, id, _append); + + // context is just supported by tokenizer "strict" + if(depth){ + + if((word_length > 1) && (i < (word_length - 1))){ + + // check inner dupes to skip repeating words in the current context + const dupes_inner = create_object(); + const resolution = this.resolution_ctx; + const keyword = term; + const size = Math.min(depth + 1, word_length - i); + + dupes_inner[keyword] = 1; + + for(let x = 1; x < size; x++){ + + term = content[this.rtl ? word_length - 1 - i - x : i + x]; + + if(term /*&& (term.length >= this.minlength)*/ && !dupes_inner[term]){ + + dupes_inner[term] = 1; + + const context_score = this.score + ? this.score(content, keyword, i, term, x) + : get_score(resolution + ((word_length / 2) > resolution ? 0 : 1), word_length, i, size - 1, x - 1); + const swap = this.bidirectional && (term > keyword); + this.push_index(dupes_ctx, swap ? keyword : term, context_score, id, _append, swap ? term : keyword); + } + } + } + } + } + } + } + + this.fastupdate || this.reg.add(id); + } + else { + content = ""; + } + } + + if(this.db){ + // when the term has no valid content (e.g. empty), + // then it was not added to the ID registry for removal + content || this.commit_task.push({ "del": id }); + this.commit_auto && autoCommit(this); + } + + return this; +}; + +/** + * @private + * @param dupes + * @param term + * @param score + * @param id + * @param {boolean=} append + * @param {string=} keyword + */ + +Index.prototype.push_index = function(dupes, term, score, id, append, keyword){ + + let arr = keyword ? this.ctx : this.map; + let tmp; + + if(!dupes[term] || !keyword || !(tmp = dupes[term])[keyword]){ + + if(keyword){ + + dupes = tmp || (dupes[term] = create_object()); + dupes[keyword] = 1; + + if(this.compress){ + keyword = default_compress(keyword); + } + + tmp = arr.get(keyword); + tmp ? arr = tmp + : arr.set(keyword, arr = new Map()); + } + else { + + dupes[term] = 1; + } + + if(this.compress){ + term = default_compress(term); + } + + tmp = arr.get(term); + tmp ? arr = tmp : arr.set(term, arr = tmp = []); + // the ID array will be upgraded dynamically + arr = arr[score] || (arr[score] = []); + + if(!append || !arr.includes(id)){ + + // auto-upgrade to keystore array if max size exceeded + { + if(arr.length === 2**31-1 /*|| !(arr instanceof KeystoreArray)*/){ + const keystore = new KeystoreArray(arr); + if(this.fastupdate){ + for(let value of this.reg.values()){ + if(value.includes(arr)){ + value[value.indexOf(arr)] = keystore; + } + } + } + tmp[score] = arr = keystore; + } + } + + arr.push(id); + + // add a reference to the register for fast updates + if(this.fastupdate){ + const tmp = this.reg.get(id); + tmp ? tmp.push(arr) + : this.reg.set(id, [arr]); + } + } + } +}; + +/** + * @param {number} resolution + * @param {number} length + * @param {number} i + * @param {number=} term_length + * @param {number=} x + * @returns {number} + */ + +function get_score(resolution, length, i, term_length, x){ + + // console.log("resolution", resolution); + // console.log("length", length); + // console.log("term_length", term_length); + // console.log("i", i); + // console.log("x", x); + // console.log((resolution - 1) / (length + (term_length || 0)) * (i + (x || 0)) + 1); + + // the first resolution slot is reserved for the best match, + // when a query matches the first word(s). + + // also to stretch score to the whole range of resolution, the + // calculation is shift by one and cut the floating point. + // this needs the resolution "1" to be handled additionally. + + // do not stretch the resolution more than the term length will + // improve performance and memory, also it improves scoring in + // most cases between a short document and a long document + + return i && (resolution > 1) ? ( + + (length + (term_length || 0)) <= resolution ? + + i + (x || 0) + : + ((resolution - 1) / (length + (term_length || 0)) * (i + (x || 0)) + 1) | 0 + ): + 0; +} + +/* + from -> res[score][id] + to -> [id] +*/ + +/** + * Aggregate the union of a single raw result + * @param {!Array} result + * @param {!number} limit + * @param {number=} offset + * @param {boolean=} enrich + * @return Array + */ + +function resolve_default(result, limit, offset, enrich){ + + // fast path: when there is just one slot in the result + if(result.length === 1){ + result = result[0]; + result = offset || (result.length > limit) + ? (limit + ? result.slice(offset, offset + limit) + : result.slice(offset) + ) + : result; + return enrich + ? enrich_result(result) + : result; + } + + // this is an optimized workaround instead of + // just doing result = concat(result) + + let final = []; + + for(let i = 0, arr, len; i < result.length; i++){ + if(!(arr = result[i]) || !(len = arr.length)) continue; + + if(offset){ + // forward offset pointer + if(offset >= len){ + offset -= len; + continue; + } + // apply offset pointer when length differs + if(offset < len){ + arr = limit + ? arr.slice(offset, offset + limit) + : arr.slice(offset); + len = arr.length; + offset = 0; + } + } + + if(!final.length){ + // fast path: when limit was reached in first slot + if(len >= limit){ + if(len > limit){ + arr = arr.slice(0, limit); + } + return enrich + ? enrich_result(arr) + : arr; + } + final = [arr]; + } + else { + if(len > limit){ + arr = arr.slice(0, limit); + len = arr.length; + } + final.push(arr); + } + + // reduce limit + limit -= len; + + // todo remove + // if(limit < 0){ + // throw new Error("Impl.Error"); + // } + + // break if limit was reached + if(!limit){ + break; + } + } + + // todo remove + if(!final.length){ + //throw new Error("No results found"); + return final; + } + + final = final.length > 1 + ? concat(final) + : final[0]; + + return enrich + ? enrich_result(final) + : final; +} + +function enrich_result(ids){ + for(let i = 0; i < ids.length; i++){ + ids[i] = { + score: i, + id: ids[i] + }; + } + return ids; +} + +// import xor from "./xor.js"; +// import and from "./and.js"; +// import not from "./not.js"; + +Resolver.prototype.or = function(){ + + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.or.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.or.apply(this, first_argument); + } + } + + // for(let i = 0; i < args.length; i++){ + // if(args[i].result instanceof Promise){ + // return; + // } + // } + + // if(args.length < 2){ + // if(first_argument.index){ + // first_argument.resolve = false; + // const result = first_argument.index.search(first_argument); + // if(result instanceof Promise){ + // result.then(function(result){ + // final = self.result.concat(result); + // self.result = resolver(final, limit, offset, enrich, !resolve); + // return resolve ? self.result : self; + // }); + // } + // else{ + // final = this.result.concat(result); + // this.result = resolver(final, limit, offset, enrich, !resolve); + // return resolve ? this.result : this; + // } + // } + // } + + let final = []; + let promises = []; + let limit = 0, offset = 0, enrich, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.and){ + result = this.and(query.and); + } + else if(query.xor){ + result = this.xor(query.xor); + } + else if(query.not){ + result = this.not(query.not); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + enrich = query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + self.result.length && (final = [self.result].concat(final)); + self.result = resolver(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? self.result : self; + }); + } + + this.result.length && (final = [this.result].concat(final)); + this.result = resolver(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? this.result : this; +}; + +/** + * Aggregate the union of N raw results + * @param result + * @param limit + * @param offset + * @param enrich + * @param resolve + * @param boost + * @return {Array} + */ + +function resolver(result, limit, offset, enrich, resolve, boost){ + + if(!result.length){ + // todo remove + //console.log("Empty Result") + return result; + } + + if(typeof limit === "object"){ + offset = limit.offset || 0; + enrich = limit.enrich || false; + limit = limit.limit || 0; + } + + if(result.length < 2){ + // todo remove + //console.log("Single Result") + if(resolve){ + return resolve_default(result[0], limit, offset, enrich); + } + else { + return result[0]; + } + } + + let final = []; + let count = 0; + let dupe = create_object(); + let maxres = get_max_len(result); + + for(let j = 0, ids; j < maxres; j++){ + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res) continue; + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + if(!dupe[id]){ + dupe[id] = 1; + if(offset){ + offset--; + continue; + } + if(resolve){ + final.push(id); + } + else { + // shift resolution by boost (inverse) + const index = j + (i ? boost : 0); + final[index] || (final[index] = []); + final[index].push(id); + } + if(limit && ++count === limit){ + //this.boost = 0; + return final; + } + } + } + } + } + + //this.boost = 0; + return final; +} + +// import xor from "./xor.js"; +// import or from "./or.js"; +// import not from "./not.js"; + +Resolver.prototype.and = function(){ + if(this.result.length){ + + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.and.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.and.apply(this, first_argument); + } + } + + // for(let i = 0; i < args.length; i++){ + // if(args[i].result instanceof Promise){ + // return; + // } + // } + + // if(args.length < 2){ + // if(first_argument.index){ + // first_argument.resolve = false; + // return first_argument.index.search(first_argument); + // } + // } + + let final = []; + let promises = []; + let limit = 0, offset = 0, enrich, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.or){ + result = this.or(query.or); + } + else if(query.xor){ + result = this.xor(query.xor); + } + else if(query.not){ + result = this.not(query.not); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + enrich = query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + final = [self.result].concat(final); + self.result = intersect$1(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? self.result : self; + }); + } + + final = [this.result].concat(final); + this.result = intersect$1(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? this.result : this; + } + return this; +}; + +/** + * Aggregate the intersection of N raw results + * @param result + * @param limit + * @param offset + * @param enrich + * @param resolve + * @param boost + * @return {Array} + */ + +function intersect$1(result, limit, offset, enrich, resolve, boost){ + + // if(!result.length){ + // // todo remove + // console.log("Empty Result") + // return result; + // } + + if(result.length < 2){ + // todo remove + //console.log("Single Result") + return []; + // if(resolve){ + // return default_resolver(result[0], limit, offset, enrich); + // } + // else{ + // return result[0]; + // } + } + + let final = []; + let count = 0; + + // fast path single slot + // if(result.length < 2){ + // if(limit || offset){ + // let res = result[0]; + // for(let j = 0, ids; j < res.length; j++){ + // ids = res[j]; + // if(!ids) continue; + // for(let k = 0, id; k < ids.length; k++){ + // id = ids[k]; + // if(offset){ + // offset--; + // continue; + // } + // if(resolve){ + // final.push(id); + // } + // else{ + // final[j + this.boost] || (final[j + this.boost] = []); + // final[j + this.boost].push(id); + // } + // if(limit && ++count === limit){ + // this.boost = 0; + // return final; + // } + // } + // } + // } + // this.boost = 0; + // return result[0]; + // } + + let contain = create_object(); + let maxres = get_max_len(result); + if(!maxres) return final; + + // for(let j = 0, ids, res = result[0]; j < res.length; j++){ + // ids = res[j]; + // for(let k = 0; k < ids.length; k++){ + // contain[ids[k]] = 1; + // } + // } + + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res || !res.length) return []; + let contain_new = create_object(); + let match = 0; + let last_round = i === result.length - 1; + + for(let j = 0, ids; j < maxres; j++){ + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id, min; k < ids.length; k++){ + id = ids[k]; + // fill in first round + if(!i){ + // shift resolution +1 + // shift resolution by boost (inverse) + contain_new[id] = j + 1 + (i ? boost : 0); + match = 1; + } + // result in last round + else if(last_round){ + if((min = contain[id])){ + match = 1; + //if(!contain_new[id]){ + if(offset){ + offset--; + continue; + } + if(resolve){ + final.push(id); + } + else { + // reduce resolution -1 + min--; + if(j < min) min = j; + final[min] || (final[min] = []); + final[min].push(id); + } + if(limit && ++count === limit){ + //this.boost = 0; + return final; + } + // shift resolution +1 + //contain_new[id] = min + 1; + //} + } + } + // check for intersection + else if((min = contain[id])){ + // shift resolution +1 + if(j + 1 < min) min = j + 1; + contain_new[id] = min; + match = 1; + } + } + } + + if(!match){ + //this.boost = 0; + return []; + } + + contain = contain_new; + } + + //this.boost = 0; + return final; +} + +// import or from "./or.js"; +// import and from "./and.js"; +// import not from "./not.js"; + +Resolver.prototype.xor = function(){ + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.xor.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.xor.apply(this, first_argument); + } + } + + let final = []; + let promises = []; + let limit = 0, offset = 0, enrich, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.or){ + result = this.or(query.or); + } + else if(query.and){ + result = this.and(query.and); + } + else if(query.not){ + result = this.not(query.not); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + enrich = query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + self.result.length && (final = [self.result].concat(final)); + self.result = exclusive(final, limit, offset, enrich, !resolve, self.boostval); + return resolve ? self.result : self; + }); + } + + this.result.length && (final = [this.result].concat(final)); + this.result = exclusive(final, limit, offset, enrich, !resolve, self.boostval); + return resolve ? this.result : this; +}; + +/** + * @param result + * @param limit + * @param offset + * @param enrich + * @param resolve + * @param boost + * @return {Array} + */ + +function exclusive(result, limit, offset, enrich, resolve, boost){ + + if(!result.length){ + // todo remove + //console.log("Empty Result") + return result; + } + + if(result.length < 2){ + // todo remove + //console.log("Single Result") + if(resolve){ + return resolve_default(result[0], limit, offset, enrich); + } + else { + return result[0]; + } + } + + const final = []; + const check = create_object(); + + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res) continue; + + for(let j = 0, ids; j < res.length; j++){ + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + check[id] + ? check[id]++ + : check[id] = 1; + } + } + } + + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res) continue; + + for(let j = 0, ids; j < res.length; j++){ + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + if(check[id] === 1){ + if(resolve){ + final.push(id); + } + else { + // shift resolution by boost (inverse) + const index = j + (i ? boost : 0); + final[index] || (final[index] = []); + final[index].push(id); + } + } + } + } + } + + //this.boost = 0; + return final; +} + +// import or from "./or.js"; +// import and from "./and.js"; +// import xor from "./xor.js"; + +Resolver.prototype.not = function(){ + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.not.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.not.apply(this, first_argument); + } + } + + let final = []; + let promises = []; + let limit = 0, offset = 0, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.or){ + result = this.or(query.or); + } + else if(query.and){ + result = this.and(query.and); + } + else if(query.xor){ + result = this.xor(query.xor); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + self.result = exclusion.call(self, final, limit, offset, resolve); + return resolve ? self.result : self; + }); + } + + this.result = exclusion.call(this, final, limit, offset, resolve); + return resolve ? this.result : this; +}; + +/** + * @param result + * @param limit + * @param offset + * @param resolve + * @this Resolver + * @return {Array} + */ + +function exclusion(result, limit, offset, resolve){ + + if(!result.length){ + return this.result; + } + + const final = []; + const exclude = new Set(result.flat().flat()); + + for(let j = 0, ids; j < this.result.length; j++){ + ids = this.result[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + if(!exclude.has(id)){ + if(resolve){ + final.push(id); + } + else { + final[j] || (final[j] = []); + final[j].push(id); + } + } + } + } + + return final; +} + +/** + * @param result + * @constructor + */ + +function Resolver(result){ + if(!this){ + return new Resolver(result); + } + if(result && result.index){ + result.resolve = false; + this.index = result.index; + return result.index.search(result); + } + if(result.constructor === Resolver){ + // todo test this branch + //console.log("Resolver Loopback") + return result; + } + this.index = null; + this.result = result || []; + this.boostval = 0; +} + +Resolver.prototype.limit = function(limit){ + if(this.result.length){ + const final = []; + let count = 0; + for(let j = 0, ids; j < this.result.length; j++){ + ids = this.result[j]; + if(ids.length + count < limit){ + final[j] = ids; + count += ids.length; + } + else { + final[j] = ids.slice(0, limit - count); + this.result = final; + break; + } + } + } + return this; +}; + +Resolver.prototype.offset = function(offset){ + if(this.result.length){ + const final = []; + let count = 0; + for(let j = 0, ids; j < this.result.length; j++){ + ids = this.result[j]; + if(ids.length + count < offset){ + count += ids.length; + } + else { + final[j] = ids.slice(offset - count); + count = offset; + } + } + this.result = final; + } + return this; +}; + +Resolver.prototype.boost = function(boost){ + this.boostval += boost; + return this; +}; + +Resolver.prototype.resolve = function(limit, offset, enrich){ + set_resolve(1); + const result = this.result; + this.index = null; + this.result = null; + + if(result.length){ + if(typeof limit === "object"){ + enrich = limit.enrich; + offset = limit.offset; + limit = limit.limit; + } + return resolve_default(result, limit || 100, offset, enrich); + } + + return result; +}; + +/* + + from -> result[ + res[score][id], + res[score][id], + ] + + to -> [id] + + */ + +/** + * Implementation based on Object[key] provides better suggestions + * capabilities and has less performance scaling issues on large indexes. + * + * @param arrays + * @param limit + * @param offset + * @param {boolean|Array=} suggest + * @param {boolean=} resolve + * @returns {Array} + */ + +function intersect(arrays, limit, offset, suggest, resolve) { + + const length = arrays.length; + + // todo remove + // if(length < 2){ + // throw new Error("Not optimized intersect"); + // } + + let result = []; + let size = 0; + let check; + let check_suggest; + let check_new; + let res_arr; + + if(suggest){ + suggest = []; + } + + // 1. a reversed order prioritize the order of words from a query + // because it ends with the first term. + // 2. process terms in reversed order often has advantage for + // the fast path "end reached". + + // alternatively the results could be sorted by length up + //arrays.sort(sort_by_length_up); + + // todo the outer loop should be the res array instead of term array, + // this isn't just simple because the intersection calculation needs to reflect this + //const maxlen = get_max_len(arrays); + + for(let x = length - 1, found; x >= 0; x--){ + //for(let x = 0, found; x < length; x++){ + + res_arr = arrays[x]; + check_new = create_object(); + found = !check; + + // process relevance in forward order (direction is + // important for adding IDs during the last round) + + for(let y = 0, ids; y < res_arr.length; y++){ + + ids = res_arr[y]; + if(!ids || !ids.length) continue; + + for(let z = 0, id; z < ids.length; z++){ + + id = ids[z]; + + // check exists starting from the 2nd slot + if(check){ + if(check[id]){ + + // check if in last round + if(!x){ + //if(x === length - 1){ + + if(offset){ + offset--; + } + else { + + result[size++] = id; + + if(size === limit){ + // fast path "end reached" + return result /*resolve === false + ? { result, suggest } + :*/ + } + } + } + + if(x || suggest){ + //if((x < length - 1) || suggest){ + check_new[id] = 1; + } + + found = true; + } + + if(suggest){ + + if(!check_suggest[id]){ + check_suggest[id] = 1; + const arr = suggest[y] || (suggest[y] = []); + arr.push(id); + } + + // OLD: + // + // check_idx = (check_suggest[id] || 0) + 1; + // check_suggest[id] = check_idx; + // + // // do not adding IDs which are already included in the result (saves one loop) + // // the first intersection match has the check index 2, so shift by -2 + // + // if(check_idx < length){ + // + // const tmp = suggest[check_idx - 2] || (suggest[check_idx - 2] = []); + // tmp[tmp.length] = id; + // } + } + } + else { + + // pre-fill in first round + check_new[id] = 1; + } + } + } + + if(suggest){ + + // re-use the first pre-filled check for suggestions + check || (check_suggest = check_new); + } + else if(!found){ + + return []; + } + + check = check_new; + } + + // return intermediate result + // if(resolve === false){ + // return { result, suggest }; + // } + + if(suggest){ + + // needs to iterate in reverse direction + for(let x = suggest.length - 1, ids, len; x >= 0; x--){ + + ids = suggest[x]; + len = ids.length; + + for(let y = 0, id; y < len; y++){ + + id = ids[y]; + + if(!check[id]){ + + if(offset){ + offset--; + } + else { + + result[size++] = id; + + if(size === limit){ + // fast path "end reached" + return result; + } + } + + check[id] = 1; + } + } + } + } + + return result; +} + +/** + * @param mandatory + * @param arrays + * @returns {Array} + */ + +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.push(id); + } + } + } + } + + return result; +} + + +/** + * Implementation based on Array.includes() provides better performance, + * but it needs at least one word in the query which is less frequent. + * Also on large indexes it does not scale well performance-wise. + * This strategy also lacks of suggestion capabilities (matching & sorting). + * + * @param arrays + * @param limit + * @param offset + * @param {boolean|Array=} suggest + * @returns {Array} + */ + +// export function intersect(arrays, limit, offset, suggest) { +// +// const length = arrays.length; +// let result = []; +// let check; +// +// // determine shortest array and collect results +// // from the sparse relevance arrays +// +// let smallest_size; +// let smallest_arr; +// let smallest_index; +// +// for(let x = 0; x < length; x++){ +// +// const arr = arrays[x]; +// const len = arr.length; +// +// let size = 0; +// +// for(let y = 0, tmp; y < len; y++){ +// +// tmp = arr[y]; +// +// if(tmp){ +// +// size += tmp.length; +// } +// } +// +// if(!smallest_size || (size < smallest_size)){ +// +// smallest_size = size; +// smallest_arr = arr; +// smallest_index = x; +// } +// } +// +// smallest_arr = smallest_arr.length === 1 ? +// +// smallest_arr[0] +// : +// concat(smallest_arr); +// +// if(suggest){ +// +// suggest = [smallest_arr]; +// check = create_object(); +// } +// +// let size = 0; +// let steps = 0; +// +// // process terms in reversed order often results in better performance. +// // the outer loop must be the words array, using the +// // smallest array here disables the "fast fail" optimization. +// +// for(let x = length - 1; x >= 0; x--){ +// +// if(x !== smallest_index){ +// +// steps++; +// +// const word_arr = arrays[x]; +// const word_arr_len = word_arr.length; +// const new_arr = []; +// +// let count = 0; +// +// for(let z = 0, id; z < smallest_arr.length; z++){ +// +// id = smallest_arr[z]; +// +// let found; +// +// // process relevance in forward order (direction is +// // important for adding IDs during the last round) +// +// for(let y = 0; y < word_arr_len; y++){ +// +// const arr = word_arr[y]; +// +// if(arr.length){ +// +// found = arr.includes(id); +// +// if(found){ +// +// // check if in last round +// +// if(steps === length - 1){ +// +// if(offset){ +// +// offset--; +// } +// else{ +// +// result[size++] = id; +// +// if(size === limit){ +// +// // fast path "end reached" +// +// return result; +// } +// } +// +// if(suggest){ +// +// check[id] = 1; +// } +// } +// +// break; +// } +// } +// } +// +// if(found){ +// +// new_arr[count++] = id; +// } +// } +// +// if(suggest){ +// +// suggest[steps] = new_arr; +// } +// else if(!count){ +// +// return []; +// } +// +// smallest_arr = new_arr; +// } +// } +// +// if(suggest){ +// +// // needs to iterate in reverse direction +// +// for(let x = suggest.length - 1, arr, len; x >= 0; x--){ +// +// arr = suggest[x]; +// len = arr && arr.length; +// +// if(len){ +// +// for(let y = 0, id; y < len; y++){ +// +// id = arr[y]; +// +// if(!check[id]){ +// +// check[id] = 1; +// +// if(offset){ +// +// offset--; +// } +// else{ +// +// result[size++] = id; +// +// if(size === limit){ +// +// // fast path "end reached" +// +// return result; +// } +// } +// } +// } +// } +// } +// } +// +// return result; +// } + +// COMPILER BLOCK --> + +let global_resolve = 1; +function set_resolve(resolve){ + global_resolve = resolve; +} + +/** + * @param {string|SearchOptions} query + * @param {number|SearchOptions=} limit + * @param {SearchOptions=} options + * @returns {Array|Resolver|Promise} + */ + +Index.prototype.search = function(query, limit, options){ + + if(!options){ + if(!limit && is_object(query)){ + options = /** @type {!SearchOptions} */ (query); + query = ""; + } + else if(is_object(limit)){ + options = /** @type {!SearchOptions} */ (limit); + limit = 0; + } + } + + let result = []; + let length; + let context, suggest, offset = 0, resolve, enrich, tag; + + if(options){ + query = options.query || query; + limit = options.limit || limit; + offset = options.offset || 0; + context = options.context; + suggest = options.suggest; + resolve = (global_resolve && options.resolve !== false); + resolve || (global_resolve = 0); + enrich = resolve && options.enrich; + tag = this.db && options.tag; + } + else { + resolve = this.resolve || global_resolve; + } + + // todo: term deduplication during encoding when context is disabled + + // do not force a string as input + // https://github.com/nextapps-de/flexsearch/issues/432 + query = /** @type {Array} */ (this.encoder.encode(query)); + length = query.length; + limit || !resolve || (limit = 100); + + // fast path single term + if(length === 1){ + return single_term_query.call( + this, + query[0], // term + "", // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + // TODO: dedupe terms within encoder? + // TODO: deduplication will break the context chain + + context = this.depth && context !== false; + + // fast path single context + if(length === 2 && context && !suggest){ + return single_term_query.call( + this, + query[0], // term + query[1], // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + let maxlength = 0; + let minlength = 0; + + if(length > 1){ + + // term deduplication will break the context chain + // todo add context to dupe check + const dupes = create_object(); + const query_new = []; + + // if(context){ + // keyword = query[0]; + // dupes[keyword] = 1; + // query_new.push(keyword); + // maxlength = minlength = keyword.length; + // i = 1; + // } + + for(let i = 0, term; i < length; i++){ + + term = query[i]; + + if(term && !dupes[term]){ + + // todo add keyword check + // this fast path can't apply to persistent indexes + if(!suggest && !(this.db) && !this.get_array(term/*, keyword*/)){ + + // fast path "not found" + return resolve + ? result + : new Resolver(result); + } + else { + + query_new.push(term); + dupes[term] = 1; + } + + const term_length = term.length; + maxlength = Math.max(maxlength, term_length); + minlength = minlength ? Math.min(minlength, term_length) : term_length; + } + // else if(term && (!this.depth || context === false)){ + // query_new.push(term); + // } + } + + query = query_new; + length = query.length; + } + + // the term length could be changed after deduplication + + if(!length){ + return resolve + ? result + : new Resolver(result); + } + + let index = 0, keyword; + + // fast path single term + if(length === 1){ + return single_term_query.call( + this, + query[0], // term + "", // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + // fast path single context + if(length === 2 && context && !suggest){ + return single_term_query.call( + this, + query[0], // term + query[1], // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + if(length > 1){ + if(context){ + // start with context right away + keyword = query[0]; + index = 1; + } + // todo + else if(maxlength > 9 && (maxlength / minlength) > 3){ + // sorting terms will break the context chain + // bigger terms has less occurrence + // this might also reduce the intersection task + // todo check intersection order + query.sort(sort_by_length_down); + } + } + + if(this.db){ + + if(this.db.search){ + // when the configuration is not supported it returns false + const result = this.db.search(this, query, limit, offset, suggest, resolve, enrich, tag); + if(result !== false) return result; + } + + const self = this; + return (async function(){ + + for(let arr, term; index < length; index++){ + + term = query[index]; + + if(keyword){ + + arr = await self.get_array(term, keyword); + arr = add_result( + arr, + result, + suggest, + self.resolution_ctx, + /** @type {!number} */ (limit), + offset, + length === 2 + /*, term, keyword*/ + ); + + // the context is a moving window where the keyword is going forward like a cursor + // 1. when suggestion enabled just forward keyword if term was found + // 2. as long as the result is empty forward the pointer also + if(!suggest || (arr !== false) || !result.length){ + keyword = term; + } + } + else { + + arr = await self.get_array(term); + arr = add_result( + arr, + result, + suggest, + self.resolution, + /** @type {!number} */ (limit), + offset, + length === 1 + /*, term*/ + ); + } + + // limit reached + if(arr){ + return arr; + } + + // apply suggestions on last loop + if(suggest && (index === length - 1)){ + let length = result.length; + if(!length){ + // fallback to non-contextual search when no result was found + if(keyword){ + keyword = ""; + index = -1; + continue; + } + return result; + } + else if(length === 1){ + return resolve + ? resolve_default(result[0], /** @type {number} */ (limit), offset) + : new Resolver(result[0]); + } + } + } + + return resolve + ? intersect(result, /** @type {number} */ (limit), offset, suggest) + : new Resolver(result[0]) + }()); + } + + for(let arr, term; index < length; index++){ + + term = query[index]; + + if(keyword){ + + arr = this.get_array(term, keyword); + arr = /*this.*/add_result( + arr, + result, + suggest, + this.resolution_ctx, + /** @type {!number} */ (limit), + offset, + length === 2 + /*, term, keyword*/ + ); + + // 1. when suggestion enabled just forward keyword if term was found + // 2. as long as the result is empty forward the pointer also + if(!suggest || (arr !== false) || !result.length){ + keyword = term; + } + } + else { + + arr = this.get_array(term); + arr = /*this.*/add_result( + arr, + result, + suggest, + this.resolution, + /** @type {!number} */ (limit), + offset, + length === 1 + /*, term*/ + ); + } + + // limit reached + if(arr){ + return /** @type {Array} */ (arr); + } + + // apply suggestions on last loop + if(suggest && (index === length - 1)){ + const length = result.length; + if(!length){ + // fallback to non-contextual search when no result was found + if(keyword){ + keyword = ""; + index = -1; + continue; + } + return result; + } + else if(length === 1){ + return resolve + ? resolve_default(result[0], limit, offset) + : new Resolver(result[0]); + } + } + } + + return resolve + ? intersect(result, limit, offset, suggest) + : new Resolver(result[0]); +}; + +/** + * @param term + * @param keyword + * @param limit + * @param offset + * @param resolve + * @param enrich + * @param tag + * @this Index + * @return {Array|Resolver} + */ + +function single_term_query(term, keyword, limit, offset, resolve, enrich, tag){ + + const result = this.get_array(term, keyword, limit, offset, resolve, enrich, tag); + + if(this.db){ + return result.then(function(result){ + if(resolve) return result; + return result && result.length + ? (resolve ? resolve_default(result, limit, offset): new Resolver(result)) + : resolve ? [] : new Resolver([]); + }); + } + + return result && result.length + ? (resolve ? resolve_default(result, limit, offset) : new Resolver(result)) + : resolve ? [] : new Resolver([]); +} + +/** + * Returns a 1-dimensional finalized array when the result is done (fast path return), + * returns false when suggestions is enabled and no result was found, + * or returns nothing when a set was pushed successfully to the results + * + * @private + * @param {Array} arr + * @param {Array} result + * @param {Array} suggest + * @param {number} resolution + * @param {number} limit + * @param {number} offset + * @param {boolean} single_term + * @return {Array|boolean|undefined} + */ + +function add_result(arr, result, suggest, resolution, limit, offset, single_term/*, term, keyword*/){ + + let word_arr = []; + //let arr;// = keyword ? this.ctx : this.map; + //arr = this.get_array(term, keyword); + + if(arr){ + + //const resolution = Math.min(arr.length, keyword ? this.resolution_ctx : this.resolution); + // apply reduced resolution for queries + resolution = Math.min(arr.length, resolution); + + for(let x = 0, size = 0, tmp; x < resolution; x++){ + if((tmp = arr[x])){ + + if(offset){ + // apply offset right here on single terms + if(tmp && single_term){ + if(tmp.length <= offset){ + offset -= tmp.length; + tmp = null; + } + else { + tmp = tmp.slice(offset); + offset = 0; + } + } + } + + if(tmp){ + + // keep score (sparse array): + word_arr[x] = tmp; + // simplified score order: + //word_arr.push(tmp); + + if(single_term){ + size += tmp.length; + if(size >= limit){ + // fast path: + // a single term does not need to pre-collect results + break; + } + } + } + } + } + + if(word_arr.length){ + if(single_term){ + // fast path optimization + // offset was already applied at this point + // return an array will stop the query process immediately + return resolve_default(word_arr, limit, 0); + } + + result.push(word_arr); + // return nothing will continue the query + return; + } + } + + // 1. return an empty array will stop the loop + // 2. return a false value to prevent stop when using suggestions + return !suggest && word_arr; +} + +Index.prototype.get_array = function(term, keyword, limit, offset, resolve, enrich, tag){ + + let arr, swap; + + if(keyword){ + swap = this.bidirectional && (term > keyword); + } + + if(this.compress){ + term = default_compress(term); + keyword && (keyword = default_compress(keyword)); + } + + if(this.db){ + return keyword + ? this.db.get( + swap ? keyword : term, // term + swap ? term : keyword, // ctx + limit, + offset, + resolve, + enrich, + tag + ) + : this.db.get( + term, + "", // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + if(keyword){ + // the frequency of the starting letter is slightly less + // on the last half of the alphabet (m-z) in almost every latin language, + // so we sort downwards (https://en.wikipedia.org/wiki/Letter_frequency) + arr = this.ctx.get(swap ? term : keyword); + arr = arr && arr.get(swap ? keyword : term); + } + else { + arr = this.map.get(term); + } + + return arr; +}; + +// COMPILER BLOCK --> + +/** + * @param {boolean=} _skip_deletion + */ + +Index.prototype.remove = function(id, _skip_deletion){ + + const refs = this.reg.size && ( + this.fastupdate + ? this.reg.get(id) + : this.reg.has(id) + ); + + if(refs){ + + if(this.fastupdate){ + + // fast updates did not fully cleanup the key entries + + for(let i = 0, tmp; i < refs.length; i++){ + if((tmp = refs[i])){ + // todo check + //if(tmp.length < 1) throw new Error("invalid length"); + //if(tmp.indexOf(id) < 0) throw new Error("invalid id"); + if(tmp.length < 2){ + tmp.pop(); + } + else { + const index = tmp.indexOf(id); + index === refs.length - 1 + ? tmp.pop() + : tmp.splice(index, 1); + } + } + } + + // todo variation which cleans up, requires to push [ctx, key] instead of arr to the index.reg + // for(let i = 0, arr, term, keyword; i < refs.length; i++){ + // arr = refs[i]; + // if(typeof arr === "string"){ + // arr = this.map.get(term = arr); + // } + // else{ + // arr = this.ctx.get(keyword = arr[0]); + // arr && (arr = arr.get(arr[1])); + // } + // let counter = 0, found; + // if(arr && arr.length){ + // for(let j = 0, tmp; j < arr.length; j++){ + // if((tmp = arr[j])){ + // if(!found && tmp.length){ + // const index = tmp.indexOf(id); + // if(index >= 0){ + // tmp.splice(index, 1); + // // the index [ctx, key]:[res, id] is unique + // found = 1; + // } + // } + // if(tmp.length){ + // counter++; + // if(found){ + // break; + // } + // } + // else{ + // delete arr[j]; + // } + // } + // } + // } + // if(!counter){ + // keyword + // ? this.ctx.delete(keyword) + // : this.map.delete(term); + // } + // } + } + else { + + remove_index(this.map, id/*, this.resolution*/); + this.depth && + remove_index(this.ctx, id/*, this.resolution_ctx*/); + } + + _skip_deletion || this.reg.delete(id); + } + + if(this.db){ + this.commit_task.push({ "del": id }); + this.commit_auto && autoCommit(this); + //return this.db.remove(id); + } + + // the cache could be used outside the InMemory store + if(this.cache){ + this.cache.remove(id); + } + + return this; +}; + +/** + * @param map + * @param id + * @return {number} + */ + +function remove_index(map, id){ + + // a check counter of filled resolution slots + // to prevent removing the field + let count = 0; + + if(is_array(map)){ + for(let x = 0, arr, index; x < map.length; x++){ + if((arr = map[x]) && arr.length){ + index = arr.indexOf(id); + if(index >= 0){ + if(arr.length > 1){ + arr.splice(index, 1); + count++; + } + else { + // remove resolution slot + delete map[x]; + } + // the index key:[res, id] is unique + break; + } + else { + count++; + } + } + } + } + else for(let item of map){ + const key = item[0]; + const value = item[1]; + const tmp = remove_index(value, id); + tmp ? count += tmp + : map.delete(key); + } + + return count; +} + +/**! + * FlexSearch.js + * Author and Copyright: Thomas Wilkerling + * Licence: Apache-2.0 + * Hosted by Nextapps GmbH + * https://github.com/nextapps-de/flexsearch + */ + + +/** + * @constructor + * @param {IndexOptions|string=} options Options or preset as string + * @param {Map|Set|KeystoreSet|KeystoreMap=} _register + */ + +function Index(options$1, _register){ + + if(!this){ + return new Index(options$1); + } + + tick("Index.create"); + + options$1 = options$1 + ? apply_preset(options$1) + : {}; + + /** @type ContextOptions */ + const context = options$1.context || {}; + const encoder = options$1.encode || options$1.encoder || ( + options + ); + /** @type Encoder */ + this.encoder = encoder.encode + ? encoder + : typeof encoder === "object" + ? (new Encoder(encoder) + + ) + : { encode: encoder }; + + { + this.compress = options$1.compress || options$1.compression || false; + } + + let tmp; + this.resolution = options$1.resolution || 9; + this.tokenize = tmp = options$1.tokenize || "strict"; + this.depth = (tmp === "strict" && context.depth) || 0; + this.bidirectional = context.bidirectional !== false; + this.fastupdate = !!options$1.fastupdate; + this.score = options$1.score || null; + + tmp = (options$1.keystore || 0); + tmp && (this.keystore = tmp); + + this.map = tmp && SUPPORT_KEYSTORE ? new KeystoreMap(tmp) : new Map(); + this.ctx = tmp && SUPPORT_KEYSTORE ? new KeystoreMap(tmp) : new Map(); + this.reg = _register || ( + this.fastupdate + ? (tmp && SUPPORT_KEYSTORE ? new KeystoreMap(tmp) : new Map()) + : (tmp && SUPPORT_KEYSTORE ? new KeystoreSet(tmp) : new Set()) + ); + this.resolution_ctx = context.resolution || 1; + this.rtl = (encoder.rtl) || options$1.rtl || false; + + { + this.cache = (tmp = options$1.cache || null) && new CacheClass(tmp); + } + + { + this.resolve = options$1.resolve !== false; + } + + { + if((tmp = options$1.db)){ + this.db = tmp.mount(this); + } + this.commit_auto = options$1.commit !== false; + this.commit_task = []; + this.commit_timer = null; + } +} + +{ + Index.prototype.mount = function(db){ + if(this.commit_timer){ + clearTimeout(this.commit_timer); + this.commit_timer = null; + } + return db.mount(this); + }; + Index.prototype.commit = function(replace, append){ + if(this.commit_timer){ + clearTimeout(this.commit_timer); + this.commit_timer = null; + } + return this.db.commit(this, replace, append); + }; +} + +// if(SUPPORT_RESOLVER){ +// Index.prototype.resolve = function(params){ +// return new Resolver(params); +// }; +// } + +/** + * @param {!Index} self + * @param {boolean=} replace + * @param {boolean=} append + */ + +function autoCommit(self, replace, append){ + if(!self.commit_timer){ + self.commit_timer = setTimeout(function(){ + self.commit_timer = null; + self.db.commit(self, replace, append); + }, 0); + } +} + +Index.prototype.clear = function(){ + + //this.map = new Map(); + //this.ctx = new Map(); + //this.reg = this.fastupdate ? new Map() : new Set(); + this.map.clear(); + this.ctx.clear(); + this.reg.clear(); + + { + this.cache && + this.cache.clear(); + } + + if(this.db){ + this.commit_timer && clearTimeout(this.commit_timer); + this.commit_timer = null; + this.commit_task = [{ "clear": true }]; + //return this.db.clear(); + } + + return this; +}; + +//Index.prototype.pipeline = pipeline; + +/** + * @param {!number|string} id + * @param {!string} content + */ + +Index.prototype.append = function(id, content){ + return this.add(id, content, true); +}; + +Index.prototype.contain = function(id){ + + if(this.db){ + return this.db.has(id); + } + + return this.reg.has(id); +}; + +Index.prototype.update = function(id, content){ + + // todo check the async part + if(this.async /*|| (SUPPORT_PERSISTENT && this.db)*/){ + const self = this; + const res = this.remove(id); + return res.then ? res.then( + () => self.add(id, content) + ) : this.add(id, content); + } + + return this.remove(id).add(id, content); +}; + +/** + * @param map + * @return {number} + */ + +function cleanup_index(map){ + + let count = 0; + + if(is_array(map)){ + for(let i = 0, arr; i < map.length; i++){ + (arr = map[i]) && + (count += arr.length); + } + } + else for(const item of map){ + const key = item[0]; + const value = item[1]; + const tmp = cleanup_index(value); + tmp ? count += tmp + : map.delete(key); + } + + return count; +} + +Index.prototype.cleanup = function(){ + + if(!this.fastupdate){ + console.info("Cleanup the index isn't required when not using \"fastupdate\"."); + return this; + } + + cleanup_index(this.map); + this.depth && + cleanup_index(this.ctx); + + return this; +}; + +{ + + Index.prototype.searchCache = searchCache; +} + +{ + + Index.prototype.export = exportIndex; + Index.prototype.import = importIndex; +} + +{ + + apply_async(Index.prototype); +} + +async function handler(data) { + + data = data["data"]; + + /** @type Index */ + const index = self["_index"]; + const args = data["args"]; + const task = data["task"]; + + switch(task){ + + case "init": + + /** @type IndexOptions */ + let options = data["options"] || {}; + let filepath = options.config; + if(filepath){ + options = filepath; + // will be replaced after build with the line below because + // there is an issue with closure compiler dynamic import + //options = await import(filepath); + } + + // deprecated: + // const encode = options.encode; + // if(encode && (encode.indexOf("function") === 0)){ + // options.encode = Function("return " + encode)(); + // } + + const factory = data["factory"]; + + if(factory){ + + // export the FlexSearch global payload to "self" + Function("return " + factory)()(self); + + /** @type Index */ + self["_index"] = new self["FlexSearch"]["Index"](options); + + // destroy the exported payload + delete self["FlexSearch"]; + } + else { + + self["_index"] = new Index(options); + } + + postMessage({ "id": data["id"] }); + break; + + default: + + const id = data["id"]; + const message = index[task].apply(index, args); + postMessage(task === "search" ? { "id": id, "msg": message } : { "id": id }); + } +} + +//import { promise as Promise } from "../polyfill.js"; + +let pid = 0; + +/** + * @param {IndexOptions=} options + * @constructor + */ + +function WorkerIndex(options){ + + if(!this) { + return new WorkerIndex(options); + } + + if(options); + else { + options = {}; + } + + // the factory is the outer wrapper from the build + // we use "self" as a trap for node.js + + let factory = (self||window)["_factory"]; + if(factory){ + factory = factory.toString(); + } + + const is_node_js = typeof window === "undefined" && self["exports"]; + const _self = this; + + this.worker = create(factory, is_node_js, options.worker); + this.resolver = create_object(); + + if(!this.worker){ + + return; + } + + function onmessage(msg){ + msg = msg["data"] || msg; + const id = msg["id"]; + const res = id && _self.resolver[id]; + if(res){ + res(msg["msg"]); + delete _self.resolver[id]; + } + } + + is_node_js + ? this.worker["on"]("message", onmessage) + : this.worker.onmessage = onmessage; + + if(options["config"]){ + + // when extern configuration needs to be loaded + // it needs to return a promise to await for + return new Promise(function(resolve){ + _self.resolver[++pid] = function(){ + resolve(_self); + }; + _self.worker.postMessage({ + "id": pid, + "task": "init", + "factory": factory, + "options": options + }); + }); + } + + this.worker.postMessage({ + "task": "init", + "factory": factory, + "options": options + }); +} + +register("add"); +register("append"); +register("search"); +register("update"); +register("remove"); + +function register(key){ + + WorkerIndex.prototype[key] = + WorkerIndex.prototype[key + "Async"] = function(){ + + const self = this; + const args = [].slice.call(arguments); + const arg = args[args.length - 1]; + let callback; + + if(is_function(arg)){ + callback = arg; + args.splice(args.length - 1, 1); + } + + const promise = new Promise(function(resolve){ + //setTimeout(function(){ + self.resolver[++pid] = resolve; + self.worker.postMessage({ + "task": key, + "id": pid, + "args": args + }); + //}); + }); + + if(callback){ + promise.then(callback); + return this; + } + else { + + return promise; + } + }; +} + +function create(factory, is_node_js, worker_path){ + + let worker; + + worker = is_node_js ? + // This eval will be removed when compiling, it isn't there in final build + (0, eval)('new (require("worker_threads")["Worker"])(__dirname + "/node/node.js")') + //eval('new (require("worker_threads")["Worker"])(__dirname + "/node/node.js")') + :( + factory ? + new window.Worker(URL.createObjectURL( + new Blob( + ["onmessage=" + handler.toString()], + { "type": "text/javascript" } + ) + )) + : + new window.Worker(is_string(worker_path) ? worker_path : "worker/worker.js", { type: "module" }) + ); + + return worker; +} + +// COMPILER BLOCK --> + +/** + * + * @param id + * @param content + * @param {boolean=} _append + * @returns {Document|Promise} + */ + +Document.prototype.add = function(id, content, _append){ + + if(is_object(id)){ + + content = id; + id = parse_simple(content, this.key); + } + + if(content && (id || (id === 0))){ + + if(!_append && this.reg.has(id)){ + return this.update(id, content); + } + + // this.field does not include db tag indexes + for(let i = 0, tree; i < this.field.length; i++){ + + tree = this.tree[i]; + + const index = this.index.get(this.field[i]); + if(typeof tree === "function"){ + const tmp = tree(content); + if(tmp){ + index.add(id, tmp, /* append: */ false, /* skip update: */ true); + } + } + else { + const filter = tree._filter; + if(filter && !filter(content)){ + continue; + } + if(tree.constructor === String){ + tree = ["" + tree]; + } + else if(is_string(tree)){ + tree = [tree]; + } + add_index(content, tree, this.marker, 0, index, id, tree[0], _append); + } + } + + if(this.tag){ + + //console.log(this.tag, this.tagtree) + + for(let x = 0; x < this.tagtree.length; x++){ + + let tree = this.tagtree[x]; + let field = this.tagfield[x]; + let ref = this.tag.get(field); + let dupes = create_object(); + let tags; + + if(typeof tree === "function"){ + tags = tree(content); + if(!tags) continue; + } + else { + const filter = tree._filter; + if(filter && !filter(content)){ + continue; + } + if(tree.constructor === String){ + tree = "" + tree; + } + tags = parse_simple(content, tree); + } + + if(!ref || !tags){ + ref || (console.warn("Tag '" + field + "' was not found")); + continue; + } + + if(is_string(tags)){ + tags = [tags]; + } + + for(let i = 0, tag, arr; i < tags.length; i++){ + + tag = tags[i]; + //console.log(this.tag, tag, key, field) + + if(!dupes[tag]){ + dupes[tag] = 1; + + let tmp; + tmp = ref.get(tag); + tmp ? arr = tmp : ref.set(tag, arr = []); + + if(!_append || ! /** @type {!Array|KeystoreArray} */(arr).includes(id)){ + + // auto-upgrade to keystore array if max size exceeded + { + if(arr.length === 2**31-1 /*|| !(arr instanceof KeystoreArray)*/){ + const keystore = new KeystoreArray(arr); + if(this.fastupdate){ + for(let value of this.reg.values()){ + if(value.includes(arr)){ + value[value.indexOf(arr)] = keystore; + } + } + } + ref.set(tag, arr = keystore); + } + } + + arr.push(id); + + // add a reference to the register for fast updates + if(this.fastupdate){ + const tmp = this.reg.get(id); + tmp ? tmp.push(arr) + : this.reg.set(id, [arr]); + } + } + } + } + } + } + + if(this.store && (!_append || !this.store.has(id))){ + + let payload; + + if(this.storetree){ + + payload = create_object(); + + for(let i = 0, tree; i < this.storetree.length; i++){ + tree = this.storetree[i]; + + const filter = tree._filter; + if(filter && !filter(content)){ + continue; + } + let custom; + if(typeof tree === "function"){ + custom = tree(content); + if(!custom) continue; + tree = [tree._field]; + } + else if(is_string(tree) || tree.constructor === String){ + payload[tree] = content[tree]; + continue; + } + + store_value(content, payload, tree, 0, tree[0], custom); + } + } + + this.store.set(id, payload || content); + } + } + + return this; +}; + +// TODO support generic function created from string when tree depth > 1 + +/** + * @param obj + * @param store + * @param tree + * @param pos + * @param key + * @param {*=} custom + */ + +function store_value(obj, store, tree, pos, key, custom){ + + obj = obj[key]; + + // reached target field + if(pos === (tree.length - 1)){ + + // store target value + store[key] = custom || obj; + } + else if(obj){ + + if(is_array(obj)){ + + store = store[key] = new Array(obj.length); + + for(let i = 0; i < obj.length; i++){ + // do not increase pos (an array is not a field) + store_value(obj, store, tree, pos, i); + } + } + else { + + store = store[key] || (store[key] = create_object()); + key = tree[++pos]; + store_value(obj, store, tree, pos, key); + } + } +} + +function add_index(obj, tree, marker, pos, index, id, key, _append){ + + if((obj = obj[key])){ + + // reached target field + if(pos === (tree.length - 1)){ + + // handle target value + if(is_array(obj)){ + + // append array contents so each entry gets a new scoring context + if(marker[pos]){ + for(let i = 0; i < obj.length; i++){ + index.add(id, obj[i], /* append: */ true, /* skip update: */ true); + } + return; + } + + // or join array contents and use one scoring context + obj = obj.join(" "); + } + + index.add(id, obj, _append, /* skip_update: */ true); + } + else { + + if(is_array(obj)){ + for(let i = 0; i < obj.length; i++){ + // do not increase index, an array is not a field + add_index(obj, tree, marker, pos, index, id, i, _append); + } + } + else { + key = tree[++pos]; + add_index(obj, tree, marker, pos, index, id, key, _append); + } + } + } + else { + if(index.db){ + index.remove(id); + } + } +} + +// COMPILER BLOCK --> + +/** + * @param {!string|DocumentSearchOptions} query + * @param {number|DocumentSearchOptions=} limit + * @param {DocumentSearchOptions=} options + * @param {Array=} _resolve For internal use only. + * @returns {Promise|Array} + */ + +Document.prototype.search = function(query, limit, options, _resolve){ + + if(!options){ + if(!limit && is_object(query)){ + options = /** @type {DocumentSearchOptions} */ (query); + query = ""; + } + else if(is_object(limit)){ + options = /** @type {DocumentSearchOptions} */ (limit); + limit = 0; + } + } + + let result = [], result_field = []; + let pluck, enrich, merge, suggest; + let field, tag, offset, count = 0; + + if(options){ + + // todo remove support? + if(is_array(options)){ + field = options; + options = null; + } + else { + + query = options.query || query; + pluck = options.pluck; + merge = options.merge; + field = pluck || options.field || options.index; + tag = this.tag && options.tag; + enrich = this.store && options.enrich; + suggest = options.suggest; + limit = options.limit || limit; + offset = options.offset || 0; + limit || (limit = 100); + + if(tag && (!this.db || !_resolve)){ + + if(tag.constructor !== Array){ + tag = [tag]; + } + + let pairs = []; + + for(let i = 0, field; i < tag.length; i++){ + field = tag[i]; + if(is_string(field)){ + throw new Error("A tag option can't be a string, instead it needs a { field: tag } format."); + } + // default array notation + if(field.field && field.tag){ + const value = field.tag; + if(value.constructor === Array){ + for(let k = 0; k < value.length; k++){ + pairs.push(field.field, value[k]); + } + } + else { + pairs.push(field.field, value); + } + } + // shorter object notation + else { + const keys = Object.keys(field); + for(let j = 0, key, value; j < keys.length; j++){ + key = keys[j]; + value = field[key]; + if(value.constructor === Array){ + for(let k = 0; k < value.length; k++){ + pairs.push(key, value[k]); + } + } + else { + pairs.push(key, value); + } + } + } + } + + if(!pairs.length){ + throw new Error("Your tag definition within the search options is probably wrong. No valid tags found."); + } + + // tag used as pairs from this point + tag = pairs; + + // when tags is used and no query was set, + // then just return the tag indexes + if(!query){ + + let promises = []; + if(pairs.length) for(let j = 0; j < pairs.length; j+=2){ + let ids; + if(this.db){ + const index = this.index.get(pairs[j]); + if(!index){ + { + console.warn("Tag '" + pairs[j] + ":" + pairs[j + 1] + "' will be skipped because there is no field '" + pairs[j] + "'."); + } + continue; + } + promises.push(ids = index.db.tag(pairs[j + 1], limit, offset, enrich)); + } + else { + ids = get_tag.call(this, pairs[j], pairs[j + 1], limit, offset, enrich); + } + result.push({ + "field": pairs[j], + "tag": pairs[j + 1], + "result": ids + }); + } + + if(promises.length){ + return Promise.all(promises).then(function(promises){ + for(let j = 0; j < promises.length; j++){ + result[j].result = promises[j]; + } + return result; + }); + } + + return result; + } + } + + // extend to multi field search by default + if(is_string(field)){ + field = [field]; + } + } + } + + field || (field = this.field); + let promises = !_resolve && (this.worker || this.async) && []; + let db_tag_search; + + // multi field search + // field could be a custom set of selected fields by this query + // db tag indexes are also included in this field list + for(let i = 0, res, key, len; i < field.length; i++){ + + key = field[i]; + + if(this.db && this.tag){ + // tree is missing when it is a tag-only index (db) + if(!this.tree[i]){ + continue; + } + } + + let field_options; + + if(!is_string(key)){ + field_options = key; + key = field_options.field; + query = field_options.query || query; + limit = field_options.limit || limit; + //offset = field_options.offset || offset; + suggest = (field_options.suggest || suggest); + //enrich = SUPPORT_STORE && this.store && (field_options.enrich || enrich); + } + + if(_resolve){ + res = _resolve[i]; + } + else { + let opt = field_options || options; + let index = this.index.get(key); + + if(tag){ + if(this.db){ + opt.tag = tag; + db_tag_search = index.db.support_tag_search; + opt.field = field; + } + if(!db_tag_search){ + opt.enrich = false; + } + } + if(promises){ + promises[i] = index.searchAsync(query, limit, opt); + // restore enrich state + opt && enrich && (opt.enrich = enrich); + // just collect and continue + continue; + } + else { + res = index.search(query, limit, opt); + // restore enrich state + opt && enrich && (opt.enrich = enrich); + } + } + + len = res && res.length; + + // todo when no term was matched but tag was retrieved extend suggestion to tags + // every field has to intersect against all selected tag fields + if(tag && len){ + + const arr = []; + let count = 0; + + // tags are only applied in resolve phase when it's a db + if(this.db && _resolve){ + if(!db_tag_search){ + + // retrieve tag results assigned to it's field + for(let y = field.length; y < _resolve.length; y++){ + + let ids = _resolve[y]; + let len = ids && ids.length; + + if(len){ + count++; + arr.push(ids); + } + else if(!suggest){ + // no tags found + return result; + } + } + } + } + else { + + // tag[] are pairs at this line + for(let y = 0, ids, len; y < tag.length; y+=2){ + ids = this.tag.get(tag[y]); + + if(!ids){ + { + console.warn("Tag '" + tag[y] + ":" + tag[y + 1] + "' will be skipped because there is no field '" + tag[y] + "'."); + } + if(suggest){ + continue; + } + else { + return result; + } + } + + ids = ids && ids.get(tag[y + 1]); + len = ids && ids.length; + + if(len){ + count++; + arr.push(ids); + } + else if(!suggest){ + // no tags found + return result; + } + } + } + + if(count){ + res = intersect_union(res, arr); // intersect(arr, limit, offset) + len = res.length; + if(!len && !suggest){ + // nothing matched + return result; + } + // move counter back by 1 + count--; + } + } + + if(len){ + result_field[count] = key; + result.push(res); + count++; + } + else if(field.length === 1){ + // fast path: nothing matched + return result; + } + } + + if(promises){ + if(this.db){ + // todo when a tag index is never a search index this could be extracted + // push tag promises to the end + if(tag && tag.length && !db_tag_search){ + for(let y = 0; y < tag.length; y += 2){ + // it needs to retrieve data from tag pairs + const index = this.index.get(tag[y]); + if(!index){ + { + console.warn("Tag '" + tag[y] + ":" + tag[y + 1] + "' was not found because there is no field '" + tag[y] + "'."); + } + if(suggest){ + continue; + } + else { + return result; + } + } + promises.push(index.db.tag(tag[y + 1], limit, offset, /* enrich */ false)); + } + } + } + + const self = this; + + // TODO unroll this recursion + return Promise.all(promises).then(function(result){ + return result.length + ? self.search(query, limit, options, /* resolve: */ result) + : result; + }); + } + + if(!count){ + return result; + } + if(pluck && (!enrich || !this.store)){ + return result[0]; + } + + promises = []; + + for(let i = 0, res; i < result_field.length; i++){ + + res = result[i]; + + if(enrich && res.length && !res[0].doc){ + if(!this.db){ + if(res.length){ + res = apply_enrich.call(this, res); + } + } + else { + promises.push(res = this.index.get(this.field[0]).db.enrich(res)); + } + } + + if(pluck){ + return res; + } + + result[i] = { + "field": result_field[i], + "result": res + }; + } + + if(enrich && SUPPORT_PERSISTENT && this.db && promises.length){ + return Promise.all(promises).then(function(promises){ + for(let j = 0; j < promises.length; j++){ + result[j].result = promises[j]; + } + return merge + ? merge_fields(result, limit) + : result; + }); + } + + return merge + ? merge_fields(result, limit) + : result; +}; + +// todo support Resolver +// todo when searching through multiple fields each term should +// be found at least by one field to get a valid match without +// using suggestion explicitly + +function merge_fields(fields, limit, offset){ + const final = []; + const set = create_object(); + for(let i = 0, field, res; i < fields.length; i++){ + field = fields[i]; + res = field.result; + for(let j = 0, id, entry, tmp; j < res.length; j++){ + entry = res[j]; + id = entry.id; + tmp = set[id]; + if(!tmp){ + // offset was already applied on field indexes + // if(offset){ + // offset--; + // continue; + // } + // apply limit from last round, because just fields could + // be pushed without adding new results + if(final.length === limit){ + return final; + } + entry.field = set[id] = [field.field]; + final.push(entry); + } + else { + tmp.push(field.field); + } + } + } + return final; +} + +/** + * @this Document + */ + +function get_tag(tag, key, limit, offset, enrich){ + let res = this.tag.get(tag); + if(!res){ + console.warn("Tag '" + tag + "' was not found"); + return []; + } + res = res && res.get(key); + let len = res && (res.length - offset); + + if(len && (len > 0)){ + if((len > limit) || offset){ + res = res.slice(offset, offset + limit); + } + if(enrich){ + res = apply_enrich.call(this, res); + } + return res; + } +} + +/** + * @this Document + */ + +function apply_enrich(res){ + + const arr = new Array(res.length); + + for(let x = 0, id; x < res.length; x++){ + id = res[x]; + arr[x] = { + "id": id, + "doc": this.store.get(id) + }; + } + + return arr; +} + +/**! + * FlexSearch.js + * Author and Copyright: Thomas Wilkerling + * Licence: Apache-2.0 + * Hosted by Nextapps GmbH + * https://github.com/nextapps-de/flexsearch + */ + + +/** + * @constructor + * @param {!DocumentOptions} options + */ + +function Document(options){ + + if(!this) { + return new Document(options); + } + + /** @type DocumentDescriptor */ + const document = options.document || options.doc || options; + let tmp, keystore; + + this.tree = []; + this.field = []; + this.marker = []; + this.key = ((tmp = document.key || document.id) && parse_tree(tmp, this.marker)) || "id"; + + keystore = (options.keystore || 0); + keystore && (this.keystore = keystore); + this.fastupdate = !!options.fastupdate; + this.reg = this.fastupdate + ? (keystore && SUPPORT_KEYSTORE ? new KeystoreMap(keystore) : new Map()) + : (keystore && SUPPORT_KEYSTORE ? new KeystoreSet(keystore) : new Set()); + + { + // todo support custom filter function + this.storetree = (tmp = document.store || null) && tmp !== true && []; + this.store = tmp && ( + keystore && SUPPORT_KEYSTORE + ? new KeystoreMap(keystore) + : new Map() + ); + } + + { + this.cache = (tmp = options.cache || null) && new CacheClass(tmp); + // do not apply cache again for the indexes since .searchCache() + // is just a wrapper over .search() + options.cache = false; + } + + { + this.worker = options.worker; + } + + { + // this switch is used by recall of promise callbacks + this.async = false; + } + + /** @export */ + this.index = parse_descriptor.call(this, options, document); + + { + this.tag = null; + // TODO case-insensitive tags? + if((tmp = document.tag)){ + if(typeof tmp === "string"){ + tmp = [tmp]; + } + if(tmp.length){ + this.tag = new Map(); + this.tagtree = []; + this.tagfield = []; + for(let i = 0, params, field; i < tmp.length; i++){ + params = tmp[i]; + field = params.field || params; + if(!field){ + throw new Error("The tag field from the document descriptor is undefined."); + } + if(params.custom){ + this.tagtree[i] = params.custom; + } + else { + this.tagtree[i] = parse_tree(field, this.marker); + if(params.filter){ + if(typeof this.tagtree[i] === "string"){ + // it needs an object to put a property to it + this.tagtree[i] = new String(this.tagtree[i]); + } + this.tagtree[i]._filter = params.filter; + } + } + // the tag fields needs to be hold by indices + this.tagfield[i] = field; + this.tag.set(field, new Map()); + } + } + } + } + + { + options.db && this.mount(options.db); + } +} + +{ + + Document.prototype.mount = function(db){ + + let fields = this.field; + + if(this.tag){ + // tag indexes are referenced by field + // move tags to their field indexes respectively + for(let i = 0, field; i < this.tagfield.length; i++){ + field = this.tagfield[i]; + let index = this.index.get(field); + if(!index){ + // create raw index when not exists + this.index.set(field, index = new Index({}, this.reg)); + // copy and push to the field selection + if(fields === this.field){ + fields = fields.slice(0); + } + // tag indexes also needs to be upgraded to db instances + fields.push(field); + } + // assign reference + index.tag = this.tag.get(field); + } + } + + const promises = []; + const config = { + db: db.db, + type: db.type, + fastupdate: db.fastupdate + }; + + // upgrade all indexes to db instances + for(let i = 0, index, field; i < fields.length; i++){ + config.field = field = fields[i]; + index = this.index.get(field); + const dbi = new db.constructor(db.id, config); + // take over the storage id + dbi.id = db.id; + promises[i] = dbi.mount(index); + // add an identification property + index.document = true; + if(i){ + // the register has to export just one time + // also it's needed by the index for ID contain check + index.bypass = true; + } + else { + // the datastore has to export one time + index.store = this.store; + } + } + + this.async = true; + this.db = true; + return Promise.all(promises); + }; + + Document.prototype.commit = async function(replace, append){ + // parallel: + const promises = []; + for(const index of this.index.values()){ + promises.push(index.db.commit(index, replace, append)); + } + await Promise.all(promises); + this.reg.clear(); + // queued: + // for(const index of this.index.values()){ + // await index.db.commit(index, replace, append); + // } + // this.reg.clear(); + }; +} + +/** + * @this Document + */ + +function parse_descriptor(options, document){ + + const index = new Map(); + let field = document.index || document.field || document; + + if(is_string(field)){ + field = [field]; + } + + for(let i = 0, key, opt; i < field.length; i++){ + + key = field[i]; + + if(!is_string(key)){ + opt = key; + key = key.field; + } + + opt = /** @type DocumentIndexOptions */ ( + is_object(opt) + ? Object.assign({}, options, opt) + : options + ); + + if(this.worker){ + const worker = new WorkerIndex(opt); + index.set(key, worker); + if(!worker.worker){ + // fallback when not supported + this.worker = false; + } + } + + if(!this.worker){ + index.set(key, new Index(opt, this.reg)); + } + + if(opt.custom){ + this.tree[i] = opt.custom; + } + else { + this.tree[i] = parse_tree(key, this.marker); + if(opt.filter){ + if(typeof this.tree[i] === "string"){ + // it needs an object to put a property to it + this.tree[i] = new String(this.tree[i]); + } + this.tree[i]._filter = opt.filter; + } + } + + this.field[i] = key; + } + + if(this.storetree){ + + let stores = document.store; + if(is_string(stores)) stores = [stores]; + + for(let i = 0, store, field; i < stores.length; i++){ + store = /** @type Array */ (stores[i]); + field = store.field || store; + if(store.custom){ + this.storetree[i] = store.custom; + store.custom._field = field; + } + else { + this.storetree[i] = parse_tree(field, this.marker); + if(store.filter){ + if(typeof this.storetree[i] === "string"){ + // it needs an object to put a property to it + this.storetree[i] = new String(this.storetree[i]); + } + this.storetree[i]._filter = store.filter; + } + } + } + } + + return index; +} + +function parse_tree(key, marker){ + + const tree = key.split(":"); + let count = 0; + + for(let i = 0; i < tree.length; i++){ + key = tree[i]; + // todo apply some indexes e.g. [0], [-1], [0-2] + if(key[key.length - 1] === "]"){ + key = key.substring(0, key.length - 2); + if(key){ + marker[count] = true; + } + } + if(key){ + tree[count++] = key; + } + } + + if(count < tree.length){ + tree.length = count; + } + + return count > 1 ? tree : tree[0]; +} + +Document.prototype.append = function(id, content){ + return this.add(id, content, true); +}; + +Document.prototype.update = function(id, content){ + return this.remove(id).add(id, content); +}; + +Document.prototype.remove = function(id){ + + if(is_object(id)){ + id = parse_simple(id, this.key); + } + + for(const index of this.index.values()){ + index.remove(id, /* skip deletion */ true); + } + + if(this.reg.has(id)){ + + if(this.tag){ + // when fastupdate was enabled all ids are already removed + if(!this.fastupdate){ + for(let field of this.tag.values()){ + for(let item of field){ + const tag = item[0]; + const ids = item[1]; + const pos = ids.indexOf(id); + if(pos > -1){ + ids.length > 1 + ? ids.splice(pos, 1) + : field.delete(tag); + } + } + } + } + } + + if(this.store){ + this.store.delete(id); + } + + this.reg.delete(id); + } + + // the cache could be used outside the InMemory store + if(this.cache){ + this.cache.remove(id); + } + + return this; +}; + +Document.prototype.clear = function(){ + + //const promises = []; + + for(const index of this.index.values()){ + // db index will add clear task + index.clear(); + // const promise = index.clear(); + // if(promise instanceof Promise){ + // promises.push(promise); + // } + } + + if(this.tag){ + for(const tags of this.tag.values()){ + tags.clear(); + } + } + + if(this.store){ + this.store.clear(); + } + + return this; /*promises.length + ? Promise.all(promises) + :*/ +}; + +Document.prototype.contain = function(id){ + + if(this.db){ + return this.index.get(this.field[0]).db.has(id); + } + + return this.reg.has(id); +}; + +Document.prototype.cleanup = function(){ + + for(const index of this.index.values()){ + index.cleanup(); + } + + return this; +}; + +{ + + Document.prototype.get = function(id){ + + if(this.db){ + return this.index.get(this.field[0]).db.enrich(id).then(function(result){ + return result[0] && result[0].doc; + }); + } + + return this.store.get(id); + }; + + Document.prototype.set = function(id, store){ + + this.store.set(id, store); + return this; + }; +} + +{ + // todo mo + Document.prototype.searchCache = searchCache; +} + +{ + + Document.prototype.export = exportDocument; + Document.prototype.import = importDocument; +} + +{ + + apply_async(Document.prototype); +} + +// COMPILER BLOCK --> + +const VERSION = 1; +const IndexedDB = typeof window !== "undefined" && ( + window.indexedDB || + window.mozIndexedDB || + window.webkitIndexedDB || + window.msIndexedDB +); +const fields = ["map", "ctx", "tag", "reg", "cfg"]; + +function sanitize(str) { + return str.toLowerCase().replace(/[^a-z0-9_\-]/g, ""); +} + +/** + * @constructor + * @implements StorageInterface + */ + +function IdxDB(name, config = {}){ + if(!this){ + return new IdxDB(name, config); + } + if(typeof name === "object"){ + name = name.name; + config = name; + } + if(!name){ + console.info("Default storage space was used, because a name was not passed."); + } + this.id = "flexsearch" + (name ? ":" + sanitize(name) : ""); + this.field = config.field ? sanitize(config.field) : ""; + this.support_tag_search = false; + this.db = null; + this.trx = {}; +} +IdxDB.prototype.mount = function(flexsearch){ + if(flexsearch.constructor === Document){ + return flexsearch.mount(this); + } + flexsearch.db = this; + return this.open(); +}; + +IdxDB.prototype.open = function(){ + + let self = this; + + navigator.storage && + navigator.storage.persist(); + + return this.db || new Promise(function(resolve, reject){ + + const req = IndexedDB.open(self.id + (self.field ? ":" + self.field : ""), VERSION); + + req.onupgradeneeded = function(event){ + + const db = self.db = this.result; + + // Using Indexes + IDBKeyRange on schema map => [key, res, id] performs + // too bad and blows up amazingly in size + // The schema map:key => [res][id] is currently used instead + // In fact that bypass the idea of a storage solution, + // IndexedDB is such a poor contribution :( + + fields.forEach(ref => { + db.objectStoreNames.contains(ref) || + db.createObjectStore(ref);//{ autoIncrement: true /*keyPath: "id"*/ } + //.createIndex("idx", "ids", { multiEntry: true, unique: false }); + }); + + // switch(event.oldVersion){ // existing db version + // case 0: + // // version 0 means that the client had no database + // // perform initialization + // case 1: + // // client had version 1 + // // update + // } + }; + + req.onblocked = function(event) { + // this event shouldn't trigger if we handle onversionchange correctly + // it means that there's another open connection to the same database + // and it wasn't closed after db.onversionchange triggered for it + console.error("blocked", event); + reject(); + }; + + req.onerror = function(event){ + console.error(this.error, event); + reject(); + }; + + req.onsuccess = function(event){ + self.db = this.result; //event.target.result; + self.db.onversionchange = function(){ + //database is outdated + self.close(); + }; + resolve(self); + }; + }); +}; + +IdxDB.prototype.close = function(){ + this.db.close(); + this.db = null; +}; + +IdxDB.prototype.destroy = function(){ + this.db && this.close(); + return IndexedDB.deleteDatabase(this.id + (this.field ? ":" + this.field : "")); +}; + +// IdxDB.prototype.set = function(ref, key, ctx, data){ +// const transaction = this.db.transaction(ref, "readwrite"); +// const map = transaction.objectStore(ref); +// const req = map.put(data, ctx ? ctx + ":" + key : key); +// return transaction;//promisfy(req, callback); +// }; + +// IdxDB.prototype.delete = function(ref, key, ctx){ +// const transaction = this.db.transaction(ref, "readwrite"); +// const map = transaction.objectStore(ref); +// const req = map.delete(ctx ? ctx + ":" + key : key); +// return transaction;//promisfy(req, callback); +// }; + +IdxDB.prototype.clear = function(){ + const transaction = this.db.transaction(fields, "readwrite"); + for(let i = 0; i < fields.length; i++){ + transaction.objectStore(fields[i]).clear(); + } + return promisfy(transaction); +}; + +IdxDB.prototype.get = function(key, ctx, limit = 0, offset = 0, resolve = true, enrich = false){ + const transaction = this.db.transaction(ctx ? "ctx" : "map", "readonly"); + const map = transaction.objectStore(ctx ? "ctx" : "map"); + const req = map.get(ctx ? ctx + ":" + key : key); + const self = this; + return promisfy(req).then(function(res){ + let result = []; + if(!res || !res.length) return result; + if(resolve){ + if(!limit && !offset && res.length === 1){ + return res[0]; + } + for(let i = 0, arr; i < res.length; i++){ + if((arr = res[i]) && arr.length){ + if(offset >= arr.length){ + offset -= arr.length; + continue; + } + const end = limit + ? offset + Math.min(arr.length - offset, limit) + : arr.length; + for(let j = offset; j < end; j++){ + result.push(arr[j]); + } + offset = 0; + if(result.length === limit){ + break; + } + } + } + return enrich + ? self.enrich(result) + : result; + } + else { + return res; + } + }); +}; + +{ + + IdxDB.prototype.tag = function(tag, limit = 0, offset = 0, enrich = false){ + const transaction = this.db.transaction("tag", "readonly"); + const map = transaction.objectStore("tag"); + const req = map.get(tag); + const self = this; + return promisfy(req).then(function(ids){ + if(!ids || !ids.length || offset >= ids.length) return []; + if(!limit && !offset) return ids; + const result = ids.slice(offset, offset + limit); + return enrich + ? self.enrich(result) + : result; + }); + }; +} + +{ + + IdxDB.prototype.enrich = function(ids){ + if(typeof ids !== "object"){ + ids = [ids]; + } + const transaction = this.db.transaction("reg", "readonly"); + const map = transaction.objectStore("reg"); + const promises = []; + for(let i = 0; i < ids.length; i++){ + promises[i] = promisfy(map.get(ids[i])); + } + return Promise.all(promises).then(function(docs){ + for(let i = 0; i < docs.length; i++){ + docs[i] = { + id: ids[i], + doc: docs[i] ? JSON.parse(docs[i]) : null + }; + } + return docs; + }); + }; +} + +IdxDB.prototype.has = function(id){ + const transaction = this.db.transaction("reg", "readonly"); + const map = transaction.objectStore("reg"); + const req = map.getKey(id); + return promisfy(req); +}; + +IdxDB.prototype.search = null; + +// IdxDB.prototype.has = function(ref, key, ctx){ +// const transaction = this.db.transaction(ref, "readonly"); +// const map = transaction.objectStore(ref); +// const req = map.getKey(ctx ? ctx + ":" + key : key); +// return promisfy(req); +// }; + +IdxDB.prototype.info = function(){ + // todo +}; + +/** + * @param {!string} ref + * @param {!string} modifier + * @param {!Function} task + */ + +IdxDB.prototype.transaction = function(ref, modifier, task){ + + let store = this.trx[ref + ":" + modifier]; + if(store) return task.call(this, store); + + let transaction = this.db.transaction(ref, modifier); + this.trx[ref + ":" + modifier] = store = transaction.objectStore(ref); + + return new Promise((resolve, reject) => { + transaction.onerror = (err) => { + this.trx[ref+ ":" + modifier] = null; + transaction.abort(); + transaction = store = null; + reject(err); + //db.close; + }; + transaction.oncomplete = (res) => { + this.trx[ref+ ":" + modifier] = null; + transaction = store = null; + resolve(res || true); + //db.close; + }; + return task.call(this, store); + }); +}; + +IdxDB.prototype.commit = async function(flexsearch, _replace, _append){ + + // process cleanup tasks + if(_replace){ + await this.clear(); + // there are just removals in the task queue + flexsearch.commit_task = []; + } + else { + let tasks = flexsearch.commit_task; + flexsearch.commit_task = []; + for(let i = 0, task; i < tasks.length; i++){ + task = tasks[i]; + // there are just removals in the task queue + if(task.clear){ + await this.clear(); + _replace = true; + break; + } + else { + tasks[i] = task.del; + } + } + if(!_replace){ + if(!_append){ + tasks = tasks.concat(toArray(flexsearch.reg)); + } + tasks.length && await this.remove(tasks); + } + } + + if(!flexsearch.reg.size){ + return; + } + + await this.transaction("map", "readwrite", function(store){ + + for(const item of flexsearch.map){ + + const key = item[0]; + const value = item[1]; + if(!value.length) continue; + + if(_replace){ + store.put(value, key); + continue; + } + + store.get(key).onsuccess = function(){ + let result = this.result; + let changed; + if(result && result.length){ + const maxlen = Math.max(result.length, value.length); + for(let i = 0, res, val; i < maxlen; i++){ + val = value[i]; + if(val && val.length){ + res = result[i]; + if(res && res.length){ + for(let j = 0; j < val.length; j++){ + res.push(val[j]); + } + changed = 1; + //result[i] = res.concat(val); + //result[i] = new Set([...result[i], ...value[i]]); + //result[i] = result[i].union(new Set(value[i])); + } + else { + result[i] = val; + changed = 1; + //result[i] = new Set(value[i]) + } + } + } + } + else { + result = value; + changed = 1; + //result = []; + //for(let i = 0; i < value.length; i++){ + // if(value[i]) result[i] = new Set(value[i]); + //} + } + + changed && + store.put(result, key); + }; + } + }); + + await this.transaction("ctx", "readwrite", function(store){ + + for(const ctx of flexsearch.ctx){ + const ctx_key = ctx[0]; + const ctx_value = ctx[1]; + + for(const item of ctx_value){ + const key = item[0]; + const value = item[1]; + if(!value.length) continue; + + if(_replace){ + store.put(value, ctx_key + ":" + key); + continue; + } + + store.get(ctx_key + ":" + key).onsuccess = function(){ + let result = this.result; + let changed; + if(result && result.length){ + const maxlen = Math.max(result.length, value.length); + for(let i = 0, res, val; i < maxlen; i++){ + val = value[i]; + if(val && val.length){ + res = result[i]; + if(res && res.length){ + for(let j = 0; j < val.length; j++){ + res.push(val[j]); + } + //result[i] = res.concat(val); + changed = 1; + } + else { + result[i] = val; + changed = 1; + } + } + } + } + else { + result = value; + changed = 1; + } + + changed && + store.put(result, ctx_key + ":" + key); + }; + } + } + }); + + if(flexsearch.store){ + await this.transaction("reg", "readwrite", function(store){ + for(const item of flexsearch.store){ + const id = item[0]; + const doc = item[1]; + store.put(typeof doc === "object" + ? JSON.stringify(doc) + : 1 + , id); + } + }); + } + else if(!flexsearch.bypass){ + await this.transaction("reg", "readwrite", function(store){ + for(const id of flexsearch.reg.keys()){ + store.put(1, id); + } + }); + } + + if(flexsearch.tag){ + await this.transaction("tag", "readwrite", function(store){ + for(const item of flexsearch.tag){ + const tag = item[0]; + const ids = item[1]; + if(!ids.length) continue; + + store.get(tag).onsuccess = function(){ + let result = this.result; + result = result && result.length + ? result.concat(ids) + : ids; + store.put(result, tag); + }; + } + }); + } + + // TODO + // await this.transaction("cfg", "readwrite", function(store){ + // store.put({ + // "charset": flexsearch.charset, + // "tokenize": flexsearch.tokenize, + // "resolution": flexsearch.resolution, + // "fastupdate": flexsearch.fastupdate, + // "compress": flexsearch.compress, + // "encoder": { + // "minlength": flexsearch.encoder.minlength + // }, + // "context": { + // "depth": flexsearch.depth, + // "bidirectional": flexsearch.bidirectional, + // "resolution": flexsearch.resolution_ctx + // } + // }, "current"); + // }); + + flexsearch.map.clear(); + flexsearch.ctx.clear(); + { + flexsearch.tag && + flexsearch.tag.clear(); + } + { + flexsearch.store && + flexsearch.store.clear(); + } + flexsearch.document || + flexsearch.reg.clear(); +}; + +/** + * @param {IDBCursorWithValue} cursor + * @param {Array} ids + * @param {boolean=} _tag + */ + +function handle(cursor, ids, _tag){ + + const arr = cursor.value; + let changed; + let parse; + let count = 0; + + for(let x = 0, result; x < arr.length; x++){ + // tags has no resolution layer + if((result = _tag ? arr : arr[x])){ + for(let i = 0, pos, id; i < ids.length; i++){ + id = ids[i]; + pos = result.indexOf(parse ? parseInt(id, 10) : id); + if(pos < 0 && !parse && typeof id === "string" && !isNaN(id)){ + pos = result.indexOf(parseInt(id, 10)); + pos && (parse = 1); + } + if(pos >= 0){ + changed = 1; + if(result.length > 1){ + result.splice(pos, 1); + } + else { + arr[x] = []; + break; + } + } + } + + count += result.length; + } + if(_tag) break; + } + + if(!count){ + + cursor.delete(); + //store.delete(cursor.key); + } + else if(changed){ + + //await new Promise(resolve => { + cursor.update(arr);//.onsuccess = resolve; + //}); + } + + cursor.continue(); +} + +IdxDB.prototype.remove = function(ids){ + + if(typeof ids !== "object"){ + ids = [ids]; + } + + return /** @type {!Promise} */(Promise.all([ + this.transaction("map", "readwrite", function(store){ + store.openCursor().onsuccess = function(){ + const cursor = this.result; + cursor && handle(cursor, ids); + }; + }), + this.transaction("ctx", "readwrite", function(store){ + store.openCursor().onsuccess = function(){ + const cursor = this.result; + cursor && handle(cursor, ids); + }; + }), + this.transaction("tag", "readwrite", function(store){ + store.openCursor().onsuccess = function(){ + const cursor = this.result; + cursor && handle(cursor, ids, /* tag? */ true); + }; + }), + // let filtered = []; + this.transaction("reg", "readwrite", function(store){ + for(let i = 0; i < ids.length; i++){ + store.delete(ids[i]); + } + // return new Promise(resolve => { + // store.openCursor().onsuccess = function(){ + // const cursor = this.result; + // if(cursor){ + // const id = cursor.value; + // if(ids.includes(id)){ + // filtered.push(id); + // cursor.delete(); + // } + // cursor.continue(); + // } + // else{ + // resolve(); + // } + // }; + // }); + }) + // ids = filtered; + ])); +}; + +/** + * @param {IDBRequest} req + * @param {Function=} callback + * @return {!Promise} + */ + +function promisfy(req, callback){ + return new Promise((resolve, reject) => { + /** @this IDBRequest */ + req.onsuccess = function(){ + resolve(this.result); + }; + /** @this IDBRequest */ + req.oncomplete = function(){ + resolve(this.result); + }; + req.onerror = reject; + req = null; + }); +} + +module.exports = IdxDB; diff --git a/dist/db/mongo/index.cjs b/dist/db/mongo/index.cjs new file mode 100644 index 0000000..d5cded5 --- /dev/null +++ b/dist/db/mongo/index.cjs @@ -0,0 +1,6408 @@ +'use strict'; + +var mongodb = require('mongodb'); + +/** @define {string} */ + +/** @define {boolean} */ +const SUPPORT_PERSISTENT = true; + +/** @define {boolean} */ +const SUPPORT_KEYSTORE = true; + +/** + * @param {*} value + * @param {*} default_value + * @param {*=} merge_value + * @return {*} + */ + +function parse_option(value, default_value, merge_value){ + + const type_merge = typeof merge_value; + const type_value = typeof value; + + if(type_merge !== "undefined"){ + if(type_value !== "undefined"){ + + if(merge_value){ + if(type_value === "function" && + type_merge === type_value){ + return function(str){ + return /** @type Function */ (value)( + /** @type Function */ (merge_value)(str) + ); + } + } + + const constructor_value = value.constructor; + const constructor_merge = merge_value.constructor; + + if(constructor_value === constructor_merge){ + + if(constructor_value === Array){ + return merge_value.concat(value); + } + + if(constructor_value === Map){ + const map = new Map(/** @type !Map */ (merge_value)); + for(const item of /** @type !Map */ (value)){ + const key = item[0]; + const val = item[1]; + map.set(key, val); + } + return map; + } + + if(constructor_value === Set){ + const set = new Set(/** @type !Set */ (merge_value)); + for(const val of /** @type !Set */ (value).values()){ + set.add(val); + } + return set; + } + } + } + + return value; + } + else { + return merge_value; + } + } + + return type_value === "undefined" + ? default_value + : value; +} + +function create_object(){ + return Object.create(null); +} + +function concat(arrays){ + return [].concat.apply([], arrays); +} + +function sort_by_length_down(a, b){ + return b.length - a.length; +} + +function is_array(val){ + return val.constructor === Array; +} + +function is_string(val){ + return typeof val === "string"; +} + +function is_object(val){ + return typeof val === "object"; +} + +function is_function(val){ + return typeof val === "function"; +} + +/** + * @param {Map|Set} val + * @param {boolean=} stringify + * @return {Array} + */ + +function toArray(val, stringify){ + const result = []; + for(const key of val.keys()){ + result.push(key); + } + return result; +} + +// TODO support generic function created from string when tree depth > 1 +function parse_simple(obj, tree){ + + if(is_string(tree)){ + obj = obj[tree]; + } + else for(let i = 0; obj && (i < tree.length); i++){ + obj = obj[tree[i]]; + } + + return obj; +} + +function get_max_len(arr){ + let len = 0; + for(let i = 0, res; i < arr.length; i++){ + if((res = arr[i])){ + if(len < res.length){ + len = res.length; + } + } + } + return len; +} + +var normalize_polyfill = [ + + // Charset Normalization + + ["ª","a"], + ["²","2"], + ["³","3"], + ["¹","1"], + ["º","o"], + ["¼","1⁄4"], + ["½","1⁄2"], + ["¾","3⁄4"], + ["à","a"], + ["á","a"], + ["â","a"], + ["ã","a"], + ["ä","a"], + ["å","a"], + ["ç","c"], + ["è","e"], + ["é","e"], + ["ê","e"], + ["ë","e"], + ["ì","i"], + ["í","i"], + ["î","i"], + ["ï","i"], + ["ñ","n"], + ["ò","o"], + ["ó","o"], + ["ô","o"], + ["õ","o"], + ["ö","o"], + ["ù","u"], + ["ú","u"], + ["û","u"], + ["ü","u"], + ["ý","y"], + ["ÿ","y"], + ["ā","a"], + ["ă","a"], + ["ą","a"], + ["ć","c"], + ["ĉ","c"], + ["ċ","c"], + ["č","c"], + ["ď","d"], + ["ē","e"], + ["ĕ","e"], + ["ė","e"], + ["ę","e"], + ["ě","e"], + ["ĝ","g"], + ["ğ","g"], + ["ġ","g"], + ["ģ","g"], + ["ĥ","h"], + ["ĩ","i"], + ["ī","i"], + ["ĭ","i"], + ["į","i"], + ["ij","ij"], + ["ĵ","j"], + ["ķ","k"], + ["ĺ","l"], + ["ļ","l"], + ["ľ","l"], + ["ŀ","l"], + ["ń","n"], + ["ņ","n"], + ["ň","n"], + ["ʼn","n"], + ["ō","o"], + ["ŏ","o"], + ["ő","o"], + ["ŕ","r"], + ["ŗ","r"], + ["ř","r"], + ["ś","s"], + ["ŝ","s"], + ["ş","s"], + ["š","s"], + ["ţ","t"], + ["ť","t"], + ["ũ","u"], + ["ū","u"], + ["ŭ","u"], + ["ů","u"], + ["ű","u"], + ["ų","u"], + ["ŵ","w"], + ["ŷ","y"], + ["ź","z"], + ["ż","z"], + ["ž","z"], + ["ſ","s"], + ["ơ","o"], + ["ư","u"], + ["dž","dz"], + ["lj","lj"], + ["nj","nj"], + ["ǎ","a"], + ["ǐ","i"], + ["ǒ","o"], + ["ǔ","u"], + ["ǖ","u"], + ["ǘ","u"], + ["ǚ","u"], + ["ǜ","u"], + ["ǟ","a"], + ["ǡ","a"], + ["ǣ","ae"], + ["æ","ae"], + ["ǽ","ae"], + ["ǧ","g"], + ["ǩ","k"], + ["ǫ","o"], + ["ǭ","o"], + ["ǯ","ʒ"], + ["ǰ","j"], + ["dz","dz"], + ["ǵ","g"], + ["ǹ","n"], + ["ǻ","a"], + ["ǿ","ø"], + ["ȁ","a"], + ["ȃ","a"], + ["ȅ","e"], + ["ȇ","e"], + ["ȉ","i"], + ["ȋ","i"], + ["ȍ","o"], + ["ȏ","o"], + ["ȑ","r"], + ["ȓ","r"], + ["ȕ","u"], + ["ȗ","u"], + ["ș","s"], + ["ț","t"], + ["ȟ","h"], + ["ȧ","a"], + ["ȩ","e"], + ["ȫ","o"], + ["ȭ","o"], + ["ȯ","o"], + ["ȱ","o"], + ["ȳ","y"], + ["ʰ","h"], + ["ʱ","h"], + ["ɦ","h"], + ["ʲ","j"], + ["ʳ","r"], + ["ʴ","ɹ"], + ["ʵ","ɻ"], + ["ʶ","ʁ"], + ["ʷ","w"], + ["ʸ","y"], + ["ˠ","ɣ"], + ["ˡ","l"], + ["ˢ","s"], + ["ˣ","x"], + ["ˤ","ʕ"], + ["ΐ","ι"], + ["ά","α"], + ["έ","ε"], + ["ή","η"], + ["ί","ι"], + ["ΰ","υ"], + ["ϊ","ι"], + ["ϋ","υ"], + ["ό","ο"], + ["ύ","υ"], + ["ώ","ω"], + ["ϐ","β"], + ["ϑ","θ"], + ["ϒ","Υ"], + ["ϓ","Υ"], + ["ϔ","Υ"], + ["ϕ","φ"], + ["ϖ","π"], + ["ϰ","κ"], + ["ϱ","ρ"], + ["ϲ","ς"], + ["ϵ","ε"], + ["й","и"], + ["ѐ","е"], + ["ё","е"], + ["ѓ","г"], + ["ї","і"], + ["ќ","к"], + ["ѝ","и"], + ["ў","у"], + ["ѷ","ѵ"], + ["ӂ","ж"], + ["ӑ","а"], + ["ӓ","а"], + ["ӗ","е"], + ["ӛ","ә"], + ["ӝ","ж"], + ["ӟ","з"], + ["ӣ","и"], + ["ӥ","и"], + ["ӧ","о"], + ["ӫ","ө"], + ["ӭ","э"], + ["ӯ","у"], + ["ӱ","у"], + ["ӳ","у"], + ["ӵ","ч"] + + // Term Separators + + // ["'", ""], // it's -> its + // ["´", ""], + // ["`", ""], + // ["’", ""], + // ["ʼ", ""], + + // Numeric-Separators Chars Removal + + // [",", ""], + // [".", ""] + + // Non-Whitespace Separators + + // already was split by default via p{P} + // ["-", " "], + // [":", " "], + // ["_", " "], + // ["|", " "], + // ["/", " "], + // ["\\", " "] +]; + +// COMPILER BLOCK --> + +/* + +Custom Encoder +---------------- + +// Split a passed string into an Array of words: +function englishEncoder(string){ + return string.toLowerCase().split(/[^a-z]+/) +} + +// For CJK split a passed string into an Array of chars: +function chineseEncoder(string){ + return string.replace(/\s+/, "").split("") +} + +// Alternatively do not split the input: +function fixedEncoder(string){ + return [string] +} + +Built-in Encoder (Workflow) +---------------------------- +Pipeline: + 1. apply this.normalize: charset normalization: + applied on the whole input string e.g. lowercase, + will also apply on: filter, matcher, stemmer, mapper + 2. apply this.split: split input into terms (includes/excludes) + 3. apply this.filter (pre-filter) + 4. apply this.matcher (replace terms) + 5. apply this.stemmer (replace term endings) + 6. apply this.filter (post-filter) + 7. apply this.mapper (replace chars) + 8. apply this.replacer (custom regex) + 9. apply this.dedupe (letter deduplication) + 10. apply this.finalize +*/ + +const whitespace = /[^\p{L}\p{N}]+/u; // /[\p{Z}\p{S}\p{P}\p{C}]+/u; +//const numeric_split = /(\d{3})/g; +const numeric_split_length = /(\d{3})/g; +const numeric_split_prev_char = /(\D)(\d{3})/g; +const numeric_split_next_char = /(\d{3})(\D)/g; +//.replace(/(\d{3})/g, "$1 ") +//.replace(/([^\d])([\d])/g, "$1 $2") +//.replace(/([\d])([^\d])/g, "$1 $2") +const normalize = "".normalize && /[\u0300-\u036f]/g; // '´`’ʼ., +//const normalize_mapper = SUPPORT_CHARSET && !normalize && normalize_polyfill; + +/** + * @param {EncoderOptions=} options + * @constructor + */ + +function Encoder(options){ + + if(!this){ + return new Encoder(...arguments); + } + + for(let i = 0; i < arguments.length; i++){ + this.assign(arguments[i]); + } +} +/** + * @param {!EncoderOptions} options + */ +Encoder.prototype.assign = function(options){ + + /** + * pre-processing string input + * @type {Function|boolean} + */ + this.normalize = /** @type {Function|boolean} */ ( + parse_option(options.normalize, true, this.normalize) + ); + + // { + // letter: true, + // number: true, + // whitespace: true, + // symbol: true, + // punctuation: true, + // control: true, + // char: "" + // } + + let include = options.include; + let tmp = include || options.exclude || options.split; + + if(typeof tmp === "object"){ + let numeric = !include; + let regex = ""; + // split on whitespace by default + options.include || ( + regex += "\\p{Z}" + ); + if(tmp.letter){ + regex += "\\p{L}"; + } + if(tmp.number){ + regex += "\\p{N}"; + numeric = !!include; + } + if(tmp.symbol){ + regex += "\\p{S}"; + } + if(tmp.punctuation){ + regex += "\\p{P}"; + } + if(tmp.control){ + regex += "\\p{C}"; + } + if((tmp = tmp.char)){ + regex += typeof tmp === "object" ? tmp.join("") : tmp; + } + + this.split = new RegExp("[" + (include ? "^" : "") + regex + "]+", "u"); + this.numeric = numeric; + } + else { + + /** + * split string input into terms + * @type {string|RegExp|boolean|null} + */ + this.split = /** @type {string|RegExp|boolean} */ (parse_option(tmp, whitespace, this.split)); + this.numeric = parse_option(this.numeric, true); + } + + /** + * post-processing terms + * @type {Function|null} + */ + this.prepare = /** @type {Function|null} */ ( + parse_option(options.prepare, null, this.prepare) + ); + /** + * final processing + * @type {Function|null} + */ + this.finalize = /** @type {Function|null} */ ( + parse_option(options.finalize, null, this.finalize) + ); + + // assign the normalization fallback to the mapper + if(!normalize){ + this.mapper = new Map( + /** @type {Array>} */ ( + normalize_polyfill + ) + ); + } + + // options + + this.rtl = options.rtl || false; + this.dedupe = parse_option(options.dedupe, true, this.dedupe); + this.filter = parse_option((tmp = options.filter) && new Set(tmp), null, this.filter); + this.matcher = parse_option((tmp = options.matcher) && new Map(tmp), null, this.matcher); + this.mapper = parse_option((tmp = options.mapper) && new Map(tmp), null, this.mapper); + this.stemmer = parse_option((tmp = options.stemmer) && new Map(tmp), null, this.stemmer); + this.replacer = parse_option(options.replacer, null, this.replacer); + this.minlength = parse_option(options.minlength, 1, this.minlength); + this.maxlength = parse_option(options.maxlength, 0, this.maxlength); + + // minimum required tokenizer by this encoder + //this.tokenize = options["tokenize"] || ""; + + // auto-balanced cache + { + this.cache = tmp = parse_option(options.cache, true, this.cache); + if(tmp){ + this.timer = null; + this.cache_size = typeof tmp === "number" ? tmp : 2e5; + this.cache_enc = new Map(); + this.cache_prt = new Map(); + this.cache_enc_length = 128; + this.cache_prt_length = 128; + } + } + + // regex temporary state + this.matcher_str = ""; + this.matcher_test = null; + this.stemmer_str = ""; + this.stemmer_test = null; + + // prebuilt + // if(this.filter && this.split){ + // for(const key of this.filter){ + // const tmp = key.replace(this.split, ""); + // if(key !== tmp){ + // this.filter.delete(key); + // this.filter.add(tmp); + // } + // } + // } + if(this.matcher){ + for(const key of this.matcher.keys()){ + this.matcher_str += (this.matcher_str ? "|" : "") + key; + } + } + if(this.stemmer){ + for(const key of this.stemmer.keys()){ + this.stemmer_str += (this.stemmer_str ? "|" : "") + key; + } + } + + // if(SUPPORT_COMPRESSION){ + // this.compression = parse_option(options.compress || options.compression, 0, this.compression); + // if(this.compression && !table){ + // table = new Array(radix); + // for(let i = 0; i < radix; i++) table[i] = i + 33; + // table = String.fromCharCode.apply(null, table); + // } + // } + + return this; +}; + +Encoder.prototype.addMatcher = function(match, replace){ + // regex: + if(typeof match === "object"){ + return this.addReplacer(match, replace); + } + // a single char: + if(match.length < 2){ + return this.addMapper(match, replace); + } + this.matcher || (this.matcher = new Map()); + this.matcher.set(match , replace); + this.matcher_str += (this.matcher_str ? "|" : "") + match; + this.matcher_test = null; //new RegExp("(" + this.matcher_str + ")"); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addStemmer = function(match, replace){ + this.stemmer || (this.stemmer = new Map()); + this.stemmer.set(match, replace); + this.stemmer_str += (this.stemmer_str ? "|" : "") + match; + this.stemmer_test = null; //new RegExp("(" + this.stemmer_str + ")"); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addFilter = function(str){ + this.filter || (this.filter = new Set()); + this.filter.add(str); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addMapper = function(char_match, char_replace){ + // regex: + if(typeof char_match === "object"){ + return this.addReplacer(char_match, char_replace); + } + // not a char: + if(char_match.length > 1){ + return this.addMatcher(char_match, char_replace); + } + this.mapper || (this.mapper = new Map()); + this.mapper.set(char_match, char_replace); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addReplacer = function(match, replace){ + if(typeof match === "string") match = new RegExp(match, "g"); + this.replacer || (this.replacer = []); + this.replacer.push(match, replace || ""); + this.cache && this.invalidate(); + return this; +}; + +{ + Encoder.prototype.invalidate = function(){ + this.cache_enc.clear(); + this.cache_prt.clear(); + }; +} + +Encoder.prototype.encode = function(str){ + + //if(!str) return str; + // todo remove dupe terms + + if(this.cache && str.length <= this.cache_enc_length){ + if(this.timer){ + if(this.cache_enc.has(str)){ + return this.cache_enc.get(str); + } + } + else { + this.timer = setTimeout(clear$2, 0, this); + } + } + + // 1. apply charset normalization + if(this.normalize){ + if(typeof this.normalize === "function"){ + str = this.normalize(str); + } + else if(normalize){ + str = str.normalize("NFKD").replace(normalize, "").toLowerCase(); + } + else { + str = str.toLowerCase(); + // if(SUPPORT_CHARSET){ + // this.mapper = this.mapper + // // todo replace spread + // ? new Map([.../** @type {!Iterable} */(normalize_mapper), ...this.mapper]) + // : new Map(/** @type {Map} */ (normalize_mapper)); + // } + } + //if(!str) return str; + } + + // 2. apply custom encoder (can replace split) + if(this.prepare){ + str = this.prepare(str); + } + + // 3. split numbers into triplets + if(this.numeric && str.length > 3){ + str = str.replace(numeric_split_prev_char, "$1 $2") + .replace(numeric_split_next_char, "$1 $2") + .replace(numeric_split_length, "$1 "); + } + + // if(this.matcher && (str.length > 1)){ + // this.matcher_test || ( + // this.matcher_test = new RegExp("(" + this.matcher_str + ")", "g") + // ); + // str = str.replace(this.matcher_test, match => this.matcher.get(match)); + // } + // if(this.stemmer){ + // this.stemmer_test || ( + // this.stemmer_test = new RegExp("(?!\\b)(" + this.stemmer_str + ")(\\b|_)", "g") + // ); + // str = str.replace(this.stemmer_test, match => this.stemmer.get(match)); + // } + + const skip = !(this.dedupe || this.mapper || this.filter || this.matcher || this.stemmer || this.replacer); + let final = []; + let words = this.split || this.split === "" + ? str.split(/** @type {string|RegExp} */ (this.split)) + : str; //[str]; + + for(let i = 0, word, base; i < words.length; i++){ + // filter empty entries + if(!(word = base = words[i])){ + continue; + } + if(word.length < this.minlength){ + continue; + } + if(skip) { + final.push(word); + continue; + } + + // 1. pre-filter before cache + if(this.filter && this.filter.has(word)){ + continue; + } + + if(this.cache && word.length <= this.cache_prt_length){ + if(this.timer){ + const tmp = this.cache_prt.get(word); + //if(this.cache_prt.has(word)){ + if(tmp || tmp === ""){ + //word = this.cache_prt.get(word); + tmp && final.push(tmp); + //word ? words[i] = word : words.splice(i--, 1); + continue; + } + } + else { + this.timer = setTimeout(clear$2, 0, this); + } + } + + let postfilter; + + // if(this.normalize === true && normalize){ + // word = word.normalize("NFKD").replace(normalize, ""); + // postfilter = 1; + // } + + // if(this.normalize){ + // if(typeof this.normalize === "function"){ + // word = this.normalize(word); + // } + // else if(normalize){ + // word = word.normalize("NFKD").replace(normalize, "").toLowerCase(); + // } + // else{ + // word = word.toLowerCase(); + // this.mapper = this.mapper + // ? new Map([...normalize_mapper, ...this.mapper]) + // : new Map(/** @type {Map} */ normalize_mapper); + // } + // postfilter = 1; + // //if(!str) return str; + // } + + // 2. apply stemmer after matcher + if(this.stemmer && (word.length > 2)){ + // for(const item of this.stemmer){ + // const key = item[0]; + // const value = item[1]; + // + // if(word.length > key.length && word.endsWith(key)){ + // word = word.substring(0, word.length - key.length) + value; + // break; + // } + // + // // const position = word.length - key.length; + // // if(position > 0 && word.substring(position) === key){ + // // word = word.substring(0, position) + value; + // // break; + // // } + // } + this.stemmer_test || ( + this.stemmer_test = new RegExp("(?!^)(" + this.stemmer_str + ")$") + ); + word = word.replace(this.stemmer_test, match => this.stemmer.get(match)); + postfilter = 1; + } + + // 3. apply matcher + if(this.matcher && (word.length > 1)){ + this.matcher_test || ( + this.matcher_test = new RegExp("(" + this.matcher_str + ")", "g") + ); + word = word.replace(this.matcher_test, match => this.matcher.get(match)); + postfilter = 1; + } + + // 4. post-filter after matcher and stemmer was applied + if(word && postfilter && (word.length < this.minlength || (this.filter && this.filter.has(word)))){ + word = ""; + } + + // 5. apply mapper and collapsing + if(word && (this.mapper || (this.dedupe && word.length > 1))){ + //word = this.replace_dedupe(word); + //word = replace_deduped(word, this.mapper, true); + let final = ""; + for(let i = 0, prev = "", char, tmp; i < word.length; i++){ + char = word.charAt(i); + if(char !== prev || !this.dedupe){ + tmp = this.mapper && this.mapper.get(char); + if(!tmp && tmp !== "") + final += (prev = char); + else if((tmp !== prev || !this.dedupe) && (prev = tmp)) + final += tmp; + } + } + word = final; + } + + // apply custom regex + if(word && this.replacer){ + for(let i = 0; word && (i < this.replacer.length); i+=2){ + word = word.replace(this.replacer[i], this.replacer[i+1]); + } + } + + // slower variants for removing same chars in a row: + //word = word.replace(/([^0-9])\1+/g, "$1"); + //word = word.replace(/(.)\1+/g, "$1"); + //word = word.replace(/(?<=(.))\1+/g, ""); + + // if(word){ + // words[i] = word; + // } + + if(this.cache && base.length <= this.cache_prt_length){ + this.cache_prt.set(base, word); + if(this.cache_prt.size > this.cache_size){ + this.cache_prt.clear(); + this.cache_prt_length = this.cache_prt_length / 1.1 | 0; + } + } + + //word || words.splice(i--, 1); + word && final.push(word); + } + + //words = final; + // else if(this.filter){ + // for(let i = 0, word; i < words.length; i++){ + // if((word = words[i]) && !this.filter.has(word)){ + // //filtered.push(word); + // words.splice(i--, 1); + // } + // } + // } + + if(this.finalize){ + final = this.finalize(final) || final; + } + + if(this.cache && str.length <= this.cache_enc_length){ + this.cache_enc.set(str, final); + if(this.cache_enc.size > this.cache_size){ + this.cache_enc.clear(); + this.cache_enc_length = this.cache_enc_length / 1.1 | 0; + } + } + + return final; +}; + +// Encoder.prototype.compress = function(str) { +// +// //return str; +// //if(!str) return str; +// +// if(SUPPORT_CACHE && this.cache && str.length <= this.cache_prt_length){ +// if(this.timer){ +// if(this.cache_cmp.has(str)){ +// return this.cache_cmp.get(str); +// } +// } +// else{ +// this.timer = setTimeout(clear, 0, this); +// } +// } +// +// const result = typeof this.compression === "function" +// ? this.compression(str) +// : hash(str); //window.hash(str); +// +// if(SUPPORT_CACHE && this.cache && str.length <= this.cache_prt_length){ +// this.cache_cmp.set(str, result); +// this.cache_cmp.size > this.cache_size && +// this.cache_cmp.clear(); +// } +// +// return result; +// }; + +// function hash(str){ +// return str; +// } + +function clear$2(self){ + self.timer = null; + self.cache_enc.clear(); + self.cache_prt.clear(); +} + +/** + * @param {string|Object} query + * @param {number|Object=} limit + * @param {Object=} options + * @this {Index|Document} + * @returns {Array|Promise} + */ + +function searchCache(query, limit, options){ + + query = (typeof query === "object" + ? "" + query.query + : "" + query + ).toLowerCase(); + + //let encoded = this.encoder.encode(query).join(" "); + let cache = this.cache.get(query); + if(!cache){ + cache = this.search(query, limit, options); + if(cache.then){ + const self = this; + cache.then(function(cache){ + self.cache.set(query, cache); + return cache; + }); + } + this.cache.set(query, cache); + } + return cache; +} + +/** + * @param {boolean|number=} limit + * @constructor + */ + +function CacheClass(limit){ + /** @private */ + this.limit = (!limit || limit === true) ? 1000 : limit; + /** @private */ + this.cache = new Map(); + /** @private */ + this.last = ""; +} + +CacheClass.prototype.set = function(key, value){ + //if(!this.cache.has(key)){ + this.cache.set(this.last = key, value); + if(this.cache.size > this.limit){ + this.cache.delete(this.cache.keys().next().value); + } + //} +}; + +CacheClass.prototype.get = function(key){ + const cache = this.cache.get(key); + if(cache && this.last !== key){ + this.cache.delete(key); + this.cache.set(this.last = key, cache); + } + return cache; +}; + +CacheClass.prototype.remove = function(id){ + for(const item of this.cache){ + const key = item[0]; + const value = item[1]; + if(value.includes(id)){ + this.cache.delete(key); + } + } +}; + +CacheClass.prototype.clear = function(){ + this.cache.clear(); + this.last = ""; +}; + +/** @type EncoderOptions */ +const options = { + normalize: function(str){ + return str.toLowerCase(); + }, + dedupe: false +}; + +// import { pipeline } from "../../lang.js"; +// +// const whitespace = /[\p{Z}\p{S}\p{P}\p{C}]+/u; +// +// export const rtl = false; +// export const tokenize = ""; +// export default { +// encode: encode, +// rtl: rtl, +// tokenize: tokenize +// } +// +// /** +// * @param {string|number} str +// */ +// +// export function encode(str){ +// +// return pipeline.call( +// +// this, +// /* string: */ ("" + str).toLowerCase(), +// /* normalize: */ false, +// /* split: */ whitespace, +// /* collapse: */ false +// ); +// } + +// KeystoreObj.prototype.destroy = function(){ +// this.index = null; +// this.keys = null; +// this.proxy = null; +// }; + +function _slice(self, start, end, splice){ + let arr = []; + for(let i = 0, index; i < self.index.length; i++){ + index = self.index[i]; + if(start >= index.length){ + start -= index.length; + } + else { + const tmp = index[splice ? "splice" : "slice"](start, end); + const length = tmp.length; + if(length){ + arr = arr.length + ? arr.concat(tmp) + : tmp; + end -= length; + if(splice) self.length -= length; + if(!end) break; + } + start = 0; + } + } + return arr; +} + +/** + * @param arr + * @constructor + */ + +function KeystoreArray(arr){ + + if(!this){ + return new KeystoreArray(arr); + } + + this.index = arr ? [arr] : []; + this.length = arr ? arr.length : 0; + const self = this; + + return /*this.proxy =*/ new Proxy([], { + get(target, key) { + if(key === "length"){ + return self.length; + } + if(key === "push"){ + return function(value){ + self.index[self.index.length - 1].push(value); + self.length++; + } + } + if(key === "pop"){ + return function(){ + if(self.length){ + self.length--; + return self.index[self.index.length - 1].pop(); + } + } + } + if(key === "indexOf"){ + return function(key){ + let index = 0; + for(let i = 0, arr, tmp; i < self.index.length; i++){ + arr = self.index[i]; + //if(!arr.includes(key)) continue; + tmp = arr.indexOf(key); + if(tmp >= 0) return index + tmp; + index += arr.length; + } + return -1; + } + } + if(key === "includes"){ + return function(key){ + for(let i = 0; i < self.index.length; i++){ + if(self.index[i].includes(key)){ + return true; + } + } + return false; + } + } + if(key === "slice"){ + return function(start, end){ + return _slice( + self, + start || 0, + end || self.length, + false + ); + } + } + if(key === "splice"){ + return function(start, end){ + return _slice( + self, + start || 0, + end || self.length, + // splice: + true + ); + } + } + if(key === "constructor"){ + return Array; + } + if(typeof key === "symbol" /*|| isNaN(key)*/){ + // not supported + return; + } + const index = key / (2**31) | 0; + const arr = self.index[index]; + return arr && arr[key]; + }, + set(target, key, value){ + const index = key / (2**31) | 0; + const arr = self.index[index] || (self.index[index] = []); + arr[key] = value; + self.length++; + return true; + } + }); +} + +KeystoreArray.prototype.clear = function(){ + this.index.length = 0; +}; + +KeystoreArray.prototype.destroy = function(){ + this.index = null; + this.proxy = null; +}; + +KeystoreArray.prototype.push = function(val){}; + +/** + * @param bitlength + * @constructor + */ + +function KeystoreMap(bitlength = 8){ + + if(!this){ + return new KeystoreMap(bitlength); + } + + this.index = create_object(); + this.refs = []; + this.size = 0; + + if(bitlength > 32){ + this.crc = lcg64; + this.bit = BigInt(bitlength); + } + else { + this.crc = lcg$1; + this.bit = bitlength; + } +} + +KeystoreMap.prototype.get = function(key) { + const address = this.crc(key); + const map = this.index[address]; + return map && map.get(key); +}; + +KeystoreMap.prototype.set = function(key, value){ + const address = this.crc(key); + let map = this.index[address]; + if(map){ + let size = map.size; + map.set(key, value); + size -= map.size; + size && this.size++; + } + else { + this.index[address] = map = new Map([[key, value]]); + this.refs.push(map); + } +}; + +/** + * @param bitlength + * @constructor + */ + +function KeystoreSet(bitlength = 8){ + + if(!this){ + return new KeystoreSet(bitlength); + } + + // using plain Object with numeric key access + // just for max performance + this.index = create_object(); + this.refs = []; + + if(bitlength > 32){ + this.crc = lcg64; + this.bit = BigInt(bitlength); + } + else { + this.crc = lcg$1; + this.bit = bitlength; + } +} + +KeystoreSet.prototype.add = function(key){ + const address = this.crc(key); + let set = this.index[address]; + if(set){ + let size = set.size; + set.add(key); + size -= set.size; + size && this.size++; + } + else { + this.index[address] = set = new Set([key]); + this.refs.push(set); + } +}; + +KeystoreMap.prototype.has = +KeystoreSet.prototype.has = function(key) { + const address = this.crc(key); + const map_or_set = this.index[address]; + return map_or_set && map_or_set.has(key); +}; + +/* +KeystoreMap.prototype.size = +KeystoreSet.prototype.size = function(){ + let size = 0; + const values = Object.values(this.index); + for(let i = 0; i < values.length; i++){ + size += values[i].size; + } + return size; +}; +*/ + +KeystoreMap.prototype.delete = +KeystoreSet.prototype.delete = function(key){ + const address = this.crc(key); + const map_or_set = this.index[address]; + // set && (set.size === 1 + // ? this.index.delete(address) + // : set.delete(key)); + map_or_set && + map_or_set.delete(key) && + this.size--; +}; + +KeystoreMap.prototype.clear = +KeystoreSet.prototype.clear = function(){ + this.index = create_object(); + this.refs = []; + this.size = 0; +}; + +// KeystoreMap.prototype.destroy = +// KeystoreSet.prototype.destroy = function(){ +// this.index = null; +// this.refs = null; +// this.proxy = null; +// }; + +KeystoreMap.prototype.values = +KeystoreSet.prototype.values = function*(){ + // alternatively iterate through this.keys[] + //const refs = Object.values(this.index); + for(let i = 0; i < this.refs.length; i++){ + for(let value of this.refs[i].values()){ + yield value; + } + } +}; + +KeystoreMap.prototype.keys = +KeystoreSet.prototype.keys = function*(){ + //const values = Object.values(this.index); + for(let i = 0; i < this.refs.length; i++){ + for(let key of this.refs[i].keys()){ + yield key; + } + } +}; + +KeystoreMap.prototype.entries = +KeystoreSet.prototype.entries = function*(){ + //const values = Object.values(this.index); + for(let i = 0; i < this.refs.length; i++){ + for(let entry of this.refs[i].entries()){ + yield entry; + } + } +}; + +/** + * Linear Congruential Generator (LCG) + * @param str + * @this {KeystoreMap|KeystoreSet} + */ + +function lcg$1(str) { + let range = 2 ** this.bit - 1; + if(typeof str == "number"){ + return str & range; + } + let crc = 0, bit = this.bit + 1; + for(let i = 0; i < str.length; i++) { + crc = (crc * bit ^ str.charCodeAt(i)) & range; + } + // shift Int32 to UInt32 because negative numbers + // extremely slows down key lookup + return this.bit === 32 + ? crc + 2 ** 31 + : crc;// & 0xFFFF; +} + +/** + * @param str + * @this {KeystoreMap|KeystoreSet} + */ + +function lcg64(str) { + let range = BigInt(2) ** /** @type {!BigInt} */ (this.bit) - BigInt(1); + let type = typeof str; + if(type === "bigint"){ + return /** @type {!BigInt} */ (str) & range; + } + if(type === "number"){ + return BigInt(str) & range; + } + let crc = BigInt(0), bit = /** @type {!BigInt} */ (this.bit) + BigInt(1); + for(let i = 0; i < str.length; i++){ + crc = (crc * bit ^ BigInt(str.charCodeAt(i))) & range; + } + return crc;// & 0xFFFFFFFFFFFFFFFF; +} + +// TODO return promises instead of inner await + + +function async(callback, self, field, key, index_doc, index, data, on_done){ + + //setTimeout(function(){ + + const res = callback(field ? field + "." + key : key, JSON.stringify(data)); + + // await isn't supported by ES5 + + if(res && res["then"]){ + + res["then"](function(){ + + self.export(callback, self, field, index_doc, index + 1, on_done); + }); + } + else { + + self.export(callback, self, field, index_doc, index + 1, on_done); + } + //}); +} + +/** + * @param callback + * @param self + * @param field + * @param index_doc + * @param index + * @param on_done + * @this {Index|Document} + */ + +function exportIndex(callback, self, field, index_doc, index, on_done){ + + let return_value = true; + if (typeof on_done === 'undefined') { + return_value = new Promise((resolve) => { + on_done = resolve; + }); + } + + let key, data; + + switch(index || (index = 0)){ + + case 0: + + key = "reg"; + + // fastupdate isn't supported by export + + if(this.fastupdate){ + + data = create_object(); + + for(let key of this.reg.keys()){ + + data[key] = 1; + } + } + else { + + data = this.reg; + } + + break; + + case 1: + + key = "cfg"; + data = { + "doc": 0, + "opt": this.optimize ? 1 : 0 + }; + + break; + + case 2: + + key = "map"; + data = this.map; + break; + + case 3: + + key = "ctx"; + data = this.ctx; + break; + + default: + + if (typeof field === 'undefined' && on_done) { + + on_done(); + } + + return; + } + + async(callback, self || this, field, key, index_doc, index, data, on_done); + + return return_value; +} + +/** + * @this Index + */ + +function importIndex(key, data){ + + if(!data){ + + return; + } + + if(is_string(data)){ + + data = JSON.parse(data); + } + + switch(key){ + + case "cfg": + + this.optimize = !!data["opt"]; + break; + + case "reg": + + // fastupdate isn't supported by import + + this.fastupdate = false; + this.reg = data; + break; + + case "map": + + this.map = data; + break; + + case "ctx": + + this.ctx = data; + break; + } +} + +/** + * @this Document + */ + +function exportDocument(callback, self, field, index_doc, index, on_done){ + + let return_value; + if (typeof on_done === 'undefined') { + return_value = new Promise((resolve) => { + on_done = resolve; + }); + } + + index || (index = 0); + index_doc || (index_doc = 0); + + if(index_doc < this.field.length){ + + const field = this.field[index_doc]; + const idx = this.index[field]; + + self = this; + + //setTimeout(function(){ + + if(!idx.export(callback, self, index ? field/*.replace(":", "-")*/ : "", index_doc, index++, on_done)){ + + index_doc++; + index = 1; + + self.export(callback, self, field, index_doc, index, on_done); + } + //}); + } + else { + + let key, data; + + switch(index){ + + case 1: + + key = "tag"; + data = this.tagindex; + field = null; + break; + + case 2: + + key = "store"; + data = this.store; + field = null; + break; + + // case 3: + // + // key = "reg"; + // data = this.register; + // break; + + default: + + on_done(); + return; + } + + async(callback, this, field, key, index_doc, index, data, on_done); + } + + return return_value +} + +/** + * @this Document + */ + +function importDocument(key, data){ + + if(!data){ + + return; + } + + if(is_string(data)){ + + data = JSON.parse(data); + } + + switch(key){ + + case "tag": + + this.tagindex = data; + break; + + case "reg": + + // fastupdate isn't supported by import + + this.fastupdate = false; + this.reg = data; + + for(let i = 0, index; i < this.field.length; i++){ + + index = this.index[this.field[i]]; + index.reg = data; + index.fastupdate = false; + } + + break; + + case "store": + + this.store = data; + break; + + default: + + key = key.split("."); + const field = key[0]; + key = key[1]; + + if(field && key){ + + this.index[field].import(key, data); + } + } +} + +// COMPILER BLOCK --> + +/** + * @enum {Object} + * @const + */ + +const presets = { + + "memory": { + resolution: 1 + }, + + "performance": { + resolution: 6, + fastupdate: true, + context: { + depth: 1, + resolution: 3 + } + }, + + "match": { + tokenize: "forward" + }, + + "score": { + resolution: 9, + context: { + depth: 2, + resolution: 9 + } + } +}; + +/** + * + * @param {!IndexOptions|string} options + * @return {IndexOptions} + */ + +function apply_preset(options){ + + const preset = is_string(options) + ? options + : options["preset"]; + + if(preset){ + if(!presets[preset]){ + console.warn("Preset not found: " + preset); + } + options = Object.assign({}, presets[preset], /** @type {Object} */ (options)); + } + + return options; +} + +function apply_async(prototype){ + register$1.call(prototype, "add"); + register$1.call(prototype, "append"); + register$1.call(prototype, "search"); + register$1.call(prototype, "update"); + register$1.call(prototype, "remove"); +} + +// let cycle; +// let budget = 0; +// +// function tick(resolve){ +// cycle = null; +// budget = 0; +// resolve(); +// } + +/** + * @param {!string} key + * @this {Index|Document} + */ + +function register$1(key){ + this[key + "Async"] = function(){ + + // // prevent stack overflow of adding too much tasks to the same event loop + // // actually limit stack to 1,000,000 tasks every ~4ms + // cycle || ( + // cycle = new Promise(resolve => setTimeout(tick, 0, resolve)) + // ); + // + // // apply different performance budgets + // if(key === "update" || key === "remove" && this.fastupdate === false){ + // budget += 1000 * this.resolution; + // if(this.depth) + // budget += 1000 * this.resolution_ctx; + // } + // else if(key === "search"){ + // budget++; + // } + // else{ + // budget += 20 * this.resolution; + // if(this.depth) + // budget += 20 * this.resolution_ctx; + // } + // + // // wait for the event loop cycle + // if(budget >= 1e6){ + // await cycle; + // } + + const args = /*[].slice.call*/(arguments); + const arg = args[args.length - 1]; + let callback; + + if(typeof arg === "function"){ + callback = arg; + delete args[args.length - 1]; + } + + this.async = true; + const res = this[key].apply(this, args); + this.async = false; + res.then ? res.then(callback) + : callback(res); + return res; + }; +} + +const data = create_object(); + +/** + * @param {!string} name + */ + +function tick(name){ + + /** @type {!Object} */ + const profiler = data["profiler"] || (data["profiler"] = {}); + + profiler[name] || (profiler[name] = 0); + profiler[name]++; +} + +// COMPILER BLOCK --> +// <-- COMPILER BLOCK + +let table;// = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; +let timer; +const cache = new Map(); + +function toRadix(number, radix = 255) { + + if(!table){ + table = new Array(radix); + // the char code 0 could be a special marker + for(let i = 0; i < radix; i++) table[i] = i + 1; + table = String.fromCharCode.apply(null, table); + } + + let rixit; + let residual = number; + let result = ""; + + while(true){ + rixit = residual % radix; + result = table.charAt(rixit) + result; + residual = residual / radix | 0; + if(!residual) + break; + } + + return result; +} + +function default_compress(str){ + + { + if(timer){ + if(cache.has(str)){ + return cache.get(str); + } + } + else { + timer = setTimeout(clear$1); + } + } + + /* 2 ** ((level + 1.5) * 1.6 | 0) */ + + const result = toRadix( + typeof str == "number" + ? str + : lcg(str) + ); + + { + cache.size > 2e5 && cache.clear(); + cache.set(str, result); + } + + return result; +} + +function lcg(str) { + let range = 2 ** 32 - 1; + if(typeof str == "number"){ + return str & range; + } + let crc = 0, bit = 32 + 1; + for(let i = 0; i < str.length; i++) { + crc = (crc * bit ^ str.charCodeAt(i)) & range; + } + // shift up from Int32 to UInt32 range 0xFFFFFFFF + return crc + 2 ** 31; +} + +function clear$1(){ + timer = null; + cache.clear(); +} + +// COMPILER BLOCK --> + +// TODO: +// string + number as text +// boolean, null, undefined as ? + + +/** + * @param {!number|string} id + * @param {!string} content + * @param {boolean=} _append + * @param {boolean=} _skip_update + */ + +Index.prototype.add = function(id, content, _append, _skip_update){ + + if(content && (id || (id === 0))){ + + // todo check skip_update + //_skip_update = false; + + if(!_skip_update && !_append){ + if(this.reg.has(id)){ + return this.update(id, content); + } + } + + // do not force a string as input + // https://github.com/nextapps-de/flexsearch/issues/432 + content = this.encoder.encode(content); + const word_length = content.length; + + if(word_length){ + + // check context dupes to skip all contextual redundancy along a document + + const dupes_ctx = create_object(); + const dupes = create_object(); + const depth = this.depth; + const resolution = this.resolution; + + for(let i = 0; i < word_length; i++){ + + let term = content[this.rtl ? word_length - 1 - i : i]; + let term_length = term.length; + + // skip dupes will break the context chain + + if(term_length /*&& (term_length >= this.minlength)*/ && (depth || !dupes[term])){ + + let score = this.score + ? this.score(content, term, i, null, 0) + : get_score(resolution, word_length, i); + let token = ""; + + switch(this.tokenize){ + + case "full": + if(term_length > 2){ + for(let x = 0; x < term_length; x++){ + for(let y = term_length; y > x; y--){ + + //if((y - x) >= this.minlength){ + token = term.substring(x, y); + const partial_score = this.score + ? this.score(content, term, i, token, x) + : get_score(resolution, word_length, i, term_length, x); + this.push_index(dupes, token, partial_score, id, _append); + //} + } + } + break; + } + // fallthrough to next case when term length < 3 + case "reverse": + // skip last round (this token exist already in "forward") + if(term_length > 1){ + for(let x = term_length - 1; x > 0; x--){ + token = term[x] + token; + //if(token.length >= this.minlength){ + const partial_score = this.score + ? this.score(content, term, i, token, x) + : get_score(resolution, word_length, i, term_length, x); + this.push_index(dupes, token, partial_score, id, _append); + //} + } + token = ""; + } + + // fallthrough to next case to apply forward also + case "forward": + if(term_length > 1){ + for(let x = 0; x < term_length; x++){ + token += term[x]; + //if(token.length >= this.minlength){ + this.push_index(dupes, token, score, id, _append); + //} + } + break; + } + + // fallthrough to next case when token has a length of 1 + default: + // case "strict": + + // todo move boost to search + // if(this.boost){ + // score = Math.min((score / this.boost(content, term, i)) | 0, resolution - 1); + // } + + this.push_index(dupes, term, score, id, _append); + + // context is just supported by tokenizer "strict" + if(depth){ + + if((word_length > 1) && (i < (word_length - 1))){ + + // check inner dupes to skip repeating words in the current context + const dupes_inner = create_object(); + const resolution = this.resolution_ctx; + const keyword = term; + const size = Math.min(depth + 1, word_length - i); + + dupes_inner[keyword] = 1; + + for(let x = 1; x < size; x++){ + + term = content[this.rtl ? word_length - 1 - i - x : i + x]; + + if(term /*&& (term.length >= this.minlength)*/ && !dupes_inner[term]){ + + dupes_inner[term] = 1; + + const context_score = this.score + ? this.score(content, keyword, i, term, x) + : get_score(resolution + ((word_length / 2) > resolution ? 0 : 1), word_length, i, size - 1, x - 1); + const swap = this.bidirectional && (term > keyword); + this.push_index(dupes_ctx, swap ? keyword : term, context_score, id, _append, swap ? term : keyword); + } + } + } + } + } + } + } + + this.fastupdate || this.reg.add(id); + } + else { + content = ""; + } + } + + if(this.db){ + // when the term has no valid content (e.g. empty), + // then it was not added to the ID registry for removal + content || this.commit_task.push({ "del": id }); + this.commit_auto && autoCommit(this); + } + + return this; +}; + +/** + * @private + * @param dupes + * @param term + * @param score + * @param id + * @param {boolean=} append + * @param {string=} keyword + */ + +Index.prototype.push_index = function(dupes, term, score, id, append, keyword){ + + let arr = keyword ? this.ctx : this.map; + let tmp; + + if(!dupes[term] || !keyword || !(tmp = dupes[term])[keyword]){ + + if(keyword){ + + dupes = tmp || (dupes[term] = create_object()); + dupes[keyword] = 1; + + if(this.compress){ + keyword = default_compress(keyword); + } + + tmp = arr.get(keyword); + tmp ? arr = tmp + : arr.set(keyword, arr = new Map()); + } + else { + + dupes[term] = 1; + } + + if(this.compress){ + term = default_compress(term); + } + + tmp = arr.get(term); + tmp ? arr = tmp : arr.set(term, arr = tmp = []); + // the ID array will be upgraded dynamically + arr = arr[score] || (arr[score] = []); + + if(!append || !arr.includes(id)){ + + // auto-upgrade to keystore array if max size exceeded + { + if(arr.length === 2**31-1 /*|| !(arr instanceof KeystoreArray)*/){ + const keystore = new KeystoreArray(arr); + if(this.fastupdate){ + for(let value of this.reg.values()){ + if(value.includes(arr)){ + value[value.indexOf(arr)] = keystore; + } + } + } + tmp[score] = arr = keystore; + } + } + + arr.push(id); + + // add a reference to the register for fast updates + if(this.fastupdate){ + const tmp = this.reg.get(id); + tmp ? tmp.push(arr) + : this.reg.set(id, [arr]); + } + } + } +}; + +/** + * @param {number} resolution + * @param {number} length + * @param {number} i + * @param {number=} term_length + * @param {number=} x + * @returns {number} + */ + +function get_score(resolution, length, i, term_length, x){ + + // console.log("resolution", resolution); + // console.log("length", length); + // console.log("term_length", term_length); + // console.log("i", i); + // console.log("x", x); + // console.log((resolution - 1) / (length + (term_length || 0)) * (i + (x || 0)) + 1); + + // the first resolution slot is reserved for the best match, + // when a query matches the first word(s). + + // also to stretch score to the whole range of resolution, the + // calculation is shift by one and cut the floating point. + // this needs the resolution "1" to be handled additionally. + + // do not stretch the resolution more than the term length will + // improve performance and memory, also it improves scoring in + // most cases between a short document and a long document + + return i && (resolution > 1) ? ( + + (length + (term_length || 0)) <= resolution ? + + i + (x || 0) + : + ((resolution - 1) / (length + (term_length || 0)) * (i + (x || 0)) + 1) | 0 + ): + 0; +} + +/* + from -> res[score][id] + to -> [id] +*/ + +/** + * Aggregate the union of a single raw result + * @param {!Array} result + * @param {!number} limit + * @param {number=} offset + * @param {boolean=} enrich + * @return Array + */ + +function resolve_default(result, limit, offset, enrich){ + + // fast path: when there is just one slot in the result + if(result.length === 1){ + result = result[0]; + result = offset || (result.length > limit) + ? (limit + ? result.slice(offset, offset + limit) + : result.slice(offset) + ) + : result; + return enrich + ? enrich_result(result) + : result; + } + + // this is an optimized workaround instead of + // just doing result = concat(result) + + let final = []; + + for(let i = 0, arr, len; i < result.length; i++){ + if(!(arr = result[i]) || !(len = arr.length)) continue; + + if(offset){ + // forward offset pointer + if(offset >= len){ + offset -= len; + continue; + } + // apply offset pointer when length differs + if(offset < len){ + arr = limit + ? arr.slice(offset, offset + limit) + : arr.slice(offset); + len = arr.length; + offset = 0; + } + } + + if(!final.length){ + // fast path: when limit was reached in first slot + if(len >= limit){ + if(len > limit){ + arr = arr.slice(0, limit); + } + return enrich + ? enrich_result(arr) + : arr; + } + final = [arr]; + } + else { + if(len > limit){ + arr = arr.slice(0, limit); + len = arr.length; + } + final.push(arr); + } + + // reduce limit + limit -= len; + + // todo remove + // if(limit < 0){ + // throw new Error("Impl.Error"); + // } + + // break if limit was reached + if(!limit){ + break; + } + } + + // todo remove + if(!final.length){ + //throw new Error("No results found"); + return final; + } + + final = final.length > 1 + ? concat(final) + : final[0]; + + return enrich + ? enrich_result(final) + : final; +} + +function enrich_result(ids){ + for(let i = 0; i < ids.length; i++){ + ids[i] = { + score: i, + id: ids[i] + }; + } + return ids; +} + +// import xor from "./xor.js"; +// import and from "./and.js"; +// import not from "./not.js"; + +Resolver.prototype.or = function(){ + + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.or.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.or.apply(this, first_argument); + } + } + + // for(let i = 0; i < args.length; i++){ + // if(args[i].result instanceof Promise){ + // return; + // } + // } + + // if(args.length < 2){ + // if(first_argument.index){ + // first_argument.resolve = false; + // const result = first_argument.index.search(first_argument); + // if(result instanceof Promise){ + // result.then(function(result){ + // final = self.result.concat(result); + // self.result = resolver(final, limit, offset, enrich, !resolve); + // return resolve ? self.result : self; + // }); + // } + // else{ + // final = this.result.concat(result); + // this.result = resolver(final, limit, offset, enrich, !resolve); + // return resolve ? this.result : this; + // } + // } + // } + + let final = []; + let promises = []; + let limit = 0, offset = 0, enrich, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.and){ + result = this.and(query.and); + } + else if(query.xor){ + result = this.xor(query.xor); + } + else if(query.not){ + result = this.not(query.not); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + enrich = query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + self.result.length && (final = [self.result].concat(final)); + self.result = resolver(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? self.result : self; + }); + } + + this.result.length && (final = [this.result].concat(final)); + this.result = resolver(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? this.result : this; +}; + +/** + * Aggregate the union of N raw results + * @param result + * @param limit + * @param offset + * @param enrich + * @param resolve + * @param boost + * @return {Array} + */ + +function resolver(result, limit, offset, enrich, resolve, boost){ + + if(!result.length){ + // todo remove + //console.log("Empty Result") + return result; + } + + if(typeof limit === "object"){ + offset = limit.offset || 0; + enrich = limit.enrich || false; + limit = limit.limit || 0; + } + + if(result.length < 2){ + // todo remove + //console.log("Single Result") + if(resolve){ + return resolve_default(result[0], limit, offset, enrich); + } + else { + return result[0]; + } + } + + let final = []; + let count = 0; + let dupe = create_object(); + let maxres = get_max_len(result); + + for(let j = 0, ids; j < maxres; j++){ + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res) continue; + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + if(!dupe[id]){ + dupe[id] = 1; + if(offset){ + offset--; + continue; + } + if(resolve){ + final.push(id); + } + else { + // shift resolution by boost (inverse) + const index = j + (i ? boost : 0); + final[index] || (final[index] = []); + final[index].push(id); + } + if(limit && ++count === limit){ + //this.boost = 0; + return final; + } + } + } + } + } + + //this.boost = 0; + return final; +} + +// import xor from "./xor.js"; +// import or from "./or.js"; +// import not from "./not.js"; + +Resolver.prototype.and = function(){ + if(this.result.length){ + + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.and.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.and.apply(this, first_argument); + } + } + + // for(let i = 0; i < args.length; i++){ + // if(args[i].result instanceof Promise){ + // return; + // } + // } + + // if(args.length < 2){ + // if(first_argument.index){ + // first_argument.resolve = false; + // return first_argument.index.search(first_argument); + // } + // } + + let final = []; + let promises = []; + let limit = 0, offset = 0, enrich, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.or){ + result = this.or(query.or); + } + else if(query.xor){ + result = this.xor(query.xor); + } + else if(query.not){ + result = this.not(query.not); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + enrich = query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + final = [self.result].concat(final); + self.result = intersect$1(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? self.result : self; + }); + } + + final = [this.result].concat(final); + this.result = intersect$1(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? this.result : this; + } + return this; +}; + +/** + * Aggregate the intersection of N raw results + * @param result + * @param limit + * @param offset + * @param enrich + * @param resolve + * @param boost + * @return {Array} + */ + +function intersect$1(result, limit, offset, enrich, resolve, boost){ + + // if(!result.length){ + // // todo remove + // console.log("Empty Result") + // return result; + // } + + if(result.length < 2){ + // todo remove + //console.log("Single Result") + return []; + // if(resolve){ + // return default_resolver(result[0], limit, offset, enrich); + // } + // else{ + // return result[0]; + // } + } + + let final = []; + let count = 0; + + // fast path single slot + // if(result.length < 2){ + // if(limit || offset){ + // let res = result[0]; + // for(let j = 0, ids; j < res.length; j++){ + // ids = res[j]; + // if(!ids) continue; + // for(let k = 0, id; k < ids.length; k++){ + // id = ids[k]; + // if(offset){ + // offset--; + // continue; + // } + // if(resolve){ + // final.push(id); + // } + // else{ + // final[j + this.boost] || (final[j + this.boost] = []); + // final[j + this.boost].push(id); + // } + // if(limit && ++count === limit){ + // this.boost = 0; + // return final; + // } + // } + // } + // } + // this.boost = 0; + // return result[0]; + // } + + let contain = create_object(); + let maxres = get_max_len(result); + if(!maxres) return final; + + // for(let j = 0, ids, res = result[0]; j < res.length; j++){ + // ids = res[j]; + // for(let k = 0; k < ids.length; k++){ + // contain[ids[k]] = 1; + // } + // } + + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res || !res.length) return []; + let contain_new = create_object(); + let match = 0; + let last_round = i === result.length - 1; + + for(let j = 0, ids; j < maxres; j++){ + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id, min; k < ids.length; k++){ + id = ids[k]; + // fill in first round + if(!i){ + // shift resolution +1 + // shift resolution by boost (inverse) + contain_new[id] = j + 1 + (i ? boost : 0); + match = 1; + } + // result in last round + else if(last_round){ + if((min = contain[id])){ + match = 1; + //if(!contain_new[id]){ + if(offset){ + offset--; + continue; + } + if(resolve){ + final.push(id); + } + else { + // reduce resolution -1 + min--; + if(j < min) min = j; + final[min] || (final[min] = []); + final[min].push(id); + } + if(limit && ++count === limit){ + //this.boost = 0; + return final; + } + // shift resolution +1 + //contain_new[id] = min + 1; + //} + } + } + // check for intersection + else if((min = contain[id])){ + // shift resolution +1 + if(j + 1 < min) min = j + 1; + contain_new[id] = min; + match = 1; + } + } + } + + if(!match){ + //this.boost = 0; + return []; + } + + contain = contain_new; + } + + //this.boost = 0; + return final; +} + +// import or from "./or.js"; +// import and from "./and.js"; +// import not from "./not.js"; + +Resolver.prototype.xor = function(){ + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.xor.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.xor.apply(this, first_argument); + } + } + + let final = []; + let promises = []; + let limit = 0, offset = 0, enrich, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.or){ + result = this.or(query.or); + } + else if(query.and){ + result = this.and(query.and); + } + else if(query.not){ + result = this.not(query.not); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + enrich = query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + self.result.length && (final = [self.result].concat(final)); + self.result = exclusive(final, limit, offset, enrich, !resolve, self.boostval); + return resolve ? self.result : self; + }); + } + + this.result.length && (final = [this.result].concat(final)); + this.result = exclusive(final, limit, offset, enrich, !resolve, self.boostval); + return resolve ? this.result : this; +}; + +/** + * @param result + * @param limit + * @param offset + * @param enrich + * @param resolve + * @param boost + * @return {Array} + */ + +function exclusive(result, limit, offset, enrich, resolve, boost){ + + if(!result.length){ + // todo remove + //console.log("Empty Result") + return result; + } + + if(result.length < 2){ + // todo remove + //console.log("Single Result") + if(resolve){ + return resolve_default(result[0], limit, offset, enrich); + } + else { + return result[0]; + } + } + + const final = []; + const check = create_object(); + + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res) continue; + + for(let j = 0, ids; j < res.length; j++){ + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + check[id] + ? check[id]++ + : check[id] = 1; + } + } + } + + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res) continue; + + for(let j = 0, ids; j < res.length; j++){ + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + if(check[id] === 1){ + if(resolve){ + final.push(id); + } + else { + // shift resolution by boost (inverse) + const index = j + (i ? boost : 0); + final[index] || (final[index] = []); + final[index].push(id); + } + } + } + } + } + + //this.boost = 0; + return final; +} + +// import or from "./or.js"; +// import and from "./and.js"; +// import xor from "./xor.js"; + +Resolver.prototype.not = function(){ + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.not.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.not.apply(this, first_argument); + } + } + + let final = []; + let promises = []; + let limit = 0, offset = 0, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.or){ + result = this.or(query.or); + } + else if(query.and){ + result = this.and(query.and); + } + else if(query.xor){ + result = this.xor(query.xor); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + self.result = exclusion.call(self, final, limit, offset, resolve); + return resolve ? self.result : self; + }); + } + + this.result = exclusion.call(this, final, limit, offset, resolve); + return resolve ? this.result : this; +}; + +/** + * @param result + * @param limit + * @param offset + * @param resolve + * @this Resolver + * @return {Array} + */ + +function exclusion(result, limit, offset, resolve){ + + if(!result.length){ + return this.result; + } + + const final = []; + const exclude = new Set(result.flat().flat()); + + for(let j = 0, ids; j < this.result.length; j++){ + ids = this.result[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + if(!exclude.has(id)){ + if(resolve){ + final.push(id); + } + else { + final[j] || (final[j] = []); + final[j].push(id); + } + } + } + } + + return final; +} + +/** + * @param result + * @constructor + */ + +function Resolver(result){ + if(!this){ + return new Resolver(result); + } + if(result && result.index){ + result.resolve = false; + this.index = result.index; + return result.index.search(result); + } + if(result.constructor === Resolver){ + // todo test this branch + //console.log("Resolver Loopback") + return result; + } + this.index = null; + this.result = result || []; + this.boostval = 0; +} + +Resolver.prototype.limit = function(limit){ + if(this.result.length){ + const final = []; + let count = 0; + for(let j = 0, ids; j < this.result.length; j++){ + ids = this.result[j]; + if(ids.length + count < limit){ + final[j] = ids; + count += ids.length; + } + else { + final[j] = ids.slice(0, limit - count); + this.result = final; + break; + } + } + } + return this; +}; + +Resolver.prototype.offset = function(offset){ + if(this.result.length){ + const final = []; + let count = 0; + for(let j = 0, ids; j < this.result.length; j++){ + ids = this.result[j]; + if(ids.length + count < offset){ + count += ids.length; + } + else { + final[j] = ids.slice(offset - count); + count = offset; + } + } + this.result = final; + } + return this; +}; + +Resolver.prototype.boost = function(boost){ + this.boostval += boost; + return this; +}; + +Resolver.prototype.resolve = function(limit, offset, enrich){ + set_resolve(1); + const result = this.result; + this.index = null; + this.result = null; + + if(result.length){ + if(typeof limit === "object"){ + enrich = limit.enrich; + offset = limit.offset; + limit = limit.limit; + } + return resolve_default(result, limit || 100, offset, enrich); + } + + return result; +}; + +/* + + from -> result[ + res[score][id], + res[score][id], + ] + + to -> [id] + + */ + +/** + * Implementation based on Object[key] provides better suggestions + * capabilities and has less performance scaling issues on large indexes. + * + * @param arrays + * @param limit + * @param offset + * @param {boolean|Array=} suggest + * @param {boolean=} resolve + * @returns {Array} + */ + +function intersect(arrays, limit, offset, suggest, resolve) { + + const length = arrays.length; + + // todo remove + // if(length < 2){ + // throw new Error("Not optimized intersect"); + // } + + let result = []; + let size = 0; + let check; + let check_suggest; + let check_new; + let res_arr; + + if(suggest){ + suggest = []; + } + + // 1. a reversed order prioritize the order of words from a query + // because it ends with the first term. + // 2. process terms in reversed order often has advantage for + // the fast path "end reached". + + // alternatively the results could be sorted by length up + //arrays.sort(sort_by_length_up); + + // todo the outer loop should be the res array instead of term array, + // this isn't just simple because the intersection calculation needs to reflect this + //const maxlen = get_max_len(arrays); + + for(let x = length - 1, found; x >= 0; x--){ + //for(let x = 0, found; x < length; x++){ + + res_arr = arrays[x]; + check_new = create_object(); + found = !check; + + // process relevance in forward order (direction is + // important for adding IDs during the last round) + + for(let y = 0, ids; y < res_arr.length; y++){ + + ids = res_arr[y]; + if(!ids || !ids.length) continue; + + for(let z = 0, id; z < ids.length; z++){ + + id = ids[z]; + + // check exists starting from the 2nd slot + if(check){ + if(check[id]){ + + // check if in last round + if(!x){ + //if(x === length - 1){ + + if(offset){ + offset--; + } + else { + + result[size++] = id; + + if(size === limit){ + // fast path "end reached" + return result /*resolve === false + ? { result, suggest } + :*/ + } + } + } + + if(x || suggest){ + //if((x < length - 1) || suggest){ + check_new[id] = 1; + } + + found = true; + } + + if(suggest){ + + if(!check_suggest[id]){ + check_suggest[id] = 1; + const arr = suggest[y] || (suggest[y] = []); + arr.push(id); + } + + // OLD: + // + // check_idx = (check_suggest[id] || 0) + 1; + // check_suggest[id] = check_idx; + // + // // do not adding IDs which are already included in the result (saves one loop) + // // the first intersection match has the check index 2, so shift by -2 + // + // if(check_idx < length){ + // + // const tmp = suggest[check_idx - 2] || (suggest[check_idx - 2] = []); + // tmp[tmp.length] = id; + // } + } + } + else { + + // pre-fill in first round + check_new[id] = 1; + } + } + } + + if(suggest){ + + // re-use the first pre-filled check for suggestions + check || (check_suggest = check_new); + } + else if(!found){ + + return []; + } + + check = check_new; + } + + // return intermediate result + // if(resolve === false){ + // return { result, suggest }; + // } + + if(suggest){ + + // needs to iterate in reverse direction + for(let x = suggest.length - 1, ids, len; x >= 0; x--){ + + ids = suggest[x]; + len = ids.length; + + for(let y = 0, id; y < len; y++){ + + id = ids[y]; + + if(!check[id]){ + + if(offset){ + offset--; + } + else { + + result[size++] = id; + + if(size === limit){ + // fast path "end reached" + return result; + } + } + + check[id] = 1; + } + } + } + } + + return result; +} + +/** + * @param mandatory + * @param arrays + * @returns {Array} + */ + +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.push(id); + } + } + } + } + + return result; +} + + +/** + * Implementation based on Array.includes() provides better performance, + * but it needs at least one word in the query which is less frequent. + * Also on large indexes it does not scale well performance-wise. + * This strategy also lacks of suggestion capabilities (matching & sorting). + * + * @param arrays + * @param limit + * @param offset + * @param {boolean|Array=} suggest + * @returns {Array} + */ + +// export function intersect(arrays, limit, offset, suggest) { +// +// const length = arrays.length; +// let result = []; +// let check; +// +// // determine shortest array and collect results +// // from the sparse relevance arrays +// +// let smallest_size; +// let smallest_arr; +// let smallest_index; +// +// for(let x = 0; x < length; x++){ +// +// const arr = arrays[x]; +// const len = arr.length; +// +// let size = 0; +// +// for(let y = 0, tmp; y < len; y++){ +// +// tmp = arr[y]; +// +// if(tmp){ +// +// size += tmp.length; +// } +// } +// +// if(!smallest_size || (size < smallest_size)){ +// +// smallest_size = size; +// smallest_arr = arr; +// smallest_index = x; +// } +// } +// +// smallest_arr = smallest_arr.length === 1 ? +// +// smallest_arr[0] +// : +// concat(smallest_arr); +// +// if(suggest){ +// +// suggest = [smallest_arr]; +// check = create_object(); +// } +// +// let size = 0; +// let steps = 0; +// +// // process terms in reversed order often results in better performance. +// // the outer loop must be the words array, using the +// // smallest array here disables the "fast fail" optimization. +// +// for(let x = length - 1; x >= 0; x--){ +// +// if(x !== smallest_index){ +// +// steps++; +// +// const word_arr = arrays[x]; +// const word_arr_len = word_arr.length; +// const new_arr = []; +// +// let count = 0; +// +// for(let z = 0, id; z < smallest_arr.length; z++){ +// +// id = smallest_arr[z]; +// +// let found; +// +// // process relevance in forward order (direction is +// // important for adding IDs during the last round) +// +// for(let y = 0; y < word_arr_len; y++){ +// +// const arr = word_arr[y]; +// +// if(arr.length){ +// +// found = arr.includes(id); +// +// if(found){ +// +// // check if in last round +// +// if(steps === length - 1){ +// +// if(offset){ +// +// offset--; +// } +// else{ +// +// result[size++] = id; +// +// if(size === limit){ +// +// // fast path "end reached" +// +// return result; +// } +// } +// +// if(suggest){ +// +// check[id] = 1; +// } +// } +// +// break; +// } +// } +// } +// +// if(found){ +// +// new_arr[count++] = id; +// } +// } +// +// if(suggest){ +// +// suggest[steps] = new_arr; +// } +// else if(!count){ +// +// return []; +// } +// +// smallest_arr = new_arr; +// } +// } +// +// if(suggest){ +// +// // needs to iterate in reverse direction +// +// for(let x = suggest.length - 1, arr, len; x >= 0; x--){ +// +// arr = suggest[x]; +// len = arr && arr.length; +// +// if(len){ +// +// for(let y = 0, id; y < len; y++){ +// +// id = arr[y]; +// +// if(!check[id]){ +// +// check[id] = 1; +// +// if(offset){ +// +// offset--; +// } +// else{ +// +// result[size++] = id; +// +// if(size === limit){ +// +// // fast path "end reached" +// +// return result; +// } +// } +// } +// } +// } +// } +// } +// +// return result; +// } + +// COMPILER BLOCK --> + +let global_resolve = 1; +function set_resolve(resolve){ + global_resolve = resolve; +} + +/** + * @param {string|SearchOptions} query + * @param {number|SearchOptions=} limit + * @param {SearchOptions=} options + * @returns {Array|Resolver|Promise} + */ + +Index.prototype.search = function(query, limit, options){ + + if(!options){ + if(!limit && is_object(query)){ + options = /** @type {!SearchOptions} */ (query); + query = ""; + } + else if(is_object(limit)){ + options = /** @type {!SearchOptions} */ (limit); + limit = 0; + } + } + + let result = []; + let length; + let context, suggest, offset = 0, resolve, enrich, tag; + + if(options){ + query = options.query || query; + limit = options.limit || limit; + offset = options.offset || 0; + context = options.context; + suggest = options.suggest; + resolve = (global_resolve && options.resolve !== false); + resolve || (global_resolve = 0); + enrich = resolve && options.enrich; + tag = this.db && options.tag; + } + else { + resolve = this.resolve || global_resolve; + } + + // todo: term deduplication during encoding when context is disabled + + // do not force a string as input + // https://github.com/nextapps-de/flexsearch/issues/432 + query = /** @type {Array} */ (this.encoder.encode(query)); + length = query.length; + limit || !resolve || (limit = 100); + + // fast path single term + if(length === 1){ + return single_term_query.call( + this, + query[0], // term + "", // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + // TODO: dedupe terms within encoder? + // TODO: deduplication will break the context chain + + context = this.depth && context !== false; + + // fast path single context + if(length === 2 && context && !suggest){ + return single_term_query.call( + this, + query[0], // term + query[1], // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + let maxlength = 0; + let minlength = 0; + + if(length > 1){ + + // term deduplication will break the context chain + // todo add context to dupe check + const dupes = create_object(); + const query_new = []; + + // if(context){ + // keyword = query[0]; + // dupes[keyword] = 1; + // query_new.push(keyword); + // maxlength = minlength = keyword.length; + // i = 1; + // } + + for(let i = 0, term; i < length; i++){ + + term = query[i]; + + if(term && !dupes[term]){ + + // todo add keyword check + // this fast path can't apply to persistent indexes + if(!suggest && !(this.db) && !this.get_array(term/*, keyword*/)){ + + // fast path "not found" + return resolve + ? result + : new Resolver(result); + } + else { + + query_new.push(term); + dupes[term] = 1; + } + + const term_length = term.length; + maxlength = Math.max(maxlength, term_length); + minlength = minlength ? Math.min(minlength, term_length) : term_length; + } + // else if(term && (!this.depth || context === false)){ + // query_new.push(term); + // } + } + + query = query_new; + length = query.length; + } + + // the term length could be changed after deduplication + + if(!length){ + return resolve + ? result + : new Resolver(result); + } + + let index = 0, keyword; + + // fast path single term + if(length === 1){ + return single_term_query.call( + this, + query[0], // term + "", // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + // fast path single context + if(length === 2 && context && !suggest){ + return single_term_query.call( + this, + query[0], // term + query[1], // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + if(length > 1){ + if(context){ + // start with context right away + keyword = query[0]; + index = 1; + } + // todo + else if(maxlength > 9 && (maxlength / minlength) > 3){ + // sorting terms will break the context chain + // bigger terms has less occurrence + // this might also reduce the intersection task + // todo check intersection order + query.sort(sort_by_length_down); + } + } + + if(this.db){ + + if(this.db.search){ + // when the configuration is not supported it returns false + const result = this.db.search(this, query, limit, offset, suggest, resolve, enrich, tag); + if(result !== false) return result; + } + + const self = this; + return (async function(){ + + for(let arr, term; index < length; index++){ + + term = query[index]; + + if(keyword){ + + arr = await self.get_array(term, keyword); + arr = add_result( + arr, + result, + suggest, + self.resolution_ctx, + /** @type {!number} */ (limit), + offset, + length === 2 + /*, term, keyword*/ + ); + + // the context is a moving window where the keyword is going forward like a cursor + // 1. when suggestion enabled just forward keyword if term was found + // 2. as long as the result is empty forward the pointer also + if(!suggest || (arr !== false) || !result.length){ + keyword = term; + } + } + else { + + arr = await self.get_array(term); + arr = add_result( + arr, + result, + suggest, + self.resolution, + /** @type {!number} */ (limit), + offset, + length === 1 + /*, term*/ + ); + } + + // limit reached + if(arr){ + return arr; + } + + // apply suggestions on last loop + if(suggest && (index === length - 1)){ + let length = result.length; + if(!length){ + // fallback to non-contextual search when no result was found + if(keyword){ + keyword = ""; + index = -1; + continue; + } + return result; + } + else if(length === 1){ + return resolve + ? resolve_default(result[0], /** @type {number} */ (limit), offset) + : new Resolver(result[0]); + } + } + } + + return resolve + ? intersect(result, /** @type {number} */ (limit), offset, suggest) + : new Resolver(result[0]) + }()); + } + + for(let arr, term; index < length; index++){ + + term = query[index]; + + if(keyword){ + + arr = this.get_array(term, keyword); + arr = /*this.*/add_result( + arr, + result, + suggest, + this.resolution_ctx, + /** @type {!number} */ (limit), + offset, + length === 2 + /*, term, keyword*/ + ); + + // 1. when suggestion enabled just forward keyword if term was found + // 2. as long as the result is empty forward the pointer also + if(!suggest || (arr !== false) || !result.length){ + keyword = term; + } + } + else { + + arr = this.get_array(term); + arr = /*this.*/add_result( + arr, + result, + suggest, + this.resolution, + /** @type {!number} */ (limit), + offset, + length === 1 + /*, term*/ + ); + } + + // limit reached + if(arr){ + return /** @type {Array} */ (arr); + } + + // apply suggestions on last loop + if(suggest && (index === length - 1)){ + const length = result.length; + if(!length){ + // fallback to non-contextual search when no result was found + if(keyword){ + keyword = ""; + index = -1; + continue; + } + return result; + } + else if(length === 1){ + return resolve + ? resolve_default(result[0], limit, offset) + : new Resolver(result[0]); + } + } + } + + return resolve + ? intersect(result, limit, offset, suggest) + : new Resolver(result[0]); +}; + +/** + * @param term + * @param keyword + * @param limit + * @param offset + * @param resolve + * @param enrich + * @param tag + * @this Index + * @return {Array|Resolver} + */ + +function single_term_query(term, keyword, limit, offset, resolve, enrich, tag){ + + const result = this.get_array(term, keyword, limit, offset, resolve, enrich, tag); + + if(this.db){ + return result.then(function(result){ + if(resolve) return result; + return result && result.length + ? (resolve ? resolve_default(result, limit, offset): new Resolver(result)) + : resolve ? [] : new Resolver([]); + }); + } + + return result && result.length + ? (resolve ? resolve_default(result, limit, offset) : new Resolver(result)) + : resolve ? [] : new Resolver([]); +} + +/** + * Returns a 1-dimensional finalized array when the result is done (fast path return), + * returns false when suggestions is enabled and no result was found, + * or returns nothing when a set was pushed successfully to the results + * + * @private + * @param {Array} arr + * @param {Array} result + * @param {Array} suggest + * @param {number} resolution + * @param {number} limit + * @param {number} offset + * @param {boolean} single_term + * @return {Array|boolean|undefined} + */ + +function add_result(arr, result, suggest, resolution, limit, offset, single_term/*, term, keyword*/){ + + let word_arr = []; + //let arr;// = keyword ? this.ctx : this.map; + //arr = this.get_array(term, keyword); + + if(arr){ + + //const resolution = Math.min(arr.length, keyword ? this.resolution_ctx : this.resolution); + // apply reduced resolution for queries + resolution = Math.min(arr.length, resolution); + + for(let x = 0, size = 0, tmp; x < resolution; x++){ + if((tmp = arr[x])){ + + if(offset){ + // apply offset right here on single terms + if(tmp && single_term){ + if(tmp.length <= offset){ + offset -= tmp.length; + tmp = null; + } + else { + tmp = tmp.slice(offset); + offset = 0; + } + } + } + + if(tmp){ + + // keep score (sparse array): + word_arr[x] = tmp; + // simplified score order: + //word_arr.push(tmp); + + if(single_term){ + size += tmp.length; + if(size >= limit){ + // fast path: + // a single term does not need to pre-collect results + break; + } + } + } + } + } + + if(word_arr.length){ + if(single_term){ + // fast path optimization + // offset was already applied at this point + // return an array will stop the query process immediately + return resolve_default(word_arr, limit, 0); + } + + result.push(word_arr); + // return nothing will continue the query + return; + } + } + + // 1. return an empty array will stop the loop + // 2. return a false value to prevent stop when using suggestions + return !suggest && word_arr; +} + +Index.prototype.get_array = function(term, keyword, limit, offset, resolve, enrich, tag){ + + let arr, swap; + + if(keyword){ + swap = this.bidirectional && (term > keyword); + } + + if(this.compress){ + term = default_compress(term); + keyword && (keyword = default_compress(keyword)); + } + + if(this.db){ + return keyword + ? this.db.get( + swap ? keyword : term, // term + swap ? term : keyword, // ctx + limit, + offset, + resolve, + enrich, + tag + ) + : this.db.get( + term, + "", // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + if(keyword){ + // the frequency of the starting letter is slightly less + // on the last half of the alphabet (m-z) in almost every latin language, + // so we sort downwards (https://en.wikipedia.org/wiki/Letter_frequency) + arr = this.ctx.get(swap ? term : keyword); + arr = arr && arr.get(swap ? keyword : term); + } + else { + arr = this.map.get(term); + } + + return arr; +}; + +// COMPILER BLOCK --> + +/** + * @param {boolean=} _skip_deletion + */ + +Index.prototype.remove = function(id, _skip_deletion){ + + const refs = this.reg.size && ( + this.fastupdate + ? this.reg.get(id) + : this.reg.has(id) + ); + + if(refs){ + + if(this.fastupdate){ + + // fast updates did not fully cleanup the key entries + + for(let i = 0, tmp; i < refs.length; i++){ + if((tmp = refs[i])){ + // todo check + //if(tmp.length < 1) throw new Error("invalid length"); + //if(tmp.indexOf(id) < 0) throw new Error("invalid id"); + if(tmp.length < 2){ + tmp.pop(); + } + else { + const index = tmp.indexOf(id); + index === refs.length - 1 + ? tmp.pop() + : tmp.splice(index, 1); + } + } + } + + // todo variation which cleans up, requires to push [ctx, key] instead of arr to the index.reg + // for(let i = 0, arr, term, keyword; i < refs.length; i++){ + // arr = refs[i]; + // if(typeof arr === "string"){ + // arr = this.map.get(term = arr); + // } + // else{ + // arr = this.ctx.get(keyword = arr[0]); + // arr && (arr = arr.get(arr[1])); + // } + // let counter = 0, found; + // if(arr && arr.length){ + // for(let j = 0, tmp; j < arr.length; j++){ + // if((tmp = arr[j])){ + // if(!found && tmp.length){ + // const index = tmp.indexOf(id); + // if(index >= 0){ + // tmp.splice(index, 1); + // // the index [ctx, key]:[res, id] is unique + // found = 1; + // } + // } + // if(tmp.length){ + // counter++; + // if(found){ + // break; + // } + // } + // else{ + // delete arr[j]; + // } + // } + // } + // } + // if(!counter){ + // keyword + // ? this.ctx.delete(keyword) + // : this.map.delete(term); + // } + // } + } + else { + + remove_index(this.map, id/*, this.resolution*/); + this.depth && + remove_index(this.ctx, id/*, this.resolution_ctx*/); + } + + _skip_deletion || this.reg.delete(id); + } + + if(this.db){ + this.commit_task.push({ "del": id }); + this.commit_auto && autoCommit(this); + //return this.db.remove(id); + } + + // the cache could be used outside the InMemory store + if(this.cache){ + this.cache.remove(id); + } + + return this; +}; + +/** + * @param map + * @param id + * @return {number} + */ + +function remove_index(map, id){ + + // a check counter of filled resolution slots + // to prevent removing the field + let count = 0; + + if(is_array(map)){ + for(let x = 0, arr, index; x < map.length; x++){ + if((arr = map[x]) && arr.length){ + index = arr.indexOf(id); + if(index >= 0){ + if(arr.length > 1){ + arr.splice(index, 1); + count++; + } + else { + // remove resolution slot + delete map[x]; + } + // the index key:[res, id] is unique + break; + } + else { + count++; + } + } + } + } + else for(let item of map){ + const key = item[0]; + const value = item[1]; + const tmp = remove_index(value, id); + tmp ? count += tmp + : map.delete(key); + } + + return count; +} + +/**! + * FlexSearch.js + * Author and Copyright: Thomas Wilkerling + * Licence: Apache-2.0 + * Hosted by Nextapps GmbH + * https://github.com/nextapps-de/flexsearch + */ + + +/** + * @constructor + * @param {IndexOptions|string=} options Options or preset as string + * @param {Map|Set|KeystoreSet|KeystoreMap=} _register + */ + +function Index(options$1, _register){ + + if(!this){ + return new Index(options$1); + } + + tick("Index.create"); + + options$1 = options$1 + ? apply_preset(options$1) + : {}; + + /** @type ContextOptions */ + const context = options$1.context || {}; + const encoder = options$1.encode || options$1.encoder || ( + options + ); + /** @type Encoder */ + this.encoder = encoder.encode + ? encoder + : typeof encoder === "object" + ? (new Encoder(encoder) + + ) + : { encode: encoder }; + + { + this.compress = options$1.compress || options$1.compression || false; + } + + let tmp; + this.resolution = options$1.resolution || 9; + this.tokenize = tmp = options$1.tokenize || "strict"; + this.depth = (tmp === "strict" && context.depth) || 0; + this.bidirectional = context.bidirectional !== false; + this.fastupdate = !!options$1.fastupdate; + this.score = options$1.score || null; + + tmp = (options$1.keystore || 0); + tmp && (this.keystore = tmp); + + this.map = tmp && SUPPORT_KEYSTORE ? new KeystoreMap(tmp) : new Map(); + this.ctx = tmp && SUPPORT_KEYSTORE ? new KeystoreMap(tmp) : new Map(); + this.reg = _register || ( + this.fastupdate + ? (tmp && SUPPORT_KEYSTORE ? new KeystoreMap(tmp) : new Map()) + : (tmp && SUPPORT_KEYSTORE ? new KeystoreSet(tmp) : new Set()) + ); + this.resolution_ctx = context.resolution || 1; + this.rtl = (encoder.rtl) || options$1.rtl || false; + + { + this.cache = (tmp = options$1.cache || null) && new CacheClass(tmp); + } + + { + this.resolve = options$1.resolve !== false; + } + + { + if((tmp = options$1.db)){ + this.db = tmp.mount(this); + } + this.commit_auto = options$1.commit !== false; + this.commit_task = []; + this.commit_timer = null; + } +} + +{ + Index.prototype.mount = function(db){ + if(this.commit_timer){ + clearTimeout(this.commit_timer); + this.commit_timer = null; + } + return db.mount(this); + }; + Index.prototype.commit = function(replace, append){ + if(this.commit_timer){ + clearTimeout(this.commit_timer); + this.commit_timer = null; + } + return this.db.commit(this, replace, append); + }; +} + +// if(SUPPORT_RESOLVER){ +// Index.prototype.resolve = function(params){ +// return new Resolver(params); +// }; +// } + +/** + * @param {!Index} self + * @param {boolean=} replace + * @param {boolean=} append + */ + +function autoCommit(self, replace, append){ + if(!self.commit_timer){ + self.commit_timer = setTimeout(function(){ + self.commit_timer = null; + self.db.commit(self, replace, append); + }, 0); + } +} + +Index.prototype.clear = function(){ + + //this.map = new Map(); + //this.ctx = new Map(); + //this.reg = this.fastupdate ? new Map() : new Set(); + this.map.clear(); + this.ctx.clear(); + this.reg.clear(); + + { + this.cache && + this.cache.clear(); + } + + if(this.db){ + this.commit_timer && clearTimeout(this.commit_timer); + this.commit_timer = null; + this.commit_task = [{ "clear": true }]; + //return this.db.clear(); + } + + return this; +}; + +//Index.prototype.pipeline = pipeline; + +/** + * @param {!number|string} id + * @param {!string} content + */ + +Index.prototype.append = function(id, content){ + return this.add(id, content, true); +}; + +Index.prototype.contain = function(id){ + + if(this.db){ + return this.db.has(id); + } + + return this.reg.has(id); +}; + +Index.prototype.update = function(id, content){ + + // todo check the async part + if(this.async /*|| (SUPPORT_PERSISTENT && this.db)*/){ + const self = this; + const res = this.remove(id); + return res.then ? res.then( + () => self.add(id, content) + ) : this.add(id, content); + } + + return this.remove(id).add(id, content); +}; + +/** + * @param map + * @return {number} + */ + +function cleanup_index(map){ + + let count = 0; + + if(is_array(map)){ + for(let i = 0, arr; i < map.length; i++){ + (arr = map[i]) && + (count += arr.length); + } + } + else for(const item of map){ + const key = item[0]; + const value = item[1]; + const tmp = cleanup_index(value); + tmp ? count += tmp + : map.delete(key); + } + + return count; +} + +Index.prototype.cleanup = function(){ + + if(!this.fastupdate){ + console.info("Cleanup the index isn't required when not using \"fastupdate\"."); + return this; + } + + cleanup_index(this.map); + this.depth && + cleanup_index(this.ctx); + + return this; +}; + +{ + + Index.prototype.searchCache = searchCache; +} + +{ + + Index.prototype.export = exportIndex; + Index.prototype.import = importIndex; +} + +{ + + apply_async(Index.prototype); +} + +async function handler(data) { + + data = data["data"]; + + /** @type Index */ + const index = self["_index"]; + const args = data["args"]; + const task = data["task"]; + + switch(task){ + + case "init": + + /** @type IndexOptions */ + let options = data["options"] || {}; + let filepath = options.config; + if(filepath){ + options = filepath; + // will be replaced after build with the line below because + // there is an issue with closure compiler dynamic import + //options = await import(filepath); + } + + // deprecated: + // const encode = options.encode; + // if(encode && (encode.indexOf("function") === 0)){ + // options.encode = Function("return " + encode)(); + // } + + const factory = data["factory"]; + + if(factory){ + + // export the FlexSearch global payload to "self" + Function("return " + factory)()(self); + + /** @type Index */ + self["_index"] = new self["FlexSearch"]["Index"](options); + + // destroy the exported payload + delete self["FlexSearch"]; + } + else { + + self["_index"] = new Index(options); + } + + postMessage({ "id": data["id"] }); + break; + + default: + + const id = data["id"]; + const message = index[task].apply(index, args); + postMessage(task === "search" ? { "id": id, "msg": message } : { "id": id }); + } +} + +//import { promise as Promise } from "../polyfill.js"; + +let pid = 0; + +/** + * @param {IndexOptions=} options + * @constructor + */ + +function WorkerIndex(options){ + + if(!this) { + return new WorkerIndex(options); + } + + if(options); + else { + options = {}; + } + + // the factory is the outer wrapper from the build + // we use "self" as a trap for node.js + + let factory = (self||window)["_factory"]; + if(factory){ + factory = factory.toString(); + } + + const is_node_js = typeof window === "undefined" && self["exports"]; + const _self = this; + + this.worker = create(factory, is_node_js, options.worker); + this.resolver = create_object(); + + if(!this.worker){ + + return; + } + + function onmessage(msg){ + msg = msg["data"] || msg; + const id = msg["id"]; + const res = id && _self.resolver[id]; + if(res){ + res(msg["msg"]); + delete _self.resolver[id]; + } + } + + is_node_js + ? this.worker["on"]("message", onmessage) + : this.worker.onmessage = onmessage; + + if(options["config"]){ + + // when extern configuration needs to be loaded + // it needs to return a promise to await for + return new Promise(function(resolve){ + _self.resolver[++pid] = function(){ + resolve(_self); + }; + _self.worker.postMessage({ + "id": pid, + "task": "init", + "factory": factory, + "options": options + }); + }); + } + + this.worker.postMessage({ + "task": "init", + "factory": factory, + "options": options + }); +} + +register("add"); +register("append"); +register("search"); +register("update"); +register("remove"); + +function register(key){ + + WorkerIndex.prototype[key] = + WorkerIndex.prototype[key + "Async"] = function(){ + + const self = this; + const args = [].slice.call(arguments); + const arg = args[args.length - 1]; + let callback; + + if(is_function(arg)){ + callback = arg; + args.splice(args.length - 1, 1); + } + + const promise = new Promise(function(resolve){ + //setTimeout(function(){ + self.resolver[++pid] = resolve; + self.worker.postMessage({ + "task": key, + "id": pid, + "args": args + }); + //}); + }); + + if(callback){ + promise.then(callback); + return this; + } + else { + + return promise; + } + }; +} + +function create(factory, is_node_js, worker_path){ + + let worker; + + worker = is_node_js ? + // This eval will be removed when compiling, it isn't there in final build + (0, eval)('new (require("worker_threads")["Worker"])(__dirname + "/node/node.js")') + //eval('new (require("worker_threads")["Worker"])(__dirname + "/node/node.js")') + :( + factory ? + new window.Worker(URL.createObjectURL( + new Blob( + ["onmessage=" + handler.toString()], + { "type": "text/javascript" } + ) + )) + : + new window.Worker(is_string(worker_path) ? worker_path : "worker/worker.js", { type: "module" }) + ); + + return worker; +} + +// COMPILER BLOCK --> + +/** + * + * @param id + * @param content + * @param {boolean=} _append + * @returns {Document|Promise} + */ + +Document.prototype.add = function(id, content, _append){ + + if(is_object(id)){ + + content = id; + id = parse_simple(content, this.key); + } + + if(content && (id || (id === 0))){ + + if(!_append && this.reg.has(id)){ + return this.update(id, content); + } + + // this.field does not include db tag indexes + for(let i = 0, tree; i < this.field.length; i++){ + + tree = this.tree[i]; + + const index = this.index.get(this.field[i]); + if(typeof tree === "function"){ + const tmp = tree(content); + if(tmp){ + index.add(id, tmp, /* append: */ false, /* skip update: */ true); + } + } + else { + const filter = tree._filter; + if(filter && !filter(content)){ + continue; + } + if(tree.constructor === String){ + tree = ["" + tree]; + } + else if(is_string(tree)){ + tree = [tree]; + } + add_index(content, tree, this.marker, 0, index, id, tree[0], _append); + } + } + + if(this.tag){ + + //console.log(this.tag, this.tagtree) + + for(let x = 0; x < this.tagtree.length; x++){ + + let tree = this.tagtree[x]; + let field = this.tagfield[x]; + let ref = this.tag.get(field); + let dupes = create_object(); + let tags; + + if(typeof tree === "function"){ + tags = tree(content); + if(!tags) continue; + } + else { + const filter = tree._filter; + if(filter && !filter(content)){ + continue; + } + if(tree.constructor === String){ + tree = "" + tree; + } + tags = parse_simple(content, tree); + } + + if(!ref || !tags){ + ref || (console.warn("Tag '" + field + "' was not found")); + continue; + } + + if(is_string(tags)){ + tags = [tags]; + } + + for(let i = 0, tag, arr; i < tags.length; i++){ + + tag = tags[i]; + //console.log(this.tag, tag, key, field) + + if(!dupes[tag]){ + dupes[tag] = 1; + + let tmp; + tmp = ref.get(tag); + tmp ? arr = tmp : ref.set(tag, arr = []); + + if(!_append || ! /** @type {!Array|KeystoreArray} */(arr).includes(id)){ + + // auto-upgrade to keystore array if max size exceeded + { + if(arr.length === 2**31-1 /*|| !(arr instanceof KeystoreArray)*/){ + const keystore = new KeystoreArray(arr); + if(this.fastupdate){ + for(let value of this.reg.values()){ + if(value.includes(arr)){ + value[value.indexOf(arr)] = keystore; + } + } + } + ref.set(tag, arr = keystore); + } + } + + arr.push(id); + + // add a reference to the register for fast updates + if(this.fastupdate){ + const tmp = this.reg.get(id); + tmp ? tmp.push(arr) + : this.reg.set(id, [arr]); + } + } + } + } + } + } + + if(this.store && (!_append || !this.store.has(id))){ + + let payload; + + if(this.storetree){ + + payload = create_object(); + + for(let i = 0, tree; i < this.storetree.length; i++){ + tree = this.storetree[i]; + + const filter = tree._filter; + if(filter && !filter(content)){ + continue; + } + let custom; + if(typeof tree === "function"){ + custom = tree(content); + if(!custom) continue; + tree = [tree._field]; + } + else if(is_string(tree) || tree.constructor === String){ + payload[tree] = content[tree]; + continue; + } + + store_value(content, payload, tree, 0, tree[0], custom); + } + } + + this.store.set(id, payload || content); + } + } + + return this; +}; + +// TODO support generic function created from string when tree depth > 1 + +/** + * @param obj + * @param store + * @param tree + * @param pos + * @param key + * @param {*=} custom + */ + +function store_value(obj, store, tree, pos, key, custom){ + + obj = obj[key]; + + // reached target field + if(pos === (tree.length - 1)){ + + // store target value + store[key] = custom || obj; + } + else if(obj){ + + if(is_array(obj)){ + + store = store[key] = new Array(obj.length); + + for(let i = 0; i < obj.length; i++){ + // do not increase pos (an array is not a field) + store_value(obj, store, tree, pos, i); + } + } + else { + + store = store[key] || (store[key] = create_object()); + key = tree[++pos]; + store_value(obj, store, tree, pos, key); + } + } +} + +function add_index(obj, tree, marker, pos, index, id, key, _append){ + + if((obj = obj[key])){ + + // reached target field + if(pos === (tree.length - 1)){ + + // handle target value + if(is_array(obj)){ + + // append array contents so each entry gets a new scoring context + if(marker[pos]){ + for(let i = 0; i < obj.length; i++){ + index.add(id, obj[i], /* append: */ true, /* skip update: */ true); + } + return; + } + + // or join array contents and use one scoring context + obj = obj.join(" "); + } + + index.add(id, obj, _append, /* skip_update: */ true); + } + else { + + if(is_array(obj)){ + for(let i = 0; i < obj.length; i++){ + // do not increase index, an array is not a field + add_index(obj, tree, marker, pos, index, id, i, _append); + } + } + else { + key = tree[++pos]; + add_index(obj, tree, marker, pos, index, id, key, _append); + } + } + } + else { + if(index.db){ + index.remove(id); + } + } +} + +// COMPILER BLOCK --> + +/** + * @param {!string|DocumentSearchOptions} query + * @param {number|DocumentSearchOptions=} limit + * @param {DocumentSearchOptions=} options + * @param {Array=} _resolve For internal use only. + * @returns {Promise|Array} + */ + +Document.prototype.search = function(query, limit, options, _resolve){ + + if(!options){ + if(!limit && is_object(query)){ + options = /** @type {DocumentSearchOptions} */ (query); + query = ""; + } + else if(is_object(limit)){ + options = /** @type {DocumentSearchOptions} */ (limit); + limit = 0; + } + } + + let result = [], result_field = []; + let pluck, enrich, merge, suggest; + let field, tag, offset, count = 0; + + if(options){ + + // todo remove support? + if(is_array(options)){ + field = options; + options = null; + } + else { + + query = options.query || query; + pluck = options.pluck; + merge = options.merge; + field = pluck || options.field || options.index; + tag = this.tag && options.tag; + enrich = this.store && options.enrich; + suggest = options.suggest; + limit = options.limit || limit; + offset = options.offset || 0; + limit || (limit = 100); + + if(tag && (!this.db || !_resolve)){ + + if(tag.constructor !== Array){ + tag = [tag]; + } + + let pairs = []; + + for(let i = 0, field; i < tag.length; i++){ + field = tag[i]; + if(is_string(field)){ + throw new Error("A tag option can't be a string, instead it needs a { field: tag } format."); + } + // default array notation + if(field.field && field.tag){ + const value = field.tag; + if(value.constructor === Array){ + for(let k = 0; k < value.length; k++){ + pairs.push(field.field, value[k]); + } + } + else { + pairs.push(field.field, value); + } + } + // shorter object notation + else { + const keys = Object.keys(field); + for(let j = 0, key, value; j < keys.length; j++){ + key = keys[j]; + value = field[key]; + if(value.constructor === Array){ + for(let k = 0; k < value.length; k++){ + pairs.push(key, value[k]); + } + } + else { + pairs.push(key, value); + } + } + } + } + + if(!pairs.length){ + throw new Error("Your tag definition within the search options is probably wrong. No valid tags found."); + } + + // tag used as pairs from this point + tag = pairs; + + // when tags is used and no query was set, + // then just return the tag indexes + if(!query){ + + let promises = []; + if(pairs.length) for(let j = 0; j < pairs.length; j+=2){ + let ids; + if(this.db){ + const index = this.index.get(pairs[j]); + if(!index){ + { + console.warn("Tag '" + pairs[j] + ":" + pairs[j + 1] + "' will be skipped because there is no field '" + pairs[j] + "'."); + } + continue; + } + promises.push(ids = index.db.tag(pairs[j + 1], limit, offset, enrich)); + } + else { + ids = get_tag.call(this, pairs[j], pairs[j + 1], limit, offset, enrich); + } + result.push({ + "field": pairs[j], + "tag": pairs[j + 1], + "result": ids + }); + } + + if(promises.length){ + return Promise.all(promises).then(function(promises){ + for(let j = 0; j < promises.length; j++){ + result[j].result = promises[j]; + } + return result; + }); + } + + return result; + } + } + + // extend to multi field search by default + if(is_string(field)){ + field = [field]; + } + } + } + + field || (field = this.field); + let promises = !_resolve && (this.worker || this.async) && []; + let db_tag_search; + + // multi field search + // field could be a custom set of selected fields by this query + // db tag indexes are also included in this field list + for(let i = 0, res, key, len; i < field.length; i++){ + + key = field[i]; + + if(this.db && this.tag){ + // tree is missing when it is a tag-only index (db) + if(!this.tree[i]){ + continue; + } + } + + let field_options; + + if(!is_string(key)){ + field_options = key; + key = field_options.field; + query = field_options.query || query; + limit = field_options.limit || limit; + //offset = field_options.offset || offset; + suggest = (field_options.suggest || suggest); + //enrich = SUPPORT_STORE && this.store && (field_options.enrich || enrich); + } + + if(_resolve){ + res = _resolve[i]; + } + else { + let opt = field_options || options; + let index = this.index.get(key); + + if(tag){ + if(this.db){ + opt.tag = tag; + db_tag_search = index.db.support_tag_search; + opt.field = field; + } + if(!db_tag_search){ + opt.enrich = false; + } + } + if(promises){ + promises[i] = index.searchAsync(query, limit, opt); + // restore enrich state + opt && enrich && (opt.enrich = enrich); + // just collect and continue + continue; + } + else { + res = index.search(query, limit, opt); + // restore enrich state + opt && enrich && (opt.enrich = enrich); + } + } + + len = res && res.length; + + // todo when no term was matched but tag was retrieved extend suggestion to tags + // every field has to intersect against all selected tag fields + if(tag && len){ + + const arr = []; + let count = 0; + + // tags are only applied in resolve phase when it's a db + if(this.db && _resolve){ + if(!db_tag_search){ + + // retrieve tag results assigned to it's field + for(let y = field.length; y < _resolve.length; y++){ + + let ids = _resolve[y]; + let len = ids && ids.length; + + if(len){ + count++; + arr.push(ids); + } + else if(!suggest){ + // no tags found + return result; + } + } + } + } + else { + + // tag[] are pairs at this line + for(let y = 0, ids, len; y < tag.length; y+=2){ + ids = this.tag.get(tag[y]); + + if(!ids){ + { + console.warn("Tag '" + tag[y] + ":" + tag[y + 1] + "' will be skipped because there is no field '" + tag[y] + "'."); + } + if(suggest){ + continue; + } + else { + return result; + } + } + + ids = ids && ids.get(tag[y + 1]); + len = ids && ids.length; + + if(len){ + count++; + arr.push(ids); + } + else if(!suggest){ + // no tags found + return result; + } + } + } + + if(count){ + res = intersect_union(res, arr); // intersect(arr, limit, offset) + len = res.length; + if(!len && !suggest){ + // nothing matched + return result; + } + // move counter back by 1 + count--; + } + } + + if(len){ + result_field[count] = key; + result.push(res); + count++; + } + else if(field.length === 1){ + // fast path: nothing matched + return result; + } + } + + if(promises){ + if(this.db){ + // todo when a tag index is never a search index this could be extracted + // push tag promises to the end + if(tag && tag.length && !db_tag_search){ + for(let y = 0; y < tag.length; y += 2){ + // it needs to retrieve data from tag pairs + const index = this.index.get(tag[y]); + if(!index){ + { + console.warn("Tag '" + tag[y] + ":" + tag[y + 1] + "' was not found because there is no field '" + tag[y] + "'."); + } + if(suggest){ + continue; + } + else { + return result; + } + } + promises.push(index.db.tag(tag[y + 1], limit, offset, /* enrich */ false)); + } + } + } + + const self = this; + + // TODO unroll this recursion + return Promise.all(promises).then(function(result){ + return result.length + ? self.search(query, limit, options, /* resolve: */ result) + : result; + }); + } + + if(!count){ + return result; + } + if(pluck && (!enrich || !this.store)){ + return result[0]; + } + + promises = []; + + for(let i = 0, res; i < result_field.length; i++){ + + res = result[i]; + + if(enrich && res.length && !res[0].doc){ + if(!this.db){ + if(res.length){ + res = apply_enrich.call(this, res); + } + } + else { + promises.push(res = this.index.get(this.field[0]).db.enrich(res)); + } + } + + if(pluck){ + return res; + } + + result[i] = { + "field": result_field[i], + "result": res + }; + } + + if(enrich && SUPPORT_PERSISTENT && this.db && promises.length){ + return Promise.all(promises).then(function(promises){ + for(let j = 0; j < promises.length; j++){ + result[j].result = promises[j]; + } + return merge + ? merge_fields(result, limit) + : result; + }); + } + + return merge + ? merge_fields(result, limit) + : result; +}; + +// todo support Resolver +// todo when searching through multiple fields each term should +// be found at least by one field to get a valid match without +// using suggestion explicitly + +function merge_fields(fields, limit, offset){ + const final = []; + const set = create_object(); + for(let i = 0, field, res; i < fields.length; i++){ + field = fields[i]; + res = field.result; + for(let j = 0, id, entry, tmp; j < res.length; j++){ + entry = res[j]; + id = entry.id; + tmp = set[id]; + if(!tmp){ + // offset was already applied on field indexes + // if(offset){ + // offset--; + // continue; + // } + // apply limit from last round, because just fields could + // be pushed without adding new results + if(final.length === limit){ + return final; + } + entry.field = set[id] = [field.field]; + final.push(entry); + } + else { + tmp.push(field.field); + } + } + } + return final; +} + +/** + * @this Document + */ + +function get_tag(tag, key, limit, offset, enrich){ + let res = this.tag.get(tag); + if(!res){ + console.warn("Tag '" + tag + "' was not found"); + return []; + } + res = res && res.get(key); + let len = res && (res.length - offset); + + if(len && (len > 0)){ + if((len > limit) || offset){ + res = res.slice(offset, offset + limit); + } + if(enrich){ + res = apply_enrich.call(this, res); + } + return res; + } +} + +/** + * @this Document + */ + +function apply_enrich(res){ + + const arr = new Array(res.length); + + for(let x = 0, id; x < res.length; x++){ + id = res[x]; + arr[x] = { + "id": id, + "doc": this.store.get(id) + }; + } + + return arr; +} + +/**! + * FlexSearch.js + * Author and Copyright: Thomas Wilkerling + * Licence: Apache-2.0 + * Hosted by Nextapps GmbH + * https://github.com/nextapps-de/flexsearch + */ + + +/** + * @constructor + * @param {!DocumentOptions} options + */ + +function Document(options){ + + if(!this) { + return new Document(options); + } + + /** @type DocumentDescriptor */ + const document = options.document || options.doc || options; + let tmp, keystore; + + this.tree = []; + this.field = []; + this.marker = []; + this.key = ((tmp = document.key || document.id) && parse_tree(tmp, this.marker)) || "id"; + + keystore = (options.keystore || 0); + keystore && (this.keystore = keystore); + this.fastupdate = !!options.fastupdate; + this.reg = this.fastupdate + ? (keystore && SUPPORT_KEYSTORE ? new KeystoreMap(keystore) : new Map()) + : (keystore && SUPPORT_KEYSTORE ? new KeystoreSet(keystore) : new Set()); + + { + // todo support custom filter function + this.storetree = (tmp = document.store || null) && tmp !== true && []; + this.store = tmp && ( + keystore && SUPPORT_KEYSTORE + ? new KeystoreMap(keystore) + : new Map() + ); + } + + { + this.cache = (tmp = options.cache || null) && new CacheClass(tmp); + // do not apply cache again for the indexes since .searchCache() + // is just a wrapper over .search() + options.cache = false; + } + + { + this.worker = options.worker; + } + + { + // this switch is used by recall of promise callbacks + this.async = false; + } + + /** @export */ + this.index = parse_descriptor.call(this, options, document); + + { + this.tag = null; + // TODO case-insensitive tags? + if((tmp = document.tag)){ + if(typeof tmp === "string"){ + tmp = [tmp]; + } + if(tmp.length){ + this.tag = new Map(); + this.tagtree = []; + this.tagfield = []; + for(let i = 0, params, field; i < tmp.length; i++){ + params = tmp[i]; + field = params.field || params; + if(!field){ + throw new Error("The tag field from the document descriptor is undefined."); + } + if(params.custom){ + this.tagtree[i] = params.custom; + } + else { + this.tagtree[i] = parse_tree(field, this.marker); + if(params.filter){ + if(typeof this.tagtree[i] === "string"){ + // it needs an object to put a property to it + this.tagtree[i] = new String(this.tagtree[i]); + } + this.tagtree[i]._filter = params.filter; + } + } + // the tag fields needs to be hold by indices + this.tagfield[i] = field; + this.tag.set(field, new Map()); + } + } + } + } + + { + options.db && this.mount(options.db); + } +} + +{ + + Document.prototype.mount = function(db){ + + let fields = this.field; + + if(this.tag){ + // tag indexes are referenced by field + // move tags to their field indexes respectively + for(let i = 0, field; i < this.tagfield.length; i++){ + field = this.tagfield[i]; + let index = this.index.get(field); + if(!index){ + // create raw index when not exists + this.index.set(field, index = new Index({}, this.reg)); + // copy and push to the field selection + if(fields === this.field){ + fields = fields.slice(0); + } + // tag indexes also needs to be upgraded to db instances + fields.push(field); + } + // assign reference + index.tag = this.tag.get(field); + } + } + + const promises = []; + const config = { + db: db.db, + type: db.type, + fastupdate: db.fastupdate + }; + + // upgrade all indexes to db instances + for(let i = 0, index, field; i < fields.length; i++){ + config.field = field = fields[i]; + index = this.index.get(field); + const dbi = new db.constructor(db.id, config); + // take over the storage id + dbi.id = db.id; + promises[i] = dbi.mount(index); + // add an identification property + index.document = true; + if(i){ + // the register has to export just one time + // also it's needed by the index for ID contain check + index.bypass = true; + } + else { + // the datastore has to export one time + index.store = this.store; + } + } + + this.async = true; + this.db = true; + return Promise.all(promises); + }; + + Document.prototype.commit = async function(replace, append){ + // parallel: + const promises = []; + for(const index of this.index.values()){ + promises.push(index.db.commit(index, replace, append)); + } + await Promise.all(promises); + this.reg.clear(); + // queued: + // for(const index of this.index.values()){ + // await index.db.commit(index, replace, append); + // } + // this.reg.clear(); + }; +} + +/** + * @this Document + */ + +function parse_descriptor(options, document){ + + const index = new Map(); + let field = document.index || document.field || document; + + if(is_string(field)){ + field = [field]; + } + + for(let i = 0, key, opt; i < field.length; i++){ + + key = field[i]; + + if(!is_string(key)){ + opt = key; + key = key.field; + } + + opt = /** @type DocumentIndexOptions */ ( + is_object(opt) + ? Object.assign({}, options, opt) + : options + ); + + if(this.worker){ + const worker = new WorkerIndex(opt); + index.set(key, worker); + if(!worker.worker){ + // fallback when not supported + this.worker = false; + } + } + + if(!this.worker){ + index.set(key, new Index(opt, this.reg)); + } + + if(opt.custom){ + this.tree[i] = opt.custom; + } + else { + this.tree[i] = parse_tree(key, this.marker); + if(opt.filter){ + if(typeof this.tree[i] === "string"){ + // it needs an object to put a property to it + this.tree[i] = new String(this.tree[i]); + } + this.tree[i]._filter = opt.filter; + } + } + + this.field[i] = key; + } + + if(this.storetree){ + + let stores = document.store; + if(is_string(stores)) stores = [stores]; + + for(let i = 0, store, field; i < stores.length; i++){ + store = /** @type Array */ (stores[i]); + field = store.field || store; + if(store.custom){ + this.storetree[i] = store.custom; + store.custom._field = field; + } + else { + this.storetree[i] = parse_tree(field, this.marker); + if(store.filter){ + if(typeof this.storetree[i] === "string"){ + // it needs an object to put a property to it + this.storetree[i] = new String(this.storetree[i]); + } + this.storetree[i]._filter = store.filter; + } + } + } + } + + return index; +} + +function parse_tree(key, marker){ + + const tree = key.split(":"); + let count = 0; + + for(let i = 0; i < tree.length; i++){ + key = tree[i]; + // todo apply some indexes e.g. [0], [-1], [0-2] + if(key[key.length - 1] === "]"){ + key = key.substring(0, key.length - 2); + if(key){ + marker[count] = true; + } + } + if(key){ + tree[count++] = key; + } + } + + if(count < tree.length){ + tree.length = count; + } + + return count > 1 ? tree : tree[0]; +} + +Document.prototype.append = function(id, content){ + return this.add(id, content, true); +}; + +Document.prototype.update = function(id, content){ + return this.remove(id).add(id, content); +}; + +Document.prototype.remove = function(id){ + + if(is_object(id)){ + id = parse_simple(id, this.key); + } + + for(const index of this.index.values()){ + index.remove(id, /* skip deletion */ true); + } + + if(this.reg.has(id)){ + + if(this.tag){ + // when fastupdate was enabled all ids are already removed + if(!this.fastupdate){ + for(let field of this.tag.values()){ + for(let item of field){ + const tag = item[0]; + const ids = item[1]; + const pos = ids.indexOf(id); + if(pos > -1){ + ids.length > 1 + ? ids.splice(pos, 1) + : field.delete(tag); + } + } + } + } + } + + if(this.store){ + this.store.delete(id); + } + + this.reg.delete(id); + } + + // the cache could be used outside the InMemory store + if(this.cache){ + this.cache.remove(id); + } + + return this; +}; + +Document.prototype.clear = function(){ + + //const promises = []; + + for(const index of this.index.values()){ + // db index will add clear task + index.clear(); + // const promise = index.clear(); + // if(promise instanceof Promise){ + // promises.push(promise); + // } + } + + if(this.tag){ + for(const tags of this.tag.values()){ + tags.clear(); + } + } + + if(this.store){ + this.store.clear(); + } + + return this; /*promises.length + ? Promise.all(promises) + :*/ +}; + +Document.prototype.contain = function(id){ + + if(this.db){ + return this.index.get(this.field[0]).db.has(id); + } + + return this.reg.has(id); +}; + +Document.prototype.cleanup = function(){ + + for(const index of this.index.values()){ + index.cleanup(); + } + + return this; +}; + +{ + + Document.prototype.get = function(id){ + + if(this.db){ + return this.index.get(this.field[0]).db.enrich(id).then(function(result){ + return result[0] && result[0].doc; + }); + } + + return this.store.get(id); + }; + + Document.prototype.set = function(id, store){ + + this.store.set(id, store); + return this; + }; +} + +{ + // todo mo + Document.prototype.searchCache = searchCache; +} + +{ + + Document.prototype.export = exportDocument; + Document.prototype.import = importDocument; +} + +{ + + apply_async(Document.prototype); +} + +const defaults = { + host: "localhost", + port: "27017", + user: null, + pass: null +}; +const fields = ["map", "ctx", "tag", "reg", "cfg"]; + +function sanitize(str) { + return str.toLowerCase().replace(/[^a-z0-9_\-]/g, ""); +} + +let CLIENT; +let DB = Object.create(null); + +/** + * @constructor + * @implements StorageInterface + */ + +function MongoDB(name, config = {}){ + if(!this){ + return new MongoDB(name, config); + } + if(typeof name === "object"){ + name = name.name; + config = name; + } + if(!name){ + console.info("Default storage space was used, because a name was not passed."); + } + this.id = "flexsearch" + (name ? "-" + sanitize(name) : ""); + this.field = config.field ? "-" + sanitize(config.field) : ""; + this.type = config.type || ""; + this.db = config.db || DB[this.id] || CLIENT || null; + this.trx = false; + this.support_tag_search = true; + Object.assign(defaults, config); + this.db && delete defaults.db; +} +// MongoDB.mount = function(flexsearch){ +// return new this().mount(flexsearch); +// }; + +MongoDB.prototype.mount = function(flexsearch){ + if(flexsearch.constructor === Document){ + return flexsearch.mount(this); + } + flexsearch.db = this; + return this.open(); +}; + +async function createCollection(db, ref, field){ + switch(ref){ + case "map": + await db.createCollection("map" + field); + await db.collection("map" + field).createIndex({ key: 1 }); + await db.collection("map" + field).createIndex({ id: 1 }); + break; + case "ctx": + await db.createCollection("ctx" + field); + await db.collection("ctx" + field).createIndex({ ctx: 1, key: 1 }); + await db.collection("ctx" + field).createIndex({ id: 1 }); + break; + case "tag": + await db.createCollection("tag" + field); + await db.collection("tag" + field).createIndex({ tag: 1 }); + await db.collection("tag" + field).createIndex({ id: 1 }); + break; + case "reg": + await db.createCollection("reg"); + await db.collection("reg").createIndex({ id: 1 }); + break; + case "cfg": + await db.createCollection("cfg" + field); + } +} + +MongoDB.prototype.open = async function(){ + + if(!this.db){ + if(!(this.db = DB[this.id])){ + if(!(this.db = CLIENT)){ + + let url = defaults.url; + if(!url){ + url = defaults.user + ? `mongodb://${ defaults.user }:${ defaults.pass }@${ defaults.host }:${ defaults.port }` + : `mongodb://${ defaults.host }:${ defaults.port }`; + } + this.db = CLIENT = new mongodb.MongoClient(url); + await this.db.connect(); + } + } + } + + if(this.db.db){ + this.db = DB[this.id] = this.db.db(this.id); + } + + const collections = await this.db.listCollections().toArray(); + + for(let i = 0; i < fields.length; i++){ + let found = false; + for(let j = 0; j < collections.length; j++){ + if(collections[j].name === fields[i] + (fields[i] !== "reg" ? this.field : "")){ + found = true; + break; + } + } + if(!found){ + await createCollection(this.db, fields[i], this.field); + } + } + + return this.db; +}; + +MongoDB.prototype.close = function(){ + this.db.close(); + this.db = null; + return this; +}; + +MongoDB.prototype.destroy = async function(){ + await Promise.all([ + this.db.dropCollection("map" + this.field), + this.db.dropCollection("ctx" + this.field), + this.db.dropCollection("tag" + this.field), + this.db.dropCollection("cfg" + this.field), + this.db.dropCollection("reg") + ]); + this.close(); +}; + +async function clear(ref){ + await this.db.dropCollection(ref); + await createCollection(this.db, ref, this.field); +} + +MongoDB.prototype.clear = function(){ + return Promise.all([ + clear.call(this, "map" + this.field), + clear.call(this, "ctx" + this.field), + clear.call(this, "tag" + this.field), + clear.call(this, "cfg" + this.field), + clear.call(this, "reg") + ]); +}; + +function create_result(rows, resolve, enrich){ + if(resolve){ + if(!enrich) for(let i = 0; i < rows.length; i++){ + rows[i] = rows[i].id; + } + return rows; + } + else { + const arr = []; + for(let i = 0, row; i < rows.length; i++){ + row = rows[i]; + arr[row.res] || (arr[row.res] = []); + arr[row.res].push(enrich + ? row + : row.id + ); + } + return arr; + } +} + +MongoDB.prototype.get = async function(key, ctx, limit = 0, offset = 0, resolve = true, enrich = false, tags){ + let rows; + let params = ctx ? { ctx, key } : { key }; + if(!enrich && !tags){ + rows = await this.db.collection((ctx ? "ctx" : "map") + this.field) + .find(params, { projection: { _id: 0, res: 1, id: 1 }, limit, skip: offset }) + .toArray(); + } + else { + + const project = { _id: 0, id: 1 }; + const stmt = [ + { $match: params } + ]; + + if(!resolve){ + project["res"] = 1; + } + + if(enrich){ + project["doc"] = "$doc.doc"; + stmt.push( + { $lookup: { + from: "reg", + localField: "id", + foreignField: "id", + as: "doc" + } }, + { $unwind: { + path: "$doc", + preserveNullAndEmptyArrays: true + } } + ); + } + + if(tags){ + + const match = {}; + for(let i = 0, count = 1; i < tags.length; i += 2){ + project["tag" + count] = "$tag" + count + ".tag"; + match["tag" + count] = tags[i + 1]; + stmt.push( + { $lookup: { + from: "tag-" + sanitize(tags[i]), + localField: "id", + foreignField: "id", + as: "tag" + count + } } + ); + count++; + } + + stmt.push( + { $project: project }, + { $match: match }, + { $project: { id: 1, doc: 1 } } + ); + } + else { + stmt.push( + { $project: project } + ); + } + + stmt.push( + { $sort: { res: 1 } }, + { $skip: offset}, + { $limit: limit } + ); + + rows = []; + const result = await this.db.collection((ctx ? "ctx" : "map") + this.field).aggregate(stmt); + + while(true/*await rows.hasNext()*/){ + const row = await result.next(); + if(row) rows.push(row); + else break; + } + } + + return create_result(rows, resolve, enrich); +}; + +MongoDB.prototype.tag = async function(tag, limit = 0, offset = 0, enrich = false){ + + if(!enrich){ + + const rows = await this.db.collection("tag" + this.field) + .find({ tag }, { projection: { _id: 0, id: 1 }, limit, skip: offset }) + .toArray(); + return create_result(rows, true, false); + } + else { + + const stmt = [ + { $match: { tag } }, + { $skip: offset}, + { $limit: limit }, + { $lookup: { + from: "reg", + localField: "id", + foreignField: "id", + as: "doc" + } }, + { $project: { _id: 0, id: 1, doc: "$doc.doc" } }, + { $unwind: { + path: "$doc", + preserveNullAndEmptyArrays: true + } } + ]; + + let rows = []; + const result = await this.db.collection("tag" + this.field).aggregate(stmt); + + while(true/*await rows.hasNext()*/){ + const row = await result.next(); + if(row) rows.push(row); + else break; + } + + return rows; + } +}; + +MongoDB.prototype.enrich = function(ids){ + if(typeof ids !== "object"){ + ids = [ids]; + } + return this.db.collection("reg") + .find({ id: { $in: ids } }, { projection: { _id: 0, id: 1, doc: 1 } }) + .toArray(); +}; + +MongoDB.prototype.has = function(id){ + return this.db.collection("reg").countDocuments({ id }, { limit: 1 }); +}; + +MongoDB.prototype.search = async function(flexsearch, query, limit = 100, offset = 0, suggest = false, resolve = true, enrich = false, tags){ + + let result = [], rows; + + if(query.length > 1 && flexsearch.depth){ + + let params = []; + let keyword = query[0]; + let term; + + for(let i = 1; i < query.length; i++){ + term = query[i]; + const swap = flexsearch.bidirectional && (term > keyword); + params.push({ + ctx: swap ? term : keyword, + key: swap ? keyword : term + }); + keyword = term; + } + + let project = resolve + ? { _id: 1 } + : { _id: 1, res: 1 }; + + const stmt = [ + { $match: { $or: params } }, + { $group: { + _id: "$id", + res: suggest ? { $sum: 1 } : { $min: 1 }, + count: { $sum: 1 } + } } + ]; + + suggest || stmt.push( + { $match: { count: query.length - 1 } } + ); + + if(enrich){ + project["doc"] = "$doc.doc"; + stmt.push( + { $lookup: { + from: "reg", + localField: "_id", + foreignField: "id", + as: "doc" + } }, + { $unwind: { + path: "$doc", + preserveNullAndEmptyArrays: true + } } + ); + } + + if(tags){ + + const match = {}; + for(let i = 0, count = 1; i < tags.length; i += 2){ + project["tag" + count] = "$tag" + count + ".tag"; + match["tag" + count] = tags[i + 1]; + stmt.push( + { $lookup: { + from: "tag-" + sanitize(tags[i]), + localField: "_id", + foreignField: "id", + as: "tag" + count + } } + ); + count++; + } + + stmt.push( + { $project: project }, + { $match: match } + ); + } + else { + stmt.push( + { $project: project } + ); + } + + stmt.push( + { $sort: suggest + ? { count: -1, res: 1} + : { res: 1 } }, + { $skip: offset}, + { $limit: limit } + ); + + if(tags){ + project = { _id: 1 }; + if(!resolve) project["res"] = 1; + if(enrich) project["doc"] = 1; + + stmt.push( + { $project: project } + ); + } + + rows = await this.db.collection("ctx" + this.field).aggregate(stmt); + } + else { + + let project = resolve + ? { _id: 1 } + : { _id: 1, res: 1 }; + + const stmt = [ + { $match: { + key: { $in: query } + } }, + { $group: { + _id: "$id", + res: suggest ? { $sum: 1 } : { $min: 1 }, + count: { $sum: 1 } + } } + ]; + + suggest || stmt.push( + { $match: { count: query.length } } + ); + + if(enrich){ + project["doc"] = "$doc.doc"; + stmt.push( + { $lookup: { + from: "reg", + localField: "_id", + foreignField: "id", + as: "doc" + } }, + { $unwind: { + path: "$doc", + preserveNullAndEmptyArrays: true + } } + ); + } + + if(tags){ + + const match = {}; + for(let i = 0, count = 1; i < tags.length; i += 2){ + project["tag" + count] = "$tag" + count + ".tag"; + match["tag" + count] = tags[i + 1]; + stmt.push( + { $lookup: { + from: "tag-" + sanitize(tags[i]), + localField: "_id", + foreignField: "id", + as: "tag" + count + } } + ); + count++; + } + + stmt.push( + { $project: project }, + { $match: match } + ); + } + else { + stmt.push( + { $project: project } + ); + } + + stmt.push( + { $sort: suggest + ? { count: -1, res: 1 } + : { res: 1 } }, + { $skip: offset}, + { $limit: limit } + ); + + if(tags){ + project = { _id: 1 }; + if(!resolve) project["res"] = 1; + if(enrich) project["doc"] = 1; + + stmt.push( + { $project: project } + ); + } + + rows = await this.db.collection("map" + this.field).aggregate(stmt); + } + + while(true/*await rows.hasNext()*/) { + const row = await rows.next(); + if(row){ + if(resolve && !enrich){ + result.push(row._id); + } + else { + row.id = row._id; + delete row._id; + result.push(row); + } + } + else break; + } + + if(resolve && !enrich){ + return result; + } + else { + return create_result(result, resolve, enrich); + } +}; + +MongoDB.prototype.info = function(){ + // todo +}; + +MongoDB.prototype.transaction = function(task){ + // not supported + return task.call(this); +}; + +MongoDB.prototype.commit = async function(flexsearch, _replace, _append){ + + // process cleanup tasks + if(_replace){ + await this.clear(); + // there are just removals in the task queue + flexsearch.commit_task = []; + } + else { + let tasks = flexsearch.commit_task; + flexsearch.commit_task = []; + for(let i = 0, task; i < tasks.length; i++){ + task = tasks[i]; + // there are just removals in the task queue + if(task.clear){ + await this.clear(); + _replace = true; + break; + } + else { + tasks[i] = task.del; + } + } + if(!_replace){ + if(!_append){ + tasks = tasks.concat(toArray(flexsearch.reg)); + } + tasks.length && await this.remove(tasks); + } + } + + if(!flexsearch.reg.size){ + return; + } + + if(flexsearch.map.size){ + let data = []; + for(const item of flexsearch.map){ + const key = item[0]; + const arr = item[1]; + for(let i = 0, ids; i < arr.length; i++){ + if((ids = arr[i]) && ids.length){ + this.type || (this.type = typeof ids[0]); + for(let j = 0; j < ids.length; j++){ + data.push({ + key: key, + res: i, + id: ids[j] + }); + } + } + } + } + if(data.length){ + await this.db.collection("map" + this.field).insertMany(data); + flexsearch.map.clear(); + } + } + + if(flexsearch.ctx.size){ + let data = []; + for(const ctx of flexsearch.ctx){ + const ctx_key = ctx[0]; + const ctx_value = ctx[1]; + for(const item of ctx_value){ + const key = item[0]; + const arr = item[1]; + for(let i = 0, ids; i < arr.length; i++){ + if((ids = arr[i]) && ids.length){ + for(let j = 0; j < ids.length; j++){ + data.push({ + ctx: ctx_key, + key: key, + res: i, + id: ids[j] + }); + } + } + } + } + } + if(data.length){ + await this.db.collection("ctx" + this.field).insertMany(data); + flexsearch.ctx.clear(); + } + } + + if(flexsearch.tag){ + let data = []; + if(flexsearch.tag){ + for(const item of flexsearch.tag){ + const tag = item[0]; + const ids = item[1]; + if(!ids.length) continue; + for(let j = 0; j < ids.length; j++){ + data.push({ tag, id: ids[j] }); + } + } + } + if(data.length){ + await this.db.collection("tag" + this.field).insertMany(data); + flexsearch.tag.clear(); + } + } + + let data = []; + if(flexsearch.store){ + for(const item of flexsearch.store.entries()){ + const id = item[0]; + const doc = item[1]; + data.push({ id, doc }); + } + } + else if(!flexsearch.bypass){ + for(const id of flexsearch.reg.keys()){ + data.push({ id }); + } + } + if(data.length){ + await this.db.collection("reg").insertMany(data); + flexsearch.store && + flexsearch.store.clear(); + flexsearch.document || + flexsearch.reg.clear(); + } + + // TODO + // await this.db.collection("cfg" + this.field).insertOne({ + // "encode": typeof flexsearch.encode === "string" ? flexsearch.encode : "", + // "charset": typeof flexsearch.charset === "string" ? flexsearch.charset : "", + // "tokenize": flexsearch.tokenize, + // "resolution": flexsearch.resolution, + // "minlength": flexsearch.minlength, + // "optimize": flexsearch.optimize, + // "fastupdate": flexsearch.fastupdate, + // "encoder": flexsearch.encoder, + // "context": { + // "depth": flexsearch.depth, + // "bidirectional": flexsearch.bidirectional, + // "resolution": flexsearch.resolution_ctx + // } + // }); +}; + +MongoDB.prototype.remove = function(ids){ + + if(!ids && ids !== 0) return; + if(typeof ids !== "object"){ + ids = [ids]; + } + + // if(this.type !== "string" && typeof ids[0] !== "number"){ + // ids = ids.map(item => parseInt(item, 10)); + // } + + return Promise.all([ + this.db.collection("map" + this.field).deleteMany({ "id": { "$in": ids }}), + this.db.collection("ctx" + this.field).deleteMany({ "id": { "$in": ids }}), + this.db.collection("tag" + this.field).deleteMany({ "id": { "$in": ids }}), + this.db.collection("reg").deleteMany({ "id": { "$in": ids }}) + ]); +}; + +module.exports = MongoDB; diff --git a/dist/db/postgres/index.cjs b/dist/db/postgres/index.cjs new file mode 100644 index 0000000..0ea9fff --- /dev/null +++ b/dist/db/postgres/index.cjs @@ -0,0 +1,6727 @@ +'use strict'; + +var pg_promise = require('pg-promise'); + +/** @define {string} */ + +/** @define {boolean} */ +const SUPPORT_PERSISTENT = true; + +/** @define {boolean} */ +const SUPPORT_KEYSTORE = true; + +/** + * @param {*} value + * @param {*} default_value + * @param {*=} merge_value + * @return {*} + */ + +function parse_option(value, default_value, merge_value){ + + const type_merge = typeof merge_value; + const type_value = typeof value; + + if(type_merge !== "undefined"){ + if(type_value !== "undefined"){ + + if(merge_value){ + if(type_value === "function" && + type_merge === type_value){ + return function(str){ + return /** @type Function */ (value)( + /** @type Function */ (merge_value)(str) + ); + } + } + + const constructor_value = value.constructor; + const constructor_merge = merge_value.constructor; + + if(constructor_value === constructor_merge){ + + if(constructor_value === Array){ + return merge_value.concat(value); + } + + if(constructor_value === Map){ + const map = new Map(/** @type !Map */ (merge_value)); + for(const item of /** @type !Map */ (value)){ + const key = item[0]; + const val = item[1]; + map.set(key, val); + } + return map; + } + + if(constructor_value === Set){ + const set = new Set(/** @type !Set */ (merge_value)); + for(const val of /** @type !Set */ (value).values()){ + set.add(val); + } + return set; + } + } + } + + return value; + } + else { + return merge_value; + } + } + + return type_value === "undefined" + ? default_value + : value; +} + +function create_object(){ + return Object.create(null); +} + +function concat(arrays){ + return [].concat.apply([], arrays); +} + +function sort_by_length_down(a, b){ + return b.length - a.length; +} + +function is_array(val){ + return val.constructor === Array; +} + +function is_string(val){ + return typeof val === "string"; +} + +function is_object(val){ + return typeof val === "object"; +} + +function is_function(val){ + return typeof val === "function"; +} + +/** + * @param {Map|Set} val + * @param {boolean=} stringify + * @return {Array} + */ + +function toArray(val, stringify){ + const result = []; + for(const key of val.keys()){ + result.push(key); + } + return result; +} + +// TODO support generic function created from string when tree depth > 1 +function parse_simple(obj, tree){ + + if(is_string(tree)){ + obj = obj[tree]; + } + else for(let i = 0; obj && (i < tree.length); i++){ + obj = obj[tree[i]]; + } + + return obj; +} + +function get_max_len(arr){ + let len = 0; + for(let i = 0, res; i < arr.length; i++){ + if((res = arr[i])){ + if(len < res.length){ + len = res.length; + } + } + } + return len; +} + +var normalize_polyfill = [ + + // Charset Normalization + + ["ª","a"], + ["²","2"], + ["³","3"], + ["¹","1"], + ["º","o"], + ["¼","1⁄4"], + ["½","1⁄2"], + ["¾","3⁄4"], + ["à","a"], + ["á","a"], + ["â","a"], + ["ã","a"], + ["ä","a"], + ["å","a"], + ["ç","c"], + ["è","e"], + ["é","e"], + ["ê","e"], + ["ë","e"], + ["ì","i"], + ["í","i"], + ["î","i"], + ["ï","i"], + ["ñ","n"], + ["ò","o"], + ["ó","o"], + ["ô","o"], + ["õ","o"], + ["ö","o"], + ["ù","u"], + ["ú","u"], + ["û","u"], + ["ü","u"], + ["ý","y"], + ["ÿ","y"], + ["ā","a"], + ["ă","a"], + ["ą","a"], + ["ć","c"], + ["ĉ","c"], + ["ċ","c"], + ["č","c"], + ["ď","d"], + ["ē","e"], + ["ĕ","e"], + ["ė","e"], + ["ę","e"], + ["ě","e"], + ["ĝ","g"], + ["ğ","g"], + ["ġ","g"], + ["ģ","g"], + ["ĥ","h"], + ["ĩ","i"], + ["ī","i"], + ["ĭ","i"], + ["į","i"], + ["ij","ij"], + ["ĵ","j"], + ["ķ","k"], + ["ĺ","l"], + ["ļ","l"], + ["ľ","l"], + ["ŀ","l"], + ["ń","n"], + ["ņ","n"], + ["ň","n"], + ["ʼn","n"], + ["ō","o"], + ["ŏ","o"], + ["ő","o"], + ["ŕ","r"], + ["ŗ","r"], + ["ř","r"], + ["ś","s"], + ["ŝ","s"], + ["ş","s"], + ["š","s"], + ["ţ","t"], + ["ť","t"], + ["ũ","u"], + ["ū","u"], + ["ŭ","u"], + ["ů","u"], + ["ű","u"], + ["ų","u"], + ["ŵ","w"], + ["ŷ","y"], + ["ź","z"], + ["ż","z"], + ["ž","z"], + ["ſ","s"], + ["ơ","o"], + ["ư","u"], + ["dž","dz"], + ["lj","lj"], + ["nj","nj"], + ["ǎ","a"], + ["ǐ","i"], + ["ǒ","o"], + ["ǔ","u"], + ["ǖ","u"], + ["ǘ","u"], + ["ǚ","u"], + ["ǜ","u"], + ["ǟ","a"], + ["ǡ","a"], + ["ǣ","ae"], + ["æ","ae"], + ["ǽ","ae"], + ["ǧ","g"], + ["ǩ","k"], + ["ǫ","o"], + ["ǭ","o"], + ["ǯ","ʒ"], + ["ǰ","j"], + ["dz","dz"], + ["ǵ","g"], + ["ǹ","n"], + ["ǻ","a"], + ["ǿ","ø"], + ["ȁ","a"], + ["ȃ","a"], + ["ȅ","e"], + ["ȇ","e"], + ["ȉ","i"], + ["ȋ","i"], + ["ȍ","o"], + ["ȏ","o"], + ["ȑ","r"], + ["ȓ","r"], + ["ȕ","u"], + ["ȗ","u"], + ["ș","s"], + ["ț","t"], + ["ȟ","h"], + ["ȧ","a"], + ["ȩ","e"], + ["ȫ","o"], + ["ȭ","o"], + ["ȯ","o"], + ["ȱ","o"], + ["ȳ","y"], + ["ʰ","h"], + ["ʱ","h"], + ["ɦ","h"], + ["ʲ","j"], + ["ʳ","r"], + ["ʴ","ɹ"], + ["ʵ","ɻ"], + ["ʶ","ʁ"], + ["ʷ","w"], + ["ʸ","y"], + ["ˠ","ɣ"], + ["ˡ","l"], + ["ˢ","s"], + ["ˣ","x"], + ["ˤ","ʕ"], + ["ΐ","ι"], + ["ά","α"], + ["έ","ε"], + ["ή","η"], + ["ί","ι"], + ["ΰ","υ"], + ["ϊ","ι"], + ["ϋ","υ"], + ["ό","ο"], + ["ύ","υ"], + ["ώ","ω"], + ["ϐ","β"], + ["ϑ","θ"], + ["ϒ","Υ"], + ["ϓ","Υ"], + ["ϔ","Υ"], + ["ϕ","φ"], + ["ϖ","π"], + ["ϰ","κ"], + ["ϱ","ρ"], + ["ϲ","ς"], + ["ϵ","ε"], + ["й","и"], + ["ѐ","е"], + ["ё","е"], + ["ѓ","г"], + ["ї","і"], + ["ќ","к"], + ["ѝ","и"], + ["ў","у"], + ["ѷ","ѵ"], + ["ӂ","ж"], + ["ӑ","а"], + ["ӓ","а"], + ["ӗ","е"], + ["ӛ","ә"], + ["ӝ","ж"], + ["ӟ","з"], + ["ӣ","и"], + ["ӥ","и"], + ["ӧ","о"], + ["ӫ","ө"], + ["ӭ","э"], + ["ӯ","у"], + ["ӱ","у"], + ["ӳ","у"], + ["ӵ","ч"] + + // Term Separators + + // ["'", ""], // it's -> its + // ["´", ""], + // ["`", ""], + // ["’", ""], + // ["ʼ", ""], + + // Numeric-Separators Chars Removal + + // [",", ""], + // [".", ""] + + // Non-Whitespace Separators + + // already was split by default via p{P} + // ["-", " "], + // [":", " "], + // ["_", " "], + // ["|", " "], + // ["/", " "], + // ["\\", " "] +]; + +// COMPILER BLOCK --> + +/* + +Custom Encoder +---------------- + +// Split a passed string into an Array of words: +function englishEncoder(string){ + return string.toLowerCase().split(/[^a-z]+/) +} + +// For CJK split a passed string into an Array of chars: +function chineseEncoder(string){ + return string.replace(/\s+/, "").split("") +} + +// Alternatively do not split the input: +function fixedEncoder(string){ + return [string] +} + +Built-in Encoder (Workflow) +---------------------------- +Pipeline: + 1. apply this.normalize: charset normalization: + applied on the whole input string e.g. lowercase, + will also apply on: filter, matcher, stemmer, mapper + 2. apply this.split: split input into terms (includes/excludes) + 3. apply this.filter (pre-filter) + 4. apply this.matcher (replace terms) + 5. apply this.stemmer (replace term endings) + 6. apply this.filter (post-filter) + 7. apply this.mapper (replace chars) + 8. apply this.replacer (custom regex) + 9. apply this.dedupe (letter deduplication) + 10. apply this.finalize +*/ + +const whitespace = /[^\p{L}\p{N}]+/u; // /[\p{Z}\p{S}\p{P}\p{C}]+/u; +//const numeric_split = /(\d{3})/g; +const numeric_split_length = /(\d{3})/g; +const numeric_split_prev_char = /(\D)(\d{3})/g; +const numeric_split_next_char = /(\d{3})(\D)/g; +//.replace(/(\d{3})/g, "$1 ") +//.replace(/([^\d])([\d])/g, "$1 $2") +//.replace(/([\d])([^\d])/g, "$1 $2") +const normalize = "".normalize && /[\u0300-\u036f]/g; // '´`’ʼ., +//const normalize_mapper = SUPPORT_CHARSET && !normalize && normalize_polyfill; + +/** + * @param {EncoderOptions=} options + * @constructor + */ + +function Encoder(options){ + + if(!this){ + return new Encoder(...arguments); + } + + for(let i = 0; i < arguments.length; i++){ + this.assign(arguments[i]); + } +} +/** + * @param {!EncoderOptions} options + */ +Encoder.prototype.assign = function(options){ + + /** + * pre-processing string input + * @type {Function|boolean} + */ + this.normalize = /** @type {Function|boolean} */ ( + parse_option(options.normalize, true, this.normalize) + ); + + // { + // letter: true, + // number: true, + // whitespace: true, + // symbol: true, + // punctuation: true, + // control: true, + // char: "" + // } + + let include = options.include; + let tmp = include || options.exclude || options.split; + + if(typeof tmp === "object"){ + let numeric = !include; + let regex = ""; + // split on whitespace by default + options.include || ( + regex += "\\p{Z}" + ); + if(tmp.letter){ + regex += "\\p{L}"; + } + if(tmp.number){ + regex += "\\p{N}"; + numeric = !!include; + } + if(tmp.symbol){ + regex += "\\p{S}"; + } + if(tmp.punctuation){ + regex += "\\p{P}"; + } + if(tmp.control){ + regex += "\\p{C}"; + } + if((tmp = tmp.char)){ + regex += typeof tmp === "object" ? tmp.join("") : tmp; + } + + this.split = new RegExp("[" + (include ? "^" : "") + regex + "]+", "u"); + this.numeric = numeric; + } + else { + + /** + * split string input into terms + * @type {string|RegExp|boolean|null} + */ + this.split = /** @type {string|RegExp|boolean} */ (parse_option(tmp, whitespace, this.split)); + this.numeric = parse_option(this.numeric, true); + } + + /** + * post-processing terms + * @type {Function|null} + */ + this.prepare = /** @type {Function|null} */ ( + parse_option(options.prepare, null, this.prepare) + ); + /** + * final processing + * @type {Function|null} + */ + this.finalize = /** @type {Function|null} */ ( + parse_option(options.finalize, null, this.finalize) + ); + + // assign the normalization fallback to the mapper + if(!normalize){ + this.mapper = new Map( + /** @type {Array>} */ ( + normalize_polyfill + ) + ); + } + + // options + + this.rtl = options.rtl || false; + this.dedupe = parse_option(options.dedupe, true, this.dedupe); + this.filter = parse_option((tmp = options.filter) && new Set(tmp), null, this.filter); + this.matcher = parse_option((tmp = options.matcher) && new Map(tmp), null, this.matcher); + this.mapper = parse_option((tmp = options.mapper) && new Map(tmp), null, this.mapper); + this.stemmer = parse_option((tmp = options.stemmer) && new Map(tmp), null, this.stemmer); + this.replacer = parse_option(options.replacer, null, this.replacer); + this.minlength = parse_option(options.minlength, 1, this.minlength); + this.maxlength = parse_option(options.maxlength, 0, this.maxlength); + + // minimum required tokenizer by this encoder + //this.tokenize = options["tokenize"] || ""; + + // auto-balanced cache + { + this.cache = tmp = parse_option(options.cache, true, this.cache); + if(tmp){ + this.timer = null; + this.cache_size = typeof tmp === "number" ? tmp : 2e5; + this.cache_enc = new Map(); + this.cache_prt = new Map(); + this.cache_enc_length = 128; + this.cache_prt_length = 128; + } + } + + // regex temporary state + this.matcher_str = ""; + this.matcher_test = null; + this.stemmer_str = ""; + this.stemmer_test = null; + + // prebuilt + // if(this.filter && this.split){ + // for(const key of this.filter){ + // const tmp = key.replace(this.split, ""); + // if(key !== tmp){ + // this.filter.delete(key); + // this.filter.add(tmp); + // } + // } + // } + if(this.matcher){ + for(const key of this.matcher.keys()){ + this.matcher_str += (this.matcher_str ? "|" : "") + key; + } + } + if(this.stemmer){ + for(const key of this.stemmer.keys()){ + this.stemmer_str += (this.stemmer_str ? "|" : "") + key; + } + } + + // if(SUPPORT_COMPRESSION){ + // this.compression = parse_option(options.compress || options.compression, 0, this.compression); + // if(this.compression && !table){ + // table = new Array(radix); + // for(let i = 0; i < radix; i++) table[i] = i + 33; + // table = String.fromCharCode.apply(null, table); + // } + // } + + return this; +}; + +Encoder.prototype.addMatcher = function(match, replace){ + // regex: + if(typeof match === "object"){ + return this.addReplacer(match, replace); + } + // a single char: + if(match.length < 2){ + return this.addMapper(match, replace); + } + this.matcher || (this.matcher = new Map()); + this.matcher.set(match , replace); + this.matcher_str += (this.matcher_str ? "|" : "") + match; + this.matcher_test = null; //new RegExp("(" + this.matcher_str + ")"); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addStemmer = function(match, replace){ + this.stemmer || (this.stemmer = new Map()); + this.stemmer.set(match, replace); + this.stemmer_str += (this.stemmer_str ? "|" : "") + match; + this.stemmer_test = null; //new RegExp("(" + this.stemmer_str + ")"); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addFilter = function(str){ + this.filter || (this.filter = new Set()); + this.filter.add(str); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addMapper = function(char_match, char_replace){ + // regex: + if(typeof char_match === "object"){ + return this.addReplacer(char_match, char_replace); + } + // not a char: + if(char_match.length > 1){ + return this.addMatcher(char_match, char_replace); + } + this.mapper || (this.mapper = new Map()); + this.mapper.set(char_match, char_replace); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addReplacer = function(match, replace){ + if(typeof match === "string") match = new RegExp(match, "g"); + this.replacer || (this.replacer = []); + this.replacer.push(match, replace || ""); + this.cache && this.invalidate(); + return this; +}; + +{ + Encoder.prototype.invalidate = function(){ + this.cache_enc.clear(); + this.cache_prt.clear(); + }; +} + +Encoder.prototype.encode = function(str){ + + //if(!str) return str; + // todo remove dupe terms + + if(this.cache && str.length <= this.cache_enc_length){ + if(this.timer){ + if(this.cache_enc.has(str)){ + return this.cache_enc.get(str); + } + } + else { + this.timer = setTimeout(clear$1, 0, this); + } + } + + // 1. apply charset normalization + if(this.normalize){ + if(typeof this.normalize === "function"){ + str = this.normalize(str); + } + else if(normalize){ + str = str.normalize("NFKD").replace(normalize, "").toLowerCase(); + } + else { + str = str.toLowerCase(); + // if(SUPPORT_CHARSET){ + // this.mapper = this.mapper + // // todo replace spread + // ? new Map([.../** @type {!Iterable} */(normalize_mapper), ...this.mapper]) + // : new Map(/** @type {Map} */ (normalize_mapper)); + // } + } + //if(!str) return str; + } + + // 2. apply custom encoder (can replace split) + if(this.prepare){ + str = this.prepare(str); + } + + // 3. split numbers into triplets + if(this.numeric && str.length > 3){ + str = str.replace(numeric_split_prev_char, "$1 $2") + .replace(numeric_split_next_char, "$1 $2") + .replace(numeric_split_length, "$1 "); + } + + // if(this.matcher && (str.length > 1)){ + // this.matcher_test || ( + // this.matcher_test = new RegExp("(" + this.matcher_str + ")", "g") + // ); + // str = str.replace(this.matcher_test, match => this.matcher.get(match)); + // } + // if(this.stemmer){ + // this.stemmer_test || ( + // this.stemmer_test = new RegExp("(?!\\b)(" + this.stemmer_str + ")(\\b|_)", "g") + // ); + // str = str.replace(this.stemmer_test, match => this.stemmer.get(match)); + // } + + const skip = !(this.dedupe || this.mapper || this.filter || this.matcher || this.stemmer || this.replacer); + let final = []; + let words = this.split || this.split === "" + ? str.split(/** @type {string|RegExp} */ (this.split)) + : str; //[str]; + + for(let i = 0, word, base; i < words.length; i++){ + // filter empty entries + if(!(word = base = words[i])){ + continue; + } + if(word.length < this.minlength){ + continue; + } + if(skip) { + final.push(word); + continue; + } + + // 1. pre-filter before cache + if(this.filter && this.filter.has(word)){ + continue; + } + + if(this.cache && word.length <= this.cache_prt_length){ + if(this.timer){ + const tmp = this.cache_prt.get(word); + //if(this.cache_prt.has(word)){ + if(tmp || tmp === ""){ + //word = this.cache_prt.get(word); + tmp && final.push(tmp); + //word ? words[i] = word : words.splice(i--, 1); + continue; + } + } + else { + this.timer = setTimeout(clear$1, 0, this); + } + } + + let postfilter; + + // if(this.normalize === true && normalize){ + // word = word.normalize("NFKD").replace(normalize, ""); + // postfilter = 1; + // } + + // if(this.normalize){ + // if(typeof this.normalize === "function"){ + // word = this.normalize(word); + // } + // else if(normalize){ + // word = word.normalize("NFKD").replace(normalize, "").toLowerCase(); + // } + // else{ + // word = word.toLowerCase(); + // this.mapper = this.mapper + // ? new Map([...normalize_mapper, ...this.mapper]) + // : new Map(/** @type {Map} */ normalize_mapper); + // } + // postfilter = 1; + // //if(!str) return str; + // } + + // 2. apply stemmer after matcher + if(this.stemmer && (word.length > 2)){ + // for(const item of this.stemmer){ + // const key = item[0]; + // const value = item[1]; + // + // if(word.length > key.length && word.endsWith(key)){ + // word = word.substring(0, word.length - key.length) + value; + // break; + // } + // + // // const position = word.length - key.length; + // // if(position > 0 && word.substring(position) === key){ + // // word = word.substring(0, position) + value; + // // break; + // // } + // } + this.stemmer_test || ( + this.stemmer_test = new RegExp("(?!^)(" + this.stemmer_str + ")$") + ); + word = word.replace(this.stemmer_test, match => this.stemmer.get(match)); + postfilter = 1; + } + + // 3. apply matcher + if(this.matcher && (word.length > 1)){ + this.matcher_test || ( + this.matcher_test = new RegExp("(" + this.matcher_str + ")", "g") + ); + word = word.replace(this.matcher_test, match => this.matcher.get(match)); + postfilter = 1; + } + + // 4. post-filter after matcher and stemmer was applied + if(word && postfilter && (word.length < this.minlength || (this.filter && this.filter.has(word)))){ + word = ""; + } + + // 5. apply mapper and collapsing + if(word && (this.mapper || (this.dedupe && word.length > 1))){ + //word = this.replace_dedupe(word); + //word = replace_deduped(word, this.mapper, true); + let final = ""; + for(let i = 0, prev = "", char, tmp; i < word.length; i++){ + char = word.charAt(i); + if(char !== prev || !this.dedupe){ + tmp = this.mapper && this.mapper.get(char); + if(!tmp && tmp !== "") + final += (prev = char); + else if((tmp !== prev || !this.dedupe) && (prev = tmp)) + final += tmp; + } + } + word = final; + } + + // apply custom regex + if(word && this.replacer){ + for(let i = 0; word && (i < this.replacer.length); i+=2){ + word = word.replace(this.replacer[i], this.replacer[i+1]); + } + } + + // slower variants for removing same chars in a row: + //word = word.replace(/([^0-9])\1+/g, "$1"); + //word = word.replace(/(.)\1+/g, "$1"); + //word = word.replace(/(?<=(.))\1+/g, ""); + + // if(word){ + // words[i] = word; + // } + + if(this.cache && base.length <= this.cache_prt_length){ + this.cache_prt.set(base, word); + if(this.cache_prt.size > this.cache_size){ + this.cache_prt.clear(); + this.cache_prt_length = this.cache_prt_length / 1.1 | 0; + } + } + + //word || words.splice(i--, 1); + word && final.push(word); + } + + //words = final; + // else if(this.filter){ + // for(let i = 0, word; i < words.length; i++){ + // if((word = words[i]) && !this.filter.has(word)){ + // //filtered.push(word); + // words.splice(i--, 1); + // } + // } + // } + + if(this.finalize){ + final = this.finalize(final) || final; + } + + if(this.cache && str.length <= this.cache_enc_length){ + this.cache_enc.set(str, final); + if(this.cache_enc.size > this.cache_size){ + this.cache_enc.clear(); + this.cache_enc_length = this.cache_enc_length / 1.1 | 0; + } + } + + return final; +}; + +// Encoder.prototype.compress = function(str) { +// +// //return str; +// //if(!str) return str; +// +// if(SUPPORT_CACHE && this.cache && str.length <= this.cache_prt_length){ +// if(this.timer){ +// if(this.cache_cmp.has(str)){ +// return this.cache_cmp.get(str); +// } +// } +// else{ +// this.timer = setTimeout(clear, 0, this); +// } +// } +// +// const result = typeof this.compression === "function" +// ? this.compression(str) +// : hash(str); //window.hash(str); +// +// if(SUPPORT_CACHE && this.cache && str.length <= this.cache_prt_length){ +// this.cache_cmp.set(str, result); +// this.cache_cmp.size > this.cache_size && +// this.cache_cmp.clear(); +// } +// +// return result; +// }; + +// function hash(str){ +// return str; +// } + +function clear$1(self){ + self.timer = null; + self.cache_enc.clear(); + self.cache_prt.clear(); +} + +/** + * @param {string|Object} query + * @param {number|Object=} limit + * @param {Object=} options + * @this {Index|Document} + * @returns {Array|Promise} + */ + +function searchCache(query, limit, options){ + + query = (typeof query === "object" + ? "" + query.query + : "" + query + ).toLowerCase(); + + //let encoded = this.encoder.encode(query).join(" "); + let cache = this.cache.get(query); + if(!cache){ + cache = this.search(query, limit, options); + if(cache.then){ + const self = this; + cache.then(function(cache){ + self.cache.set(query, cache); + return cache; + }); + } + this.cache.set(query, cache); + } + return cache; +} + +/** + * @param {boolean|number=} limit + * @constructor + */ + +function CacheClass(limit){ + /** @private */ + this.limit = (!limit || limit === true) ? 1000 : limit; + /** @private */ + this.cache = new Map(); + /** @private */ + this.last = ""; +} + +CacheClass.prototype.set = function(key, value){ + //if(!this.cache.has(key)){ + this.cache.set(this.last = key, value); + if(this.cache.size > this.limit){ + this.cache.delete(this.cache.keys().next().value); + } + //} +}; + +CacheClass.prototype.get = function(key){ + const cache = this.cache.get(key); + if(cache && this.last !== key){ + this.cache.delete(key); + this.cache.set(this.last = key, cache); + } + return cache; +}; + +CacheClass.prototype.remove = function(id){ + for(const item of this.cache){ + const key = item[0]; + const value = item[1]; + if(value.includes(id)){ + this.cache.delete(key); + } + } +}; + +CacheClass.prototype.clear = function(){ + this.cache.clear(); + this.last = ""; +}; + +/** @type EncoderOptions */ +const options = { + normalize: function(str){ + return str.toLowerCase(); + }, + dedupe: false +}; + +// import { pipeline } from "../../lang.js"; +// +// const whitespace = /[\p{Z}\p{S}\p{P}\p{C}]+/u; +// +// export const rtl = false; +// export const tokenize = ""; +// export default { +// encode: encode, +// rtl: rtl, +// tokenize: tokenize +// } +// +// /** +// * @param {string|number} str +// */ +// +// export function encode(str){ +// +// return pipeline.call( +// +// this, +// /* string: */ ("" + str).toLowerCase(), +// /* normalize: */ false, +// /* split: */ whitespace, +// /* collapse: */ false +// ); +// } + +// KeystoreObj.prototype.destroy = function(){ +// this.index = null; +// this.keys = null; +// this.proxy = null; +// }; + +function _slice(self, start, end, splice){ + let arr = []; + for(let i = 0, index; i < self.index.length; i++){ + index = self.index[i]; + if(start >= index.length){ + start -= index.length; + } + else { + const tmp = index[splice ? "splice" : "slice"](start, end); + const length = tmp.length; + if(length){ + arr = arr.length + ? arr.concat(tmp) + : tmp; + end -= length; + if(splice) self.length -= length; + if(!end) break; + } + start = 0; + } + } + return arr; +} + +/** + * @param arr + * @constructor + */ + +function KeystoreArray(arr){ + + if(!this){ + return new KeystoreArray(arr); + } + + this.index = arr ? [arr] : []; + this.length = arr ? arr.length : 0; + const self = this; + + return /*this.proxy =*/ new Proxy([], { + get(target, key) { + if(key === "length"){ + return self.length; + } + if(key === "push"){ + return function(value){ + self.index[self.index.length - 1].push(value); + self.length++; + } + } + if(key === "pop"){ + return function(){ + if(self.length){ + self.length--; + return self.index[self.index.length - 1].pop(); + } + } + } + if(key === "indexOf"){ + return function(key){ + let index = 0; + for(let i = 0, arr, tmp; i < self.index.length; i++){ + arr = self.index[i]; + //if(!arr.includes(key)) continue; + tmp = arr.indexOf(key); + if(tmp >= 0) return index + tmp; + index += arr.length; + } + return -1; + } + } + if(key === "includes"){ + return function(key){ + for(let i = 0; i < self.index.length; i++){ + if(self.index[i].includes(key)){ + return true; + } + } + return false; + } + } + if(key === "slice"){ + return function(start, end){ + return _slice( + self, + start || 0, + end || self.length, + false + ); + } + } + if(key === "splice"){ + return function(start, end){ + return _slice( + self, + start || 0, + end || self.length, + // splice: + true + ); + } + } + if(key === "constructor"){ + return Array; + } + if(typeof key === "symbol" /*|| isNaN(key)*/){ + // not supported + return; + } + const index = key / (2**31) | 0; + const arr = self.index[index]; + return arr && arr[key]; + }, + set(target, key, value){ + const index = key / (2**31) | 0; + const arr = self.index[index] || (self.index[index] = []); + arr[key] = value; + self.length++; + return true; + } + }); +} + +KeystoreArray.prototype.clear = function(){ + this.index.length = 0; +}; + +KeystoreArray.prototype.destroy = function(){ + this.index = null; + this.proxy = null; +}; + +KeystoreArray.prototype.push = function(val){}; + +/** + * @param bitlength + * @constructor + */ + +function KeystoreMap(bitlength = 8){ + + if(!this){ + return new KeystoreMap(bitlength); + } + + this.index = create_object(); + this.refs = []; + this.size = 0; + + if(bitlength > 32){ + this.crc = lcg64; + this.bit = BigInt(bitlength); + } + else { + this.crc = lcg$1; + this.bit = bitlength; + } +} + +KeystoreMap.prototype.get = function(key) { + const address = this.crc(key); + const map = this.index[address]; + return map && map.get(key); +}; + +KeystoreMap.prototype.set = function(key, value){ + const address = this.crc(key); + let map = this.index[address]; + if(map){ + let size = map.size; + map.set(key, value); + size -= map.size; + size && this.size++; + } + else { + this.index[address] = map = new Map([[key, value]]); + this.refs.push(map); + } +}; + +/** + * @param bitlength + * @constructor + */ + +function KeystoreSet(bitlength = 8){ + + if(!this){ + return new KeystoreSet(bitlength); + } + + // using plain Object with numeric key access + // just for max performance + this.index = create_object(); + this.refs = []; + + if(bitlength > 32){ + this.crc = lcg64; + this.bit = BigInt(bitlength); + } + else { + this.crc = lcg$1; + this.bit = bitlength; + } +} + +KeystoreSet.prototype.add = function(key){ + const address = this.crc(key); + let set = this.index[address]; + if(set){ + let size = set.size; + set.add(key); + size -= set.size; + size && this.size++; + } + else { + this.index[address] = set = new Set([key]); + this.refs.push(set); + } +}; + +KeystoreMap.prototype.has = +KeystoreSet.prototype.has = function(key) { + const address = this.crc(key); + const map_or_set = this.index[address]; + return map_or_set && map_or_set.has(key); +}; + +/* +KeystoreMap.prototype.size = +KeystoreSet.prototype.size = function(){ + let size = 0; + const values = Object.values(this.index); + for(let i = 0; i < values.length; i++){ + size += values[i].size; + } + return size; +}; +*/ + +KeystoreMap.prototype.delete = +KeystoreSet.prototype.delete = function(key){ + const address = this.crc(key); + const map_or_set = this.index[address]; + // set && (set.size === 1 + // ? this.index.delete(address) + // : set.delete(key)); + map_or_set && + map_or_set.delete(key) && + this.size--; +}; + +KeystoreMap.prototype.clear = +KeystoreSet.prototype.clear = function(){ + this.index = create_object(); + this.refs = []; + this.size = 0; +}; + +// KeystoreMap.prototype.destroy = +// KeystoreSet.prototype.destroy = function(){ +// this.index = null; +// this.refs = null; +// this.proxy = null; +// }; + +KeystoreMap.prototype.values = +KeystoreSet.prototype.values = function*(){ + // alternatively iterate through this.keys[] + //const refs = Object.values(this.index); + for(let i = 0; i < this.refs.length; i++){ + for(let value of this.refs[i].values()){ + yield value; + } + } +}; + +KeystoreMap.prototype.keys = +KeystoreSet.prototype.keys = function*(){ + //const values = Object.values(this.index); + for(let i = 0; i < this.refs.length; i++){ + for(let key of this.refs[i].keys()){ + yield key; + } + } +}; + +KeystoreMap.prototype.entries = +KeystoreSet.prototype.entries = function*(){ + //const values = Object.values(this.index); + for(let i = 0; i < this.refs.length; i++){ + for(let entry of this.refs[i].entries()){ + yield entry; + } + } +}; + +/** + * Linear Congruential Generator (LCG) + * @param str + * @this {KeystoreMap|KeystoreSet} + */ + +function lcg$1(str) { + let range = 2 ** this.bit - 1; + if(typeof str == "number"){ + return str & range; + } + let crc = 0, bit = this.bit + 1; + for(let i = 0; i < str.length; i++) { + crc = (crc * bit ^ str.charCodeAt(i)) & range; + } + // shift Int32 to UInt32 because negative numbers + // extremely slows down key lookup + return this.bit === 32 + ? crc + 2 ** 31 + : crc;// & 0xFFFF; +} + +/** + * @param str + * @this {KeystoreMap|KeystoreSet} + */ + +function lcg64(str) { + let range = BigInt(2) ** /** @type {!BigInt} */ (this.bit) - BigInt(1); + let type = typeof str; + if(type === "bigint"){ + return /** @type {!BigInt} */ (str) & range; + } + if(type === "number"){ + return BigInt(str) & range; + } + let crc = BigInt(0), bit = /** @type {!BigInt} */ (this.bit) + BigInt(1); + for(let i = 0; i < str.length; i++){ + crc = (crc * bit ^ BigInt(str.charCodeAt(i))) & range; + } + return crc;// & 0xFFFFFFFFFFFFFFFF; +} + +// TODO return promises instead of inner await + + +function async(callback, self, field, key, index_doc, index, data, on_done){ + + //setTimeout(function(){ + + const res = callback(field ? field + "." + key : key, JSON.stringify(data)); + + // await isn't supported by ES5 + + if(res && res["then"]){ + + res["then"](function(){ + + self.export(callback, self, field, index_doc, index + 1, on_done); + }); + } + else { + + self.export(callback, self, field, index_doc, index + 1, on_done); + } + //}); +} + +/** + * @param callback + * @param self + * @param field + * @param index_doc + * @param index + * @param on_done + * @this {Index|Document} + */ + +function exportIndex(callback, self, field, index_doc, index, on_done){ + + let return_value = true; + if (typeof on_done === 'undefined') { + return_value = new Promise((resolve) => { + on_done = resolve; + }); + } + + let key, data; + + switch(index || (index = 0)){ + + case 0: + + key = "reg"; + + // fastupdate isn't supported by export + + if(this.fastupdate){ + + data = create_object(); + + for(let key of this.reg.keys()){ + + data[key] = 1; + } + } + else { + + data = this.reg; + } + + break; + + case 1: + + key = "cfg"; + data = { + "doc": 0, + "opt": this.optimize ? 1 : 0 + }; + + break; + + case 2: + + key = "map"; + data = this.map; + break; + + case 3: + + key = "ctx"; + data = this.ctx; + break; + + default: + + if (typeof field === 'undefined' && on_done) { + + on_done(); + } + + return; + } + + async(callback, self || this, field, key, index_doc, index, data, on_done); + + return return_value; +} + +/** + * @this Index + */ + +function importIndex(key, data){ + + if(!data){ + + return; + } + + if(is_string(data)){ + + data = JSON.parse(data); + } + + switch(key){ + + case "cfg": + + this.optimize = !!data["opt"]; + break; + + case "reg": + + // fastupdate isn't supported by import + + this.fastupdate = false; + this.reg = data; + break; + + case "map": + + this.map = data; + break; + + case "ctx": + + this.ctx = data; + break; + } +} + +/** + * @this Document + */ + +function exportDocument(callback, self, field, index_doc, index, on_done){ + + let return_value; + if (typeof on_done === 'undefined') { + return_value = new Promise((resolve) => { + on_done = resolve; + }); + } + + index || (index = 0); + index_doc || (index_doc = 0); + + if(index_doc < this.field.length){ + + const field = this.field[index_doc]; + const idx = this.index[field]; + + self = this; + + //setTimeout(function(){ + + if(!idx.export(callback, self, index ? field/*.replace(":", "-")*/ : "", index_doc, index++, on_done)){ + + index_doc++; + index = 1; + + self.export(callback, self, field, index_doc, index, on_done); + } + //}); + } + else { + + let key, data; + + switch(index){ + + case 1: + + key = "tag"; + data = this.tagindex; + field = null; + break; + + case 2: + + key = "store"; + data = this.store; + field = null; + break; + + // case 3: + // + // key = "reg"; + // data = this.register; + // break; + + default: + + on_done(); + return; + } + + async(callback, this, field, key, index_doc, index, data, on_done); + } + + return return_value +} + +/** + * @this Document + */ + +function importDocument(key, data){ + + if(!data){ + + return; + } + + if(is_string(data)){ + + data = JSON.parse(data); + } + + switch(key){ + + case "tag": + + this.tagindex = data; + break; + + case "reg": + + // fastupdate isn't supported by import + + this.fastupdate = false; + this.reg = data; + + for(let i = 0, index; i < this.field.length; i++){ + + index = this.index[this.field[i]]; + index.reg = data; + index.fastupdate = false; + } + + break; + + case "store": + + this.store = data; + break; + + default: + + key = key.split("."); + const field = key[0]; + key = key[1]; + + if(field && key){ + + this.index[field].import(key, data); + } + } +} + +// COMPILER BLOCK --> + +/** + * @enum {Object} + * @const + */ + +const presets = { + + "memory": { + resolution: 1 + }, + + "performance": { + resolution: 6, + fastupdate: true, + context: { + depth: 1, + resolution: 3 + } + }, + + "match": { + tokenize: "forward" + }, + + "score": { + resolution: 9, + context: { + depth: 2, + resolution: 9 + } + } +}; + +/** + * + * @param {!IndexOptions|string} options + * @return {IndexOptions} + */ + +function apply_preset(options){ + + const preset = is_string(options) + ? options + : options["preset"]; + + if(preset){ + if(!presets[preset]){ + console.warn("Preset not found: " + preset); + } + options = Object.assign({}, presets[preset], /** @type {Object} */ (options)); + } + + return options; +} + +function apply_async(prototype){ + register$1.call(prototype, "add"); + register$1.call(prototype, "append"); + register$1.call(prototype, "search"); + register$1.call(prototype, "update"); + register$1.call(prototype, "remove"); +} + +// let cycle; +// let budget = 0; +// +// function tick(resolve){ +// cycle = null; +// budget = 0; +// resolve(); +// } + +/** + * @param {!string} key + * @this {Index|Document} + */ + +function register$1(key){ + this[key + "Async"] = function(){ + + // // prevent stack overflow of adding too much tasks to the same event loop + // // actually limit stack to 1,000,000 tasks every ~4ms + // cycle || ( + // cycle = new Promise(resolve => setTimeout(tick, 0, resolve)) + // ); + // + // // apply different performance budgets + // if(key === "update" || key === "remove" && this.fastupdate === false){ + // budget += 1000 * this.resolution; + // if(this.depth) + // budget += 1000 * this.resolution_ctx; + // } + // else if(key === "search"){ + // budget++; + // } + // else{ + // budget += 20 * this.resolution; + // if(this.depth) + // budget += 20 * this.resolution_ctx; + // } + // + // // wait for the event loop cycle + // if(budget >= 1e6){ + // await cycle; + // } + + const args = /*[].slice.call*/(arguments); + const arg = args[args.length - 1]; + let callback; + + if(typeof arg === "function"){ + callback = arg; + delete args[args.length - 1]; + } + + this.async = true; + const res = this[key].apply(this, args); + this.async = false; + res.then ? res.then(callback) + : callback(res); + return res; + }; +} + +const data = create_object(); + +/** + * @param {!string} name + */ + +function tick(name){ + + /** @type {!Object} */ + const profiler = data["profiler"] || (data["profiler"] = {}); + + profiler[name] || (profiler[name] = 0); + profiler[name]++; +} + +// COMPILER BLOCK --> +// <-- COMPILER BLOCK + +let table;// = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; +let timer; +const cache = new Map(); + +function toRadix(number, radix = 255) { + + if(!table){ + table = new Array(radix); + // the char code 0 could be a special marker + for(let i = 0; i < radix; i++) table[i] = i + 1; + table = String.fromCharCode.apply(null, table); + } + + let rixit; + let residual = number; + let result = ""; + + while(true){ + rixit = residual % radix; + result = table.charAt(rixit) + result; + residual = residual / radix | 0; + if(!residual) + break; + } + + return result; +} + +function default_compress(str){ + + { + if(timer){ + if(cache.has(str)){ + return cache.get(str); + } + } + else { + timer = setTimeout(clear); + } + } + + /* 2 ** ((level + 1.5) * 1.6 | 0) */ + + const result = toRadix( + typeof str == "number" + ? str + : lcg(str) + ); + + { + cache.size > 2e5 && cache.clear(); + cache.set(str, result); + } + + return result; +} + +function lcg(str) { + let range = 2 ** 32 - 1; + if(typeof str == "number"){ + return str & range; + } + let crc = 0, bit = 32 + 1; + for(let i = 0; i < str.length; i++) { + crc = (crc * bit ^ str.charCodeAt(i)) & range; + } + // shift up from Int32 to UInt32 range 0xFFFFFFFF + return crc + 2 ** 31; +} + +function clear(){ + timer = null; + cache.clear(); +} + +// COMPILER BLOCK --> + +// TODO: +// string + number as text +// boolean, null, undefined as ? + + +/** + * @param {!number|string} id + * @param {!string} content + * @param {boolean=} _append + * @param {boolean=} _skip_update + */ + +Index.prototype.add = function(id, content, _append, _skip_update){ + + if(content && (id || (id === 0))){ + + // todo check skip_update + //_skip_update = false; + + if(!_skip_update && !_append){ + if(this.reg.has(id)){ + return this.update(id, content); + } + } + + // do not force a string as input + // https://github.com/nextapps-de/flexsearch/issues/432 + content = this.encoder.encode(content); + const word_length = content.length; + + if(word_length){ + + // check context dupes to skip all contextual redundancy along a document + + const dupes_ctx = create_object(); + const dupes = create_object(); + const depth = this.depth; + const resolution = this.resolution; + + for(let i = 0; i < word_length; i++){ + + let term = content[this.rtl ? word_length - 1 - i : i]; + let term_length = term.length; + + // skip dupes will break the context chain + + if(term_length /*&& (term_length >= this.minlength)*/ && (depth || !dupes[term])){ + + let score = this.score + ? this.score(content, term, i, null, 0) + : get_score(resolution, word_length, i); + let token = ""; + + switch(this.tokenize){ + + case "full": + if(term_length > 2){ + for(let x = 0; x < term_length; x++){ + for(let y = term_length; y > x; y--){ + + //if((y - x) >= this.minlength){ + token = term.substring(x, y); + const partial_score = this.score + ? this.score(content, term, i, token, x) + : get_score(resolution, word_length, i, term_length, x); + this.push_index(dupes, token, partial_score, id, _append); + //} + } + } + break; + } + // fallthrough to next case when term length < 3 + case "reverse": + // skip last round (this token exist already in "forward") + if(term_length > 1){ + for(let x = term_length - 1; x > 0; x--){ + token = term[x] + token; + //if(token.length >= this.minlength){ + const partial_score = this.score + ? this.score(content, term, i, token, x) + : get_score(resolution, word_length, i, term_length, x); + this.push_index(dupes, token, partial_score, id, _append); + //} + } + token = ""; + } + + // fallthrough to next case to apply forward also + case "forward": + if(term_length > 1){ + for(let x = 0; x < term_length; x++){ + token += term[x]; + //if(token.length >= this.minlength){ + this.push_index(dupes, token, score, id, _append); + //} + } + break; + } + + // fallthrough to next case when token has a length of 1 + default: + // case "strict": + + // todo move boost to search + // if(this.boost){ + // score = Math.min((score / this.boost(content, term, i)) | 0, resolution - 1); + // } + + this.push_index(dupes, term, score, id, _append); + + // context is just supported by tokenizer "strict" + if(depth){ + + if((word_length > 1) && (i < (word_length - 1))){ + + // check inner dupes to skip repeating words in the current context + const dupes_inner = create_object(); + const resolution = this.resolution_ctx; + const keyword = term; + const size = Math.min(depth + 1, word_length - i); + + dupes_inner[keyword] = 1; + + for(let x = 1; x < size; x++){ + + term = content[this.rtl ? word_length - 1 - i - x : i + x]; + + if(term /*&& (term.length >= this.minlength)*/ && !dupes_inner[term]){ + + dupes_inner[term] = 1; + + const context_score = this.score + ? this.score(content, keyword, i, term, x) + : get_score(resolution + ((word_length / 2) > resolution ? 0 : 1), word_length, i, size - 1, x - 1); + const swap = this.bidirectional && (term > keyword); + this.push_index(dupes_ctx, swap ? keyword : term, context_score, id, _append, swap ? term : keyword); + } + } + } + } + } + } + } + + this.fastupdate || this.reg.add(id); + } + else { + content = ""; + } + } + + if(this.db){ + // when the term has no valid content (e.g. empty), + // then it was not added to the ID registry for removal + content || this.commit_task.push({ "del": id }); + this.commit_auto && autoCommit(this); + } + + return this; +}; + +/** + * @private + * @param dupes + * @param term + * @param score + * @param id + * @param {boolean=} append + * @param {string=} keyword + */ + +Index.prototype.push_index = function(dupes, term, score, id, append, keyword){ + + let arr = keyword ? this.ctx : this.map; + let tmp; + + if(!dupes[term] || !keyword || !(tmp = dupes[term])[keyword]){ + + if(keyword){ + + dupes = tmp || (dupes[term] = create_object()); + dupes[keyword] = 1; + + if(this.compress){ + keyword = default_compress(keyword); + } + + tmp = arr.get(keyword); + tmp ? arr = tmp + : arr.set(keyword, arr = new Map()); + } + else { + + dupes[term] = 1; + } + + if(this.compress){ + term = default_compress(term); + } + + tmp = arr.get(term); + tmp ? arr = tmp : arr.set(term, arr = tmp = []); + // the ID array will be upgraded dynamically + arr = arr[score] || (arr[score] = []); + + if(!append || !arr.includes(id)){ + + // auto-upgrade to keystore array if max size exceeded + { + if(arr.length === 2**31-1 /*|| !(arr instanceof KeystoreArray)*/){ + const keystore = new KeystoreArray(arr); + if(this.fastupdate){ + for(let value of this.reg.values()){ + if(value.includes(arr)){ + value[value.indexOf(arr)] = keystore; + } + } + } + tmp[score] = arr = keystore; + } + } + + arr.push(id); + + // add a reference to the register for fast updates + if(this.fastupdate){ + const tmp = this.reg.get(id); + tmp ? tmp.push(arr) + : this.reg.set(id, [arr]); + } + } + } +}; + +/** + * @param {number} resolution + * @param {number} length + * @param {number} i + * @param {number=} term_length + * @param {number=} x + * @returns {number} + */ + +function get_score(resolution, length, i, term_length, x){ + + // console.log("resolution", resolution); + // console.log("length", length); + // console.log("term_length", term_length); + // console.log("i", i); + // console.log("x", x); + // console.log((resolution - 1) / (length + (term_length || 0)) * (i + (x || 0)) + 1); + + // the first resolution slot is reserved for the best match, + // when a query matches the first word(s). + + // also to stretch score to the whole range of resolution, the + // calculation is shift by one and cut the floating point. + // this needs the resolution "1" to be handled additionally. + + // do not stretch the resolution more than the term length will + // improve performance and memory, also it improves scoring in + // most cases between a short document and a long document + + return i && (resolution > 1) ? ( + + (length + (term_length || 0)) <= resolution ? + + i + (x || 0) + : + ((resolution - 1) / (length + (term_length || 0)) * (i + (x || 0)) + 1) | 0 + ): + 0; +} + +/* + from -> res[score][id] + to -> [id] +*/ + +/** + * Aggregate the union of a single raw result + * @param {!Array} result + * @param {!number} limit + * @param {number=} offset + * @param {boolean=} enrich + * @return Array + */ + +function resolve_default(result, limit, offset, enrich){ + + // fast path: when there is just one slot in the result + if(result.length === 1){ + result = result[0]; + result = offset || (result.length > limit) + ? (limit + ? result.slice(offset, offset + limit) + : result.slice(offset) + ) + : result; + return enrich + ? enrich_result(result) + : result; + } + + // this is an optimized workaround instead of + // just doing result = concat(result) + + let final = []; + + for(let i = 0, arr, len; i < result.length; i++){ + if(!(arr = result[i]) || !(len = arr.length)) continue; + + if(offset){ + // forward offset pointer + if(offset >= len){ + offset -= len; + continue; + } + // apply offset pointer when length differs + if(offset < len){ + arr = limit + ? arr.slice(offset, offset + limit) + : arr.slice(offset); + len = arr.length; + offset = 0; + } + } + + if(!final.length){ + // fast path: when limit was reached in first slot + if(len >= limit){ + if(len > limit){ + arr = arr.slice(0, limit); + } + return enrich + ? enrich_result(arr) + : arr; + } + final = [arr]; + } + else { + if(len > limit){ + arr = arr.slice(0, limit); + len = arr.length; + } + final.push(arr); + } + + // reduce limit + limit -= len; + + // todo remove + // if(limit < 0){ + // throw new Error("Impl.Error"); + // } + + // break if limit was reached + if(!limit){ + break; + } + } + + // todo remove + if(!final.length){ + //throw new Error("No results found"); + return final; + } + + final = final.length > 1 + ? concat(final) + : final[0]; + + return enrich + ? enrich_result(final) + : final; +} + +function enrich_result(ids){ + for(let i = 0; i < ids.length; i++){ + ids[i] = { + score: i, + id: ids[i] + }; + } + return ids; +} + +// import xor from "./xor.js"; +// import and from "./and.js"; +// import not from "./not.js"; + +Resolver.prototype.or = function(){ + + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.or.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.or.apply(this, first_argument); + } + } + + // for(let i = 0; i < args.length; i++){ + // if(args[i].result instanceof Promise){ + // return; + // } + // } + + // if(args.length < 2){ + // if(first_argument.index){ + // first_argument.resolve = false; + // const result = first_argument.index.search(first_argument); + // if(result instanceof Promise){ + // result.then(function(result){ + // final = self.result.concat(result); + // self.result = resolver(final, limit, offset, enrich, !resolve); + // return resolve ? self.result : self; + // }); + // } + // else{ + // final = this.result.concat(result); + // this.result = resolver(final, limit, offset, enrich, !resolve); + // return resolve ? this.result : this; + // } + // } + // } + + let final = []; + let promises = []; + let limit = 0, offset = 0, enrich, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.and){ + result = this.and(query.and); + } + else if(query.xor){ + result = this.xor(query.xor); + } + else if(query.not){ + result = this.not(query.not); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + enrich = query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + self.result.length && (final = [self.result].concat(final)); + self.result = resolver(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? self.result : self; + }); + } + + this.result.length && (final = [this.result].concat(final)); + this.result = resolver(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? this.result : this; +}; + +/** + * Aggregate the union of N raw results + * @param result + * @param limit + * @param offset + * @param enrich + * @param resolve + * @param boost + * @return {Array} + */ + +function resolver(result, limit, offset, enrich, resolve, boost){ + + if(!result.length){ + // todo remove + //console.log("Empty Result") + return result; + } + + if(typeof limit === "object"){ + offset = limit.offset || 0; + enrich = limit.enrich || false; + limit = limit.limit || 0; + } + + if(result.length < 2){ + // todo remove + //console.log("Single Result") + if(resolve){ + return resolve_default(result[0], limit, offset, enrich); + } + else { + return result[0]; + } + } + + let final = []; + let count = 0; + let dupe = create_object(); + let maxres = get_max_len(result); + + for(let j = 0, ids; j < maxres; j++){ + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res) continue; + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + if(!dupe[id]){ + dupe[id] = 1; + if(offset){ + offset--; + continue; + } + if(resolve){ + final.push(id); + } + else { + // shift resolution by boost (inverse) + const index = j + (i ? boost : 0); + final[index] || (final[index] = []); + final[index].push(id); + } + if(limit && ++count === limit){ + //this.boost = 0; + return final; + } + } + } + } + } + + //this.boost = 0; + return final; +} + +// import xor from "./xor.js"; +// import or from "./or.js"; +// import not from "./not.js"; + +Resolver.prototype.and = function(){ + if(this.result.length){ + + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.and.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.and.apply(this, first_argument); + } + } + + // for(let i = 0; i < args.length; i++){ + // if(args[i].result instanceof Promise){ + // return; + // } + // } + + // if(args.length < 2){ + // if(first_argument.index){ + // first_argument.resolve = false; + // return first_argument.index.search(first_argument); + // } + // } + + let final = []; + let promises = []; + let limit = 0, offset = 0, enrich, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.or){ + result = this.or(query.or); + } + else if(query.xor){ + result = this.xor(query.xor); + } + else if(query.not){ + result = this.not(query.not); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + enrich = query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + final = [self.result].concat(final); + self.result = intersect$1(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? self.result : self; + }); + } + + final = [this.result].concat(final); + this.result = intersect$1(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? this.result : this; + } + return this; +}; + +/** + * Aggregate the intersection of N raw results + * @param result + * @param limit + * @param offset + * @param enrich + * @param resolve + * @param boost + * @return {Array} + */ + +function intersect$1(result, limit, offset, enrich, resolve, boost){ + + // if(!result.length){ + // // todo remove + // console.log("Empty Result") + // return result; + // } + + if(result.length < 2){ + // todo remove + //console.log("Single Result") + return []; + // if(resolve){ + // return default_resolver(result[0], limit, offset, enrich); + // } + // else{ + // return result[0]; + // } + } + + let final = []; + let count = 0; + + // fast path single slot + // if(result.length < 2){ + // if(limit || offset){ + // let res = result[0]; + // for(let j = 0, ids; j < res.length; j++){ + // ids = res[j]; + // if(!ids) continue; + // for(let k = 0, id; k < ids.length; k++){ + // id = ids[k]; + // if(offset){ + // offset--; + // continue; + // } + // if(resolve){ + // final.push(id); + // } + // else{ + // final[j + this.boost] || (final[j + this.boost] = []); + // final[j + this.boost].push(id); + // } + // if(limit && ++count === limit){ + // this.boost = 0; + // return final; + // } + // } + // } + // } + // this.boost = 0; + // return result[0]; + // } + + let contain = create_object(); + let maxres = get_max_len(result); + if(!maxres) return final; + + // for(let j = 0, ids, res = result[0]; j < res.length; j++){ + // ids = res[j]; + // for(let k = 0; k < ids.length; k++){ + // contain[ids[k]] = 1; + // } + // } + + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res || !res.length) return []; + let contain_new = create_object(); + let match = 0; + let last_round = i === result.length - 1; + + for(let j = 0, ids; j < maxres; j++){ + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id, min; k < ids.length; k++){ + id = ids[k]; + // fill in first round + if(!i){ + // shift resolution +1 + // shift resolution by boost (inverse) + contain_new[id] = j + 1 + (i ? boost : 0); + match = 1; + } + // result in last round + else if(last_round){ + if((min = contain[id])){ + match = 1; + //if(!contain_new[id]){ + if(offset){ + offset--; + continue; + } + if(resolve){ + final.push(id); + } + else { + // reduce resolution -1 + min--; + if(j < min) min = j; + final[min] || (final[min] = []); + final[min].push(id); + } + if(limit && ++count === limit){ + //this.boost = 0; + return final; + } + // shift resolution +1 + //contain_new[id] = min + 1; + //} + } + } + // check for intersection + else if((min = contain[id])){ + // shift resolution +1 + if(j + 1 < min) min = j + 1; + contain_new[id] = min; + match = 1; + } + } + } + + if(!match){ + //this.boost = 0; + return []; + } + + contain = contain_new; + } + + //this.boost = 0; + return final; +} + +// import or from "./or.js"; +// import and from "./and.js"; +// import not from "./not.js"; + +Resolver.prototype.xor = function(){ + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.xor.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.xor.apply(this, first_argument); + } + } + + let final = []; + let promises = []; + let limit = 0, offset = 0, enrich, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.or){ + result = this.or(query.or); + } + else if(query.and){ + result = this.and(query.and); + } + else if(query.not){ + result = this.not(query.not); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + enrich = query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + self.result.length && (final = [self.result].concat(final)); + self.result = exclusive(final, limit, offset, enrich, !resolve, self.boostval); + return resolve ? self.result : self; + }); + } + + this.result.length && (final = [this.result].concat(final)); + this.result = exclusive(final, limit, offset, enrich, !resolve, self.boostval); + return resolve ? this.result : this; +}; + +/** + * @param result + * @param limit + * @param offset + * @param enrich + * @param resolve + * @param boost + * @return {Array} + */ + +function exclusive(result, limit, offset, enrich, resolve, boost){ + + if(!result.length){ + // todo remove + //console.log("Empty Result") + return result; + } + + if(result.length < 2){ + // todo remove + //console.log("Single Result") + if(resolve){ + return resolve_default(result[0], limit, offset, enrich); + } + else { + return result[0]; + } + } + + const final = []; + const check = create_object(); + + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res) continue; + + for(let j = 0, ids; j < res.length; j++){ + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + check[id] + ? check[id]++ + : check[id] = 1; + } + } + } + + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res) continue; + + for(let j = 0, ids; j < res.length; j++){ + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + if(check[id] === 1){ + if(resolve){ + final.push(id); + } + else { + // shift resolution by boost (inverse) + const index = j + (i ? boost : 0); + final[index] || (final[index] = []); + final[index].push(id); + } + } + } + } + } + + //this.boost = 0; + return final; +} + +// import or from "./or.js"; +// import and from "./and.js"; +// import xor from "./xor.js"; + +Resolver.prototype.not = function(){ + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.not.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.not.apply(this, first_argument); + } + } + + let final = []; + let promises = []; + let limit = 0, offset = 0, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.or){ + result = this.or(query.or); + } + else if(query.and){ + result = this.and(query.and); + } + else if(query.xor){ + result = this.xor(query.xor); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + self.result = exclusion.call(self, final, limit, offset, resolve); + return resolve ? self.result : self; + }); + } + + this.result = exclusion.call(this, final, limit, offset, resolve); + return resolve ? this.result : this; +}; + +/** + * @param result + * @param limit + * @param offset + * @param resolve + * @this Resolver + * @return {Array} + */ + +function exclusion(result, limit, offset, resolve){ + + if(!result.length){ + return this.result; + } + + const final = []; + const exclude = new Set(result.flat().flat()); + + for(let j = 0, ids; j < this.result.length; j++){ + ids = this.result[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + if(!exclude.has(id)){ + if(resolve){ + final.push(id); + } + else { + final[j] || (final[j] = []); + final[j].push(id); + } + } + } + } + + return final; +} + +/** + * @param result + * @constructor + */ + +function Resolver(result){ + if(!this){ + return new Resolver(result); + } + if(result && result.index){ + result.resolve = false; + this.index = result.index; + return result.index.search(result); + } + if(result.constructor === Resolver){ + // todo test this branch + //console.log("Resolver Loopback") + return result; + } + this.index = null; + this.result = result || []; + this.boostval = 0; +} + +Resolver.prototype.limit = function(limit){ + if(this.result.length){ + const final = []; + let count = 0; + for(let j = 0, ids; j < this.result.length; j++){ + ids = this.result[j]; + if(ids.length + count < limit){ + final[j] = ids; + count += ids.length; + } + else { + final[j] = ids.slice(0, limit - count); + this.result = final; + break; + } + } + } + return this; +}; + +Resolver.prototype.offset = function(offset){ + if(this.result.length){ + const final = []; + let count = 0; + for(let j = 0, ids; j < this.result.length; j++){ + ids = this.result[j]; + if(ids.length + count < offset){ + count += ids.length; + } + else { + final[j] = ids.slice(offset - count); + count = offset; + } + } + this.result = final; + } + return this; +}; + +Resolver.prototype.boost = function(boost){ + this.boostval += boost; + return this; +}; + +Resolver.prototype.resolve = function(limit, offset, enrich){ + set_resolve(1); + const result = this.result; + this.index = null; + this.result = null; + + if(result.length){ + if(typeof limit === "object"){ + enrich = limit.enrich; + offset = limit.offset; + limit = limit.limit; + } + return resolve_default(result, limit || 100, offset, enrich); + } + + return result; +}; + +/* + + from -> result[ + res[score][id], + res[score][id], + ] + + to -> [id] + + */ + +/** + * Implementation based on Object[key] provides better suggestions + * capabilities and has less performance scaling issues on large indexes. + * + * @param arrays + * @param limit + * @param offset + * @param {boolean|Array=} suggest + * @param {boolean=} resolve + * @returns {Array} + */ + +function intersect(arrays, limit, offset, suggest, resolve) { + + const length = arrays.length; + + // todo remove + // if(length < 2){ + // throw new Error("Not optimized intersect"); + // } + + let result = []; + let size = 0; + let check; + let check_suggest; + let check_new; + let res_arr; + + if(suggest){ + suggest = []; + } + + // 1. a reversed order prioritize the order of words from a query + // because it ends with the first term. + // 2. process terms in reversed order often has advantage for + // the fast path "end reached". + + // alternatively the results could be sorted by length up + //arrays.sort(sort_by_length_up); + + // todo the outer loop should be the res array instead of term array, + // this isn't just simple because the intersection calculation needs to reflect this + //const maxlen = get_max_len(arrays); + + for(let x = length - 1, found; x >= 0; x--){ + //for(let x = 0, found; x < length; x++){ + + res_arr = arrays[x]; + check_new = create_object(); + found = !check; + + // process relevance in forward order (direction is + // important for adding IDs during the last round) + + for(let y = 0, ids; y < res_arr.length; y++){ + + ids = res_arr[y]; + if(!ids || !ids.length) continue; + + for(let z = 0, id; z < ids.length; z++){ + + id = ids[z]; + + // check exists starting from the 2nd slot + if(check){ + if(check[id]){ + + // check if in last round + if(!x){ + //if(x === length - 1){ + + if(offset){ + offset--; + } + else { + + result[size++] = id; + + if(size === limit){ + // fast path "end reached" + return result /*resolve === false + ? { result, suggest } + :*/ + } + } + } + + if(x || suggest){ + //if((x < length - 1) || suggest){ + check_new[id] = 1; + } + + found = true; + } + + if(suggest){ + + if(!check_suggest[id]){ + check_suggest[id] = 1; + const arr = suggest[y] || (suggest[y] = []); + arr.push(id); + } + + // OLD: + // + // check_idx = (check_suggest[id] || 0) + 1; + // check_suggest[id] = check_idx; + // + // // do not adding IDs which are already included in the result (saves one loop) + // // the first intersection match has the check index 2, so shift by -2 + // + // if(check_idx < length){ + // + // const tmp = suggest[check_idx - 2] || (suggest[check_idx - 2] = []); + // tmp[tmp.length] = id; + // } + } + } + else { + + // pre-fill in first round + check_new[id] = 1; + } + } + } + + if(suggest){ + + // re-use the first pre-filled check for suggestions + check || (check_suggest = check_new); + } + else if(!found){ + + return []; + } + + check = check_new; + } + + // return intermediate result + // if(resolve === false){ + // return { result, suggest }; + // } + + if(suggest){ + + // needs to iterate in reverse direction + for(let x = suggest.length - 1, ids, len; x >= 0; x--){ + + ids = suggest[x]; + len = ids.length; + + for(let y = 0, id; y < len; y++){ + + id = ids[y]; + + if(!check[id]){ + + if(offset){ + offset--; + } + else { + + result[size++] = id; + + if(size === limit){ + // fast path "end reached" + return result; + } + } + + check[id] = 1; + } + } + } + } + + return result; +} + +/** + * @param mandatory + * @param arrays + * @returns {Array} + */ + +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.push(id); + } + } + } + } + + return result; +} + + +/** + * Implementation based on Array.includes() provides better performance, + * but it needs at least one word in the query which is less frequent. + * Also on large indexes it does not scale well performance-wise. + * This strategy also lacks of suggestion capabilities (matching & sorting). + * + * @param arrays + * @param limit + * @param offset + * @param {boolean|Array=} suggest + * @returns {Array} + */ + +// export function intersect(arrays, limit, offset, suggest) { +// +// const length = arrays.length; +// let result = []; +// let check; +// +// // determine shortest array and collect results +// // from the sparse relevance arrays +// +// let smallest_size; +// let smallest_arr; +// let smallest_index; +// +// for(let x = 0; x < length; x++){ +// +// const arr = arrays[x]; +// const len = arr.length; +// +// let size = 0; +// +// for(let y = 0, tmp; y < len; y++){ +// +// tmp = arr[y]; +// +// if(tmp){ +// +// size += tmp.length; +// } +// } +// +// if(!smallest_size || (size < smallest_size)){ +// +// smallest_size = size; +// smallest_arr = arr; +// smallest_index = x; +// } +// } +// +// smallest_arr = smallest_arr.length === 1 ? +// +// smallest_arr[0] +// : +// concat(smallest_arr); +// +// if(suggest){ +// +// suggest = [smallest_arr]; +// check = create_object(); +// } +// +// let size = 0; +// let steps = 0; +// +// // process terms in reversed order often results in better performance. +// // the outer loop must be the words array, using the +// // smallest array here disables the "fast fail" optimization. +// +// for(let x = length - 1; x >= 0; x--){ +// +// if(x !== smallest_index){ +// +// steps++; +// +// const word_arr = arrays[x]; +// const word_arr_len = word_arr.length; +// const new_arr = []; +// +// let count = 0; +// +// for(let z = 0, id; z < smallest_arr.length; z++){ +// +// id = smallest_arr[z]; +// +// let found; +// +// // process relevance in forward order (direction is +// // important for adding IDs during the last round) +// +// for(let y = 0; y < word_arr_len; y++){ +// +// const arr = word_arr[y]; +// +// if(arr.length){ +// +// found = arr.includes(id); +// +// if(found){ +// +// // check if in last round +// +// if(steps === length - 1){ +// +// if(offset){ +// +// offset--; +// } +// else{ +// +// result[size++] = id; +// +// if(size === limit){ +// +// // fast path "end reached" +// +// return result; +// } +// } +// +// if(suggest){ +// +// check[id] = 1; +// } +// } +// +// break; +// } +// } +// } +// +// if(found){ +// +// new_arr[count++] = id; +// } +// } +// +// if(suggest){ +// +// suggest[steps] = new_arr; +// } +// else if(!count){ +// +// return []; +// } +// +// smallest_arr = new_arr; +// } +// } +// +// if(suggest){ +// +// // needs to iterate in reverse direction +// +// for(let x = suggest.length - 1, arr, len; x >= 0; x--){ +// +// arr = suggest[x]; +// len = arr && arr.length; +// +// if(len){ +// +// for(let y = 0, id; y < len; y++){ +// +// id = arr[y]; +// +// if(!check[id]){ +// +// check[id] = 1; +// +// if(offset){ +// +// offset--; +// } +// else{ +// +// result[size++] = id; +// +// if(size === limit){ +// +// // fast path "end reached" +// +// return result; +// } +// } +// } +// } +// } +// } +// } +// +// return result; +// } + +// COMPILER BLOCK --> + +let global_resolve = 1; +function set_resolve(resolve){ + global_resolve = resolve; +} + +/** + * @param {string|SearchOptions} query + * @param {number|SearchOptions=} limit + * @param {SearchOptions=} options + * @returns {Array|Resolver|Promise} + */ + +Index.prototype.search = function(query, limit, options){ + + if(!options){ + if(!limit && is_object(query)){ + options = /** @type {!SearchOptions} */ (query); + query = ""; + } + else if(is_object(limit)){ + options = /** @type {!SearchOptions} */ (limit); + limit = 0; + } + } + + let result = []; + let length; + let context, suggest, offset = 0, resolve, enrich, tag; + + if(options){ + query = options.query || query; + limit = options.limit || limit; + offset = options.offset || 0; + context = options.context; + suggest = options.suggest; + resolve = (global_resolve && options.resolve !== false); + resolve || (global_resolve = 0); + enrich = resolve && options.enrich; + tag = this.db && options.tag; + } + else { + resolve = this.resolve || global_resolve; + } + + // todo: term deduplication during encoding when context is disabled + + // do not force a string as input + // https://github.com/nextapps-de/flexsearch/issues/432 + query = /** @type {Array} */ (this.encoder.encode(query)); + length = query.length; + limit || !resolve || (limit = 100); + + // fast path single term + if(length === 1){ + return single_term_query.call( + this, + query[0], // term + "", // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + // TODO: dedupe terms within encoder? + // TODO: deduplication will break the context chain + + context = this.depth && context !== false; + + // fast path single context + if(length === 2 && context && !suggest){ + return single_term_query.call( + this, + query[0], // term + query[1], // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + let maxlength = 0; + let minlength = 0; + + if(length > 1){ + + // term deduplication will break the context chain + // todo add context to dupe check + const dupes = create_object(); + const query_new = []; + + // if(context){ + // keyword = query[0]; + // dupes[keyword] = 1; + // query_new.push(keyword); + // maxlength = minlength = keyword.length; + // i = 1; + // } + + for(let i = 0, term; i < length; i++){ + + term = query[i]; + + if(term && !dupes[term]){ + + // todo add keyword check + // this fast path can't apply to persistent indexes + if(!suggest && !(this.db) && !this.get_array(term/*, keyword*/)){ + + // fast path "not found" + return resolve + ? result + : new Resolver(result); + } + else { + + query_new.push(term); + dupes[term] = 1; + } + + const term_length = term.length; + maxlength = Math.max(maxlength, term_length); + minlength = minlength ? Math.min(minlength, term_length) : term_length; + } + // else if(term && (!this.depth || context === false)){ + // query_new.push(term); + // } + } + + query = query_new; + length = query.length; + } + + // the term length could be changed after deduplication + + if(!length){ + return resolve + ? result + : new Resolver(result); + } + + let index = 0, keyword; + + // fast path single term + if(length === 1){ + return single_term_query.call( + this, + query[0], // term + "", // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + // fast path single context + if(length === 2 && context && !suggest){ + return single_term_query.call( + this, + query[0], // term + query[1], // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + if(length > 1){ + if(context){ + // start with context right away + keyword = query[0]; + index = 1; + } + // todo + else if(maxlength > 9 && (maxlength / minlength) > 3){ + // sorting terms will break the context chain + // bigger terms has less occurrence + // this might also reduce the intersection task + // todo check intersection order + query.sort(sort_by_length_down); + } + } + + if(this.db){ + + if(this.db.search){ + // when the configuration is not supported it returns false + const result = this.db.search(this, query, limit, offset, suggest, resolve, enrich, tag); + if(result !== false) return result; + } + + const self = this; + return (async function(){ + + for(let arr, term; index < length; index++){ + + term = query[index]; + + if(keyword){ + + arr = await self.get_array(term, keyword); + arr = add_result( + arr, + result, + suggest, + self.resolution_ctx, + /** @type {!number} */ (limit), + offset, + length === 2 + /*, term, keyword*/ + ); + + // the context is a moving window where the keyword is going forward like a cursor + // 1. when suggestion enabled just forward keyword if term was found + // 2. as long as the result is empty forward the pointer also + if(!suggest || (arr !== false) || !result.length){ + keyword = term; + } + } + else { + + arr = await self.get_array(term); + arr = add_result( + arr, + result, + suggest, + self.resolution, + /** @type {!number} */ (limit), + offset, + length === 1 + /*, term*/ + ); + } + + // limit reached + if(arr){ + return arr; + } + + // apply suggestions on last loop + if(suggest && (index === length - 1)){ + let length = result.length; + if(!length){ + // fallback to non-contextual search when no result was found + if(keyword){ + keyword = ""; + index = -1; + continue; + } + return result; + } + else if(length === 1){ + return resolve + ? resolve_default(result[0], /** @type {number} */ (limit), offset) + : new Resolver(result[0]); + } + } + } + + return resolve + ? intersect(result, /** @type {number} */ (limit), offset, suggest) + : new Resolver(result[0]) + }()); + } + + for(let arr, term; index < length; index++){ + + term = query[index]; + + if(keyword){ + + arr = this.get_array(term, keyword); + arr = /*this.*/add_result( + arr, + result, + suggest, + this.resolution_ctx, + /** @type {!number} */ (limit), + offset, + length === 2 + /*, term, keyword*/ + ); + + // 1. when suggestion enabled just forward keyword if term was found + // 2. as long as the result is empty forward the pointer also + if(!suggest || (arr !== false) || !result.length){ + keyword = term; + } + } + else { + + arr = this.get_array(term); + arr = /*this.*/add_result( + arr, + result, + suggest, + this.resolution, + /** @type {!number} */ (limit), + offset, + length === 1 + /*, term*/ + ); + } + + // limit reached + if(arr){ + return /** @type {Array} */ (arr); + } + + // apply suggestions on last loop + if(suggest && (index === length - 1)){ + const length = result.length; + if(!length){ + // fallback to non-contextual search when no result was found + if(keyword){ + keyword = ""; + index = -1; + continue; + } + return result; + } + else if(length === 1){ + return resolve + ? resolve_default(result[0], limit, offset) + : new Resolver(result[0]); + } + } + } + + return resolve + ? intersect(result, limit, offset, suggest) + : new Resolver(result[0]); +}; + +/** + * @param term + * @param keyword + * @param limit + * @param offset + * @param resolve + * @param enrich + * @param tag + * @this Index + * @return {Array|Resolver} + */ + +function single_term_query(term, keyword, limit, offset, resolve, enrich, tag){ + + const result = this.get_array(term, keyword, limit, offset, resolve, enrich, tag); + + if(this.db){ + return result.then(function(result){ + if(resolve) return result; + return result && result.length + ? (resolve ? resolve_default(result, limit, offset): new Resolver(result)) + : resolve ? [] : new Resolver([]); + }); + } + + return result && result.length + ? (resolve ? resolve_default(result, limit, offset) : new Resolver(result)) + : resolve ? [] : new Resolver([]); +} + +/** + * Returns a 1-dimensional finalized array when the result is done (fast path return), + * returns false when suggestions is enabled and no result was found, + * or returns nothing when a set was pushed successfully to the results + * + * @private + * @param {Array} arr + * @param {Array} result + * @param {Array} suggest + * @param {number} resolution + * @param {number} limit + * @param {number} offset + * @param {boolean} single_term + * @return {Array|boolean|undefined} + */ + +function add_result(arr, result, suggest, resolution, limit, offset, single_term/*, term, keyword*/){ + + let word_arr = []; + //let arr;// = keyword ? this.ctx : this.map; + //arr = this.get_array(term, keyword); + + if(arr){ + + //const resolution = Math.min(arr.length, keyword ? this.resolution_ctx : this.resolution); + // apply reduced resolution for queries + resolution = Math.min(arr.length, resolution); + + for(let x = 0, size = 0, tmp; x < resolution; x++){ + if((tmp = arr[x])){ + + if(offset){ + // apply offset right here on single terms + if(tmp && single_term){ + if(tmp.length <= offset){ + offset -= tmp.length; + tmp = null; + } + else { + tmp = tmp.slice(offset); + offset = 0; + } + } + } + + if(tmp){ + + // keep score (sparse array): + word_arr[x] = tmp; + // simplified score order: + //word_arr.push(tmp); + + if(single_term){ + size += tmp.length; + if(size >= limit){ + // fast path: + // a single term does not need to pre-collect results + break; + } + } + } + } + } + + if(word_arr.length){ + if(single_term){ + // fast path optimization + // offset was already applied at this point + // return an array will stop the query process immediately + return resolve_default(word_arr, limit, 0); + } + + result.push(word_arr); + // return nothing will continue the query + return; + } + } + + // 1. return an empty array will stop the loop + // 2. return a false value to prevent stop when using suggestions + return !suggest && word_arr; +} + +Index.prototype.get_array = function(term, keyword, limit, offset, resolve, enrich, tag){ + + let arr, swap; + + if(keyword){ + swap = this.bidirectional && (term > keyword); + } + + if(this.compress){ + term = default_compress(term); + keyword && (keyword = default_compress(keyword)); + } + + if(this.db){ + return keyword + ? this.db.get( + swap ? keyword : term, // term + swap ? term : keyword, // ctx + limit, + offset, + resolve, + enrich, + tag + ) + : this.db.get( + term, + "", // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + if(keyword){ + // the frequency of the starting letter is slightly less + // on the last half of the alphabet (m-z) in almost every latin language, + // so we sort downwards (https://en.wikipedia.org/wiki/Letter_frequency) + arr = this.ctx.get(swap ? term : keyword); + arr = arr && arr.get(swap ? keyword : term); + } + else { + arr = this.map.get(term); + } + + return arr; +}; + +// COMPILER BLOCK --> + +/** + * @param {boolean=} _skip_deletion + */ + +Index.prototype.remove = function(id, _skip_deletion){ + + const refs = this.reg.size && ( + this.fastupdate + ? this.reg.get(id) + : this.reg.has(id) + ); + + if(refs){ + + if(this.fastupdate){ + + // fast updates did not fully cleanup the key entries + + for(let i = 0, tmp; i < refs.length; i++){ + if((tmp = refs[i])){ + // todo check + //if(tmp.length < 1) throw new Error("invalid length"); + //if(tmp.indexOf(id) < 0) throw new Error("invalid id"); + if(tmp.length < 2){ + tmp.pop(); + } + else { + const index = tmp.indexOf(id); + index === refs.length - 1 + ? tmp.pop() + : tmp.splice(index, 1); + } + } + } + + // todo variation which cleans up, requires to push [ctx, key] instead of arr to the index.reg + // for(let i = 0, arr, term, keyword; i < refs.length; i++){ + // arr = refs[i]; + // if(typeof arr === "string"){ + // arr = this.map.get(term = arr); + // } + // else{ + // arr = this.ctx.get(keyword = arr[0]); + // arr && (arr = arr.get(arr[1])); + // } + // let counter = 0, found; + // if(arr && arr.length){ + // for(let j = 0, tmp; j < arr.length; j++){ + // if((tmp = arr[j])){ + // if(!found && tmp.length){ + // const index = tmp.indexOf(id); + // if(index >= 0){ + // tmp.splice(index, 1); + // // the index [ctx, key]:[res, id] is unique + // found = 1; + // } + // } + // if(tmp.length){ + // counter++; + // if(found){ + // break; + // } + // } + // else{ + // delete arr[j]; + // } + // } + // } + // } + // if(!counter){ + // keyword + // ? this.ctx.delete(keyword) + // : this.map.delete(term); + // } + // } + } + else { + + remove_index(this.map, id/*, this.resolution*/); + this.depth && + remove_index(this.ctx, id/*, this.resolution_ctx*/); + } + + _skip_deletion || this.reg.delete(id); + } + + if(this.db){ + this.commit_task.push({ "del": id }); + this.commit_auto && autoCommit(this); + //return this.db.remove(id); + } + + // the cache could be used outside the InMemory store + if(this.cache){ + this.cache.remove(id); + } + + return this; +}; + +/** + * @param map + * @param id + * @return {number} + */ + +function remove_index(map, id){ + + // a check counter of filled resolution slots + // to prevent removing the field + let count = 0; + + if(is_array(map)){ + for(let x = 0, arr, index; x < map.length; x++){ + if((arr = map[x]) && arr.length){ + index = arr.indexOf(id); + if(index >= 0){ + if(arr.length > 1){ + arr.splice(index, 1); + count++; + } + else { + // remove resolution slot + delete map[x]; + } + // the index key:[res, id] is unique + break; + } + else { + count++; + } + } + } + } + else for(let item of map){ + const key = item[0]; + const value = item[1]; + const tmp = remove_index(value, id); + tmp ? count += tmp + : map.delete(key); + } + + return count; +} + +/**! + * FlexSearch.js + * Author and Copyright: Thomas Wilkerling + * Licence: Apache-2.0 + * Hosted by Nextapps GmbH + * https://github.com/nextapps-de/flexsearch + */ + + +/** + * @constructor + * @param {IndexOptions|string=} options Options or preset as string + * @param {Map|Set|KeystoreSet|KeystoreMap=} _register + */ + +function Index(options$1, _register){ + + if(!this){ + return new Index(options$1); + } + + tick("Index.create"); + + options$1 = options$1 + ? apply_preset(options$1) + : {}; + + /** @type ContextOptions */ + const context = options$1.context || {}; + const encoder = options$1.encode || options$1.encoder || ( + options + ); + /** @type Encoder */ + this.encoder = encoder.encode + ? encoder + : typeof encoder === "object" + ? (new Encoder(encoder) + + ) + : { encode: encoder }; + + { + this.compress = options$1.compress || options$1.compression || false; + } + + let tmp; + this.resolution = options$1.resolution || 9; + this.tokenize = tmp = options$1.tokenize || "strict"; + this.depth = (tmp === "strict" && context.depth) || 0; + this.bidirectional = context.bidirectional !== false; + this.fastupdate = !!options$1.fastupdate; + this.score = options$1.score || null; + + tmp = (options$1.keystore || 0); + tmp && (this.keystore = tmp); + + this.map = tmp && SUPPORT_KEYSTORE ? new KeystoreMap(tmp) : new Map(); + this.ctx = tmp && SUPPORT_KEYSTORE ? new KeystoreMap(tmp) : new Map(); + this.reg = _register || ( + this.fastupdate + ? (tmp && SUPPORT_KEYSTORE ? new KeystoreMap(tmp) : new Map()) + : (tmp && SUPPORT_KEYSTORE ? new KeystoreSet(tmp) : new Set()) + ); + this.resolution_ctx = context.resolution || 1; + this.rtl = (encoder.rtl) || options$1.rtl || false; + + { + this.cache = (tmp = options$1.cache || null) && new CacheClass(tmp); + } + + { + this.resolve = options$1.resolve !== false; + } + + { + if((tmp = options$1.db)){ + this.db = tmp.mount(this); + } + this.commit_auto = options$1.commit !== false; + this.commit_task = []; + this.commit_timer = null; + } +} + +{ + Index.prototype.mount = function(db){ + if(this.commit_timer){ + clearTimeout(this.commit_timer); + this.commit_timer = null; + } + return db.mount(this); + }; + Index.prototype.commit = function(replace, append){ + if(this.commit_timer){ + clearTimeout(this.commit_timer); + this.commit_timer = null; + } + return this.db.commit(this, replace, append); + }; +} + +// if(SUPPORT_RESOLVER){ +// Index.prototype.resolve = function(params){ +// return new Resolver(params); +// }; +// } + +/** + * @param {!Index} self + * @param {boolean=} replace + * @param {boolean=} append + */ + +function autoCommit(self, replace, append){ + if(!self.commit_timer){ + self.commit_timer = setTimeout(function(){ + self.commit_timer = null; + self.db.commit(self, replace, append); + }, 0); + } +} + +Index.prototype.clear = function(){ + + //this.map = new Map(); + //this.ctx = new Map(); + //this.reg = this.fastupdate ? new Map() : new Set(); + this.map.clear(); + this.ctx.clear(); + this.reg.clear(); + + { + this.cache && + this.cache.clear(); + } + + if(this.db){ + this.commit_timer && clearTimeout(this.commit_timer); + this.commit_timer = null; + this.commit_task = [{ "clear": true }]; + //return this.db.clear(); + } + + return this; +}; + +//Index.prototype.pipeline = pipeline; + +/** + * @param {!number|string} id + * @param {!string} content + */ + +Index.prototype.append = function(id, content){ + return this.add(id, content, true); +}; + +Index.prototype.contain = function(id){ + + if(this.db){ + return this.db.has(id); + } + + return this.reg.has(id); +}; + +Index.prototype.update = function(id, content){ + + // todo check the async part + if(this.async /*|| (SUPPORT_PERSISTENT && this.db)*/){ + const self = this; + const res = this.remove(id); + return res.then ? res.then( + () => self.add(id, content) + ) : this.add(id, content); + } + + return this.remove(id).add(id, content); +}; + +/** + * @param map + * @return {number} + */ + +function cleanup_index(map){ + + let count = 0; + + if(is_array(map)){ + for(let i = 0, arr; i < map.length; i++){ + (arr = map[i]) && + (count += arr.length); + } + } + else for(const item of map){ + const key = item[0]; + const value = item[1]; + const tmp = cleanup_index(value); + tmp ? count += tmp + : map.delete(key); + } + + return count; +} + +Index.prototype.cleanup = function(){ + + if(!this.fastupdate){ + console.info("Cleanup the index isn't required when not using \"fastupdate\"."); + return this; + } + + cleanup_index(this.map); + this.depth && + cleanup_index(this.ctx); + + return this; +}; + +{ + + Index.prototype.searchCache = searchCache; +} + +{ + + Index.prototype.export = exportIndex; + Index.prototype.import = importIndex; +} + +{ + + apply_async(Index.prototype); +} + +async function handler(data) { + + data = data["data"]; + + /** @type Index */ + const index = self["_index"]; + const args = data["args"]; + const task = data["task"]; + + switch(task){ + + case "init": + + /** @type IndexOptions */ + let options = data["options"] || {}; + let filepath = options.config; + if(filepath){ + options = filepath; + // will be replaced after build with the line below because + // there is an issue with closure compiler dynamic import + //options = await import(filepath); + } + + // deprecated: + // const encode = options.encode; + // if(encode && (encode.indexOf("function") === 0)){ + // options.encode = Function("return " + encode)(); + // } + + const factory = data["factory"]; + + if(factory){ + + // export the FlexSearch global payload to "self" + Function("return " + factory)()(self); + + /** @type Index */ + self["_index"] = new self["FlexSearch"]["Index"](options); + + // destroy the exported payload + delete self["FlexSearch"]; + } + else { + + self["_index"] = new Index(options); + } + + postMessage({ "id": data["id"] }); + break; + + default: + + const id = data["id"]; + const message = index[task].apply(index, args); + postMessage(task === "search" ? { "id": id, "msg": message } : { "id": id }); + } +} + +//import { promise as Promise } from "../polyfill.js"; + +let pid = 0; + +/** + * @param {IndexOptions=} options + * @constructor + */ + +function WorkerIndex(options){ + + if(!this) { + return new WorkerIndex(options); + } + + if(options); + else { + options = {}; + } + + // the factory is the outer wrapper from the build + // we use "self" as a trap for node.js + + let factory = (self||window)["_factory"]; + if(factory){ + factory = factory.toString(); + } + + const is_node_js = typeof window === "undefined" && self["exports"]; + const _self = this; + + this.worker = create(factory, is_node_js, options.worker); + this.resolver = create_object(); + + if(!this.worker){ + + return; + } + + function onmessage(msg){ + msg = msg["data"] || msg; + const id = msg["id"]; + const res = id && _self.resolver[id]; + if(res){ + res(msg["msg"]); + delete _self.resolver[id]; + } + } + + is_node_js + ? this.worker["on"]("message", onmessage) + : this.worker.onmessage = onmessage; + + if(options["config"]){ + + // when extern configuration needs to be loaded + // it needs to return a promise to await for + return new Promise(function(resolve){ + _self.resolver[++pid] = function(){ + resolve(_self); + }; + _self.worker.postMessage({ + "id": pid, + "task": "init", + "factory": factory, + "options": options + }); + }); + } + + this.worker.postMessage({ + "task": "init", + "factory": factory, + "options": options + }); +} + +register("add"); +register("append"); +register("search"); +register("update"); +register("remove"); + +function register(key){ + + WorkerIndex.prototype[key] = + WorkerIndex.prototype[key + "Async"] = function(){ + + const self = this; + const args = [].slice.call(arguments); + const arg = args[args.length - 1]; + let callback; + + if(is_function(arg)){ + callback = arg; + args.splice(args.length - 1, 1); + } + + const promise = new Promise(function(resolve){ + //setTimeout(function(){ + self.resolver[++pid] = resolve; + self.worker.postMessage({ + "task": key, + "id": pid, + "args": args + }); + //}); + }); + + if(callback){ + promise.then(callback); + return this; + } + else { + + return promise; + } + }; +} + +function create(factory, is_node_js, worker_path){ + + let worker; + + worker = is_node_js ? + // This eval will be removed when compiling, it isn't there in final build + (0, eval)('new (require("worker_threads")["Worker"])(__dirname + "/node/node.js")') + //eval('new (require("worker_threads")["Worker"])(__dirname + "/node/node.js")') + :( + factory ? + new window.Worker(URL.createObjectURL( + new Blob( + ["onmessage=" + handler.toString()], + { "type": "text/javascript" } + ) + )) + : + new window.Worker(is_string(worker_path) ? worker_path : "worker/worker.js", { type: "module" }) + ); + + return worker; +} + +// COMPILER BLOCK --> + +/** + * + * @param id + * @param content + * @param {boolean=} _append + * @returns {Document|Promise} + */ + +Document.prototype.add = function(id, content, _append){ + + if(is_object(id)){ + + content = id; + id = parse_simple(content, this.key); + } + + if(content && (id || (id === 0))){ + + if(!_append && this.reg.has(id)){ + return this.update(id, content); + } + + // this.field does not include db tag indexes + for(let i = 0, tree; i < this.field.length; i++){ + + tree = this.tree[i]; + + const index = this.index.get(this.field[i]); + if(typeof tree === "function"){ + const tmp = tree(content); + if(tmp){ + index.add(id, tmp, /* append: */ false, /* skip update: */ true); + } + } + else { + const filter = tree._filter; + if(filter && !filter(content)){ + continue; + } + if(tree.constructor === String){ + tree = ["" + tree]; + } + else if(is_string(tree)){ + tree = [tree]; + } + add_index(content, tree, this.marker, 0, index, id, tree[0], _append); + } + } + + if(this.tag){ + + //console.log(this.tag, this.tagtree) + + for(let x = 0; x < this.tagtree.length; x++){ + + let tree = this.tagtree[x]; + let field = this.tagfield[x]; + let ref = this.tag.get(field); + let dupes = create_object(); + let tags; + + if(typeof tree === "function"){ + tags = tree(content); + if(!tags) continue; + } + else { + const filter = tree._filter; + if(filter && !filter(content)){ + continue; + } + if(tree.constructor === String){ + tree = "" + tree; + } + tags = parse_simple(content, tree); + } + + if(!ref || !tags){ + ref || (console.warn("Tag '" + field + "' was not found")); + continue; + } + + if(is_string(tags)){ + tags = [tags]; + } + + for(let i = 0, tag, arr; i < tags.length; i++){ + + tag = tags[i]; + //console.log(this.tag, tag, key, field) + + if(!dupes[tag]){ + dupes[tag] = 1; + + let tmp; + tmp = ref.get(tag); + tmp ? arr = tmp : ref.set(tag, arr = []); + + if(!_append || ! /** @type {!Array|KeystoreArray} */(arr).includes(id)){ + + // auto-upgrade to keystore array if max size exceeded + { + if(arr.length === 2**31-1 /*|| !(arr instanceof KeystoreArray)*/){ + const keystore = new KeystoreArray(arr); + if(this.fastupdate){ + for(let value of this.reg.values()){ + if(value.includes(arr)){ + value[value.indexOf(arr)] = keystore; + } + } + } + ref.set(tag, arr = keystore); + } + } + + arr.push(id); + + // add a reference to the register for fast updates + if(this.fastupdate){ + const tmp = this.reg.get(id); + tmp ? tmp.push(arr) + : this.reg.set(id, [arr]); + } + } + } + } + } + } + + if(this.store && (!_append || !this.store.has(id))){ + + let payload; + + if(this.storetree){ + + payload = create_object(); + + for(let i = 0, tree; i < this.storetree.length; i++){ + tree = this.storetree[i]; + + const filter = tree._filter; + if(filter && !filter(content)){ + continue; + } + let custom; + if(typeof tree === "function"){ + custom = tree(content); + if(!custom) continue; + tree = [tree._field]; + } + else if(is_string(tree) || tree.constructor === String){ + payload[tree] = content[tree]; + continue; + } + + store_value(content, payload, tree, 0, tree[0], custom); + } + } + + this.store.set(id, payload || content); + } + } + + return this; +}; + +// TODO support generic function created from string when tree depth > 1 + +/** + * @param obj + * @param store + * @param tree + * @param pos + * @param key + * @param {*=} custom + */ + +function store_value(obj, store, tree, pos, key, custom){ + + obj = obj[key]; + + // reached target field + if(pos === (tree.length - 1)){ + + // store target value + store[key] = custom || obj; + } + else if(obj){ + + if(is_array(obj)){ + + store = store[key] = new Array(obj.length); + + for(let i = 0; i < obj.length; i++){ + // do not increase pos (an array is not a field) + store_value(obj, store, tree, pos, i); + } + } + else { + + store = store[key] || (store[key] = create_object()); + key = tree[++pos]; + store_value(obj, store, tree, pos, key); + } + } +} + +function add_index(obj, tree, marker, pos, index, id, key, _append){ + + if((obj = obj[key])){ + + // reached target field + if(pos === (tree.length - 1)){ + + // handle target value + if(is_array(obj)){ + + // append array contents so each entry gets a new scoring context + if(marker[pos]){ + for(let i = 0; i < obj.length; i++){ + index.add(id, obj[i], /* append: */ true, /* skip update: */ true); + } + return; + } + + // or join array contents and use one scoring context + obj = obj.join(" "); + } + + index.add(id, obj, _append, /* skip_update: */ true); + } + else { + + if(is_array(obj)){ + for(let i = 0; i < obj.length; i++){ + // do not increase index, an array is not a field + add_index(obj, tree, marker, pos, index, id, i, _append); + } + } + else { + key = tree[++pos]; + add_index(obj, tree, marker, pos, index, id, key, _append); + } + } + } + else { + if(index.db){ + index.remove(id); + } + } +} + +// COMPILER BLOCK --> + +/** + * @param {!string|DocumentSearchOptions} query + * @param {number|DocumentSearchOptions=} limit + * @param {DocumentSearchOptions=} options + * @param {Array=} _resolve For internal use only. + * @returns {Promise|Array} + */ + +Document.prototype.search = function(query, limit, options, _resolve){ + + if(!options){ + if(!limit && is_object(query)){ + options = /** @type {DocumentSearchOptions} */ (query); + query = ""; + } + else if(is_object(limit)){ + options = /** @type {DocumentSearchOptions} */ (limit); + limit = 0; + } + } + + let result = [], result_field = []; + let pluck, enrich, merge, suggest; + let field, tag, offset, count = 0; + + if(options){ + + // todo remove support? + if(is_array(options)){ + field = options; + options = null; + } + else { + + query = options.query || query; + pluck = options.pluck; + merge = options.merge; + field = pluck || options.field || options.index; + tag = this.tag && options.tag; + enrich = this.store && options.enrich; + suggest = options.suggest; + limit = options.limit || limit; + offset = options.offset || 0; + limit || (limit = 100); + + if(tag && (!this.db || !_resolve)){ + + if(tag.constructor !== Array){ + tag = [tag]; + } + + let pairs = []; + + for(let i = 0, field; i < tag.length; i++){ + field = tag[i]; + if(is_string(field)){ + throw new Error("A tag option can't be a string, instead it needs a { field: tag } format."); + } + // default array notation + if(field.field && field.tag){ + const value = field.tag; + if(value.constructor === Array){ + for(let k = 0; k < value.length; k++){ + pairs.push(field.field, value[k]); + } + } + else { + pairs.push(field.field, value); + } + } + // shorter object notation + else { + const keys = Object.keys(field); + for(let j = 0, key, value; j < keys.length; j++){ + key = keys[j]; + value = field[key]; + if(value.constructor === Array){ + for(let k = 0; k < value.length; k++){ + pairs.push(key, value[k]); + } + } + else { + pairs.push(key, value); + } + } + } + } + + if(!pairs.length){ + throw new Error("Your tag definition within the search options is probably wrong. No valid tags found."); + } + + // tag used as pairs from this point + tag = pairs; + + // when tags is used and no query was set, + // then just return the tag indexes + if(!query){ + + let promises = []; + if(pairs.length) for(let j = 0; j < pairs.length; j+=2){ + let ids; + if(this.db){ + const index = this.index.get(pairs[j]); + if(!index){ + { + console.warn("Tag '" + pairs[j] + ":" + pairs[j + 1] + "' will be skipped because there is no field '" + pairs[j] + "'."); + } + continue; + } + promises.push(ids = index.db.tag(pairs[j + 1], limit, offset, enrich)); + } + else { + ids = get_tag.call(this, pairs[j], pairs[j + 1], limit, offset, enrich); + } + result.push({ + "field": pairs[j], + "tag": pairs[j + 1], + "result": ids + }); + } + + if(promises.length){ + return Promise.all(promises).then(function(promises){ + for(let j = 0; j < promises.length; j++){ + result[j].result = promises[j]; + } + return result; + }); + } + + return result; + } + } + + // extend to multi field search by default + if(is_string(field)){ + field = [field]; + } + } + } + + field || (field = this.field); + let promises = !_resolve && (this.worker || this.async) && []; + let db_tag_search; + + // multi field search + // field could be a custom set of selected fields by this query + // db tag indexes are also included in this field list + for(let i = 0, res, key, len; i < field.length; i++){ + + key = field[i]; + + if(this.db && this.tag){ + // tree is missing when it is a tag-only index (db) + if(!this.tree[i]){ + continue; + } + } + + let field_options; + + if(!is_string(key)){ + field_options = key; + key = field_options.field; + query = field_options.query || query; + limit = field_options.limit || limit; + //offset = field_options.offset || offset; + suggest = (field_options.suggest || suggest); + //enrich = SUPPORT_STORE && this.store && (field_options.enrich || enrich); + } + + if(_resolve){ + res = _resolve[i]; + } + else { + let opt = field_options || options; + let index = this.index.get(key); + + if(tag){ + if(this.db){ + opt.tag = tag; + db_tag_search = index.db.support_tag_search; + opt.field = field; + } + if(!db_tag_search){ + opt.enrich = false; + } + } + if(promises){ + promises[i] = index.searchAsync(query, limit, opt); + // restore enrich state + opt && enrich && (opt.enrich = enrich); + // just collect and continue + continue; + } + else { + res = index.search(query, limit, opt); + // restore enrich state + opt && enrich && (opt.enrich = enrich); + } + } + + len = res && res.length; + + // todo when no term was matched but tag was retrieved extend suggestion to tags + // every field has to intersect against all selected tag fields + if(tag && len){ + + const arr = []; + let count = 0; + + // tags are only applied in resolve phase when it's a db + if(this.db && _resolve){ + if(!db_tag_search){ + + // retrieve tag results assigned to it's field + for(let y = field.length; y < _resolve.length; y++){ + + let ids = _resolve[y]; + let len = ids && ids.length; + + if(len){ + count++; + arr.push(ids); + } + else if(!suggest){ + // no tags found + return result; + } + } + } + } + else { + + // tag[] are pairs at this line + for(let y = 0, ids, len; y < tag.length; y+=2){ + ids = this.tag.get(tag[y]); + + if(!ids){ + { + console.warn("Tag '" + tag[y] + ":" + tag[y + 1] + "' will be skipped because there is no field '" + tag[y] + "'."); + } + if(suggest){ + continue; + } + else { + return result; + } + } + + ids = ids && ids.get(tag[y + 1]); + len = ids && ids.length; + + if(len){ + count++; + arr.push(ids); + } + else if(!suggest){ + // no tags found + return result; + } + } + } + + if(count){ + res = intersect_union(res, arr); // intersect(arr, limit, offset) + len = res.length; + if(!len && !suggest){ + // nothing matched + return result; + } + // move counter back by 1 + count--; + } + } + + if(len){ + result_field[count] = key; + result.push(res); + count++; + } + else if(field.length === 1){ + // fast path: nothing matched + return result; + } + } + + if(promises){ + if(this.db){ + // todo when a tag index is never a search index this could be extracted + // push tag promises to the end + if(tag && tag.length && !db_tag_search){ + for(let y = 0; y < tag.length; y += 2){ + // it needs to retrieve data from tag pairs + const index = this.index.get(tag[y]); + if(!index){ + { + console.warn("Tag '" + tag[y] + ":" + tag[y + 1] + "' was not found because there is no field '" + tag[y] + "'."); + } + if(suggest){ + continue; + } + else { + return result; + } + } + promises.push(index.db.tag(tag[y + 1], limit, offset, /* enrich */ false)); + } + } + } + + const self = this; + + // TODO unroll this recursion + return Promise.all(promises).then(function(result){ + return result.length + ? self.search(query, limit, options, /* resolve: */ result) + : result; + }); + } + + if(!count){ + return result; + } + if(pluck && (!enrich || !this.store)){ + return result[0]; + } + + promises = []; + + for(let i = 0, res; i < result_field.length; i++){ + + res = result[i]; + + if(enrich && res.length && !res[0].doc){ + if(!this.db){ + if(res.length){ + res = apply_enrich.call(this, res); + } + } + else { + promises.push(res = this.index.get(this.field[0]).db.enrich(res)); + } + } + + if(pluck){ + return res; + } + + result[i] = { + "field": result_field[i], + "result": res + }; + } + + if(enrich && SUPPORT_PERSISTENT && this.db && promises.length){ + return Promise.all(promises).then(function(promises){ + for(let j = 0; j < promises.length; j++){ + result[j].result = promises[j]; + } + return merge + ? merge_fields(result, limit) + : result; + }); + } + + return merge + ? merge_fields(result, limit) + : result; +}; + +// todo support Resolver +// todo when searching through multiple fields each term should +// be found at least by one field to get a valid match without +// using suggestion explicitly + +function merge_fields(fields, limit, offset){ + const final = []; + const set = create_object(); + for(let i = 0, field, res; i < fields.length; i++){ + field = fields[i]; + res = field.result; + for(let j = 0, id, entry, tmp; j < res.length; j++){ + entry = res[j]; + id = entry.id; + tmp = set[id]; + if(!tmp){ + // offset was already applied on field indexes + // if(offset){ + // offset--; + // continue; + // } + // apply limit from last round, because just fields could + // be pushed without adding new results + if(final.length === limit){ + return final; + } + entry.field = set[id] = [field.field]; + final.push(entry); + } + else { + tmp.push(field.field); + } + } + } + return final; +} + +/** + * @this Document + */ + +function get_tag(tag, key, limit, offset, enrich){ + let res = this.tag.get(tag); + if(!res){ + console.warn("Tag '" + tag + "' was not found"); + return []; + } + res = res && res.get(key); + let len = res && (res.length - offset); + + if(len && (len > 0)){ + if((len > limit) || offset){ + res = res.slice(offset, offset + limit); + } + if(enrich){ + res = apply_enrich.call(this, res); + } + return res; + } +} + +/** + * @this Document + */ + +function apply_enrich(res){ + + const arr = new Array(res.length); + + for(let x = 0, id; x < res.length; x++){ + id = res[x]; + arr[x] = { + "id": id, + "doc": this.store.get(id) + }; + } + + return arr; +} + +/**! + * FlexSearch.js + * Author and Copyright: Thomas Wilkerling + * Licence: Apache-2.0 + * Hosted by Nextapps GmbH + * https://github.com/nextapps-de/flexsearch + */ + + +/** + * @constructor + * @param {!DocumentOptions} options + */ + +function Document(options){ + + if(!this) { + return new Document(options); + } + + /** @type DocumentDescriptor */ + const document = options.document || options.doc || options; + let tmp, keystore; + + this.tree = []; + this.field = []; + this.marker = []; + this.key = ((tmp = document.key || document.id) && parse_tree(tmp, this.marker)) || "id"; + + keystore = (options.keystore || 0); + keystore && (this.keystore = keystore); + this.fastupdate = !!options.fastupdate; + this.reg = this.fastupdate + ? (keystore && SUPPORT_KEYSTORE ? new KeystoreMap(keystore) : new Map()) + : (keystore && SUPPORT_KEYSTORE ? new KeystoreSet(keystore) : new Set()); + + { + // todo support custom filter function + this.storetree = (tmp = document.store || null) && tmp !== true && []; + this.store = tmp && ( + keystore && SUPPORT_KEYSTORE + ? new KeystoreMap(keystore) + : new Map() + ); + } + + { + this.cache = (tmp = options.cache || null) && new CacheClass(tmp); + // do not apply cache again for the indexes since .searchCache() + // is just a wrapper over .search() + options.cache = false; + } + + { + this.worker = options.worker; + } + + { + // this switch is used by recall of promise callbacks + this.async = false; + } + + /** @export */ + this.index = parse_descriptor.call(this, options, document); + + { + this.tag = null; + // TODO case-insensitive tags? + if((tmp = document.tag)){ + if(typeof tmp === "string"){ + tmp = [tmp]; + } + if(tmp.length){ + this.tag = new Map(); + this.tagtree = []; + this.tagfield = []; + for(let i = 0, params, field; i < tmp.length; i++){ + params = tmp[i]; + field = params.field || params; + if(!field){ + throw new Error("The tag field from the document descriptor is undefined."); + } + if(params.custom){ + this.tagtree[i] = params.custom; + } + else { + this.tagtree[i] = parse_tree(field, this.marker); + if(params.filter){ + if(typeof this.tagtree[i] === "string"){ + // it needs an object to put a property to it + this.tagtree[i] = new String(this.tagtree[i]); + } + this.tagtree[i]._filter = params.filter; + } + } + // the tag fields needs to be hold by indices + this.tagfield[i] = field; + this.tag.set(field, new Map()); + } + } + } + } + + { + options.db && this.mount(options.db); + } +} + +{ + + Document.prototype.mount = function(db){ + + let fields = this.field; + + if(this.tag){ + // tag indexes are referenced by field + // move tags to their field indexes respectively + for(let i = 0, field; i < this.tagfield.length; i++){ + field = this.tagfield[i]; + let index = this.index.get(field); + if(!index){ + // create raw index when not exists + this.index.set(field, index = new Index({}, this.reg)); + // copy and push to the field selection + if(fields === this.field){ + fields = fields.slice(0); + } + // tag indexes also needs to be upgraded to db instances + fields.push(field); + } + // assign reference + index.tag = this.tag.get(field); + } + } + + const promises = []; + const config = { + db: db.db, + type: db.type, + fastupdate: db.fastupdate + }; + + // upgrade all indexes to db instances + for(let i = 0, index, field; i < fields.length; i++){ + config.field = field = fields[i]; + index = this.index.get(field); + const dbi = new db.constructor(db.id, config); + // take over the storage id + dbi.id = db.id; + promises[i] = dbi.mount(index); + // add an identification property + index.document = true; + if(i){ + // the register has to export just one time + // also it's needed by the index for ID contain check + index.bypass = true; + } + else { + // the datastore has to export one time + index.store = this.store; + } + } + + this.async = true; + this.db = true; + return Promise.all(promises); + }; + + Document.prototype.commit = async function(replace, append){ + // parallel: + const promises = []; + for(const index of this.index.values()){ + promises.push(index.db.commit(index, replace, append)); + } + await Promise.all(promises); + this.reg.clear(); + // queued: + // for(const index of this.index.values()){ + // await index.db.commit(index, replace, append); + // } + // this.reg.clear(); + }; +} + +/** + * @this Document + */ + +function parse_descriptor(options, document){ + + const index = new Map(); + let field = document.index || document.field || document; + + if(is_string(field)){ + field = [field]; + } + + for(let i = 0, key, opt; i < field.length; i++){ + + key = field[i]; + + if(!is_string(key)){ + opt = key; + key = key.field; + } + + opt = /** @type DocumentIndexOptions */ ( + is_object(opt) + ? Object.assign({}, options, opt) + : options + ); + + if(this.worker){ + const worker = new WorkerIndex(opt); + index.set(key, worker); + if(!worker.worker){ + // fallback when not supported + this.worker = false; + } + } + + if(!this.worker){ + index.set(key, new Index(opt, this.reg)); + } + + if(opt.custom){ + this.tree[i] = opt.custom; + } + else { + this.tree[i] = parse_tree(key, this.marker); + if(opt.filter){ + if(typeof this.tree[i] === "string"){ + // it needs an object to put a property to it + this.tree[i] = new String(this.tree[i]); + } + this.tree[i]._filter = opt.filter; + } + } + + this.field[i] = key; + } + + if(this.storetree){ + + let stores = document.store; + if(is_string(stores)) stores = [stores]; + + for(let i = 0, store, field; i < stores.length; i++){ + store = /** @type Array */ (stores[i]); + field = store.field || store; + if(store.custom){ + this.storetree[i] = store.custom; + store.custom._field = field; + } + else { + this.storetree[i] = parse_tree(field, this.marker); + if(store.filter){ + if(typeof this.storetree[i] === "string"){ + // it needs an object to put a property to it + this.storetree[i] = new String(this.storetree[i]); + } + this.storetree[i]._filter = store.filter; + } + } + } + } + + return index; +} + +function parse_tree(key, marker){ + + const tree = key.split(":"); + let count = 0; + + for(let i = 0; i < tree.length; i++){ + key = tree[i]; + // todo apply some indexes e.g. [0], [-1], [0-2] + if(key[key.length - 1] === "]"){ + key = key.substring(0, key.length - 2); + if(key){ + marker[count] = true; + } + } + if(key){ + tree[count++] = key; + } + } + + if(count < tree.length){ + tree.length = count; + } + + return count > 1 ? tree : tree[0]; +} + +Document.prototype.append = function(id, content){ + return this.add(id, content, true); +}; + +Document.prototype.update = function(id, content){ + return this.remove(id).add(id, content); +}; + +Document.prototype.remove = function(id){ + + if(is_object(id)){ + id = parse_simple(id, this.key); + } + + for(const index of this.index.values()){ + index.remove(id, /* skip deletion */ true); + } + + if(this.reg.has(id)){ + + if(this.tag){ + // when fastupdate was enabled all ids are already removed + if(!this.fastupdate){ + for(let field of this.tag.values()){ + for(let item of field){ + const tag = item[0]; + const ids = item[1]; + const pos = ids.indexOf(id); + if(pos > -1){ + ids.length > 1 + ? ids.splice(pos, 1) + : field.delete(tag); + } + } + } + } + } + + if(this.store){ + this.store.delete(id); + } + + this.reg.delete(id); + } + + // the cache could be used outside the InMemory store + if(this.cache){ + this.cache.remove(id); + } + + return this; +}; + +Document.prototype.clear = function(){ + + //const promises = []; + + for(const index of this.index.values()){ + // db index will add clear task + index.clear(); + // const promise = index.clear(); + // if(promise instanceof Promise){ + // promises.push(promise); + // } + } + + if(this.tag){ + for(const tags of this.tag.values()){ + tags.clear(); + } + } + + if(this.store){ + this.store.clear(); + } + + return this; /*promises.length + ? Promise.all(promises) + :*/ +}; + +Document.prototype.contain = function(id){ + + if(this.db){ + return this.index.get(this.field[0]).db.has(id); + } + + return this.reg.has(id); +}; + +Document.prototype.cleanup = function(){ + + for(const index of this.index.values()){ + index.cleanup(); + } + + return this; +}; + +{ + + Document.prototype.get = function(id){ + + if(this.db){ + return this.index.get(this.field[0]).db.enrich(id).then(function(result){ + return result[0] && result[0].doc; + }); + } + + return this.store.get(id); + }; + + Document.prototype.set = function(id, store){ + + this.store.set(id, store); + return this; + }; +} + +{ + // todo mo + Document.prototype.searchCache = searchCache; +} + +{ + + Document.prototype.export = exportDocument; + Document.prototype.import = importDocument; +} + +{ + + apply_async(Document.prototype); +} + +// COMPILER BLOCK --> +const defaults = { + schema: "flexsearch", + user: "postgres", + pass: "postgres", + name: "postgres", + host: "localhost", + port: "5432" +}; + +const pgp = pg_promise(/*{ noWarnings: true }*/); +const MAXIMUM_QUERY_VARS = 16000; +const fields = ["map", "ctx", "reg", "tag", "cfg"]; +const types = { + "text": "text", + "char": "text", + "varchar": "text", + "string": "text", + "number": "int", + "numeric": "int", + "integer": "int", + "smallint": "int", + "tinyint": "int", + "mediumint": "int", + "int": "int", + "int8": "int", + "uint8": "int", + "int16": "int", + "uint16": "int", + "int32": "int", + "uint32": "bigint", + "int64": "bigint", + "bigint": "bigint" +}; + +function sanitize(str) { + return str.toLowerCase().replace(/[^a-z0-9_]/g, ""); +} + +let DB, TRX; + +/** + * @constructor + * @implements StorageInterface + */ + +function PostgresDB(name, config = {}){ + if(!this){ + return new PostgresDB(name, config); + } + if(typeof name === "object"){ + name = name.name; + config = name; + } + if(!name){ + console.info("Default storage space was used, because a name was not passed."); + } + this.id = (config.schema ? sanitize(config.schema) : defaults.schema) + (name ? "_" + sanitize(name) : ""); + this.field = config.field ? "_" + sanitize(config.field) : ""; + this.type = config.type ? types[config.type.toLowerCase()] : "text"; + this.support_tag_search = true; + if(!this.type) throw new Error("Unknown type of ID '" + config.type + "'"); + this.db = DB || (DB = config.db || null); + Object.assign(defaults, config); + this.db && delete defaults.db; +} +PostgresDB.prototype.mount = function(flexsearch){ + if(flexsearch.constructor === Document){ + return flexsearch.mount(this); + } + flexsearch.db = this; + return this.open(); +}; + +PostgresDB.prototype.open = async function(){ + + if(!this.db) { + this.db = DB || ( + DB = pgp(`postgres://${defaults.user}:${encodeURIComponent(defaults.pass)}@${defaults.host}:${defaults.port}/${defaults.name}`) + ); + } + + const exist = await this.db.oneOrNone(` + SELECT EXISTS ( + SELECT 1 + FROM information_schema.schemata + WHERE schema_name = '${this.id}' + ); + `); + if(!exist || !exist.exists){ + await this.db.none(`CREATE SCHEMA IF NOT EXISTS ${ this.id };`); + } + + for(let i = 0; i < fields.length; i++){ + const exist = await this.db.oneOrNone(` + SELECT EXISTS ( + SELECT 1 FROM pg_tables + WHERE schemaname = '${this.id}' AND tablename = '${fields[i] + (fields[i] !== "reg" ? this.field : "")}' + ); + `); + if(exist && exist.exists) continue; + + const type = this.type === "text" + ? "varchar(128)" + : this.type; + + switch(fields[i]){ + case "map": + await this.db.none(` + CREATE TABLE IF NOT EXISTS ${this.id}.map${this.field}( + key varchar(128) NOT NULL, + res smallint NOT NULL, + id ${type} NOT NULL + ); + CREATE INDEX IF NOT EXISTS ${this.id}_map_index${this.field} + ON ${this.id}.map${this.field} (key); + CREATE INDEX IF NOT EXISTS ${this.id}_map_id${this.field} + ON ${this.id}.map${this.field} (id); + `); + break; + + case "ctx": + await this.db.none(` + CREATE TABLE IF NOT EXISTS ${this.id}.ctx${this.field}( + ctx varchar(128) NOT NULL, + key varchar(128) NOT NULL, + res smallint NOT NULL, + id ${type} NOT NULL + ); + CREATE INDEX IF NOT EXISTS ${this.id}_ctx_index${this.field} + ON ${this.id}.ctx${this.field} (ctx, key); + CREATE INDEX IF NOT EXISTS ${this.id}_ctx_id${this.field} + ON ${this.id}.ctx${this.field} (id); + `); + break; + + case "tag": + await this.db.none(` + CREATE TABLE IF NOT EXISTS ${this.id}.tag${this.field}( + tag varchar(128) NOT NULL, + id ${type} NOT NULL + ); + CREATE INDEX IF NOT EXISTS ${this.id}_tag_index${this.field} + ON ${this.id}.tag${this.field} (tag); + CREATE INDEX IF NOT EXISTS ${this.id}_tag_id${this.field} + ON ${this.id}.tag${this.field} (id); + `); + break; + + case "reg": + await this.db.none(` + CREATE TABLE IF NOT EXISTS ${this.id}.reg( + id ${type} NOT NULL + CONSTRAINT ${this.id}_reg_pk + PRIMARY KEY, + doc text DEFAULT NULL + ); + `).catch(e => { + // document indexes will try to create same registry table + // and the "IF NOT EXISTS" did not apply on parallel + }); + break; + + case "cfg": + await this.db.none(` + CREATE TABLE IF NOT EXISTS ${this.id}.cfg${this.field}( + cfg text NOT NULL + ); + `); + break; + } + } + + return this.db; +}; + +PostgresDB.prototype.close = function(){ + this.db.close && this.db.close(); + this.db = DB = null; + return this; +}; + +PostgresDB.prototype.destroy = async function(){ + await this.db.none(` + DROP TABLE IF EXISTS ${this.id}.map${this.field}; + DROP TABLE IF EXISTS ${this.id}.ctx${this.field}; + DROP TABLE IF EXISTS ${this.id}.tag${this.field}; + DROP TABLE IF EXISTS ${this.id}.cfg${this.field}; + DROP TABLE IF EXISTS ${this.id}.reg; + `); + this.close(); +}; + +PostgresDB.prototype.clear = function(){ + return this.db.none(` + TRUNCATE TABLE ${this.id}.map${ this.field }; + TRUNCATE TABLE ${this.id}.ctx${ this.field }; + TRUNCATE TABLE ${this.id}.tag${ this.field }; + TRUNCATE TABLE ${this.id}.cfg${ this.field }; + TRUNCATE TABLE ${this.id}.reg; + `); +}; + +function create_result(rows, resolve, enrich){ + if(resolve){ + for(let i = 0; i < rows.length; i++){ + if(enrich){ + if(rows[i].doc){ + rows[i].doc = JSON.parse(rows[i].doc); + } + } + else { + rows[i] = rows[i].id; + } + } + return rows; + } + else { + const arr = []; + for(let i = 0, row; i < rows.length; i++){ + row = rows[i]; + arr[row.res] || (arr[row.res] = []); + arr[row.res].push(enrich + ? row + : row.id + ); + } + return arr; + } +} + +PostgresDB.prototype.get = function(key, ctx, limit = 0, offset = 0, resolve = true, enrich = false, tags){ + let rows; + let stmt = ''; + let params = ctx ? [ctx, key] : [key]; + let table = this.id + (ctx ? ".ctx" : ".map") + this.field; + if(tags){ + for(let i = 0, count = params.length + 1; i < tags.length; i+=2){ + stmt += ` AND ${ table }.id IN (SELECT id FROM ${ this.id }.tag_${ sanitize(tags[i]) } WHERE tag = $${ count++ })`; + params.push(tags[i + 1]); + } + } + if(ctx){ + rows = this.db.any(` + SELECT ${ table }.id + ${ resolve ? "" : ", res" } + ${ enrich ? ", doc" : "" } + FROM ${ table } + ${ enrich ? ` + LEFT JOIN ${ this.id }.reg ON ${ this.id }.reg.id = ${ table }.id + ` : "" } + WHERE ctx = $1 AND key = $2 ${stmt} + ORDER BY res + ${ limit ? "LIMIT " + limit : "" } + ${ offset ? "OFFSET " + offset : "" }`, + params + ); + } + else { + rows = this.db.any(` + SELECT ${ table }.id + ${ resolve ? "" : ", res" } + ${ enrich ? ", doc" : "" } + FROM ${ table } + ${ enrich ? ` + LEFT JOIN ${ this.id }.reg ON ${ this.id }.reg.id = ${ table }.id + ` : "" } + WHERE key = $1 ${stmt} + ORDER BY res + ${ limit ? "LIMIT " + limit : "" } + ${ offset ? "OFFSET " + offset : "" }`, + params + ); + } + return rows.then(function(rows){ + return create_result(rows, resolve, enrich); + }); +}; + +PostgresDB.prototype.tag = function(tag, limit = 0, offset = 0, enrich = false){ + const table = this.id + ".tag" + this.field; + const promise = this.db.any(` + SELECT ${ table }.id + ${ enrich ? ", doc" : "" } + FROM ${ table } + ${ enrich ? ` + LEFT JOIN ${ this.id }.reg ON ${ this.id }.reg.id = ${ table }.id + ` : "" } + WHERE tag = $1 + ${ limit ? "LIMIT " + limit : "" } + ${ offset ? "OFFSET " + offset : "" }`, + [tag] + ); + enrich || promise.then(function(rows){ + return create_result(rows, true, false); + }); + return promise; +}; + +PostgresDB.prototype.enrich = async function(ids){ + let result = []; + if(typeof ids !== "object"){ + ids = [ids]; + } + for(let count = 0; count < ids.length;){ + const chunk = ids.length - count > MAXIMUM_QUERY_VARS + ? ids.slice(count, count + MAXIMUM_QUERY_VARS) + : count ? ids.slice(count) : ids; + count += chunk.length; + let stmt = ""; + for(let i = 1; i <= chunk.length; i++){ + stmt += (stmt ? "," : "") + "$" + i; + } + const res = await this.db.any(` + SELECT id, doc + FROM ${ this.id }.reg + WHERE id IN (${ stmt })`, + ids + ); + if(res && res.length){ + for(let i = 0, doc; i < res.length; i++){ + if((doc = res[i].doc)){ + res[i].doc = JSON.parse(doc); + } + } + result.push(res); + } + } + return result.length === 1 + ? result[0] + : result.length > 1 + ? concat(result) + : result; +}; + +PostgresDB.prototype.has = function(id){ + return this.db.oneOrNone("SELECT EXISTS(SELECT 1 FROM " + this.id + ".reg WHERE id = $1)", [id]); +}; + +PostgresDB.prototype.search = function(flexsearch, query, limit = 100, offset = 0, suggest = false, resolve = true, enrich = false, tags){ + + let rows; + + if(query.length > 1 && flexsearch.depth){ + + let where = ""; + let params = []; + let keyword = query[0]; + let term; + let count = 1; + // variant new + + for(let i = 1; i < query.length; i++){ + term = query[i]; + const swap = flexsearch.bidirectional && (term > keyword); + where += (where ? " OR " : "") + `(ctx = $${ count++ } AND key = $${ count++ })`; + params.push(swap ? term : keyword, swap ? keyword : term); + keyword = term; + } + + if(tags){ + where = "(" + where + ")"; + for(let i = 0; i < tags.length; i+=2){ + where += ` AND id IN (SELECT id FROM ${ this.id }.tag_${ sanitize(tags[i]) } WHERE tag = $${ count++ })`; + params.push(tags[i + 1]); + } + } + + rows = this.db.any(` + SELECT r.id + ${ resolve ? "" : ", res" } + ${ enrich ? ", doc" : "" } + FROM ( + SELECT id, count(*) as count, + ${ suggest ? "SUM" : "MIN" }(res) as res + FROM ${ this.id }.ctx${ this.field } + WHERE ${ where } + GROUP BY id + ) as r + ${ enrich ? ` + LEFT JOIN ${ this.id }.reg ON ${ this.id }.reg.id = r.id + ` : "" } + ${ suggest ? "" : "WHERE count = " + (query.length - 1) } + ORDER BY ${ suggest ? "count DESC, res" : "res" } + ${ limit ? "LIMIT " + limit : "" } + ${ offset ? "OFFSET " + offset : "" } + `, params); + + // variant 1 + + // for(let i = 1, count = 1; i < query.length; i++){ + // where += (where ? " UNION " : "") + ` + // SELECT id, res + // FROM ${this.id}.ctx${this.field} + // WHERE ctx = $${count++} AND key = $${count++} + // `; + // term = query[i]; + // const swap = flexsearch.bidirectional && (term > keyword); + // params.push( + // swap ? term : keyword, + // swap ? keyword : term + // ); + // keyword = term; + // } + // + // rows = await db.any(` + // SELECT id, res + // FROM ( + // SELECT id, ${suggest ? "SUM" : "MIN"}(res) as res, count(*) as count + // FROM (${where}) as t + // GROUP BY id + // ORDER BY ${suggest ? "count DESC, res" : "res"} + // LIMIT ${limit} + // OFFSET ${offset} + // ) as r + // ${suggest ? "" : "WHERE count = " + (query.length - 1)} + // `, params); + + } + else { + + let where = ""; + let count = 1; + let query_length = query.length; + for(let i = 0; i < query_length; i++){ + where += (where ? "," : "") + "$" + count++; + } + where = "key " + (query_length > 1 ? "IN (" + where + ")" : "= " + where ); + + if(tags){ + where = "(" + where + ")"; + for(let i = 0; i < tags.length; i+=2){ + where += ` AND id IN (SELECT id FROM ${ this.id }.tag_${ sanitize(tags[i]) } WHERE tag = $${ count++ })`; + query.push(tags[i + 1]); + } + } + + rows = this.db.any(` + SELECT r.id + ${ resolve ? "" : ", res" } + ${ enrich ? ", doc" : "" } + FROM ( + SELECT id, count(*) as count, + ${ suggest ? "SUM" : "MIN" }(res) as res + FROM ${ this.id }.map${ this.field } + WHERE ${ where } + GROUP BY id + ) as r + ${ enrich ? ` + LEFT JOIN ${ this.id }.reg ON ${ this.id }.reg.id = r.id + ` : "" } + ${ suggest ? "" : "WHERE count = " + query_length } + ORDER BY ${ suggest ? "count DESC, res" : "res" } + ${ limit ? "LIMIT " + limit : "" } + ${ offset ? "OFFSET " + offset : "" } + `, query); + + // variant 1 + // for(let i = 1; i <= query.length; i++){ + // where += (where ? " UNION " : "") + ` + // SELECT id, res + // FROM ${ this.id }.map${ this.field } + // WHERE key = $${i} + // `; + // } + // rows = await db.any(` + // SELECT id, res + // FROM ( + // SELECT id, ${suggest ? "SUM" : "MIN"}(res) as res, count(*) as count + // FROM (${where}) as t + // GROUP BY id + // ORDER BY ${suggest ? "count DESC, res" : "res"} + // LIMIT ${limit} + // OFFSET ${offset} + // ) as r + // ${ suggest ? "" : "WHERE count = " + query.length } + // `, query); + + // variant 2 + // for(let i = 1; i <= query.length; i++){ + // where += (where ? " AND EXISTS " : "") + `(SELECT FROM ${this.id}.map${this.field} as d WHERE d.id = t.id AND d.key = $` + i + ")"; + // } + // rows = await db.any(` + // SELECT t.id, min(t.res) + // FROM ${this.id}.map${this.field} AS t + // WHERE EXISTS ${where} + // GROUP BY t.id + // LIMIT ${limit || 100} + // OFFSET ${offset || 0} + // `, query); + + // variant 3 + // for(let i = 1; i <= query.length; i++){ + // where += (where ? " INTERSECT " : "") + `SELECT id FROM ${this.id}.map${this.field} WHERE key = $` + i; + // } + // rows = await db.any(` + // WITH filtering (id) AS(${where}) + // SELECT t.id, min(t.res) + // FROM ${this.id}.map${this.field} AS t + // JOIN filtering USING (id) + // GROUP BY t.id + // LIMIT ${limit || 100} + // OFFSET ${offset || 0} + // `, query); + + // variant 4 + // for(let i = 1; i <= query.length; i++){ + // where += (where ? " INTERSECT " : "") + `SELECT id FROM ${this.id}.map${this.field} WHERE key = $` + i; + // } + // rows = await db.any(` + // SELECT id, min(res) + // FROM ${this.id}.map${this.field} + // WHERE id IN (${where}) + // GROUP BY id + // LIMIT ${limit || 100} + // OFFSET ${offset || 0} + // `, query); + } + + return rows.then(function(rows){ + return create_result(rows, resolve, enrich); + }); +}; + +PostgresDB.prototype.info = function(){ + // todo +}; + +// PostgresDB.prototype.transaction = async function(task){ +// const self = this; +// if(TRX){ +// return TRX.then(function(){ +// return self.transaction(task); +// //task.call(self, TRX); +// }); +// } +// TRX = await this.db.tx(async function(trx){ +// await task.call(self, trx); +// }); +// TRX = null; +// }; + +PostgresDB.prototype.transaction = function(task){ + if(TRX){ + return task.call(this, TRX); + } + const self = this; + return (TRX || this.db).tx(function(trx){ + return task.call(self, TRX = trx); + }).finally(function(){ + TRX = null; + }) +}; + +PostgresDB.prototype.commit = async function(flexsearch, _replace, _append){ + + // process cleanup tasks + if(_replace){ + await this.clear(); + // there are just removals in the task queue + flexsearch.commit_task = []; + } + else { + let tasks = flexsearch.commit_task; + flexsearch.commit_task = []; + for(let i = 0, task; i < tasks.length; i++){ + task = tasks[i]; + // there are just removals in the task queue + if(task.clear){ + await this.clear(); + _replace = true; + break; + } + else { + tasks[i] = task.del; + } + } + if(!_replace){ + if(!_append){ + tasks = tasks.concat(toArray(flexsearch.reg)); + } + tasks.length && await this.remove(tasks); + } + + //console.log("tasks:", tasks) + } + + if(!flexsearch.reg.size){ + return; + } + + await this.transaction(function(trx){ + + const batch = []; + + // Datastore + Registry + + if(flexsearch.store){ + let data = []; + let stmt = new pgp.helpers.ColumnSet(["id", "doc"],{ + table: this.id + ".reg" + }); + for(const item of flexsearch.store.entries()){ + const id = item[0]; + const doc = item[1]; + // const migration = checkMigration.call(this, id); + // migration && await migration; + data.push({ id, doc: doc && JSON.stringify(doc) }); + if(data.length === MAXIMUM_QUERY_VARS){ + let insert = pgp.helpers.insert(data, stmt); + batch.push( + trx.none(insert.replace(/^(insert into )"([^"]+)"/, '$1 $2')) + ); + data = []; + } + } + if(data.length){ + let insert = pgp.helpers.insert(data, stmt); + batch.push( + trx.none(insert.replace(/^(insert into )"([^"]+)"/, '$1 $2')) + ); + } + // while(data.length){ + // let next; + // if(data.length > MAXIMUM_QUERY_VARS){ + // next = data.slice(MAXIMUM_QUERY_VARS); + // data = data.slice(0, MAXIMUM_QUERY_VARS); + // } + // let insert = pgp.helpers.insert(data, stmt); + // trx.none(insert.replace(/^(insert into )"([^"]+)"/, '$1 $2')); + // if(next) data = next; + // else break; + // } + } + + // Registry Only + + else if(!flexsearch.bypass){ + let data = []; + let stmt = new pgp.helpers.ColumnSet(["id"],{ + table: this.id + ".reg" + }); + for(const id of flexsearch.reg.keys()){ + // const migration = checkMigration.call(this, id); + // migration && await migration; + data.push({ id }); + if(data.length === MAXIMUM_QUERY_VARS){ + let insert = pgp.helpers.insert(data, stmt); + batch.push( + trx.none(insert.replace(/^(insert into )"([^"]+)"/, '$1 $2')) + ); + data = []; + } + } + if(data.length){ + let insert = pgp.helpers.insert(data, stmt); + batch.push( + trx.none(insert.replace(/^(insert into )"([^"]+)"/, '$1 $2')) + ); + } + } + + // Default Index + + if(flexsearch.map.size){ + let data = []; + let stmt = new pgp.helpers.ColumnSet(["key", "res", "id"], { + table: this.id + ".map" + this.field + }); + for(const item of flexsearch.map){ + const key = item[0]; + const arr = item[1]; + + for(let i = 0, ids; i < arr.length; i++){ + if((ids = arr[i]) && ids.length){ + //this.type || (this.type = typeof ids[0]); + // let stmt = "($1,$2,$3)"; + // let params = [key, i, ids[0]]; + for(let j = 0; j < ids.length; j++){ + // stmt += ",($1,$2,$3)"; + // params.push(key, i, ids[j]); + //trx.none(`INSERT INTO ${config.schema}.map${self.field} (key, res, id) VALUES ($1,$2,$3)`, [key, i, ids[j]]); + data.push({ + key: key, + res: i, + id: ids[j] + }); + if(data.length === MAXIMUM_QUERY_VARS){ + let insert = pgp.helpers.insert(data, stmt); + batch.push( + trx.none(insert.replace(/^(insert into )"([^"]+)"/, '$1 $2')) + ); + data = []; + } + } + } + } + } + if(data.length){ + let insert = pgp.helpers.insert(data, stmt); + batch.push( + trx.none(insert.replace(/^(insert into )"([^"]+)"/, '$1 $2')) + ); + } + } + + // Context Index + + if(flexsearch.ctx.size){ + let data = []; + let stmt = new pgp.helpers.ColumnSet(["ctx", "key", "res", "id"], { + table: this.id + ".ctx" + this.field + }); + for(const ctx of flexsearch.ctx){ + const ctx_key = ctx[0]; + const ctx_value = ctx[1]; + + for(const item of ctx_value){ + const key = item[0]; + const arr = item[1]; + + for(let i = 0, ids; i < arr.length; i++){ + if((ids = arr[i]) && ids.length){ + // let stmt = "(?,?,?)"; + // let params = [ctx_key + ":" + key, i, ids[0]]; + for(let j = 0; j < ids.length; j++){ + // stmt += ",(?,?,?)"; + // params.push(ctx_key + ":" + key, i, ids[j]); + //trx.none("INSERT INTO " + config.schema + ".ctx" + self.field + " (ctx, key, res, id) VALUES ($1,$2,$3,$4)", [ctx_key, key, i, ids[j]]); + data.push({ + ctx: ctx_key, + key: key, + res: i, + id: ids[j] + }); + if(data.length === MAXIMUM_QUERY_VARS){ + let insert = pgp.helpers.insert(data, stmt); + batch.push( + trx.none(insert.replace(/^(insert into )"([^"]+)"/, '$1 $2')) + ); + data = []; + } + } + } + } + } + } + if(data.length){ + let insert = pgp.helpers.insert(data, stmt); + batch.push( + trx.none(insert.replace(/^(insert into )"([^"]+)"/, '$1 $2')) + ); + } + } + + // Tag Index + + if(flexsearch.tag){ + let data = []; + let stmt = new pgp.helpers.ColumnSet(["tag", "id"],{ + table: this.id + ".tag" + this.field + }); + for(const item of flexsearch.tag){ + const tag = item[0]; + const ids = item[1]; + if(!ids.length) continue; + for(let j = 0; j < ids.length; j++){ + data.push({ tag, id: ids[j] }); + if(data.length === MAXIMUM_QUERY_VARS){ + let insert = pgp.helpers.insert(data, stmt); + batch.push( + trx.none(insert.replace(/^(insert into )"([^"]+)"/, '$1 $2')) + ); + data = []; + } + } + } + if(data.length){ + let insert = pgp.helpers.insert(data, stmt); + batch.push( + trx.none(insert.replace(/^(insert into )"([^"]+)"/, '$1 $2')) + ); + } + } + + // Field Configuration + + // TODO + // trx.none("INSERT INTO " + this.id + ".cfg" + this.field + " (cfg) VALUES ($1)", [ + // JSON.stringify({ + // "encode": typeof flexsearch.encode === "string" ? flexsearch.encode : "", + // "charset": typeof flexsearch.charset === "string" ? flexsearch.charset : "", + // "tokenize": flexsearch.tokenize, + // "resolution": flexsearch.resolution, + // "minlength": flexsearch.minlength, + // "optimize": flexsearch.optimize, + // "fastupdate": flexsearch.fastupdate, + // "encoder": flexsearch.encoder, + // "context": { + // "depth": flexsearch.depth, + // "bidirectional": flexsearch.bidirectional, + // "resolution": flexsearch.resolution_ctx + // } + // }) + // ]); + + //return Promise.all(batch); + + if(batch.length){ + return trx.batch(batch); + } + }); + + flexsearch.map.clear(); + flexsearch.ctx.clear(); + flexsearch.tag && + flexsearch.tag.clear(); + flexsearch.store && + flexsearch.store.clear(); + flexsearch.document || + flexsearch.reg.clear(); +}; + +PostgresDB.prototype.remove = function(ids){ + + if(!ids && ids !== 0){ + return; + } + if(typeof ids !== "object"){ + ids = [ids]; + } + if(!ids.length){ + return; + } + + // ids = [ids]; + // return this.db.none( + // "DELETE FROM " + this.id + ".map" + this.field + " WHERE id = ANY ($1);" + + // "DELETE FROM " + this.id + ".ctx" + this.field + " WHERE id = ANY ($1);" + + // "DELETE FROM " + this.id + ".tag" + this.field + " WHERE id = ANY ($1);" + + // "DELETE FROM " + this.id + ".reg WHERE id = ANY ($1);", [ids] + // ); + + // ids = [ids]; + // return Promise.all([ + // this.db.none({ text: "DELETE FROM " + this.id + ".map" + this.field + " WHERE id = ANY ($1)", rowMode: "array" }, ids), + // this.db.none({ text: "DELETE FROM " + this.id + ".ctx" + this.field + " WHERE id = ANY ($1)", rowMode: "array" }, ids), + // this.db.none({ text: "DELETE FROM " + this.id + ".tag" + this.field + " WHERE id = ANY ($1)", rowMode: "array" }, ids), + // this.db.none({ text: "DELETE FROM " + this.id + ".reg WHERE id = ANY ($1)", rowMode: "array" }, ids) + // ]); + + return this.transaction(function(trx){ + + //console.log("remove:", ids); + + // ids = [ids]; + // trx.none({ text: "DELETE FROM " + this.id + ".map" + this.field + " WHERE id = ANY ($1)", rowMode: "array" }, ids); + // trx.none({ text: "DELETE FROM " + this.id + ".ctx" + this.field + " WHERE id = ANY ($1)", rowMode: "array" }, ids); + // trx.none({ text: "DELETE FROM " + this.id + ".tag" + this.field + " WHERE id = ANY ($1)", rowMode: "array" }, ids); + // trx.none({ text: "DELETE FROM " + this.id + ".reg WHERE id = ANY ($1)", rowMode: "array" }, ids); + + // ids = [ids]; + // trx.none("DELETE FROM " + this.id + ".map" + this.field + " WHERE id = ANY ($1)", ids); + // trx.none("DELETE FROM " + this.id + ".ctx" + this.field + " WHERE id = ANY ($1)", ids); + // trx.none("DELETE FROM " + this.id + ".tag" + this.field + " WHERE id = ANY ($1)", ids); + // trx.none("DELETE FROM " + this.id + ".reg WHERE id = ANY ($1)", ids); + // return; + + // return trx.none( + // "DELETE FROM " + this.id + ".map" + this.field + " WHERE id = ANY ($1);" + + // "DELETE FROM " + this.id + ".ctx" + this.field + " WHERE id = ANY ($1);" + + // "DELETE FROM " + this.id + ".tag" + this.field + " WHERE id = ANY ($1);" + + // "DELETE FROM " + this.id + ".reg WHERE id = ANY ($1);", [ids] + // ); + + // while(ids.length){ + // let next; + // let stmt = ""; + // let chunk = 0; + // let migration; + // for(let i = 0; i < ids.length; i++){ + // stmt += (stmt ? "," : "") + "$" + (i + 1); // + "::text"; + // // maximum count of variables supported + // if(++chunk === MAXIMUM_QUERY_VARS){ + // next = ids.slice(MAXIMUM_QUERY_VARS); + // ids = ids.slice(0, MAXIMUM_QUERY_VARS); + // break; + // } + // } + // + // trx.batch([ + // trx.none("DELETE FROM " + this.id + ".map" + this.field + " WHERE id IN (" + stmt + ")", ids), + // trx.none("DELETE FROM " + this.id + ".ctx" + this.field + " WHERE id IN (" + stmt + ")", ids), + // trx.none("DELETE FROM " + this.id + ".tag" + this.field + " WHERE id IN (" + stmt + ")", ids), + // trx.none("DELETE FROM " + this.id + ".reg WHERE id IN (" + stmt + ")", ids) + // ]); + // + // // trx.none( + // // "DELETE FROM " + this.id + ".map" + this.field + " WHERE id IN (" + stmt + ");" + + // // "DELETE FROM " + this.id + ".ctx" + this.field + " WHERE id IN (" + stmt + ");" + + // // "DELETE FROM " + this.id + ".tag" + this.field + " WHERE id IN (" + stmt + ");" + + // // "DELETE FROM " + this.id + ".reg WHERE id IN (" + stmt + ");", ids + // // ); + // + // if(next) ids = next; + // else break; + // } + + ids = [ids]; + return trx.batch([ + trx.none({ text: "DELETE FROM " + this.id + ".map" + this.field + " WHERE id = ANY ($1)", rowMode: "array" }, ids), + trx.none({ text: "DELETE FROM " + this.id + ".ctx" + this.field + " WHERE id = ANY ($1)", rowMode: "array" }, ids), + trx.none({ text: "DELETE FROM " + this.id + ".tag" + this.field + " WHERE id = ANY ($1)", rowMode: "array" }, ids), + trx.none({ text: "DELETE FROM " + this.id + ".reg WHERE id = ANY ($1)", rowMode: "array" }, ids) + ]); + + // ids = [ids]; + // return trx.batch([ + // trx.none("DELETE FROM " + this.id + ".map" + this.field + " WHERE id = ANY ($1)", ids), + // trx.none("DELETE FROM " + this.id + ".ctx" + this.field + " WHERE id = ANY ($1)", ids), + // trx.none("DELETE FROM " + this.id + ".tag" + this.field + " WHERE id = ANY ($1)", ids), + // trx.none("DELETE FROM " + this.id + ".reg WHERE id = ANY ($1)", ids) + // ]); + + // return trx.batch([ + // trx.none("DELETE FROM " + this.id + ".map" + this.field + " WHERE id IN ($1:csv)", [ids]), + // trx.none("DELETE FROM " + this.id + ".ctx" + this.field + " WHERE id IN ($1:csv)", [ids]), + // trx.none("DELETE FROM " + this.id + ".tag" + this.field + " WHERE id IN ($1:csv)", [ids]), + // trx.none("DELETE FROM " + this.id + ".reg WHERE id IN ($1:csv)", [ids]) + // ]); + + // const type = self.type === "text" ? "text" : "int"; + // return trx.batch([ + // trx.none("DELETE FROM " + this.id + ".map" + self.field + " WHERE id = ANY($1::" + type + "[])", [ids]), + // trx.none("DELETE FROM " + this.id + ".ctx" + self.field + " WHERE id = ANY($1::" + type + "[])", [ids]), + // trx.none("DELETE FROM " + this.id + ".reg WHERE id = ANY($1::" + type + "[])", [ids]) + // ]); + + // return trx.batch([ + // trx.none("DELETE FROM " + this.id + ".map" + self.field + " WHERE id = ANY($1)", [ids]), + // trx.none("DELETE FROM " + this.id + ".ctx" + self.field + " WHERE id = ANY($1)", [ids]), + // trx.none("DELETE FROM " + this.id + ".reg WHERE id = ANY($1)", [ids]) + // ]); + }); +}; + +// if(this.type === "int" && typeof ids[0] === "string"){ +// ids = ids.map(item => parseInt(item, 10)); +// } +// if(this.type === "bigint" && typeof ids[0] === "string"){ +// ids = ids.map(item => BigInt(item)); +// } +// if(this.type === "string" && typeof ids[0] === "number"){ +// ids = ids.map(item => item + ""); +// } +// this.type !== "string" && checkMigration.call(this, ids[0]); + +// function checkMigration(id){ +// if(this.type !== "string"){ +// if(typeof id === "object"){ +// id = id[0]; +// } +// if(typeof id === "string"){ +// this.type = "string"; +// return upgradeTextId.call(this); +// } +// if(this.type !== "bigint"){ +// if(id > 2 ** 31 - 1){ +// this.type = "bigint"; +// return upgradeBigIntId.call(this); +// } +// } +// } +// } +// +// function upgradeTextId(){ +// return this.db.none(` +// ALTER TABLE ${this.id}.map${this.field} +// ALTER COLUMN id type varchar(128) +// USING id::text; +// ALTER TABLE ${this.id}.ctx${this.field} +// ALTER COLUMN id type varchar(128) +// USING id::text; +// ALTER TABLE ${this.id}.tag${this.field} +// ALTER COLUMN id type varchar(128) +// USING id::text; +// ALTER TABLE ${this.id}.reg +// ALTER COLUMN id type varchar(128) +// USING id::text; +// `); +// } +// +// function upgradeBigIntId(){ +// return this.db.none(` +// ALTER TABLE ${this.id}.map${this.field} +// ALTER COLUMN id type bigint +// USING id::bigint; +// ALTER TABLE ${this.id}.ctx${this.field} +// ALTER COLUMN id type bigint +// USING id::bigint; +// ALTER TABLE ${this.id}.tag${this.field} +// ALTER COLUMN id type bigint +// USING id::bigint; +// ALTER TABLE ${this.id}.reg +// ALTER COLUMN id type bigint +// USING id::bigint; +// `); +// } + +module.exports = PostgresDB; diff --git a/dist/db/redis/index.cjs b/dist/db/redis/index.cjs new file mode 100644 index 0000000..aa09a93 --- /dev/null +++ b/dist/db/redis/index.cjs @@ -0,0 +1,6244 @@ +'use strict'; + +var redis = require('redis'); + +/** @define {string} */ + +/** @define {boolean} */ +const SUPPORT_PERSISTENT = true; + +/** @define {boolean} */ +const SUPPORT_KEYSTORE = true; + +/** + * @param {*} value + * @param {*} default_value + * @param {*=} merge_value + * @return {*} + */ + +function parse_option(value, default_value, merge_value){ + + const type_merge = typeof merge_value; + const type_value = typeof value; + + if(type_merge !== "undefined"){ + if(type_value !== "undefined"){ + + if(merge_value){ + if(type_value === "function" && + type_merge === type_value){ + return function(str){ + return /** @type Function */ (value)( + /** @type Function */ (merge_value)(str) + ); + } + } + + const constructor_value = value.constructor; + const constructor_merge = merge_value.constructor; + + if(constructor_value === constructor_merge){ + + if(constructor_value === Array){ + return merge_value.concat(value); + } + + if(constructor_value === Map){ + const map = new Map(/** @type !Map */ (merge_value)); + for(const item of /** @type !Map */ (value)){ + const key = item[0]; + const val = item[1]; + map.set(key, val); + } + return map; + } + + if(constructor_value === Set){ + const set = new Set(/** @type !Set */ (merge_value)); + for(const val of /** @type !Set */ (value).values()){ + set.add(val); + } + return set; + } + } + } + + return value; + } + else { + return merge_value; + } + } + + return type_value === "undefined" + ? default_value + : value; +} + +function create_object(){ + return Object.create(null); +} + +function concat(arrays){ + return [].concat.apply([], arrays); +} + +function sort_by_length_down(a, b){ + return b.length - a.length; +} + +function is_array(val){ + return val.constructor === Array; +} + +function is_string(val){ + return typeof val === "string"; +} + +function is_object(val){ + return typeof val === "object"; +} + +function is_function(val){ + return typeof val === "function"; +} + +/** + * @param {Map|Set} val + * @param {boolean=} stringify + * @return {Array} + */ + +function toArray(val, stringify){ + const result = []; + for(const key of val.keys()){ + result.push("" + key ); + } + return result; +} + +// TODO support generic function created from string when tree depth > 1 +function parse_simple(obj, tree){ + + if(is_string(tree)){ + obj = obj[tree]; + } + else for(let i = 0; obj && (i < tree.length); i++){ + obj = obj[tree[i]]; + } + + return obj; +} + +function get_max_len(arr){ + let len = 0; + for(let i = 0, res; i < arr.length; i++){ + if((res = arr[i])){ + if(len < res.length){ + len = res.length; + } + } + } + return len; +} + +var normalize_polyfill = [ + + // Charset Normalization + + ["ª","a"], + ["²","2"], + ["³","3"], + ["¹","1"], + ["º","o"], + ["¼","1⁄4"], + ["½","1⁄2"], + ["¾","3⁄4"], + ["à","a"], + ["á","a"], + ["â","a"], + ["ã","a"], + ["ä","a"], + ["å","a"], + ["ç","c"], + ["è","e"], + ["é","e"], + ["ê","e"], + ["ë","e"], + ["ì","i"], + ["í","i"], + ["î","i"], + ["ï","i"], + ["ñ","n"], + ["ò","o"], + ["ó","o"], + ["ô","o"], + ["õ","o"], + ["ö","o"], + ["ù","u"], + ["ú","u"], + ["û","u"], + ["ü","u"], + ["ý","y"], + ["ÿ","y"], + ["ā","a"], + ["ă","a"], + ["ą","a"], + ["ć","c"], + ["ĉ","c"], + ["ċ","c"], + ["č","c"], + ["ď","d"], + ["ē","e"], + ["ĕ","e"], + ["ė","e"], + ["ę","e"], + ["ě","e"], + ["ĝ","g"], + ["ğ","g"], + ["ġ","g"], + ["ģ","g"], + ["ĥ","h"], + ["ĩ","i"], + ["ī","i"], + ["ĭ","i"], + ["į","i"], + ["ij","ij"], + ["ĵ","j"], + ["ķ","k"], + ["ĺ","l"], + ["ļ","l"], + ["ľ","l"], + ["ŀ","l"], + ["ń","n"], + ["ņ","n"], + ["ň","n"], + ["ʼn","n"], + ["ō","o"], + ["ŏ","o"], + ["ő","o"], + ["ŕ","r"], + ["ŗ","r"], + ["ř","r"], + ["ś","s"], + ["ŝ","s"], + ["ş","s"], + ["š","s"], + ["ţ","t"], + ["ť","t"], + ["ũ","u"], + ["ū","u"], + ["ŭ","u"], + ["ů","u"], + ["ű","u"], + ["ų","u"], + ["ŵ","w"], + ["ŷ","y"], + ["ź","z"], + ["ż","z"], + ["ž","z"], + ["ſ","s"], + ["ơ","o"], + ["ư","u"], + ["dž","dz"], + ["lj","lj"], + ["nj","nj"], + ["ǎ","a"], + ["ǐ","i"], + ["ǒ","o"], + ["ǔ","u"], + ["ǖ","u"], + ["ǘ","u"], + ["ǚ","u"], + ["ǜ","u"], + ["ǟ","a"], + ["ǡ","a"], + ["ǣ","ae"], + ["æ","ae"], + ["ǽ","ae"], + ["ǧ","g"], + ["ǩ","k"], + ["ǫ","o"], + ["ǭ","o"], + ["ǯ","ʒ"], + ["ǰ","j"], + ["dz","dz"], + ["ǵ","g"], + ["ǹ","n"], + ["ǻ","a"], + ["ǿ","ø"], + ["ȁ","a"], + ["ȃ","a"], + ["ȅ","e"], + ["ȇ","e"], + ["ȉ","i"], + ["ȋ","i"], + ["ȍ","o"], + ["ȏ","o"], + ["ȑ","r"], + ["ȓ","r"], + ["ȕ","u"], + ["ȗ","u"], + ["ș","s"], + ["ț","t"], + ["ȟ","h"], + ["ȧ","a"], + ["ȩ","e"], + ["ȫ","o"], + ["ȭ","o"], + ["ȯ","o"], + ["ȱ","o"], + ["ȳ","y"], + ["ʰ","h"], + ["ʱ","h"], + ["ɦ","h"], + ["ʲ","j"], + ["ʳ","r"], + ["ʴ","ɹ"], + ["ʵ","ɻ"], + ["ʶ","ʁ"], + ["ʷ","w"], + ["ʸ","y"], + ["ˠ","ɣ"], + ["ˡ","l"], + ["ˢ","s"], + ["ˣ","x"], + ["ˤ","ʕ"], + ["ΐ","ι"], + ["ά","α"], + ["έ","ε"], + ["ή","η"], + ["ί","ι"], + ["ΰ","υ"], + ["ϊ","ι"], + ["ϋ","υ"], + ["ό","ο"], + ["ύ","υ"], + ["ώ","ω"], + ["ϐ","β"], + ["ϑ","θ"], + ["ϒ","Υ"], + ["ϓ","Υ"], + ["ϔ","Υ"], + ["ϕ","φ"], + ["ϖ","π"], + ["ϰ","κ"], + ["ϱ","ρ"], + ["ϲ","ς"], + ["ϵ","ε"], + ["й","и"], + ["ѐ","е"], + ["ё","е"], + ["ѓ","г"], + ["ї","і"], + ["ќ","к"], + ["ѝ","и"], + ["ў","у"], + ["ѷ","ѵ"], + ["ӂ","ж"], + ["ӑ","а"], + ["ӓ","а"], + ["ӗ","е"], + ["ӛ","ә"], + ["ӝ","ж"], + ["ӟ","з"], + ["ӣ","и"], + ["ӥ","и"], + ["ӧ","о"], + ["ӫ","ө"], + ["ӭ","э"], + ["ӯ","у"], + ["ӱ","у"], + ["ӳ","у"], + ["ӵ","ч"] + + // Term Separators + + // ["'", ""], // it's -> its + // ["´", ""], + // ["`", ""], + // ["’", ""], + // ["ʼ", ""], + + // Numeric-Separators Chars Removal + + // [",", ""], + // [".", ""] + + // Non-Whitespace Separators + + // already was split by default via p{P} + // ["-", " "], + // [":", " "], + // ["_", " "], + // ["|", " "], + // ["/", " "], + // ["\\", " "] +]; + +// COMPILER BLOCK --> + +/* + +Custom Encoder +---------------- + +// Split a passed string into an Array of words: +function englishEncoder(string){ + return string.toLowerCase().split(/[^a-z]+/) +} + +// For CJK split a passed string into an Array of chars: +function chineseEncoder(string){ + return string.replace(/\s+/, "").split("") +} + +// Alternatively do not split the input: +function fixedEncoder(string){ + return [string] +} + +Built-in Encoder (Workflow) +---------------------------- +Pipeline: + 1. apply this.normalize: charset normalization: + applied on the whole input string e.g. lowercase, + will also apply on: filter, matcher, stemmer, mapper + 2. apply this.split: split input into terms (includes/excludes) + 3. apply this.filter (pre-filter) + 4. apply this.matcher (replace terms) + 5. apply this.stemmer (replace term endings) + 6. apply this.filter (post-filter) + 7. apply this.mapper (replace chars) + 8. apply this.replacer (custom regex) + 9. apply this.dedupe (letter deduplication) + 10. apply this.finalize +*/ + +const whitespace = /[^\p{L}\p{N}]+/u; // /[\p{Z}\p{S}\p{P}\p{C}]+/u; +//const numeric_split = /(\d{3})/g; +const numeric_split_length = /(\d{3})/g; +const numeric_split_prev_char = /(\D)(\d{3})/g; +const numeric_split_next_char = /(\d{3})(\D)/g; +//.replace(/(\d{3})/g, "$1 ") +//.replace(/([^\d])([\d])/g, "$1 $2") +//.replace(/([\d])([^\d])/g, "$1 $2") +const normalize = "".normalize && /[\u0300-\u036f]/g; // '´`’ʼ., +//const normalize_mapper = SUPPORT_CHARSET && !normalize && normalize_polyfill; + +/** + * @param {EncoderOptions=} options + * @constructor + */ + +function Encoder(options){ + + if(!this){ + return new Encoder(...arguments); + } + + for(let i = 0; i < arguments.length; i++){ + this.assign(arguments[i]); + } +} +/** + * @param {!EncoderOptions} options + */ +Encoder.prototype.assign = function(options){ + + /** + * pre-processing string input + * @type {Function|boolean} + */ + this.normalize = /** @type {Function|boolean} */ ( + parse_option(options.normalize, true, this.normalize) + ); + + // { + // letter: true, + // number: true, + // whitespace: true, + // symbol: true, + // punctuation: true, + // control: true, + // char: "" + // } + + let include = options.include; + let tmp = include || options.exclude || options.split; + + if(typeof tmp === "object"){ + let numeric = !include; + let regex = ""; + // split on whitespace by default + options.include || ( + regex += "\\p{Z}" + ); + if(tmp.letter){ + regex += "\\p{L}"; + } + if(tmp.number){ + regex += "\\p{N}"; + numeric = !!include; + } + if(tmp.symbol){ + regex += "\\p{S}"; + } + if(tmp.punctuation){ + regex += "\\p{P}"; + } + if(tmp.control){ + regex += "\\p{C}"; + } + if((tmp = tmp.char)){ + regex += typeof tmp === "object" ? tmp.join("") : tmp; + } + + this.split = new RegExp("[" + (include ? "^" : "") + regex + "]+", "u"); + this.numeric = numeric; + } + else { + + /** + * split string input into terms + * @type {string|RegExp|boolean|null} + */ + this.split = /** @type {string|RegExp|boolean} */ (parse_option(tmp, whitespace, this.split)); + this.numeric = parse_option(this.numeric, true); + } + + /** + * post-processing terms + * @type {Function|null} + */ + this.prepare = /** @type {Function|null} */ ( + parse_option(options.prepare, null, this.prepare) + ); + /** + * final processing + * @type {Function|null} + */ + this.finalize = /** @type {Function|null} */ ( + parse_option(options.finalize, null, this.finalize) + ); + + // assign the normalization fallback to the mapper + if(!normalize){ + this.mapper = new Map( + /** @type {Array>} */ ( + normalize_polyfill + ) + ); + } + + // options + + this.rtl = options.rtl || false; + this.dedupe = parse_option(options.dedupe, true, this.dedupe); + this.filter = parse_option((tmp = options.filter) && new Set(tmp), null, this.filter); + this.matcher = parse_option((tmp = options.matcher) && new Map(tmp), null, this.matcher); + this.mapper = parse_option((tmp = options.mapper) && new Map(tmp), null, this.mapper); + this.stemmer = parse_option((tmp = options.stemmer) && new Map(tmp), null, this.stemmer); + this.replacer = parse_option(options.replacer, null, this.replacer); + this.minlength = parse_option(options.minlength, 1, this.minlength); + this.maxlength = parse_option(options.maxlength, 0, this.maxlength); + + // minimum required tokenizer by this encoder + //this.tokenize = options["tokenize"] || ""; + + // auto-balanced cache + { + this.cache = tmp = parse_option(options.cache, true, this.cache); + if(tmp){ + this.timer = null; + this.cache_size = typeof tmp === "number" ? tmp : 2e5; + this.cache_enc = new Map(); + this.cache_prt = new Map(); + this.cache_enc_length = 128; + this.cache_prt_length = 128; + } + } + + // regex temporary state + this.matcher_str = ""; + this.matcher_test = null; + this.stemmer_str = ""; + this.stemmer_test = null; + + // prebuilt + // if(this.filter && this.split){ + // for(const key of this.filter){ + // const tmp = key.replace(this.split, ""); + // if(key !== tmp){ + // this.filter.delete(key); + // this.filter.add(tmp); + // } + // } + // } + if(this.matcher){ + for(const key of this.matcher.keys()){ + this.matcher_str += (this.matcher_str ? "|" : "") + key; + } + } + if(this.stemmer){ + for(const key of this.stemmer.keys()){ + this.stemmer_str += (this.stemmer_str ? "|" : "") + key; + } + } + + // if(SUPPORT_COMPRESSION){ + // this.compression = parse_option(options.compress || options.compression, 0, this.compression); + // if(this.compression && !table){ + // table = new Array(radix); + // for(let i = 0; i < radix; i++) table[i] = i + 33; + // table = String.fromCharCode.apply(null, table); + // } + // } + + return this; +}; + +Encoder.prototype.addMatcher = function(match, replace){ + // regex: + if(typeof match === "object"){ + return this.addReplacer(match, replace); + } + // a single char: + if(match.length < 2){ + return this.addMapper(match, replace); + } + this.matcher || (this.matcher = new Map()); + this.matcher.set(match , replace); + this.matcher_str += (this.matcher_str ? "|" : "") + match; + this.matcher_test = null; //new RegExp("(" + this.matcher_str + ")"); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addStemmer = function(match, replace){ + this.stemmer || (this.stemmer = new Map()); + this.stemmer.set(match, replace); + this.stemmer_str += (this.stemmer_str ? "|" : "") + match; + this.stemmer_test = null; //new RegExp("(" + this.stemmer_str + ")"); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addFilter = function(str){ + this.filter || (this.filter = new Set()); + this.filter.add(str); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addMapper = function(char_match, char_replace){ + // regex: + if(typeof char_match === "object"){ + return this.addReplacer(char_match, char_replace); + } + // not a char: + if(char_match.length > 1){ + return this.addMatcher(char_match, char_replace); + } + this.mapper || (this.mapper = new Map()); + this.mapper.set(char_match, char_replace); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addReplacer = function(match, replace){ + if(typeof match === "string") match = new RegExp(match, "g"); + this.replacer || (this.replacer = []); + this.replacer.push(match, replace || ""); + this.cache && this.invalidate(); + return this; +}; + +{ + Encoder.prototype.invalidate = function(){ + this.cache_enc.clear(); + this.cache_prt.clear(); + }; +} + +Encoder.prototype.encode = function(str){ + + //if(!str) return str; + // todo remove dupe terms + + if(this.cache && str.length <= this.cache_enc_length){ + if(this.timer){ + if(this.cache_enc.has(str)){ + return this.cache_enc.get(str); + } + } + else { + this.timer = setTimeout(clear$1, 0, this); + } + } + + // 1. apply charset normalization + if(this.normalize){ + if(typeof this.normalize === "function"){ + str = this.normalize(str); + } + else if(normalize){ + str = str.normalize("NFKD").replace(normalize, "").toLowerCase(); + } + else { + str = str.toLowerCase(); + // if(SUPPORT_CHARSET){ + // this.mapper = this.mapper + // // todo replace spread + // ? new Map([.../** @type {!Iterable} */(normalize_mapper), ...this.mapper]) + // : new Map(/** @type {Map} */ (normalize_mapper)); + // } + } + //if(!str) return str; + } + + // 2. apply custom encoder (can replace split) + if(this.prepare){ + str = this.prepare(str); + } + + // 3. split numbers into triplets + if(this.numeric && str.length > 3){ + str = str.replace(numeric_split_prev_char, "$1 $2") + .replace(numeric_split_next_char, "$1 $2") + .replace(numeric_split_length, "$1 "); + } + + // if(this.matcher && (str.length > 1)){ + // this.matcher_test || ( + // this.matcher_test = new RegExp("(" + this.matcher_str + ")", "g") + // ); + // str = str.replace(this.matcher_test, match => this.matcher.get(match)); + // } + // if(this.stemmer){ + // this.stemmer_test || ( + // this.stemmer_test = new RegExp("(?!\\b)(" + this.stemmer_str + ")(\\b|_)", "g") + // ); + // str = str.replace(this.stemmer_test, match => this.stemmer.get(match)); + // } + + const skip = !(this.dedupe || this.mapper || this.filter || this.matcher || this.stemmer || this.replacer); + let final = []; + let words = this.split || this.split === "" + ? str.split(/** @type {string|RegExp} */ (this.split)) + : str; //[str]; + + for(let i = 0, word, base; i < words.length; i++){ + // filter empty entries + if(!(word = base = words[i])){ + continue; + } + if(word.length < this.minlength){ + continue; + } + if(skip) { + final.push(word); + continue; + } + + // 1. pre-filter before cache + if(this.filter && this.filter.has(word)){ + continue; + } + + if(this.cache && word.length <= this.cache_prt_length){ + if(this.timer){ + const tmp = this.cache_prt.get(word); + //if(this.cache_prt.has(word)){ + if(tmp || tmp === ""){ + //word = this.cache_prt.get(word); + tmp && final.push(tmp); + //word ? words[i] = word : words.splice(i--, 1); + continue; + } + } + else { + this.timer = setTimeout(clear$1, 0, this); + } + } + + let postfilter; + + // if(this.normalize === true && normalize){ + // word = word.normalize("NFKD").replace(normalize, ""); + // postfilter = 1; + // } + + // if(this.normalize){ + // if(typeof this.normalize === "function"){ + // word = this.normalize(word); + // } + // else if(normalize){ + // word = word.normalize("NFKD").replace(normalize, "").toLowerCase(); + // } + // else{ + // word = word.toLowerCase(); + // this.mapper = this.mapper + // ? new Map([...normalize_mapper, ...this.mapper]) + // : new Map(/** @type {Map} */ normalize_mapper); + // } + // postfilter = 1; + // //if(!str) return str; + // } + + // 2. apply stemmer after matcher + if(this.stemmer && (word.length > 2)){ + // for(const item of this.stemmer){ + // const key = item[0]; + // const value = item[1]; + // + // if(word.length > key.length && word.endsWith(key)){ + // word = word.substring(0, word.length - key.length) + value; + // break; + // } + // + // // const position = word.length - key.length; + // // if(position > 0 && word.substring(position) === key){ + // // word = word.substring(0, position) + value; + // // break; + // // } + // } + this.stemmer_test || ( + this.stemmer_test = new RegExp("(?!^)(" + this.stemmer_str + ")$") + ); + word = word.replace(this.stemmer_test, match => this.stemmer.get(match)); + postfilter = 1; + } + + // 3. apply matcher + if(this.matcher && (word.length > 1)){ + this.matcher_test || ( + this.matcher_test = new RegExp("(" + this.matcher_str + ")", "g") + ); + word = word.replace(this.matcher_test, match => this.matcher.get(match)); + postfilter = 1; + } + + // 4. post-filter after matcher and stemmer was applied + if(word && postfilter && (word.length < this.minlength || (this.filter && this.filter.has(word)))){ + word = ""; + } + + // 5. apply mapper and collapsing + if(word && (this.mapper || (this.dedupe && word.length > 1))){ + //word = this.replace_dedupe(word); + //word = replace_deduped(word, this.mapper, true); + let final = ""; + for(let i = 0, prev = "", char, tmp; i < word.length; i++){ + char = word.charAt(i); + if(char !== prev || !this.dedupe){ + tmp = this.mapper && this.mapper.get(char); + if(!tmp && tmp !== "") + final += (prev = char); + else if((tmp !== prev || !this.dedupe) && (prev = tmp)) + final += tmp; + } + } + word = final; + } + + // apply custom regex + if(word && this.replacer){ + for(let i = 0; word && (i < this.replacer.length); i+=2){ + word = word.replace(this.replacer[i], this.replacer[i+1]); + } + } + + // slower variants for removing same chars in a row: + //word = word.replace(/([^0-9])\1+/g, "$1"); + //word = word.replace(/(.)\1+/g, "$1"); + //word = word.replace(/(?<=(.))\1+/g, ""); + + // if(word){ + // words[i] = word; + // } + + if(this.cache && base.length <= this.cache_prt_length){ + this.cache_prt.set(base, word); + if(this.cache_prt.size > this.cache_size){ + this.cache_prt.clear(); + this.cache_prt_length = this.cache_prt_length / 1.1 | 0; + } + } + + //word || words.splice(i--, 1); + word && final.push(word); + } + + //words = final; + // else if(this.filter){ + // for(let i = 0, word; i < words.length; i++){ + // if((word = words[i]) && !this.filter.has(word)){ + // //filtered.push(word); + // words.splice(i--, 1); + // } + // } + // } + + if(this.finalize){ + final = this.finalize(final) || final; + } + + if(this.cache && str.length <= this.cache_enc_length){ + this.cache_enc.set(str, final); + if(this.cache_enc.size > this.cache_size){ + this.cache_enc.clear(); + this.cache_enc_length = this.cache_enc_length / 1.1 | 0; + } + } + + return final; +}; + +// Encoder.prototype.compress = function(str) { +// +// //return str; +// //if(!str) return str; +// +// if(SUPPORT_CACHE && this.cache && str.length <= this.cache_prt_length){ +// if(this.timer){ +// if(this.cache_cmp.has(str)){ +// return this.cache_cmp.get(str); +// } +// } +// else{ +// this.timer = setTimeout(clear, 0, this); +// } +// } +// +// const result = typeof this.compression === "function" +// ? this.compression(str) +// : hash(str); //window.hash(str); +// +// if(SUPPORT_CACHE && this.cache && str.length <= this.cache_prt_length){ +// this.cache_cmp.set(str, result); +// this.cache_cmp.size > this.cache_size && +// this.cache_cmp.clear(); +// } +// +// return result; +// }; + +// function hash(str){ +// return str; +// } + +function clear$1(self){ + self.timer = null; + self.cache_enc.clear(); + self.cache_prt.clear(); +} + +/** + * @param {string|Object} query + * @param {number|Object=} limit + * @param {Object=} options + * @this {Index|Document} + * @returns {Array|Promise} + */ + +function searchCache(query, limit, options){ + + query = (typeof query === "object" + ? "" + query.query + : "" + query + ).toLowerCase(); + + //let encoded = this.encoder.encode(query).join(" "); + let cache = this.cache.get(query); + if(!cache){ + cache = this.search(query, limit, options); + if(cache.then){ + const self = this; + cache.then(function(cache){ + self.cache.set(query, cache); + return cache; + }); + } + this.cache.set(query, cache); + } + return cache; +} + +/** + * @param {boolean|number=} limit + * @constructor + */ + +function CacheClass(limit){ + /** @private */ + this.limit = (!limit || limit === true) ? 1000 : limit; + /** @private */ + this.cache = new Map(); + /** @private */ + this.last = ""; +} + +CacheClass.prototype.set = function(key, value){ + //if(!this.cache.has(key)){ + this.cache.set(this.last = key, value); + if(this.cache.size > this.limit){ + this.cache.delete(this.cache.keys().next().value); + } + //} +}; + +CacheClass.prototype.get = function(key){ + const cache = this.cache.get(key); + if(cache && this.last !== key){ + this.cache.delete(key); + this.cache.set(this.last = key, cache); + } + return cache; +}; + +CacheClass.prototype.remove = function(id){ + for(const item of this.cache){ + const key = item[0]; + const value = item[1]; + if(value.includes(id)){ + this.cache.delete(key); + } + } +}; + +CacheClass.prototype.clear = function(){ + this.cache.clear(); + this.last = ""; +}; + +/** @type EncoderOptions */ +const options = { + normalize: function(str){ + return str.toLowerCase(); + }, + dedupe: false +}; + +// import { pipeline } from "../../lang.js"; +// +// const whitespace = /[\p{Z}\p{S}\p{P}\p{C}]+/u; +// +// export const rtl = false; +// export const tokenize = ""; +// export default { +// encode: encode, +// rtl: rtl, +// tokenize: tokenize +// } +// +// /** +// * @param {string|number} str +// */ +// +// export function encode(str){ +// +// return pipeline.call( +// +// this, +// /* string: */ ("" + str).toLowerCase(), +// /* normalize: */ false, +// /* split: */ whitespace, +// /* collapse: */ false +// ); +// } + +// KeystoreObj.prototype.destroy = function(){ +// this.index = null; +// this.keys = null; +// this.proxy = null; +// }; + +function _slice(self, start, end, splice){ + let arr = []; + for(let i = 0, index; i < self.index.length; i++){ + index = self.index[i]; + if(start >= index.length){ + start -= index.length; + } + else { + const tmp = index[splice ? "splice" : "slice"](start, end); + const length = tmp.length; + if(length){ + arr = arr.length + ? arr.concat(tmp) + : tmp; + end -= length; + if(splice) self.length -= length; + if(!end) break; + } + start = 0; + } + } + return arr; +} + +/** + * @param arr + * @constructor + */ + +function KeystoreArray(arr){ + + if(!this){ + return new KeystoreArray(arr); + } + + this.index = arr ? [arr] : []; + this.length = arr ? arr.length : 0; + const self = this; + + return /*this.proxy =*/ new Proxy([], { + get(target, key) { + if(key === "length"){ + return self.length; + } + if(key === "push"){ + return function(value){ + self.index[self.index.length - 1].push(value); + self.length++; + } + } + if(key === "pop"){ + return function(){ + if(self.length){ + self.length--; + return self.index[self.index.length - 1].pop(); + } + } + } + if(key === "indexOf"){ + return function(key){ + let index = 0; + for(let i = 0, arr, tmp; i < self.index.length; i++){ + arr = self.index[i]; + //if(!arr.includes(key)) continue; + tmp = arr.indexOf(key); + if(tmp >= 0) return index + tmp; + index += arr.length; + } + return -1; + } + } + if(key === "includes"){ + return function(key){ + for(let i = 0; i < self.index.length; i++){ + if(self.index[i].includes(key)){ + return true; + } + } + return false; + } + } + if(key === "slice"){ + return function(start, end){ + return _slice( + self, + start || 0, + end || self.length, + false + ); + } + } + if(key === "splice"){ + return function(start, end){ + return _slice( + self, + start || 0, + end || self.length, + // splice: + true + ); + } + } + if(key === "constructor"){ + return Array; + } + if(typeof key === "symbol" /*|| isNaN(key)*/){ + // not supported + return; + } + const index = key / (2**31) | 0; + const arr = self.index[index]; + return arr && arr[key]; + }, + set(target, key, value){ + const index = key / (2**31) | 0; + const arr = self.index[index] || (self.index[index] = []); + arr[key] = value; + self.length++; + return true; + } + }); +} + +KeystoreArray.prototype.clear = function(){ + this.index.length = 0; +}; + +KeystoreArray.prototype.destroy = function(){ + this.index = null; + this.proxy = null; +}; + +KeystoreArray.prototype.push = function(val){}; + +/** + * @param bitlength + * @constructor + */ + +function KeystoreMap(bitlength = 8){ + + if(!this){ + return new KeystoreMap(bitlength); + } + + this.index = create_object(); + this.refs = []; + this.size = 0; + + if(bitlength > 32){ + this.crc = lcg64; + this.bit = BigInt(bitlength); + } + else { + this.crc = lcg$1; + this.bit = bitlength; + } +} + +KeystoreMap.prototype.get = function(key) { + const address = this.crc(key); + const map = this.index[address]; + return map && map.get(key); +}; + +KeystoreMap.prototype.set = function(key, value){ + const address = this.crc(key); + let map = this.index[address]; + if(map){ + let size = map.size; + map.set(key, value); + size -= map.size; + size && this.size++; + } + else { + this.index[address] = map = new Map([[key, value]]); + this.refs.push(map); + } +}; + +/** + * @param bitlength + * @constructor + */ + +function KeystoreSet(bitlength = 8){ + + if(!this){ + return new KeystoreSet(bitlength); + } + + // using plain Object with numeric key access + // just for max performance + this.index = create_object(); + this.refs = []; + + if(bitlength > 32){ + this.crc = lcg64; + this.bit = BigInt(bitlength); + } + else { + this.crc = lcg$1; + this.bit = bitlength; + } +} + +KeystoreSet.prototype.add = function(key){ + const address = this.crc(key); + let set = this.index[address]; + if(set){ + let size = set.size; + set.add(key); + size -= set.size; + size && this.size++; + } + else { + this.index[address] = set = new Set([key]); + this.refs.push(set); + } +}; + +KeystoreMap.prototype.has = +KeystoreSet.prototype.has = function(key) { + const address = this.crc(key); + const map_or_set = this.index[address]; + return map_or_set && map_or_set.has(key); +}; + +/* +KeystoreMap.prototype.size = +KeystoreSet.prototype.size = function(){ + let size = 0; + const values = Object.values(this.index); + for(let i = 0; i < values.length; i++){ + size += values[i].size; + } + return size; +}; +*/ + +KeystoreMap.prototype.delete = +KeystoreSet.prototype.delete = function(key){ + const address = this.crc(key); + const map_or_set = this.index[address]; + // set && (set.size === 1 + // ? this.index.delete(address) + // : set.delete(key)); + map_or_set && + map_or_set.delete(key) && + this.size--; +}; + +KeystoreMap.prototype.clear = +KeystoreSet.prototype.clear = function(){ + this.index = create_object(); + this.refs = []; + this.size = 0; +}; + +// KeystoreMap.prototype.destroy = +// KeystoreSet.prototype.destroy = function(){ +// this.index = null; +// this.refs = null; +// this.proxy = null; +// }; + +KeystoreMap.prototype.values = +KeystoreSet.prototype.values = function*(){ + // alternatively iterate through this.keys[] + //const refs = Object.values(this.index); + for(let i = 0; i < this.refs.length; i++){ + for(let value of this.refs[i].values()){ + yield value; + } + } +}; + +KeystoreMap.prototype.keys = +KeystoreSet.prototype.keys = function*(){ + //const values = Object.values(this.index); + for(let i = 0; i < this.refs.length; i++){ + for(let key of this.refs[i].keys()){ + yield key; + } + } +}; + +KeystoreMap.prototype.entries = +KeystoreSet.prototype.entries = function*(){ + //const values = Object.values(this.index); + for(let i = 0; i < this.refs.length; i++){ + for(let entry of this.refs[i].entries()){ + yield entry; + } + } +}; + +/** + * Linear Congruential Generator (LCG) + * @param str + * @this {KeystoreMap|KeystoreSet} + */ + +function lcg$1(str) { + let range = 2 ** this.bit - 1; + if(typeof str == "number"){ + return str & range; + } + let crc = 0, bit = this.bit + 1; + for(let i = 0; i < str.length; i++) { + crc = (crc * bit ^ str.charCodeAt(i)) & range; + } + // shift Int32 to UInt32 because negative numbers + // extremely slows down key lookup + return this.bit === 32 + ? crc + 2 ** 31 + : crc;// & 0xFFFF; +} + +/** + * @param str + * @this {KeystoreMap|KeystoreSet} + */ + +function lcg64(str) { + let range = BigInt(2) ** /** @type {!BigInt} */ (this.bit) - BigInt(1); + let type = typeof str; + if(type === "bigint"){ + return /** @type {!BigInt} */ (str) & range; + } + if(type === "number"){ + return BigInt(str) & range; + } + let crc = BigInt(0), bit = /** @type {!BigInt} */ (this.bit) + BigInt(1); + for(let i = 0; i < str.length; i++){ + crc = (crc * bit ^ BigInt(str.charCodeAt(i))) & range; + } + return crc;// & 0xFFFFFFFFFFFFFFFF; +} + +// TODO return promises instead of inner await + + +function async(callback, self, field, key, index_doc, index, data, on_done){ + + //setTimeout(function(){ + + const res = callback(field ? field + "." + key : key, JSON.stringify(data)); + + // await isn't supported by ES5 + + if(res && res["then"]){ + + res["then"](function(){ + + self.export(callback, self, field, index_doc, index + 1, on_done); + }); + } + else { + + self.export(callback, self, field, index_doc, index + 1, on_done); + } + //}); +} + +/** + * @param callback + * @param self + * @param field + * @param index_doc + * @param index + * @param on_done + * @this {Index|Document} + */ + +function exportIndex(callback, self, field, index_doc, index, on_done){ + + let return_value = true; + if (typeof on_done === 'undefined') { + return_value = new Promise((resolve) => { + on_done = resolve; + }); + } + + let key, data; + + switch(index || (index = 0)){ + + case 0: + + key = "reg"; + + // fastupdate isn't supported by export + + if(this.fastupdate){ + + data = create_object(); + + for(let key of this.reg.keys()){ + + data[key] = 1; + } + } + else { + + data = this.reg; + } + + break; + + case 1: + + key = "cfg"; + data = { + "doc": 0, + "opt": this.optimize ? 1 : 0 + }; + + break; + + case 2: + + key = "map"; + data = this.map; + break; + + case 3: + + key = "ctx"; + data = this.ctx; + break; + + default: + + if (typeof field === 'undefined' && on_done) { + + on_done(); + } + + return; + } + + async(callback, self || this, field, key, index_doc, index, data, on_done); + + return return_value; +} + +/** + * @this Index + */ + +function importIndex(key, data){ + + if(!data){ + + return; + } + + if(is_string(data)){ + + data = JSON.parse(data); + } + + switch(key){ + + case "cfg": + + this.optimize = !!data["opt"]; + break; + + case "reg": + + // fastupdate isn't supported by import + + this.fastupdate = false; + this.reg = data; + break; + + case "map": + + this.map = data; + break; + + case "ctx": + + this.ctx = data; + break; + } +} + +/** + * @this Document + */ + +function exportDocument(callback, self, field, index_doc, index, on_done){ + + let return_value; + if (typeof on_done === 'undefined') { + return_value = new Promise((resolve) => { + on_done = resolve; + }); + } + + index || (index = 0); + index_doc || (index_doc = 0); + + if(index_doc < this.field.length){ + + const field = this.field[index_doc]; + const idx = this.index[field]; + + self = this; + + //setTimeout(function(){ + + if(!idx.export(callback, self, index ? field/*.replace(":", "-")*/ : "", index_doc, index++, on_done)){ + + index_doc++; + index = 1; + + self.export(callback, self, field, index_doc, index, on_done); + } + //}); + } + else { + + let key, data; + + switch(index){ + + case 1: + + key = "tag"; + data = this.tagindex; + field = null; + break; + + case 2: + + key = "store"; + data = this.store; + field = null; + break; + + // case 3: + // + // key = "reg"; + // data = this.register; + // break; + + default: + + on_done(); + return; + } + + async(callback, this, field, key, index_doc, index, data, on_done); + } + + return return_value +} + +/** + * @this Document + */ + +function importDocument(key, data){ + + if(!data){ + + return; + } + + if(is_string(data)){ + + data = JSON.parse(data); + } + + switch(key){ + + case "tag": + + this.tagindex = data; + break; + + case "reg": + + // fastupdate isn't supported by import + + this.fastupdate = false; + this.reg = data; + + for(let i = 0, index; i < this.field.length; i++){ + + index = this.index[this.field[i]]; + index.reg = data; + index.fastupdate = false; + } + + break; + + case "store": + + this.store = data; + break; + + default: + + key = key.split("."); + const field = key[0]; + key = key[1]; + + if(field && key){ + + this.index[field].import(key, data); + } + } +} + +// COMPILER BLOCK --> + +/** + * @enum {Object} + * @const + */ + +const presets = { + + "memory": { + resolution: 1 + }, + + "performance": { + resolution: 6, + fastupdate: true, + context: { + depth: 1, + resolution: 3 + } + }, + + "match": { + tokenize: "forward" + }, + + "score": { + resolution: 9, + context: { + depth: 2, + resolution: 9 + } + } +}; + +/** + * + * @param {!IndexOptions|string} options + * @return {IndexOptions} + */ + +function apply_preset(options){ + + const preset = is_string(options) + ? options + : options["preset"]; + + if(preset){ + if(!presets[preset]){ + console.warn("Preset not found: " + preset); + } + options = Object.assign({}, presets[preset], /** @type {Object} */ (options)); + } + + return options; +} + +function apply_async(prototype){ + register$1.call(prototype, "add"); + register$1.call(prototype, "append"); + register$1.call(prototype, "search"); + register$1.call(prototype, "update"); + register$1.call(prototype, "remove"); +} + +// let cycle; +// let budget = 0; +// +// function tick(resolve){ +// cycle = null; +// budget = 0; +// resolve(); +// } + +/** + * @param {!string} key + * @this {Index|Document} + */ + +function register$1(key){ + this[key + "Async"] = function(){ + + // // prevent stack overflow of adding too much tasks to the same event loop + // // actually limit stack to 1,000,000 tasks every ~4ms + // cycle || ( + // cycle = new Promise(resolve => setTimeout(tick, 0, resolve)) + // ); + // + // // apply different performance budgets + // if(key === "update" || key === "remove" && this.fastupdate === false){ + // budget += 1000 * this.resolution; + // if(this.depth) + // budget += 1000 * this.resolution_ctx; + // } + // else if(key === "search"){ + // budget++; + // } + // else{ + // budget += 20 * this.resolution; + // if(this.depth) + // budget += 20 * this.resolution_ctx; + // } + // + // // wait for the event loop cycle + // if(budget >= 1e6){ + // await cycle; + // } + + const args = /*[].slice.call*/(arguments); + const arg = args[args.length - 1]; + let callback; + + if(typeof arg === "function"){ + callback = arg; + delete args[args.length - 1]; + } + + this.async = true; + const res = this[key].apply(this, args); + this.async = false; + res.then ? res.then(callback) + : callback(res); + return res; + }; +} + +const data = create_object(); + +/** + * @param {!string} name + */ + +function tick(name){ + + /** @type {!Object} */ + const profiler = data["profiler"] || (data["profiler"] = {}); + + profiler[name] || (profiler[name] = 0); + profiler[name]++; +} + +// COMPILER BLOCK --> +// <-- COMPILER BLOCK + +let table;// = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; +let timer; +const cache = new Map(); + +function toRadix(number, radix = 255) { + + if(!table){ + table = new Array(radix); + // the char code 0 could be a special marker + for(let i = 0; i < radix; i++) table[i] = i + 1; + table = String.fromCharCode.apply(null, table); + } + + let rixit; + let residual = number; + let result = ""; + + while(true){ + rixit = residual % radix; + result = table.charAt(rixit) + result; + residual = residual / radix | 0; + if(!residual) + break; + } + + return result; +} + +function default_compress(str){ + + { + if(timer){ + if(cache.has(str)){ + return cache.get(str); + } + } + else { + timer = setTimeout(clear); + } + } + + /* 2 ** ((level + 1.5) * 1.6 | 0) */ + + const result = toRadix( + typeof str == "number" + ? str + : lcg(str) + ); + + { + cache.size > 2e5 && cache.clear(); + cache.set(str, result); + } + + return result; +} + +function lcg(str) { + let range = 2 ** 32 - 1; + if(typeof str == "number"){ + return str & range; + } + let crc = 0, bit = 32 + 1; + for(let i = 0; i < str.length; i++) { + crc = (crc * bit ^ str.charCodeAt(i)) & range; + } + // shift up from Int32 to UInt32 range 0xFFFFFFFF + return crc + 2 ** 31; +} + +function clear(){ + timer = null; + cache.clear(); +} + +// COMPILER BLOCK --> + +// TODO: +// string + number as text +// boolean, null, undefined as ? + + +/** + * @param {!number|string} id + * @param {!string} content + * @param {boolean=} _append + * @param {boolean=} _skip_update + */ + +Index.prototype.add = function(id, content, _append, _skip_update){ + + if(content && (id || (id === 0))){ + + // todo check skip_update + //_skip_update = false; + + if(!_skip_update && !_append){ + if(this.reg.has(id)){ + return this.update(id, content); + } + } + + // do not force a string as input + // https://github.com/nextapps-de/flexsearch/issues/432 + content = this.encoder.encode(content); + const word_length = content.length; + + if(word_length){ + + // check context dupes to skip all contextual redundancy along a document + + const dupes_ctx = create_object(); + const dupes = create_object(); + const depth = this.depth; + const resolution = this.resolution; + + for(let i = 0; i < word_length; i++){ + + let term = content[this.rtl ? word_length - 1 - i : i]; + let term_length = term.length; + + // skip dupes will break the context chain + + if(term_length /*&& (term_length >= this.minlength)*/ && (depth || !dupes[term])){ + + let score = this.score + ? this.score(content, term, i, null, 0) + : get_score(resolution, word_length, i); + let token = ""; + + switch(this.tokenize){ + + case "full": + if(term_length > 2){ + for(let x = 0; x < term_length; x++){ + for(let y = term_length; y > x; y--){ + + //if((y - x) >= this.minlength){ + token = term.substring(x, y); + const partial_score = this.score + ? this.score(content, term, i, token, x) + : get_score(resolution, word_length, i, term_length, x); + this.push_index(dupes, token, partial_score, id, _append); + //} + } + } + break; + } + // fallthrough to next case when term length < 3 + case "reverse": + // skip last round (this token exist already in "forward") + if(term_length > 1){ + for(let x = term_length - 1; x > 0; x--){ + token = term[x] + token; + //if(token.length >= this.minlength){ + const partial_score = this.score + ? this.score(content, term, i, token, x) + : get_score(resolution, word_length, i, term_length, x); + this.push_index(dupes, token, partial_score, id, _append); + //} + } + token = ""; + } + + // fallthrough to next case to apply forward also + case "forward": + if(term_length > 1){ + for(let x = 0; x < term_length; x++){ + token += term[x]; + //if(token.length >= this.minlength){ + this.push_index(dupes, token, score, id, _append); + //} + } + break; + } + + // fallthrough to next case when token has a length of 1 + default: + // case "strict": + + // todo move boost to search + // if(this.boost){ + // score = Math.min((score / this.boost(content, term, i)) | 0, resolution - 1); + // } + + this.push_index(dupes, term, score, id, _append); + + // context is just supported by tokenizer "strict" + if(depth){ + + if((word_length > 1) && (i < (word_length - 1))){ + + // check inner dupes to skip repeating words in the current context + const dupes_inner = create_object(); + const resolution = this.resolution_ctx; + const keyword = term; + const size = Math.min(depth + 1, word_length - i); + + dupes_inner[keyword] = 1; + + for(let x = 1; x < size; x++){ + + term = content[this.rtl ? word_length - 1 - i - x : i + x]; + + if(term /*&& (term.length >= this.minlength)*/ && !dupes_inner[term]){ + + dupes_inner[term] = 1; + + const context_score = this.score + ? this.score(content, keyword, i, term, x) + : get_score(resolution + ((word_length / 2) > resolution ? 0 : 1), word_length, i, size - 1, x - 1); + const swap = this.bidirectional && (term > keyword); + this.push_index(dupes_ctx, swap ? keyword : term, context_score, id, _append, swap ? term : keyword); + } + } + } + } + } + } + } + + this.fastupdate || this.reg.add(id); + } + else { + content = ""; + } + } + + if(this.db){ + // when the term has no valid content (e.g. empty), + // then it was not added to the ID registry for removal + content || this.commit_task.push({ "del": id }); + this.commit_auto && autoCommit(this); + } + + return this; +}; + +/** + * @private + * @param dupes + * @param term + * @param score + * @param id + * @param {boolean=} append + * @param {string=} keyword + */ + +Index.prototype.push_index = function(dupes, term, score, id, append, keyword){ + + let arr = keyword ? this.ctx : this.map; + let tmp; + + if(!dupes[term] || !keyword || !(tmp = dupes[term])[keyword]){ + + if(keyword){ + + dupes = tmp || (dupes[term] = create_object()); + dupes[keyword] = 1; + + if(this.compress){ + keyword = default_compress(keyword); + } + + tmp = arr.get(keyword); + tmp ? arr = tmp + : arr.set(keyword, arr = new Map()); + } + else { + + dupes[term] = 1; + } + + if(this.compress){ + term = default_compress(term); + } + + tmp = arr.get(term); + tmp ? arr = tmp : arr.set(term, arr = tmp = []); + // the ID array will be upgraded dynamically + arr = arr[score] || (arr[score] = []); + + if(!append || !arr.includes(id)){ + + // auto-upgrade to keystore array if max size exceeded + { + if(arr.length === 2**31-1 /*|| !(arr instanceof KeystoreArray)*/){ + const keystore = new KeystoreArray(arr); + if(this.fastupdate){ + for(let value of this.reg.values()){ + if(value.includes(arr)){ + value[value.indexOf(arr)] = keystore; + } + } + } + tmp[score] = arr = keystore; + } + } + + arr.push(id); + + // add a reference to the register for fast updates + if(this.fastupdate){ + const tmp = this.reg.get(id); + tmp ? tmp.push(arr) + : this.reg.set(id, [arr]); + } + } + } +}; + +/** + * @param {number} resolution + * @param {number} length + * @param {number} i + * @param {number=} term_length + * @param {number=} x + * @returns {number} + */ + +function get_score(resolution, length, i, term_length, x){ + + // console.log("resolution", resolution); + // console.log("length", length); + // console.log("term_length", term_length); + // console.log("i", i); + // console.log("x", x); + // console.log((resolution - 1) / (length + (term_length || 0)) * (i + (x || 0)) + 1); + + // the first resolution slot is reserved for the best match, + // when a query matches the first word(s). + + // also to stretch score to the whole range of resolution, the + // calculation is shift by one and cut the floating point. + // this needs the resolution "1" to be handled additionally. + + // do not stretch the resolution more than the term length will + // improve performance and memory, also it improves scoring in + // most cases between a short document and a long document + + return i && (resolution > 1) ? ( + + (length + (term_length || 0)) <= resolution ? + + i + (x || 0) + : + ((resolution - 1) / (length + (term_length || 0)) * (i + (x || 0)) + 1) | 0 + ): + 0; +} + +/* + from -> res[score][id] + to -> [id] +*/ + +/** + * Aggregate the union of a single raw result + * @param {!Array} result + * @param {!number} limit + * @param {number=} offset + * @param {boolean=} enrich + * @return Array + */ + +function resolve_default(result, limit, offset, enrich){ + + // fast path: when there is just one slot in the result + if(result.length === 1){ + result = result[0]; + result = offset || (result.length > limit) + ? (limit + ? result.slice(offset, offset + limit) + : result.slice(offset) + ) + : result; + return enrich + ? enrich_result(result) + : result; + } + + // this is an optimized workaround instead of + // just doing result = concat(result) + + let final = []; + + for(let i = 0, arr, len; i < result.length; i++){ + if(!(arr = result[i]) || !(len = arr.length)) continue; + + if(offset){ + // forward offset pointer + if(offset >= len){ + offset -= len; + continue; + } + // apply offset pointer when length differs + if(offset < len){ + arr = limit + ? arr.slice(offset, offset + limit) + : arr.slice(offset); + len = arr.length; + offset = 0; + } + } + + if(!final.length){ + // fast path: when limit was reached in first slot + if(len >= limit){ + if(len > limit){ + arr = arr.slice(0, limit); + } + return enrich + ? enrich_result(arr) + : arr; + } + final = [arr]; + } + else { + if(len > limit){ + arr = arr.slice(0, limit); + len = arr.length; + } + final.push(arr); + } + + // reduce limit + limit -= len; + + // todo remove + // if(limit < 0){ + // throw new Error("Impl.Error"); + // } + + // break if limit was reached + if(!limit){ + break; + } + } + + // todo remove + if(!final.length){ + //throw new Error("No results found"); + return final; + } + + final = final.length > 1 + ? concat(final) + : final[0]; + + return enrich + ? enrich_result(final) + : final; +} + +function enrich_result(ids){ + for(let i = 0; i < ids.length; i++){ + ids[i] = { + score: i, + id: ids[i] + }; + } + return ids; +} + +// import xor from "./xor.js"; +// import and from "./and.js"; +// import not from "./not.js"; + +Resolver.prototype.or = function(){ + + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.or.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.or.apply(this, first_argument); + } + } + + // for(let i = 0; i < args.length; i++){ + // if(args[i].result instanceof Promise){ + // return; + // } + // } + + // if(args.length < 2){ + // if(first_argument.index){ + // first_argument.resolve = false; + // const result = first_argument.index.search(first_argument); + // if(result instanceof Promise){ + // result.then(function(result){ + // final = self.result.concat(result); + // self.result = resolver(final, limit, offset, enrich, !resolve); + // return resolve ? self.result : self; + // }); + // } + // else{ + // final = this.result.concat(result); + // this.result = resolver(final, limit, offset, enrich, !resolve); + // return resolve ? this.result : this; + // } + // } + // } + + let final = []; + let promises = []; + let limit = 0, offset = 0, enrich, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.and){ + result = this.and(query.and); + } + else if(query.xor){ + result = this.xor(query.xor); + } + else if(query.not){ + result = this.not(query.not); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + enrich = query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + self.result.length && (final = [self.result].concat(final)); + self.result = resolver(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? self.result : self; + }); + } + + this.result.length && (final = [this.result].concat(final)); + this.result = resolver(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? this.result : this; +}; + +/** + * Aggregate the union of N raw results + * @param result + * @param limit + * @param offset + * @param enrich + * @param resolve + * @param boost + * @return {Array} + */ + +function resolver(result, limit, offset, enrich, resolve, boost){ + + if(!result.length){ + // todo remove + //console.log("Empty Result") + return result; + } + + if(typeof limit === "object"){ + offset = limit.offset || 0; + enrich = limit.enrich || false; + limit = limit.limit || 0; + } + + if(result.length < 2){ + // todo remove + //console.log("Single Result") + if(resolve){ + return resolve_default(result[0], limit, offset, enrich); + } + else { + return result[0]; + } + } + + let final = []; + let count = 0; + let dupe = create_object(); + let maxres = get_max_len(result); + + for(let j = 0, ids; j < maxres; j++){ + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res) continue; + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + if(!dupe[id]){ + dupe[id] = 1; + if(offset){ + offset--; + continue; + } + if(resolve){ + final.push(id); + } + else { + // shift resolution by boost (inverse) + const index = j + (i ? boost : 0); + final[index] || (final[index] = []); + final[index].push(id); + } + if(limit && ++count === limit){ + //this.boost = 0; + return final; + } + } + } + } + } + + //this.boost = 0; + return final; +} + +// import xor from "./xor.js"; +// import or from "./or.js"; +// import not from "./not.js"; + +Resolver.prototype.and = function(){ + if(this.result.length){ + + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.and.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.and.apply(this, first_argument); + } + } + + // for(let i = 0; i < args.length; i++){ + // if(args[i].result instanceof Promise){ + // return; + // } + // } + + // if(args.length < 2){ + // if(first_argument.index){ + // first_argument.resolve = false; + // return first_argument.index.search(first_argument); + // } + // } + + let final = []; + let promises = []; + let limit = 0, offset = 0, enrich, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.or){ + result = this.or(query.or); + } + else if(query.xor){ + result = this.xor(query.xor); + } + else if(query.not){ + result = this.not(query.not); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + enrich = query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + final = [self.result].concat(final); + self.result = intersect$1(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? self.result : self; + }); + } + + final = [this.result].concat(final); + this.result = intersect$1(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? this.result : this; + } + return this; +}; + +/** + * Aggregate the intersection of N raw results + * @param result + * @param limit + * @param offset + * @param enrich + * @param resolve + * @param boost + * @return {Array} + */ + +function intersect$1(result, limit, offset, enrich, resolve, boost){ + + // if(!result.length){ + // // todo remove + // console.log("Empty Result") + // return result; + // } + + if(result.length < 2){ + // todo remove + //console.log("Single Result") + return []; + // if(resolve){ + // return default_resolver(result[0], limit, offset, enrich); + // } + // else{ + // return result[0]; + // } + } + + let final = []; + let count = 0; + + // fast path single slot + // if(result.length < 2){ + // if(limit || offset){ + // let res = result[0]; + // for(let j = 0, ids; j < res.length; j++){ + // ids = res[j]; + // if(!ids) continue; + // for(let k = 0, id; k < ids.length; k++){ + // id = ids[k]; + // if(offset){ + // offset--; + // continue; + // } + // if(resolve){ + // final.push(id); + // } + // else{ + // final[j + this.boost] || (final[j + this.boost] = []); + // final[j + this.boost].push(id); + // } + // if(limit && ++count === limit){ + // this.boost = 0; + // return final; + // } + // } + // } + // } + // this.boost = 0; + // return result[0]; + // } + + let contain = create_object(); + let maxres = get_max_len(result); + if(!maxres) return final; + + // for(let j = 0, ids, res = result[0]; j < res.length; j++){ + // ids = res[j]; + // for(let k = 0; k < ids.length; k++){ + // contain[ids[k]] = 1; + // } + // } + + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res || !res.length) return []; + let contain_new = create_object(); + let match = 0; + let last_round = i === result.length - 1; + + for(let j = 0, ids; j < maxres; j++){ + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id, min; k < ids.length; k++){ + id = ids[k]; + // fill in first round + if(!i){ + // shift resolution +1 + // shift resolution by boost (inverse) + contain_new[id] = j + 1 + (i ? boost : 0); + match = 1; + } + // result in last round + else if(last_round){ + if((min = contain[id])){ + match = 1; + //if(!contain_new[id]){ + if(offset){ + offset--; + continue; + } + if(resolve){ + final.push(id); + } + else { + // reduce resolution -1 + min--; + if(j < min) min = j; + final[min] || (final[min] = []); + final[min].push(id); + } + if(limit && ++count === limit){ + //this.boost = 0; + return final; + } + // shift resolution +1 + //contain_new[id] = min + 1; + //} + } + } + // check for intersection + else if((min = contain[id])){ + // shift resolution +1 + if(j + 1 < min) min = j + 1; + contain_new[id] = min; + match = 1; + } + } + } + + if(!match){ + //this.boost = 0; + return []; + } + + contain = contain_new; + } + + //this.boost = 0; + return final; +} + +// import or from "./or.js"; +// import and from "./and.js"; +// import not from "./not.js"; + +Resolver.prototype.xor = function(){ + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.xor.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.xor.apply(this, first_argument); + } + } + + let final = []; + let promises = []; + let limit = 0, offset = 0, enrich, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.or){ + result = this.or(query.or); + } + else if(query.and){ + result = this.and(query.and); + } + else if(query.not){ + result = this.not(query.not); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + enrich = query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + self.result.length && (final = [self.result].concat(final)); + self.result = exclusive(final, limit, offset, enrich, !resolve, self.boostval); + return resolve ? self.result : self; + }); + } + + this.result.length && (final = [this.result].concat(final)); + this.result = exclusive(final, limit, offset, enrich, !resolve, self.boostval); + return resolve ? this.result : this; +}; + +/** + * @param result + * @param limit + * @param offset + * @param enrich + * @param resolve + * @param boost + * @return {Array} + */ + +function exclusive(result, limit, offset, enrich, resolve, boost){ + + if(!result.length){ + // todo remove + //console.log("Empty Result") + return result; + } + + if(result.length < 2){ + // todo remove + //console.log("Single Result") + if(resolve){ + return resolve_default(result[0], limit, offset, enrich); + } + else { + return result[0]; + } + } + + const final = []; + const check = create_object(); + + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res) continue; + + for(let j = 0, ids; j < res.length; j++){ + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + check[id] + ? check[id]++ + : check[id] = 1; + } + } + } + + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res) continue; + + for(let j = 0, ids; j < res.length; j++){ + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + if(check[id] === 1){ + if(resolve){ + final.push(id); + } + else { + // shift resolution by boost (inverse) + const index = j + (i ? boost : 0); + final[index] || (final[index] = []); + final[index].push(id); + } + } + } + } + } + + //this.boost = 0; + return final; +} + +// import or from "./or.js"; +// import and from "./and.js"; +// import xor from "./xor.js"; + +Resolver.prototype.not = function(){ + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.not.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.not.apply(this, first_argument); + } + } + + let final = []; + let promises = []; + let limit = 0, offset = 0, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.or){ + result = this.or(query.or); + } + else if(query.and){ + result = this.and(query.and); + } + else if(query.xor){ + result = this.xor(query.xor); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + self.result = exclusion.call(self, final, limit, offset, resolve); + return resolve ? self.result : self; + }); + } + + this.result = exclusion.call(this, final, limit, offset, resolve); + return resolve ? this.result : this; +}; + +/** + * @param result + * @param limit + * @param offset + * @param resolve + * @this Resolver + * @return {Array} + */ + +function exclusion(result, limit, offset, resolve){ + + if(!result.length){ + return this.result; + } + + const final = []; + const exclude = new Set(result.flat().flat()); + + for(let j = 0, ids; j < this.result.length; j++){ + ids = this.result[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + if(!exclude.has(id)){ + if(resolve){ + final.push(id); + } + else { + final[j] || (final[j] = []); + final[j].push(id); + } + } + } + } + + return final; +} + +/** + * @param result + * @constructor + */ + +function Resolver(result){ + if(!this){ + return new Resolver(result); + } + if(result && result.index){ + result.resolve = false; + this.index = result.index; + return result.index.search(result); + } + if(result.constructor === Resolver){ + // todo test this branch + //console.log("Resolver Loopback") + return result; + } + this.index = null; + this.result = result || []; + this.boostval = 0; +} + +Resolver.prototype.limit = function(limit){ + if(this.result.length){ + const final = []; + let count = 0; + for(let j = 0, ids; j < this.result.length; j++){ + ids = this.result[j]; + if(ids.length + count < limit){ + final[j] = ids; + count += ids.length; + } + else { + final[j] = ids.slice(0, limit - count); + this.result = final; + break; + } + } + } + return this; +}; + +Resolver.prototype.offset = function(offset){ + if(this.result.length){ + const final = []; + let count = 0; + for(let j = 0, ids; j < this.result.length; j++){ + ids = this.result[j]; + if(ids.length + count < offset){ + count += ids.length; + } + else { + final[j] = ids.slice(offset - count); + count = offset; + } + } + this.result = final; + } + return this; +}; + +Resolver.prototype.boost = function(boost){ + this.boostval += boost; + return this; +}; + +Resolver.prototype.resolve = function(limit, offset, enrich){ + set_resolve(1); + const result = this.result; + this.index = null; + this.result = null; + + if(result.length){ + if(typeof limit === "object"){ + enrich = limit.enrich; + offset = limit.offset; + limit = limit.limit; + } + return resolve_default(result, limit || 100, offset, enrich); + } + + return result; +}; + +/* + + from -> result[ + res[score][id], + res[score][id], + ] + + to -> [id] + + */ + +/** + * Implementation based on Object[key] provides better suggestions + * capabilities and has less performance scaling issues on large indexes. + * + * @param arrays + * @param limit + * @param offset + * @param {boolean|Array=} suggest + * @param {boolean=} resolve + * @returns {Array} + */ + +function intersect(arrays, limit, offset, suggest, resolve) { + + const length = arrays.length; + + // todo remove + // if(length < 2){ + // throw new Error("Not optimized intersect"); + // } + + let result = []; + let size = 0; + let check; + let check_suggest; + let check_new; + let res_arr; + + if(suggest){ + suggest = []; + } + + // 1. a reversed order prioritize the order of words from a query + // because it ends with the first term. + // 2. process terms in reversed order often has advantage for + // the fast path "end reached". + + // alternatively the results could be sorted by length up + //arrays.sort(sort_by_length_up); + + // todo the outer loop should be the res array instead of term array, + // this isn't just simple because the intersection calculation needs to reflect this + //const maxlen = get_max_len(arrays); + + for(let x = length - 1, found; x >= 0; x--){ + //for(let x = 0, found; x < length; x++){ + + res_arr = arrays[x]; + check_new = create_object(); + found = !check; + + // process relevance in forward order (direction is + // important for adding IDs during the last round) + + for(let y = 0, ids; y < res_arr.length; y++){ + + ids = res_arr[y]; + if(!ids || !ids.length) continue; + + for(let z = 0, id; z < ids.length; z++){ + + id = ids[z]; + + // check exists starting from the 2nd slot + if(check){ + if(check[id]){ + + // check if in last round + if(!x){ + //if(x === length - 1){ + + if(offset){ + offset--; + } + else { + + result[size++] = id; + + if(size === limit){ + // fast path "end reached" + return result /*resolve === false + ? { result, suggest } + :*/ + } + } + } + + if(x || suggest){ + //if((x < length - 1) || suggest){ + check_new[id] = 1; + } + + found = true; + } + + if(suggest){ + + if(!check_suggest[id]){ + check_suggest[id] = 1; + const arr = suggest[y] || (suggest[y] = []); + arr.push(id); + } + + // OLD: + // + // check_idx = (check_suggest[id] || 0) + 1; + // check_suggest[id] = check_idx; + // + // // do not adding IDs which are already included in the result (saves one loop) + // // the first intersection match has the check index 2, so shift by -2 + // + // if(check_idx < length){ + // + // const tmp = suggest[check_idx - 2] || (suggest[check_idx - 2] = []); + // tmp[tmp.length] = id; + // } + } + } + else { + + // pre-fill in first round + check_new[id] = 1; + } + } + } + + if(suggest){ + + // re-use the first pre-filled check for suggestions + check || (check_suggest = check_new); + } + else if(!found){ + + return []; + } + + check = check_new; + } + + // return intermediate result + // if(resolve === false){ + // return { result, suggest }; + // } + + if(suggest){ + + // needs to iterate in reverse direction + for(let x = suggest.length - 1, ids, len; x >= 0; x--){ + + ids = suggest[x]; + len = ids.length; + + for(let y = 0, id; y < len; y++){ + + id = ids[y]; + + if(!check[id]){ + + if(offset){ + offset--; + } + else { + + result[size++] = id; + + if(size === limit){ + // fast path "end reached" + return result; + } + } + + check[id] = 1; + } + } + } + } + + return result; +} + +/** + * @param mandatory + * @param arrays + * @returns {Array} + */ + +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.push(id); + } + } + } + } + + return result; +} + + +/** + * Implementation based on Array.includes() provides better performance, + * but it needs at least one word in the query which is less frequent. + * Also on large indexes it does not scale well performance-wise. + * This strategy also lacks of suggestion capabilities (matching & sorting). + * + * @param arrays + * @param limit + * @param offset + * @param {boolean|Array=} suggest + * @returns {Array} + */ + +// export function intersect(arrays, limit, offset, suggest) { +// +// const length = arrays.length; +// let result = []; +// let check; +// +// // determine shortest array and collect results +// // from the sparse relevance arrays +// +// let smallest_size; +// let smallest_arr; +// let smallest_index; +// +// for(let x = 0; x < length; x++){ +// +// const arr = arrays[x]; +// const len = arr.length; +// +// let size = 0; +// +// for(let y = 0, tmp; y < len; y++){ +// +// tmp = arr[y]; +// +// if(tmp){ +// +// size += tmp.length; +// } +// } +// +// if(!smallest_size || (size < smallest_size)){ +// +// smallest_size = size; +// smallest_arr = arr; +// smallest_index = x; +// } +// } +// +// smallest_arr = smallest_arr.length === 1 ? +// +// smallest_arr[0] +// : +// concat(smallest_arr); +// +// if(suggest){ +// +// suggest = [smallest_arr]; +// check = create_object(); +// } +// +// let size = 0; +// let steps = 0; +// +// // process terms in reversed order often results in better performance. +// // the outer loop must be the words array, using the +// // smallest array here disables the "fast fail" optimization. +// +// for(let x = length - 1; x >= 0; x--){ +// +// if(x !== smallest_index){ +// +// steps++; +// +// const word_arr = arrays[x]; +// const word_arr_len = word_arr.length; +// const new_arr = []; +// +// let count = 0; +// +// for(let z = 0, id; z < smallest_arr.length; z++){ +// +// id = smallest_arr[z]; +// +// let found; +// +// // process relevance in forward order (direction is +// // important for adding IDs during the last round) +// +// for(let y = 0; y < word_arr_len; y++){ +// +// const arr = word_arr[y]; +// +// if(arr.length){ +// +// found = arr.includes(id); +// +// if(found){ +// +// // check if in last round +// +// if(steps === length - 1){ +// +// if(offset){ +// +// offset--; +// } +// else{ +// +// result[size++] = id; +// +// if(size === limit){ +// +// // fast path "end reached" +// +// return result; +// } +// } +// +// if(suggest){ +// +// check[id] = 1; +// } +// } +// +// break; +// } +// } +// } +// +// if(found){ +// +// new_arr[count++] = id; +// } +// } +// +// if(suggest){ +// +// suggest[steps] = new_arr; +// } +// else if(!count){ +// +// return []; +// } +// +// smallest_arr = new_arr; +// } +// } +// +// if(suggest){ +// +// // needs to iterate in reverse direction +// +// for(let x = suggest.length - 1, arr, len; x >= 0; x--){ +// +// arr = suggest[x]; +// len = arr && arr.length; +// +// if(len){ +// +// for(let y = 0, id; y < len; y++){ +// +// id = arr[y]; +// +// if(!check[id]){ +// +// check[id] = 1; +// +// if(offset){ +// +// offset--; +// } +// else{ +// +// result[size++] = id; +// +// if(size === limit){ +// +// // fast path "end reached" +// +// return result; +// } +// } +// } +// } +// } +// } +// } +// +// return result; +// } + +// COMPILER BLOCK --> + +let global_resolve = 1; +function set_resolve(resolve){ + global_resolve = resolve; +} + +/** + * @param {string|SearchOptions} query + * @param {number|SearchOptions=} limit + * @param {SearchOptions=} options + * @returns {Array|Resolver|Promise} + */ + +Index.prototype.search = function(query, limit, options){ + + if(!options){ + if(!limit && is_object(query)){ + options = /** @type {!SearchOptions} */ (query); + query = ""; + } + else if(is_object(limit)){ + options = /** @type {!SearchOptions} */ (limit); + limit = 0; + } + } + + let result = []; + let length; + let context, suggest, offset = 0, resolve, enrich, tag; + + if(options){ + query = options.query || query; + limit = options.limit || limit; + offset = options.offset || 0; + context = options.context; + suggest = options.suggest; + resolve = (global_resolve && options.resolve !== false); + resolve || (global_resolve = 0); + enrich = resolve && options.enrich; + tag = this.db && options.tag; + } + else { + resolve = this.resolve || global_resolve; + } + + // todo: term deduplication during encoding when context is disabled + + // do not force a string as input + // https://github.com/nextapps-de/flexsearch/issues/432 + query = /** @type {Array} */ (this.encoder.encode(query)); + length = query.length; + limit || !resolve || (limit = 100); + + // fast path single term + if(length === 1){ + return single_term_query.call( + this, + query[0], // term + "", // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + // TODO: dedupe terms within encoder? + // TODO: deduplication will break the context chain + + context = this.depth && context !== false; + + // fast path single context + if(length === 2 && context && !suggest){ + return single_term_query.call( + this, + query[0], // term + query[1], // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + let maxlength = 0; + let minlength = 0; + + if(length > 1){ + + // term deduplication will break the context chain + // todo add context to dupe check + const dupes = create_object(); + const query_new = []; + + // if(context){ + // keyword = query[0]; + // dupes[keyword] = 1; + // query_new.push(keyword); + // maxlength = minlength = keyword.length; + // i = 1; + // } + + for(let i = 0, term; i < length; i++){ + + term = query[i]; + + if(term && !dupes[term]){ + + // todo add keyword check + // this fast path can't apply to persistent indexes + if(!suggest && !(this.db) && !this.get_array(term/*, keyword*/)){ + + // fast path "not found" + return resolve + ? result + : new Resolver(result); + } + else { + + query_new.push(term); + dupes[term] = 1; + } + + const term_length = term.length; + maxlength = Math.max(maxlength, term_length); + minlength = minlength ? Math.min(minlength, term_length) : term_length; + } + // else if(term && (!this.depth || context === false)){ + // query_new.push(term); + // } + } + + query = query_new; + length = query.length; + } + + // the term length could be changed after deduplication + + if(!length){ + return resolve + ? result + : new Resolver(result); + } + + let index = 0, keyword; + + // fast path single term + if(length === 1){ + return single_term_query.call( + this, + query[0], // term + "", // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + // fast path single context + if(length === 2 && context && !suggest){ + return single_term_query.call( + this, + query[0], // term + query[1], // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + if(length > 1){ + if(context){ + // start with context right away + keyword = query[0]; + index = 1; + } + // todo + else if(maxlength > 9 && (maxlength / minlength) > 3){ + // sorting terms will break the context chain + // bigger terms has less occurrence + // this might also reduce the intersection task + // todo check intersection order + query.sort(sort_by_length_down); + } + } + + if(this.db){ + + if(this.db.search){ + // when the configuration is not supported it returns false + const result = this.db.search(this, query, limit, offset, suggest, resolve, enrich, tag); + if(result !== false) return result; + } + + const self = this; + return (async function(){ + + for(let arr, term; index < length; index++){ + + term = query[index]; + + if(keyword){ + + arr = await self.get_array(term, keyword); + arr = add_result( + arr, + result, + suggest, + self.resolution_ctx, + /** @type {!number} */ (limit), + offset, + length === 2 + /*, term, keyword*/ + ); + + // the context is a moving window where the keyword is going forward like a cursor + // 1. when suggestion enabled just forward keyword if term was found + // 2. as long as the result is empty forward the pointer also + if(!suggest || (arr !== false) || !result.length){ + keyword = term; + } + } + else { + + arr = await self.get_array(term); + arr = add_result( + arr, + result, + suggest, + self.resolution, + /** @type {!number} */ (limit), + offset, + length === 1 + /*, term*/ + ); + } + + // limit reached + if(arr){ + return arr; + } + + // apply suggestions on last loop + if(suggest && (index === length - 1)){ + let length = result.length; + if(!length){ + // fallback to non-contextual search when no result was found + if(keyword){ + keyword = ""; + index = -1; + continue; + } + return result; + } + else if(length === 1){ + return resolve + ? resolve_default(result[0], /** @type {number} */ (limit), offset) + : new Resolver(result[0]); + } + } + } + + return resolve + ? intersect(result, /** @type {number} */ (limit), offset, suggest) + : new Resolver(result[0]) + }()); + } + + for(let arr, term; index < length; index++){ + + term = query[index]; + + if(keyword){ + + arr = this.get_array(term, keyword); + arr = /*this.*/add_result( + arr, + result, + suggest, + this.resolution_ctx, + /** @type {!number} */ (limit), + offset, + length === 2 + /*, term, keyword*/ + ); + + // 1. when suggestion enabled just forward keyword if term was found + // 2. as long as the result is empty forward the pointer also + if(!suggest || (arr !== false) || !result.length){ + keyword = term; + } + } + else { + + arr = this.get_array(term); + arr = /*this.*/add_result( + arr, + result, + suggest, + this.resolution, + /** @type {!number} */ (limit), + offset, + length === 1 + /*, term*/ + ); + } + + // limit reached + if(arr){ + return /** @type {Array} */ (arr); + } + + // apply suggestions on last loop + if(suggest && (index === length - 1)){ + const length = result.length; + if(!length){ + // fallback to non-contextual search when no result was found + if(keyword){ + keyword = ""; + index = -1; + continue; + } + return result; + } + else if(length === 1){ + return resolve + ? resolve_default(result[0], limit, offset) + : new Resolver(result[0]); + } + } + } + + return resolve + ? intersect(result, limit, offset, suggest) + : new Resolver(result[0]); +}; + +/** + * @param term + * @param keyword + * @param limit + * @param offset + * @param resolve + * @param enrich + * @param tag + * @this Index + * @return {Array|Resolver} + */ + +function single_term_query(term, keyword, limit, offset, resolve, enrich, tag){ + + const result = this.get_array(term, keyword, limit, offset, resolve, enrich, tag); + + if(this.db){ + return result.then(function(result){ + if(resolve) return result; + return result && result.length + ? (resolve ? resolve_default(result, limit, offset): new Resolver(result)) + : resolve ? [] : new Resolver([]); + }); + } + + return result && result.length + ? (resolve ? resolve_default(result, limit, offset) : new Resolver(result)) + : resolve ? [] : new Resolver([]); +} + +/** + * Returns a 1-dimensional finalized array when the result is done (fast path return), + * returns false when suggestions is enabled and no result was found, + * or returns nothing when a set was pushed successfully to the results + * + * @private + * @param {Array} arr + * @param {Array} result + * @param {Array} suggest + * @param {number} resolution + * @param {number} limit + * @param {number} offset + * @param {boolean} single_term + * @return {Array|boolean|undefined} + */ + +function add_result(arr, result, suggest, resolution, limit, offset, single_term/*, term, keyword*/){ + + let word_arr = []; + //let arr;// = keyword ? this.ctx : this.map; + //arr = this.get_array(term, keyword); + + if(arr){ + + //const resolution = Math.min(arr.length, keyword ? this.resolution_ctx : this.resolution); + // apply reduced resolution for queries + resolution = Math.min(arr.length, resolution); + + for(let x = 0, size = 0, tmp; x < resolution; x++){ + if((tmp = arr[x])){ + + if(offset){ + // apply offset right here on single terms + if(tmp && single_term){ + if(tmp.length <= offset){ + offset -= tmp.length; + tmp = null; + } + else { + tmp = tmp.slice(offset); + offset = 0; + } + } + } + + if(tmp){ + + // keep score (sparse array): + word_arr[x] = tmp; + // simplified score order: + //word_arr.push(tmp); + + if(single_term){ + size += tmp.length; + if(size >= limit){ + // fast path: + // a single term does not need to pre-collect results + break; + } + } + } + } + } + + if(word_arr.length){ + if(single_term){ + // fast path optimization + // offset was already applied at this point + // return an array will stop the query process immediately + return resolve_default(word_arr, limit, 0); + } + + result.push(word_arr); + // return nothing will continue the query + return; + } + } + + // 1. return an empty array will stop the loop + // 2. return a false value to prevent stop when using suggestions + return !suggest && word_arr; +} + +Index.prototype.get_array = function(term, keyword, limit, offset, resolve, enrich, tag){ + + let arr, swap; + + if(keyword){ + swap = this.bidirectional && (term > keyword); + } + + if(this.compress){ + term = default_compress(term); + keyword && (keyword = default_compress(keyword)); + } + + if(this.db){ + return keyword + ? this.db.get( + swap ? keyword : term, // term + swap ? term : keyword, // ctx + limit, + offset, + resolve, + enrich, + tag + ) + : this.db.get( + term, + "", // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + if(keyword){ + // the frequency of the starting letter is slightly less + // on the last half of the alphabet (m-z) in almost every latin language, + // so we sort downwards (https://en.wikipedia.org/wiki/Letter_frequency) + arr = this.ctx.get(swap ? term : keyword); + arr = arr && arr.get(swap ? keyword : term); + } + else { + arr = this.map.get(term); + } + + return arr; +}; + +// COMPILER BLOCK --> + +/** + * @param {boolean=} _skip_deletion + */ + +Index.prototype.remove = function(id, _skip_deletion){ + + const refs = this.reg.size && ( + this.fastupdate + ? this.reg.get(id) + : this.reg.has(id) + ); + + if(refs){ + + if(this.fastupdate){ + + // fast updates did not fully cleanup the key entries + + for(let i = 0, tmp; i < refs.length; i++){ + if((tmp = refs[i])){ + // todo check + //if(tmp.length < 1) throw new Error("invalid length"); + //if(tmp.indexOf(id) < 0) throw new Error("invalid id"); + if(tmp.length < 2){ + tmp.pop(); + } + else { + const index = tmp.indexOf(id); + index === refs.length - 1 + ? tmp.pop() + : tmp.splice(index, 1); + } + } + } + + // todo variation which cleans up, requires to push [ctx, key] instead of arr to the index.reg + // for(let i = 0, arr, term, keyword; i < refs.length; i++){ + // arr = refs[i]; + // if(typeof arr === "string"){ + // arr = this.map.get(term = arr); + // } + // else{ + // arr = this.ctx.get(keyword = arr[0]); + // arr && (arr = arr.get(arr[1])); + // } + // let counter = 0, found; + // if(arr && arr.length){ + // for(let j = 0, tmp; j < arr.length; j++){ + // if((tmp = arr[j])){ + // if(!found && tmp.length){ + // const index = tmp.indexOf(id); + // if(index >= 0){ + // tmp.splice(index, 1); + // // the index [ctx, key]:[res, id] is unique + // found = 1; + // } + // } + // if(tmp.length){ + // counter++; + // if(found){ + // break; + // } + // } + // else{ + // delete arr[j]; + // } + // } + // } + // } + // if(!counter){ + // keyword + // ? this.ctx.delete(keyword) + // : this.map.delete(term); + // } + // } + } + else { + + remove_index(this.map, id/*, this.resolution*/); + this.depth && + remove_index(this.ctx, id/*, this.resolution_ctx*/); + } + + _skip_deletion || this.reg.delete(id); + } + + if(this.db){ + this.commit_task.push({ "del": id }); + this.commit_auto && autoCommit(this); + //return this.db.remove(id); + } + + // the cache could be used outside the InMemory store + if(this.cache){ + this.cache.remove(id); + } + + return this; +}; + +/** + * @param map + * @param id + * @return {number} + */ + +function remove_index(map, id){ + + // a check counter of filled resolution slots + // to prevent removing the field + let count = 0; + + if(is_array(map)){ + for(let x = 0, arr, index; x < map.length; x++){ + if((arr = map[x]) && arr.length){ + index = arr.indexOf(id); + if(index >= 0){ + if(arr.length > 1){ + arr.splice(index, 1); + count++; + } + else { + // remove resolution slot + delete map[x]; + } + // the index key:[res, id] is unique + break; + } + else { + count++; + } + } + } + } + else for(let item of map){ + const key = item[0]; + const value = item[1]; + const tmp = remove_index(value, id); + tmp ? count += tmp + : map.delete(key); + } + + return count; +} + +/**! + * FlexSearch.js + * Author and Copyright: Thomas Wilkerling + * Licence: Apache-2.0 + * Hosted by Nextapps GmbH + * https://github.com/nextapps-de/flexsearch + */ + + +/** + * @constructor + * @param {IndexOptions|string=} options Options or preset as string + * @param {Map|Set|KeystoreSet|KeystoreMap=} _register + */ + +function Index(options$1, _register){ + + if(!this){ + return new Index(options$1); + } + + tick("Index.create"); + + options$1 = options$1 + ? apply_preset(options$1) + : {}; + + /** @type ContextOptions */ + const context = options$1.context || {}; + const encoder = options$1.encode || options$1.encoder || ( + options + ); + /** @type Encoder */ + this.encoder = encoder.encode + ? encoder + : typeof encoder === "object" + ? (new Encoder(encoder) + + ) + : { encode: encoder }; + + { + this.compress = options$1.compress || options$1.compression || false; + } + + let tmp; + this.resolution = options$1.resolution || 9; + this.tokenize = tmp = options$1.tokenize || "strict"; + this.depth = (tmp === "strict" && context.depth) || 0; + this.bidirectional = context.bidirectional !== false; + this.fastupdate = !!options$1.fastupdate; + this.score = options$1.score || null; + + tmp = (options$1.keystore || 0); + tmp && (this.keystore = tmp); + + this.map = tmp && SUPPORT_KEYSTORE ? new KeystoreMap(tmp) : new Map(); + this.ctx = tmp && SUPPORT_KEYSTORE ? new KeystoreMap(tmp) : new Map(); + this.reg = _register || ( + this.fastupdate + ? (tmp && SUPPORT_KEYSTORE ? new KeystoreMap(tmp) : new Map()) + : (tmp && SUPPORT_KEYSTORE ? new KeystoreSet(tmp) : new Set()) + ); + this.resolution_ctx = context.resolution || 1; + this.rtl = (encoder.rtl) || options$1.rtl || false; + + { + this.cache = (tmp = options$1.cache || null) && new CacheClass(tmp); + } + + { + this.resolve = options$1.resolve !== false; + } + + { + if((tmp = options$1.db)){ + this.db = tmp.mount(this); + } + this.commit_auto = options$1.commit !== false; + this.commit_task = []; + this.commit_timer = null; + } +} + +{ + Index.prototype.mount = function(db){ + if(this.commit_timer){ + clearTimeout(this.commit_timer); + this.commit_timer = null; + } + return db.mount(this); + }; + Index.prototype.commit = function(replace, append){ + if(this.commit_timer){ + clearTimeout(this.commit_timer); + this.commit_timer = null; + } + return this.db.commit(this, replace, append); + }; +} + +// if(SUPPORT_RESOLVER){ +// Index.prototype.resolve = function(params){ +// return new Resolver(params); +// }; +// } + +/** + * @param {!Index} self + * @param {boolean=} replace + * @param {boolean=} append + */ + +function autoCommit(self, replace, append){ + if(!self.commit_timer){ + self.commit_timer = setTimeout(function(){ + self.commit_timer = null; + self.db.commit(self, replace, append); + }, 0); + } +} + +Index.prototype.clear = function(){ + + //this.map = new Map(); + //this.ctx = new Map(); + //this.reg = this.fastupdate ? new Map() : new Set(); + this.map.clear(); + this.ctx.clear(); + this.reg.clear(); + + { + this.cache && + this.cache.clear(); + } + + if(this.db){ + this.commit_timer && clearTimeout(this.commit_timer); + this.commit_timer = null; + this.commit_task = [{ "clear": true }]; + //return this.db.clear(); + } + + return this; +}; + +//Index.prototype.pipeline = pipeline; + +/** + * @param {!number|string} id + * @param {!string} content + */ + +Index.prototype.append = function(id, content){ + return this.add(id, content, true); +}; + +Index.prototype.contain = function(id){ + + if(this.db){ + return this.db.has(id); + } + + return this.reg.has(id); +}; + +Index.prototype.update = function(id, content){ + + // todo check the async part + if(this.async /*|| (SUPPORT_PERSISTENT && this.db)*/){ + const self = this; + const res = this.remove(id); + return res.then ? res.then( + () => self.add(id, content) + ) : this.add(id, content); + } + + return this.remove(id).add(id, content); +}; + +/** + * @param map + * @return {number} + */ + +function cleanup_index(map){ + + let count = 0; + + if(is_array(map)){ + for(let i = 0, arr; i < map.length; i++){ + (arr = map[i]) && + (count += arr.length); + } + } + else for(const item of map){ + const key = item[0]; + const value = item[1]; + const tmp = cleanup_index(value); + tmp ? count += tmp + : map.delete(key); + } + + return count; +} + +Index.prototype.cleanup = function(){ + + if(!this.fastupdate){ + console.info("Cleanup the index isn't required when not using \"fastupdate\"."); + return this; + } + + cleanup_index(this.map); + this.depth && + cleanup_index(this.ctx); + + return this; +}; + +{ + + Index.prototype.searchCache = searchCache; +} + +{ + + Index.prototype.export = exportIndex; + Index.prototype.import = importIndex; +} + +{ + + apply_async(Index.prototype); +} + +async function handler(data) { + + data = data["data"]; + + /** @type Index */ + const index = self["_index"]; + const args = data["args"]; + const task = data["task"]; + + switch(task){ + + case "init": + + /** @type IndexOptions */ + let options = data["options"] || {}; + let filepath = options.config; + if(filepath){ + options = filepath; + // will be replaced after build with the line below because + // there is an issue with closure compiler dynamic import + //options = await import(filepath); + } + + // deprecated: + // const encode = options.encode; + // if(encode && (encode.indexOf("function") === 0)){ + // options.encode = Function("return " + encode)(); + // } + + const factory = data["factory"]; + + if(factory){ + + // export the FlexSearch global payload to "self" + Function("return " + factory)()(self); + + /** @type Index */ + self["_index"] = new self["FlexSearch"]["Index"](options); + + // destroy the exported payload + delete self["FlexSearch"]; + } + else { + + self["_index"] = new Index(options); + } + + postMessage({ "id": data["id"] }); + break; + + default: + + const id = data["id"]; + const message = index[task].apply(index, args); + postMessage(task === "search" ? { "id": id, "msg": message } : { "id": id }); + } +} + +//import { promise as Promise } from "../polyfill.js"; + +let pid = 0; + +/** + * @param {IndexOptions=} options + * @constructor + */ + +function WorkerIndex(options){ + + if(!this) { + return new WorkerIndex(options); + } + + if(options); + else { + options = {}; + } + + // the factory is the outer wrapper from the build + // we use "self" as a trap for node.js + + let factory = (self||window)["_factory"]; + if(factory){ + factory = factory.toString(); + } + + const is_node_js = typeof window === "undefined" && self["exports"]; + const _self = this; + + this.worker = create(factory, is_node_js, options.worker); + this.resolver = create_object(); + + if(!this.worker){ + + return; + } + + function onmessage(msg){ + msg = msg["data"] || msg; + const id = msg["id"]; + const res = id && _self.resolver[id]; + if(res){ + res(msg["msg"]); + delete _self.resolver[id]; + } + } + + is_node_js + ? this.worker["on"]("message", onmessage) + : this.worker.onmessage = onmessage; + + if(options["config"]){ + + // when extern configuration needs to be loaded + // it needs to return a promise to await for + return new Promise(function(resolve){ + _self.resolver[++pid] = function(){ + resolve(_self); + }; + _self.worker.postMessage({ + "id": pid, + "task": "init", + "factory": factory, + "options": options + }); + }); + } + + this.worker.postMessage({ + "task": "init", + "factory": factory, + "options": options + }); +} + +register("add"); +register("append"); +register("search"); +register("update"); +register("remove"); + +function register(key){ + + WorkerIndex.prototype[key] = + WorkerIndex.prototype[key + "Async"] = function(){ + + const self = this; + const args = [].slice.call(arguments); + const arg = args[args.length - 1]; + let callback; + + if(is_function(arg)){ + callback = arg; + args.splice(args.length - 1, 1); + } + + const promise = new Promise(function(resolve){ + //setTimeout(function(){ + self.resolver[++pid] = resolve; + self.worker.postMessage({ + "task": key, + "id": pid, + "args": args + }); + //}); + }); + + if(callback){ + promise.then(callback); + return this; + } + else { + + return promise; + } + }; +} + +function create(factory, is_node_js, worker_path){ + + let worker; + + worker = is_node_js ? + // This eval will be removed when compiling, it isn't there in final build + (0, eval)('new (require("worker_threads")["Worker"])(__dirname + "/node/node.js")') + //eval('new (require("worker_threads")["Worker"])(__dirname + "/node/node.js")') + :( + factory ? + new window.Worker(URL.createObjectURL( + new Blob( + ["onmessage=" + handler.toString()], + { "type": "text/javascript" } + ) + )) + : + new window.Worker(is_string(worker_path) ? worker_path : "worker/worker.js", { type: "module" }) + ); + + return worker; +} + +// COMPILER BLOCK --> + +/** + * + * @param id + * @param content + * @param {boolean=} _append + * @returns {Document|Promise} + */ + +Document.prototype.add = function(id, content, _append){ + + if(is_object(id)){ + + content = id; + id = parse_simple(content, this.key); + } + + if(content && (id || (id === 0))){ + + if(!_append && this.reg.has(id)){ + return this.update(id, content); + } + + // this.field does not include db tag indexes + for(let i = 0, tree; i < this.field.length; i++){ + + tree = this.tree[i]; + + const index = this.index.get(this.field[i]); + if(typeof tree === "function"){ + const tmp = tree(content); + if(tmp){ + index.add(id, tmp, /* append: */ false, /* skip update: */ true); + } + } + else { + const filter = tree._filter; + if(filter && !filter(content)){ + continue; + } + if(tree.constructor === String){ + tree = ["" + tree]; + } + else if(is_string(tree)){ + tree = [tree]; + } + add_index(content, tree, this.marker, 0, index, id, tree[0], _append); + } + } + + if(this.tag){ + + //console.log(this.tag, this.tagtree) + + for(let x = 0; x < this.tagtree.length; x++){ + + let tree = this.tagtree[x]; + let field = this.tagfield[x]; + let ref = this.tag.get(field); + let dupes = create_object(); + let tags; + + if(typeof tree === "function"){ + tags = tree(content); + if(!tags) continue; + } + else { + const filter = tree._filter; + if(filter && !filter(content)){ + continue; + } + if(tree.constructor === String){ + tree = "" + tree; + } + tags = parse_simple(content, tree); + } + + if(!ref || !tags){ + ref || (console.warn("Tag '" + field + "' was not found")); + continue; + } + + if(is_string(tags)){ + tags = [tags]; + } + + for(let i = 0, tag, arr; i < tags.length; i++){ + + tag = tags[i]; + //console.log(this.tag, tag, key, field) + + if(!dupes[tag]){ + dupes[tag] = 1; + + let tmp; + tmp = ref.get(tag); + tmp ? arr = tmp : ref.set(tag, arr = []); + + if(!_append || ! /** @type {!Array|KeystoreArray} */(arr).includes(id)){ + + // auto-upgrade to keystore array if max size exceeded + { + if(arr.length === 2**31-1 /*|| !(arr instanceof KeystoreArray)*/){ + const keystore = new KeystoreArray(arr); + if(this.fastupdate){ + for(let value of this.reg.values()){ + if(value.includes(arr)){ + value[value.indexOf(arr)] = keystore; + } + } + } + ref.set(tag, arr = keystore); + } + } + + arr.push(id); + + // add a reference to the register for fast updates + if(this.fastupdate){ + const tmp = this.reg.get(id); + tmp ? tmp.push(arr) + : this.reg.set(id, [arr]); + } + } + } + } + } + } + + if(this.store && (!_append || !this.store.has(id))){ + + let payload; + + if(this.storetree){ + + payload = create_object(); + + for(let i = 0, tree; i < this.storetree.length; i++){ + tree = this.storetree[i]; + + const filter = tree._filter; + if(filter && !filter(content)){ + continue; + } + let custom; + if(typeof tree === "function"){ + custom = tree(content); + if(!custom) continue; + tree = [tree._field]; + } + else if(is_string(tree) || tree.constructor === String){ + payload[tree] = content[tree]; + continue; + } + + store_value(content, payload, tree, 0, tree[0], custom); + } + } + + this.store.set(id, payload || content); + } + } + + return this; +}; + +// TODO support generic function created from string when tree depth > 1 + +/** + * @param obj + * @param store + * @param tree + * @param pos + * @param key + * @param {*=} custom + */ + +function store_value(obj, store, tree, pos, key, custom){ + + obj = obj[key]; + + // reached target field + if(pos === (tree.length - 1)){ + + // store target value + store[key] = custom || obj; + } + else if(obj){ + + if(is_array(obj)){ + + store = store[key] = new Array(obj.length); + + for(let i = 0; i < obj.length; i++){ + // do not increase pos (an array is not a field) + store_value(obj, store, tree, pos, i); + } + } + else { + + store = store[key] || (store[key] = create_object()); + key = tree[++pos]; + store_value(obj, store, tree, pos, key); + } + } +} + +function add_index(obj, tree, marker, pos, index, id, key, _append){ + + if((obj = obj[key])){ + + // reached target field + if(pos === (tree.length - 1)){ + + // handle target value + if(is_array(obj)){ + + // append array contents so each entry gets a new scoring context + if(marker[pos]){ + for(let i = 0; i < obj.length; i++){ + index.add(id, obj[i], /* append: */ true, /* skip update: */ true); + } + return; + } + + // or join array contents and use one scoring context + obj = obj.join(" "); + } + + index.add(id, obj, _append, /* skip_update: */ true); + } + else { + + if(is_array(obj)){ + for(let i = 0; i < obj.length; i++){ + // do not increase index, an array is not a field + add_index(obj, tree, marker, pos, index, id, i, _append); + } + } + else { + key = tree[++pos]; + add_index(obj, tree, marker, pos, index, id, key, _append); + } + } + } + else { + if(index.db){ + index.remove(id); + } + } +} + +// COMPILER BLOCK --> + +/** + * @param {!string|DocumentSearchOptions} query + * @param {number|DocumentSearchOptions=} limit + * @param {DocumentSearchOptions=} options + * @param {Array=} _resolve For internal use only. + * @returns {Promise|Array} + */ + +Document.prototype.search = function(query, limit, options, _resolve){ + + if(!options){ + if(!limit && is_object(query)){ + options = /** @type {DocumentSearchOptions} */ (query); + query = ""; + } + else if(is_object(limit)){ + options = /** @type {DocumentSearchOptions} */ (limit); + limit = 0; + } + } + + let result = [], result_field = []; + let pluck, enrich, merge, suggest; + let field, tag, offset, count = 0; + + if(options){ + + // todo remove support? + if(is_array(options)){ + field = options; + options = null; + } + else { + + query = options.query || query; + pluck = options.pluck; + merge = options.merge; + field = pluck || options.field || options.index; + tag = this.tag && options.tag; + enrich = this.store && options.enrich; + suggest = options.suggest; + limit = options.limit || limit; + offset = options.offset || 0; + limit || (limit = 100); + + if(tag && (!this.db || !_resolve)){ + + if(tag.constructor !== Array){ + tag = [tag]; + } + + let pairs = []; + + for(let i = 0, field; i < tag.length; i++){ + field = tag[i]; + if(is_string(field)){ + throw new Error("A tag option can't be a string, instead it needs a { field: tag } format."); + } + // default array notation + if(field.field && field.tag){ + const value = field.tag; + if(value.constructor === Array){ + for(let k = 0; k < value.length; k++){ + pairs.push(field.field, value[k]); + } + } + else { + pairs.push(field.field, value); + } + } + // shorter object notation + else { + const keys = Object.keys(field); + for(let j = 0, key, value; j < keys.length; j++){ + key = keys[j]; + value = field[key]; + if(value.constructor === Array){ + for(let k = 0; k < value.length; k++){ + pairs.push(key, value[k]); + } + } + else { + pairs.push(key, value); + } + } + } + } + + if(!pairs.length){ + throw new Error("Your tag definition within the search options is probably wrong. No valid tags found."); + } + + // tag used as pairs from this point + tag = pairs; + + // when tags is used and no query was set, + // then just return the tag indexes + if(!query){ + + let promises = []; + if(pairs.length) for(let j = 0; j < pairs.length; j+=2){ + let ids; + if(this.db){ + const index = this.index.get(pairs[j]); + if(!index){ + { + console.warn("Tag '" + pairs[j] + ":" + pairs[j + 1] + "' will be skipped because there is no field '" + pairs[j] + "'."); + } + continue; + } + promises.push(ids = index.db.tag(pairs[j + 1], limit, offset, enrich)); + } + else { + ids = get_tag.call(this, pairs[j], pairs[j + 1], limit, offset, enrich); + } + result.push({ + "field": pairs[j], + "tag": pairs[j + 1], + "result": ids + }); + } + + if(promises.length){ + return Promise.all(promises).then(function(promises){ + for(let j = 0; j < promises.length; j++){ + result[j].result = promises[j]; + } + return result; + }); + } + + return result; + } + } + + // extend to multi field search by default + if(is_string(field)){ + field = [field]; + } + } + } + + field || (field = this.field); + let promises = !_resolve && (this.worker || this.async) && []; + let db_tag_search; + + // multi field search + // field could be a custom set of selected fields by this query + // db tag indexes are also included in this field list + for(let i = 0, res, key, len; i < field.length; i++){ + + key = field[i]; + + if(this.db && this.tag){ + // tree is missing when it is a tag-only index (db) + if(!this.tree[i]){ + continue; + } + } + + let field_options; + + if(!is_string(key)){ + field_options = key; + key = field_options.field; + query = field_options.query || query; + limit = field_options.limit || limit; + //offset = field_options.offset || offset; + suggest = (field_options.suggest || suggest); + //enrich = SUPPORT_STORE && this.store && (field_options.enrich || enrich); + } + + if(_resolve){ + res = _resolve[i]; + } + else { + let opt = field_options || options; + let index = this.index.get(key); + + if(tag){ + if(this.db){ + opt.tag = tag; + db_tag_search = index.db.support_tag_search; + opt.field = field; + } + if(!db_tag_search){ + opt.enrich = false; + } + } + if(promises){ + promises[i] = index.searchAsync(query, limit, opt); + // restore enrich state + opt && enrich && (opt.enrich = enrich); + // just collect and continue + continue; + } + else { + res = index.search(query, limit, opt); + // restore enrich state + opt && enrich && (opt.enrich = enrich); + } + } + + len = res && res.length; + + // todo when no term was matched but tag was retrieved extend suggestion to tags + // every field has to intersect against all selected tag fields + if(tag && len){ + + const arr = []; + let count = 0; + + // tags are only applied in resolve phase when it's a db + if(this.db && _resolve){ + if(!db_tag_search){ + + // retrieve tag results assigned to it's field + for(let y = field.length; y < _resolve.length; y++){ + + let ids = _resolve[y]; + let len = ids && ids.length; + + if(len){ + count++; + arr.push(ids); + } + else if(!suggest){ + // no tags found + return result; + } + } + } + } + else { + + // tag[] are pairs at this line + for(let y = 0, ids, len; y < tag.length; y+=2){ + ids = this.tag.get(tag[y]); + + if(!ids){ + { + console.warn("Tag '" + tag[y] + ":" + tag[y + 1] + "' will be skipped because there is no field '" + tag[y] + "'."); + } + if(suggest){ + continue; + } + else { + return result; + } + } + + ids = ids && ids.get(tag[y + 1]); + len = ids && ids.length; + + if(len){ + count++; + arr.push(ids); + } + else if(!suggest){ + // no tags found + return result; + } + } + } + + if(count){ + res = intersect_union(res, arr); // intersect(arr, limit, offset) + len = res.length; + if(!len && !suggest){ + // nothing matched + return result; + } + // move counter back by 1 + count--; + } + } + + if(len){ + result_field[count] = key; + result.push(res); + count++; + } + else if(field.length === 1){ + // fast path: nothing matched + return result; + } + } + + if(promises){ + if(this.db){ + // todo when a tag index is never a search index this could be extracted + // push tag promises to the end + if(tag && tag.length && !db_tag_search){ + for(let y = 0; y < tag.length; y += 2){ + // it needs to retrieve data from tag pairs + const index = this.index.get(tag[y]); + if(!index){ + { + console.warn("Tag '" + tag[y] + ":" + tag[y + 1] + "' was not found because there is no field '" + tag[y] + "'."); + } + if(suggest){ + continue; + } + else { + return result; + } + } + promises.push(index.db.tag(tag[y + 1], limit, offset, /* enrich */ false)); + } + } + } + + const self = this; + + // TODO unroll this recursion + return Promise.all(promises).then(function(result){ + return result.length + ? self.search(query, limit, options, /* resolve: */ result) + : result; + }); + } + + if(!count){ + return result; + } + if(pluck && (!enrich || !this.store)){ + return result[0]; + } + + promises = []; + + for(let i = 0, res; i < result_field.length; i++){ + + res = result[i]; + + if(enrich && res.length && !res[0].doc){ + if(!this.db){ + if(res.length){ + res = apply_enrich.call(this, res); + } + } + else { + promises.push(res = this.index.get(this.field[0]).db.enrich(res)); + } + } + + if(pluck){ + return res; + } + + result[i] = { + "field": result_field[i], + "result": res + }; + } + + if(enrich && SUPPORT_PERSISTENT && this.db && promises.length){ + return Promise.all(promises).then(function(promises){ + for(let j = 0; j < promises.length; j++){ + result[j].result = promises[j]; + } + return merge + ? merge_fields(result, limit) + : result; + }); + } + + return merge + ? merge_fields(result, limit) + : result; +}; + +// todo support Resolver +// todo when searching through multiple fields each term should +// be found at least by one field to get a valid match without +// using suggestion explicitly + +function merge_fields(fields, limit, offset){ + const final = []; + const set = create_object(); + for(let i = 0, field, res; i < fields.length; i++){ + field = fields[i]; + res = field.result; + for(let j = 0, id, entry, tmp; j < res.length; j++){ + entry = res[j]; + id = entry.id; + tmp = set[id]; + if(!tmp){ + // offset was already applied on field indexes + // if(offset){ + // offset--; + // continue; + // } + // apply limit from last round, because just fields could + // be pushed without adding new results + if(final.length === limit){ + return final; + } + entry.field = set[id] = [field.field]; + final.push(entry); + } + else { + tmp.push(field.field); + } + } + } + return final; +} + +/** + * @this Document + */ + +function get_tag(tag, key, limit, offset, enrich){ + let res = this.tag.get(tag); + if(!res){ + console.warn("Tag '" + tag + "' was not found"); + return []; + } + res = res && res.get(key); + let len = res && (res.length - offset); + + if(len && (len > 0)){ + if((len > limit) || offset){ + res = res.slice(offset, offset + limit); + } + if(enrich){ + res = apply_enrich.call(this, res); + } + return res; + } +} + +/** + * @this Document + */ + +function apply_enrich(res){ + + const arr = new Array(res.length); + + for(let x = 0, id; x < res.length; x++){ + id = res[x]; + arr[x] = { + "id": id, + "doc": this.store.get(id) + }; + } + + return arr; +} + +/**! + * FlexSearch.js + * Author and Copyright: Thomas Wilkerling + * Licence: Apache-2.0 + * Hosted by Nextapps GmbH + * https://github.com/nextapps-de/flexsearch + */ + + +/** + * @constructor + * @param {!DocumentOptions} options + */ + +function Document(options){ + + if(!this) { + return new Document(options); + } + + /** @type DocumentDescriptor */ + const document = options.document || options.doc || options; + let tmp, keystore; + + this.tree = []; + this.field = []; + this.marker = []; + this.key = ((tmp = document.key || document.id) && parse_tree(tmp, this.marker)) || "id"; + + keystore = (options.keystore || 0); + keystore && (this.keystore = keystore); + this.fastupdate = !!options.fastupdate; + this.reg = this.fastupdate + ? (keystore && SUPPORT_KEYSTORE ? new KeystoreMap(keystore) : new Map()) + : (keystore && SUPPORT_KEYSTORE ? new KeystoreSet(keystore) : new Set()); + + { + // todo support custom filter function + this.storetree = (tmp = document.store || null) && tmp !== true && []; + this.store = tmp && ( + keystore && SUPPORT_KEYSTORE + ? new KeystoreMap(keystore) + : new Map() + ); + } + + { + this.cache = (tmp = options.cache || null) && new CacheClass(tmp); + // do not apply cache again for the indexes since .searchCache() + // is just a wrapper over .search() + options.cache = false; + } + + { + this.worker = options.worker; + } + + { + // this switch is used by recall of promise callbacks + this.async = false; + } + + /** @export */ + this.index = parse_descriptor.call(this, options, document); + + { + this.tag = null; + // TODO case-insensitive tags? + if((tmp = document.tag)){ + if(typeof tmp === "string"){ + tmp = [tmp]; + } + if(tmp.length){ + this.tag = new Map(); + this.tagtree = []; + this.tagfield = []; + for(let i = 0, params, field; i < tmp.length; i++){ + params = tmp[i]; + field = params.field || params; + if(!field){ + throw new Error("The tag field from the document descriptor is undefined."); + } + if(params.custom){ + this.tagtree[i] = params.custom; + } + else { + this.tagtree[i] = parse_tree(field, this.marker); + if(params.filter){ + if(typeof this.tagtree[i] === "string"){ + // it needs an object to put a property to it + this.tagtree[i] = new String(this.tagtree[i]); + } + this.tagtree[i]._filter = params.filter; + } + } + // the tag fields needs to be hold by indices + this.tagfield[i] = field; + this.tag.set(field, new Map()); + } + } + } + } + + { + options.db && this.mount(options.db); + } +} + +{ + + Document.prototype.mount = function(db){ + + let fields = this.field; + + if(this.tag){ + // tag indexes are referenced by field + // move tags to their field indexes respectively + for(let i = 0, field; i < this.tagfield.length; i++){ + field = this.tagfield[i]; + let index = this.index.get(field); + if(!index){ + // create raw index when not exists + this.index.set(field, index = new Index({}, this.reg)); + // copy and push to the field selection + if(fields === this.field){ + fields = fields.slice(0); + } + // tag indexes also needs to be upgraded to db instances + fields.push(field); + } + // assign reference + index.tag = this.tag.get(field); + } + } + + const promises = []; + const config = { + db: db.db, + type: db.type, + fastupdate: db.fastupdate + }; + + // upgrade all indexes to db instances + for(let i = 0, index, field; i < fields.length; i++){ + config.field = field = fields[i]; + index = this.index.get(field); + const dbi = new db.constructor(db.id, config); + // take over the storage id + dbi.id = db.id; + promises[i] = dbi.mount(index); + // add an identification property + index.document = true; + if(i){ + // the register has to export just one time + // also it's needed by the index for ID contain check + index.bypass = true; + } + else { + // the datastore has to export one time + index.store = this.store; + } + } + + this.async = true; + this.db = true; + return Promise.all(promises); + }; + + Document.prototype.commit = async function(replace, append){ + // parallel: + const promises = []; + for(const index of this.index.values()){ + promises.push(index.db.commit(index, replace, append)); + } + await Promise.all(promises); + this.reg.clear(); + // queued: + // for(const index of this.index.values()){ + // await index.db.commit(index, replace, append); + // } + // this.reg.clear(); + }; +} + +/** + * @this Document + */ + +function parse_descriptor(options, document){ + + const index = new Map(); + let field = document.index || document.field || document; + + if(is_string(field)){ + field = [field]; + } + + for(let i = 0, key, opt; i < field.length; i++){ + + key = field[i]; + + if(!is_string(key)){ + opt = key; + key = key.field; + } + + opt = /** @type DocumentIndexOptions */ ( + is_object(opt) + ? Object.assign({}, options, opt) + : options + ); + + if(this.worker){ + const worker = new WorkerIndex(opt); + index.set(key, worker); + if(!worker.worker){ + // fallback when not supported + this.worker = false; + } + } + + if(!this.worker){ + index.set(key, new Index(opt, this.reg)); + } + + if(opt.custom){ + this.tree[i] = opt.custom; + } + else { + this.tree[i] = parse_tree(key, this.marker); + if(opt.filter){ + if(typeof this.tree[i] === "string"){ + // it needs an object to put a property to it + this.tree[i] = new String(this.tree[i]); + } + this.tree[i]._filter = opt.filter; + } + } + + this.field[i] = key; + } + + if(this.storetree){ + + let stores = document.store; + if(is_string(stores)) stores = [stores]; + + for(let i = 0, store, field; i < stores.length; i++){ + store = /** @type Array */ (stores[i]); + field = store.field || store; + if(store.custom){ + this.storetree[i] = store.custom; + store.custom._field = field; + } + else { + this.storetree[i] = parse_tree(field, this.marker); + if(store.filter){ + if(typeof this.storetree[i] === "string"){ + // it needs an object to put a property to it + this.storetree[i] = new String(this.storetree[i]); + } + this.storetree[i]._filter = store.filter; + } + } + } + } + + return index; +} + +function parse_tree(key, marker){ + + const tree = key.split(":"); + let count = 0; + + for(let i = 0; i < tree.length; i++){ + key = tree[i]; + // todo apply some indexes e.g. [0], [-1], [0-2] + if(key[key.length - 1] === "]"){ + key = key.substring(0, key.length - 2); + if(key){ + marker[count] = true; + } + } + if(key){ + tree[count++] = key; + } + } + + if(count < tree.length){ + tree.length = count; + } + + return count > 1 ? tree : tree[0]; +} + +Document.prototype.append = function(id, content){ + return this.add(id, content, true); +}; + +Document.prototype.update = function(id, content){ + return this.remove(id).add(id, content); +}; + +Document.prototype.remove = function(id){ + + if(is_object(id)){ + id = parse_simple(id, this.key); + } + + for(const index of this.index.values()){ + index.remove(id, /* skip deletion */ true); + } + + if(this.reg.has(id)){ + + if(this.tag){ + // when fastupdate was enabled all ids are already removed + if(!this.fastupdate){ + for(let field of this.tag.values()){ + for(let item of field){ + const tag = item[0]; + const ids = item[1]; + const pos = ids.indexOf(id); + if(pos > -1){ + ids.length > 1 + ? ids.splice(pos, 1) + : field.delete(tag); + } + } + } + } + } + + if(this.store){ + this.store.delete(id); + } + + this.reg.delete(id); + } + + // the cache could be used outside the InMemory store + if(this.cache){ + this.cache.remove(id); + } + + return this; +}; + +Document.prototype.clear = function(){ + + //const promises = []; + + for(const index of this.index.values()){ + // db index will add clear task + index.clear(); + // const promise = index.clear(); + // if(promise instanceof Promise){ + // promises.push(promise); + // } + } + + if(this.tag){ + for(const tags of this.tag.values()){ + tags.clear(); + } + } + + if(this.store){ + this.store.clear(); + } + + return this; /*promises.length + ? Promise.all(promises) + :*/ +}; + +Document.prototype.contain = function(id){ + + if(this.db){ + return this.index.get(this.field[0]).db.has(id); + } + + return this.reg.has(id); +}; + +Document.prototype.cleanup = function(){ + + for(const index of this.index.values()){ + index.cleanup(); + } + + return this; +}; + +{ + + Document.prototype.get = function(id){ + + if(this.db){ + return this.index.get(this.field[0]).db.enrich(id).then(function(result){ + return result[0] && result[0].doc; + }); + } + + return this.store.get(id); + }; + + Document.prototype.set = function(id, store){ + + this.store.set(id, store); + return this; + }; +} + +{ + // todo mo + Document.prototype.searchCache = searchCache; +} + +{ + + Document.prototype.export = exportDocument; + Document.prototype.import = importDocument; +} + +{ + + apply_async(Document.prototype); +} + +// COMPILER BLOCK --> +const defaults = { + host: "localhost", + port: "6379", + user: null, + pass: null +}; + +function sanitize(str) { + return str.toLowerCase().replace(/[^a-z0-9_\-]/g, ""); +} + +let DB, TRX; + +/** + * @constructor + * @implements StorageInterface + */ + +function RedisDB(name, config = {}){ + if(!this){ + return new RedisDB(name, config); + } + if(typeof name === "object"){ + name = name.name; + config = name; + } + if(!name){ + console.info("Default storage space was used, because a name was not passed."); + } + this.id = (name ? sanitize(name) : "flexsearch") + "|"; + this.field = config.field ? "-" + sanitize(config.field) : ""; + this.type = config.type || ""; + this.fastupdate = true; + this.db = config.db || DB || null; + this.support_tag_search = true; + //this.trx = false; + Object.assign(defaults, config); + this.db && delete defaults.db; +} +// RedisDB.mount = function(flexsearch){ +// return new this().mount(flexsearch); +// }; + +RedisDB.prototype.mount = function(flexsearch){ + if(flexsearch.constructor === Document){ + return flexsearch.mount(this); + } + flexsearch.db = this; + // todo support + //this.fastupdate = flexsearch.fastupdate; + return this.open(); +}; + +RedisDB.prototype.open = async function(){ + if(this.db){ + return this.db + } + let url = defaults.url; + if(!url){ + url = defaults.user + ? `redis://${defaults.user}:${defaults.pass}@${defaults.host}:${defaults.port}` + : `redis://${defaults.host}:${defaults.port}`; + } + return this.db = + await redis.createClient(url) + .on("error", err => console.error(err)) + .connect(); +}; + +RedisDB.prototype.close = async function(){ + await this.db.disconnect(); // this.db.client.disconnect(); + this.db = null; + return this; +}; + +RedisDB.prototype.clear = function(){ + return this.db.unlink([ + this.id + "map" + this.field, + this.id + "ctx" + this.field, + this.id + "tag" + this.field, + this.id + "cfg" + this.field, + this.id + "doc", + this.id + "reg" + ]); +}; + +function create_result(range, type, resolve, enrich){ + if(resolve){ + for(let i = 0, tmp, id; i < range.length; i++){ + tmp = range[i]; + id = type === "number" + ? parseInt(tmp.value || tmp, 10) + : tmp.value || tmp; + range[i] = /*enrich + ? { id, doc: tmp.doc } + :*/ id; + } + return range; + } + else { + let result = []; + for(let i = 0, tmp, id, score; i < range.length; i++){ + tmp = range[i]; + id = type === "number" + ? parseInt(tmp.value, 10) + : tmp.value; + score = tmp.score; + result[score] || (result[score] = []); + result[score].push( + enrich + ? { id, doc: tmp.doc } + : id + ); + } + return result; + } +} + +RedisDB.prototype.get = function(key, ctx, limit = 0, offset = 0, resolve = true, enrich = false, tags){ + + if(tags){ + // flexsearch dummy + const flexsearch = { depth: !!ctx }; + const query = ctx ? [ctx, key] : [key]; // keyword first + return this.search(flexsearch, query, limit, offset, /* suggest */ false, resolve, enrich, tags); + } + + const type = this.type; + const self = this; + let result; + + if(ctx){ + result = this.db[resolve ? "zRange" : "zRangeWithScores"]( + this.id + "ctx" + this.field + ":" + ctx + ":" + key, + "" + offset, + "" + (offset + limit - 1), + { REV: true } + ); + } + else { + result = this.db[resolve ? "zRange" : "zRangeWithScores"]( + this.id + "map" + this.field + ":" + key, + "" + offset, + "" + (offset + limit - 1), + { REV: true } + ); + } + + return result.then(async function(range){ + if(!range.length) return range; + if(enrich) range = await self.enrich(range); + return create_result(range, type, resolve, enrich); + }); +}; + +RedisDB.prototype.tag = function(tag, limit = 0, offset = 0, enrich = false){ + const self = this; + return this.db.sMembers(this.id + "tag" + this.field + ":" + tag).then(function(ids){ + if(!ids || !ids.length || offset >= ids.length) return []; + if(!limit && !offset) return ids; + const result = ids.slice(offset, offset + limit); + return enrich + ? self.enrich(result) + : result; + }); +}; + +RedisDB.prototype.enrich = function(ids){ + if(typeof ids !== "object"){ + ids = [ids]; + } + return this.db.hmGet(this.id + "doc", ids).then(function(res){ + for(let i = 0; i < res.length; i++){ + res[i] = { + id: ids[i], + doc: res[i] && JSON.parse(res[i]) + }; + } + return res; + }); +}; + +RedisDB.prototype.has = function(id){ + return this.db.sIsMember(this.id + "reg", "" + id); +}; + +RedisDB.prototype.search = function(flexsearch, query, limit = 100, offset = 0, suggest = false, resolve = true, enrich = false, tags){ + + let result; + + if(query.length > 1 && flexsearch.depth){ + + const key = this.id + "ctx" + this.field + ":"; + let params = []; + let keyword = query[0]; + let term; + + for(let i = 1, swap; i < query.length; i++){ + term = query[i]; + swap = flexsearch.bidirectional && (term > keyword); + params.push(key + (swap ? term : keyword) + ":" + (swap ? keyword : term)); + keyword = term; + } + query = params; + } + else { + + const key = this.id + "map" + this.field + ":"; + for(let i = 0; i < query.length; i++){ + query[i] = key + query[i]; + } + } + + const type = this.type; + let key = this.id + "tmp:" + Math.random(); + + if(suggest){ + { + if(tags) for(let i = 0; i < tags.length; i += 2){ + query.push(this.id + "tag-" + sanitize(tags[i]) + ":" + tags[i + 1]); + } + } + const multi = this.db.multi().zUnionStore(key, query, { AGGREGATE: "SUM" }); + result = multi + [(resolve ? "zRange" : "zRangeWithScores")](key, "" + offset, "" + (offset + limit - 1), { REV: true }) + .unlink(key) + .exec(); + } + else { + if(tags) for(let i = 0; i < tags.length; i+=2){ + query.push(this.id + "tag-" + sanitize(tags[i]) + ":" + tags[i + 1]); + } + result = this.db.multi() + .zInterStore(key, query, { AGGREGATE: "MIN" }) + [(resolve ? "zRange" : "zRangeWithScores")](key, "" + offset, "" + (offset + limit - 1), { REV: true }) + .unlink(key) + .exec(); + } + + const self = this; + return result.then(async function(range){ + range = suggest && tags + // take the 3rd result from batch return + ? range[2] + // take the 2nd result from batch return + : range[1]; + if(!range.length) return range; + if(enrich) range = await self.enrich(range); + return create_result(range, type, resolve, enrich); + }); +}; + +RedisDB.prototype.info = function(){ + // todo +}; + +RedisDB.prototype.transaction = async function(task, callback){ + + if(TRX){ + return task.call(this, TRX); + } + + TRX = this.db.multi(); + let promise1 = /*await*/ task.call(this, TRX); + let promise2 = TRX.exec(); + TRX = null; + callback && promise.then(callback); + await Promise.all([promise1, promise2]); +}; + +RedisDB.prototype.commit = async function(flexsearch, _replace, _append){ + + // process cleanup tasks + if(_replace){ + await this.clear(); + // there are just removals in the task queue + flexsearch.commit_task = []; + } + else { + let tasks = flexsearch.commit_task; + flexsearch.commit_task = []; + for(let i = 0, task; i < tasks.length; i++){ + task = tasks[i]; + // there are just removals in the task queue + if(task.clear){ + await this.clear(); + _replace = true; + break; + } + else { + tasks[i] = "" + task.del; + } + } + if(!_replace){ + if(!_append){ + tasks = tasks.concat(toArray(flexsearch.reg)); + } + tasks.length && await this.remove(tasks); + } + } + + if(!flexsearch.reg.size){ + return; + } + + await this.transaction(function(trx){ + + let refs = new Map(); + for(const item of flexsearch.map){ + const key = item[0]; + const arr = item[1]; + for(let i = 0, ids; i < arr.length; i++){ + if((ids = arr[i]) && ids.length){ + + let result = []; + for(let j = 0; j < ids.length; j++){ + result.push({ + score: i, + value: "" + ids[j] + }); + } + if(typeof ids[0] === "number"){ + this.type = "number"; + } + + const ref = this.id + "map" + this.field + ":" + key; + trx.zAdd(ref, result); + // if(this.fastupdate) for(let j = 0; j < ids.length; j++){ + // trx.sAdd("ref" + this.field + ":" + ids[j], ref); + // } + if(this.fastupdate) for(let j = 0, id; j < ids.length; j++){ + // Map performs bad when pushing numeric-like values as key + // id = ids[j]; + // let tmp = refs.get(id); + // tmp || refs.set(id, tmp = []); + // tmp.push(ref); + id = ids[j]; + let tmp = refs.get(id); + tmp || refs.set(id, tmp = []); + tmp.push(ref); + } + } + } + } + // if(this.fastupdate) for(let item of refs){ + // const key = item[0]; + // const value = item[1]; + // trx.sAdd("ref" + this.field + ":" + key, value); + // } + if(this.fastupdate) for(const item of refs){ + const key = item[0]; + const value = item[1]; + trx.sAdd(this.id + "ref" + this.field + ":" + key, value); + } + + refs = new Map(); + for(const ctx of flexsearch.ctx){ + const ctx_key = ctx[0]; + const ctx_value = ctx[1]; + for(const item of ctx_value){ + const key = item[0]; + const arr = item[1]; + for(let i = 0, ids; i < arr.length; i++){ + if((ids = arr[i]) && ids.length){ + let result = []; + for(let j = 0; j < ids.length; j++){ + result.push({ score: i, value: "" + ids[j] }); + } + if(typeof ids[0] === "number"){ + this.type = "number"; + } + const ref = this.id + "ctx" + this.field + ":" + ctx_key + ":" + key; + trx.zAdd(ref, result); + // if(this.fastupdate) for(let j = 0; j < ids.length; j++){ + // trx.sAdd("ref" + this.field + ":" + ids[j], ref); + // } + if(this.fastupdate) for(let j = 0, id; j < ids.length; j++){ + // Map performs bad when pushing numeric-like values as key + // id = ids[j]; + // let tmp = refs.get(id); + // tmp || refs.set(id, tmp = []); + // tmp.push(ref); + id = ids[j]; + let tmp = refs.get(id); + tmp || refs.set(id, tmp = []); + tmp.push(ref); + } + } + } + } + } + + if(this.fastupdate) for(const item of refs){ + const key = item[0]; + const value = item[1]; + trx.sAdd(this.id + "ref" + this.field + ":" + key, value); + } + + if(flexsearch.store){ + for(const item of flexsearch.store.entries()){ + const id = item[0]; + const doc = item[1]; + doc && trx.hSet(this.id + "doc", "" + id, JSON.stringify(doc)); + } + } + if(!flexsearch.bypass){ + let ids = toArray(flexsearch.reg); + if(ids.length){ + trx.sAdd(this.id + "reg", ids); + } + } + + if(flexsearch.tag){ + for(const item of flexsearch.tag){ + const tag = item[0]; + const ids = item[1]; + if(!ids.length) continue; + let result = []; + // for(let i = 0; i < ids.length; i++){ + // result.push({ + // score: 0, + // value: "" + ids[i] + // }); + // } + if(typeof ids[0] === "number"){ + for(let i = 0; i < ids.length; i++){ + result[i] = "" + ids[i]; + } + } + else { + result = ids; + } + trx.sAdd(this.id + "tag" + this.field + ":" + tag, result); + } + } + + // TODO + // trx.set(this.id + "cfg" + this.field, JSON.stringify({ + // "encode": typeof flexsearch.encode === "string" ? flexsearch.encode : "", + // "charset": typeof flexsearch.charset === "string" ? flexsearch.charset : "", + // "tokenize": flexsearch.tokenize, + // "resolution": flexsearch.resolution, + // "minlength": flexsearch.minlength, + // "optimize": flexsearch.optimize, + // "fastupdate": flexsearch.fastupdate, + // "encoder": flexsearch.encoder, + // "context": { + // "depth": flexsearch.depth, + // "bidirectional": flexsearch.bidirectional, + // "resolution": flexsearch.resolution_ctx + // } + // })); + }); + + flexsearch.map.clear(); + flexsearch.ctx.clear(); + flexsearch.tag && + flexsearch.tag.clear(); + flexsearch.store && + flexsearch.store.clear(); + flexsearch.document || + flexsearch.reg.clear(); +}; + +RedisDB.prototype.remove = function(ids){ + + if(!ids && ids !== 0){ + return; + } + if(typeof ids !== "object"){ + ids = [ids]; + } + if(!ids.length){ + return; + } + + return this.transaction(async function(trx){ + + while(ids.length){ + let next; + if(ids.length > 10000){ + next = ids.slice(10000); + ids = ids.slice(0, 10000); + } + + if(typeof ids[0] === "number"){ + for(let i = 0; i < ids.length; i++){ + ids[i] = "" + ids[i]; + } + } + + const check = await this.db.smIsMember(this.id + "reg", ids); + + for(let i = 0, id; i < ids.length; i++){ + if(!check[i]) continue; + id = "" + ids[i]; + + if(this.fastupdate){ + // const refs = new Map(); + const ref = await this.db.sMembers(this.id + "ref" + this.field + ":" + id); + if(ref){ + for(let j = 0; j < ref.length; j++){ + // let tmp = refs.get(ref[j]); + // tmp || refs.set(ref[j], tmp = []); + // tmp.push(id); + trx.zRem(ref[j], id); + } + trx.unlink(this.id + "ref" + this.field + ":" + id); + } + // for(let item of refs){ + // //console.log(item[0], item[1]) + // trx.zRem(item[0], item[1]); + // } + } + + trx.hDel(this.id + "doc", id); + trx.sRem(this.id + "reg", id); + } + + if(next) ids = next; + else break; + } + }); +}; + +module.exports = RedisDB; diff --git a/dist/db/sqlite/index.cjs b/dist/db/sqlite/index.cjs new file mode 100644 index 0000000..d9721dd --- /dev/null +++ b/dist/db/sqlite/index.cjs @@ -0,0 +1,6525 @@ +'use strict'; + +var sqlite3 = require('sqlite3'); +var path = require('path'); + +/** @define {string} */ + +/** @define {boolean} */ +const SUPPORT_PERSISTENT = true; + +/** @define {boolean} */ +const SUPPORT_KEYSTORE = true; + +/** + * @param {*} value + * @param {*} default_value + * @param {*=} merge_value + * @return {*} + */ + +function parse_option(value, default_value, merge_value){ + + const type_merge = typeof merge_value; + const type_value = typeof value; + + if(type_merge !== "undefined"){ + if(type_value !== "undefined"){ + + if(merge_value){ + if(type_value === "function" && + type_merge === type_value){ + return function(str){ + return /** @type Function */ (value)( + /** @type Function */ (merge_value)(str) + ); + } + } + + const constructor_value = value.constructor; + const constructor_merge = merge_value.constructor; + + if(constructor_value === constructor_merge){ + + if(constructor_value === Array){ + return merge_value.concat(value); + } + + if(constructor_value === Map){ + const map = new Map(/** @type !Map */ (merge_value)); + for(const item of /** @type !Map */ (value)){ + const key = item[0]; + const val = item[1]; + map.set(key, val); + } + return map; + } + + if(constructor_value === Set){ + const set = new Set(/** @type !Set */ (merge_value)); + for(const val of /** @type !Set */ (value).values()){ + set.add(val); + } + return set; + } + } + } + + return value; + } + else { + return merge_value; + } + } + + return type_value === "undefined" + ? default_value + : value; +} + +function create_object(){ + return Object.create(null); +} + +function concat(arrays){ + return [].concat.apply([], arrays); +} + +function sort_by_length_down(a, b){ + return b.length - a.length; +} + +function is_array(val){ + return val.constructor === Array; +} + +function is_string(val){ + return typeof val === "string"; +} + +function is_object(val){ + return typeof val === "object"; +} + +function is_function(val){ + return typeof val === "function"; +} + +/** + * @param {Map|Set} val + * @param {boolean=} stringify + * @return {Array} + */ + +function toArray(val, stringify){ + const result = []; + for(const key of val.keys()){ + result.push(key); + } + return result; +} + +// TODO support generic function created from string when tree depth > 1 +function parse_simple(obj, tree){ + + if(is_string(tree)){ + obj = obj[tree]; + } + else for(let i = 0; obj && (i < tree.length); i++){ + obj = obj[tree[i]]; + } + + return obj; +} + +function get_max_len(arr){ + let len = 0; + for(let i = 0, res; i < arr.length; i++){ + if((res = arr[i])){ + if(len < res.length){ + len = res.length; + } + } + } + return len; +} + +var normalize_polyfill = [ + + // Charset Normalization + + ["ª","a"], + ["²","2"], + ["³","3"], + ["¹","1"], + ["º","o"], + ["¼","1⁄4"], + ["½","1⁄2"], + ["¾","3⁄4"], + ["à","a"], + ["á","a"], + ["â","a"], + ["ã","a"], + ["ä","a"], + ["å","a"], + ["ç","c"], + ["è","e"], + ["é","e"], + ["ê","e"], + ["ë","e"], + ["ì","i"], + ["í","i"], + ["î","i"], + ["ï","i"], + ["ñ","n"], + ["ò","o"], + ["ó","o"], + ["ô","o"], + ["õ","o"], + ["ö","o"], + ["ù","u"], + ["ú","u"], + ["û","u"], + ["ü","u"], + ["ý","y"], + ["ÿ","y"], + ["ā","a"], + ["ă","a"], + ["ą","a"], + ["ć","c"], + ["ĉ","c"], + ["ċ","c"], + ["č","c"], + ["ď","d"], + ["ē","e"], + ["ĕ","e"], + ["ė","e"], + ["ę","e"], + ["ě","e"], + ["ĝ","g"], + ["ğ","g"], + ["ġ","g"], + ["ģ","g"], + ["ĥ","h"], + ["ĩ","i"], + ["ī","i"], + ["ĭ","i"], + ["į","i"], + ["ij","ij"], + ["ĵ","j"], + ["ķ","k"], + ["ĺ","l"], + ["ļ","l"], + ["ľ","l"], + ["ŀ","l"], + ["ń","n"], + ["ņ","n"], + ["ň","n"], + ["ʼn","n"], + ["ō","o"], + ["ŏ","o"], + ["ő","o"], + ["ŕ","r"], + ["ŗ","r"], + ["ř","r"], + ["ś","s"], + ["ŝ","s"], + ["ş","s"], + ["š","s"], + ["ţ","t"], + ["ť","t"], + ["ũ","u"], + ["ū","u"], + ["ŭ","u"], + ["ů","u"], + ["ű","u"], + ["ų","u"], + ["ŵ","w"], + ["ŷ","y"], + ["ź","z"], + ["ż","z"], + ["ž","z"], + ["ſ","s"], + ["ơ","o"], + ["ư","u"], + ["dž","dz"], + ["lj","lj"], + ["nj","nj"], + ["ǎ","a"], + ["ǐ","i"], + ["ǒ","o"], + ["ǔ","u"], + ["ǖ","u"], + ["ǘ","u"], + ["ǚ","u"], + ["ǜ","u"], + ["ǟ","a"], + ["ǡ","a"], + ["ǣ","ae"], + ["æ","ae"], + ["ǽ","ae"], + ["ǧ","g"], + ["ǩ","k"], + ["ǫ","o"], + ["ǭ","o"], + ["ǯ","ʒ"], + ["ǰ","j"], + ["dz","dz"], + ["ǵ","g"], + ["ǹ","n"], + ["ǻ","a"], + ["ǿ","ø"], + ["ȁ","a"], + ["ȃ","a"], + ["ȅ","e"], + ["ȇ","e"], + ["ȉ","i"], + ["ȋ","i"], + ["ȍ","o"], + ["ȏ","o"], + ["ȑ","r"], + ["ȓ","r"], + ["ȕ","u"], + ["ȗ","u"], + ["ș","s"], + ["ț","t"], + ["ȟ","h"], + ["ȧ","a"], + ["ȩ","e"], + ["ȫ","o"], + ["ȭ","o"], + ["ȯ","o"], + ["ȱ","o"], + ["ȳ","y"], + ["ʰ","h"], + ["ʱ","h"], + ["ɦ","h"], + ["ʲ","j"], + ["ʳ","r"], + ["ʴ","ɹ"], + ["ʵ","ɻ"], + ["ʶ","ʁ"], + ["ʷ","w"], + ["ʸ","y"], + ["ˠ","ɣ"], + ["ˡ","l"], + ["ˢ","s"], + ["ˣ","x"], + ["ˤ","ʕ"], + ["ΐ","ι"], + ["ά","α"], + ["έ","ε"], + ["ή","η"], + ["ί","ι"], + ["ΰ","υ"], + ["ϊ","ι"], + ["ϋ","υ"], + ["ό","ο"], + ["ύ","υ"], + ["ώ","ω"], + ["ϐ","β"], + ["ϑ","θ"], + ["ϒ","Υ"], + ["ϓ","Υ"], + ["ϔ","Υ"], + ["ϕ","φ"], + ["ϖ","π"], + ["ϰ","κ"], + ["ϱ","ρ"], + ["ϲ","ς"], + ["ϵ","ε"], + ["й","и"], + ["ѐ","е"], + ["ё","е"], + ["ѓ","г"], + ["ї","і"], + ["ќ","к"], + ["ѝ","и"], + ["ў","у"], + ["ѷ","ѵ"], + ["ӂ","ж"], + ["ӑ","а"], + ["ӓ","а"], + ["ӗ","е"], + ["ӛ","ә"], + ["ӝ","ж"], + ["ӟ","з"], + ["ӣ","и"], + ["ӥ","и"], + ["ӧ","о"], + ["ӫ","ө"], + ["ӭ","э"], + ["ӯ","у"], + ["ӱ","у"], + ["ӳ","у"], + ["ӵ","ч"] + + // Term Separators + + // ["'", ""], // it's -> its + // ["´", ""], + // ["`", ""], + // ["’", ""], + // ["ʼ", ""], + + // Numeric-Separators Chars Removal + + // [",", ""], + // [".", ""] + + // Non-Whitespace Separators + + // already was split by default via p{P} + // ["-", " "], + // [":", " "], + // ["_", " "], + // ["|", " "], + // ["/", " "], + // ["\\", " "] +]; + +// COMPILER BLOCK --> + +/* + +Custom Encoder +---------------- + +// Split a passed string into an Array of words: +function englishEncoder(string){ + return string.toLowerCase().split(/[^a-z]+/) +} + +// For CJK split a passed string into an Array of chars: +function chineseEncoder(string){ + return string.replace(/\s+/, "").split("") +} + +// Alternatively do not split the input: +function fixedEncoder(string){ + return [string] +} + +Built-in Encoder (Workflow) +---------------------------- +Pipeline: + 1. apply this.normalize: charset normalization: + applied on the whole input string e.g. lowercase, + will also apply on: filter, matcher, stemmer, mapper + 2. apply this.split: split input into terms (includes/excludes) + 3. apply this.filter (pre-filter) + 4. apply this.matcher (replace terms) + 5. apply this.stemmer (replace term endings) + 6. apply this.filter (post-filter) + 7. apply this.mapper (replace chars) + 8. apply this.replacer (custom regex) + 9. apply this.dedupe (letter deduplication) + 10. apply this.finalize +*/ + +const whitespace = /[^\p{L}\p{N}]+/u; // /[\p{Z}\p{S}\p{P}\p{C}]+/u; +//const numeric_split = /(\d{3})/g; +const numeric_split_length = /(\d{3})/g; +const numeric_split_prev_char = /(\D)(\d{3})/g; +const numeric_split_next_char = /(\d{3})(\D)/g; +//.replace(/(\d{3})/g, "$1 ") +//.replace(/([^\d])([\d])/g, "$1 $2") +//.replace(/([\d])([^\d])/g, "$1 $2") +const normalize = "".normalize && /[\u0300-\u036f]/g; // '´`’ʼ., +//const normalize_mapper = SUPPORT_CHARSET && !normalize && normalize_polyfill; + +/** + * @param {EncoderOptions=} options + * @constructor + */ + +function Encoder(options){ + + if(!this){ + return new Encoder(...arguments); + } + + for(let i = 0; i < arguments.length; i++){ + this.assign(arguments[i]); + } +} +/** + * @param {!EncoderOptions} options + */ +Encoder.prototype.assign = function(options){ + + /** + * pre-processing string input + * @type {Function|boolean} + */ + this.normalize = /** @type {Function|boolean} */ ( + parse_option(options.normalize, true, this.normalize) + ); + + // { + // letter: true, + // number: true, + // whitespace: true, + // symbol: true, + // punctuation: true, + // control: true, + // char: "" + // } + + let include = options.include; + let tmp = include || options.exclude || options.split; + + if(typeof tmp === "object"){ + let numeric = !include; + let regex = ""; + // split on whitespace by default + options.include || ( + regex += "\\p{Z}" + ); + if(tmp.letter){ + regex += "\\p{L}"; + } + if(tmp.number){ + regex += "\\p{N}"; + numeric = !!include; + } + if(tmp.symbol){ + regex += "\\p{S}"; + } + if(tmp.punctuation){ + regex += "\\p{P}"; + } + if(tmp.control){ + regex += "\\p{C}"; + } + if((tmp = tmp.char)){ + regex += typeof tmp === "object" ? tmp.join("") : tmp; + } + + this.split = new RegExp("[" + (include ? "^" : "") + regex + "]+", "u"); + this.numeric = numeric; + } + else { + + /** + * split string input into terms + * @type {string|RegExp|boolean|null} + */ + this.split = /** @type {string|RegExp|boolean} */ (parse_option(tmp, whitespace, this.split)); + this.numeric = parse_option(this.numeric, true); + } + + /** + * post-processing terms + * @type {Function|null} + */ + this.prepare = /** @type {Function|null} */ ( + parse_option(options.prepare, null, this.prepare) + ); + /** + * final processing + * @type {Function|null} + */ + this.finalize = /** @type {Function|null} */ ( + parse_option(options.finalize, null, this.finalize) + ); + + // assign the normalization fallback to the mapper + if(!normalize){ + this.mapper = new Map( + /** @type {Array>} */ ( + normalize_polyfill + ) + ); + } + + // options + + this.rtl = options.rtl || false; + this.dedupe = parse_option(options.dedupe, true, this.dedupe); + this.filter = parse_option((tmp = options.filter) && new Set(tmp), null, this.filter); + this.matcher = parse_option((tmp = options.matcher) && new Map(tmp), null, this.matcher); + this.mapper = parse_option((tmp = options.mapper) && new Map(tmp), null, this.mapper); + this.stemmer = parse_option((tmp = options.stemmer) && new Map(tmp), null, this.stemmer); + this.replacer = parse_option(options.replacer, null, this.replacer); + this.minlength = parse_option(options.minlength, 1, this.minlength); + this.maxlength = parse_option(options.maxlength, 0, this.maxlength); + + // minimum required tokenizer by this encoder + //this.tokenize = options["tokenize"] || ""; + + // auto-balanced cache + { + this.cache = tmp = parse_option(options.cache, true, this.cache); + if(tmp){ + this.timer = null; + this.cache_size = typeof tmp === "number" ? tmp : 2e5; + this.cache_enc = new Map(); + this.cache_prt = new Map(); + this.cache_enc_length = 128; + this.cache_prt_length = 128; + } + } + + // regex temporary state + this.matcher_str = ""; + this.matcher_test = null; + this.stemmer_str = ""; + this.stemmer_test = null; + + // prebuilt + // if(this.filter && this.split){ + // for(const key of this.filter){ + // const tmp = key.replace(this.split, ""); + // if(key !== tmp){ + // this.filter.delete(key); + // this.filter.add(tmp); + // } + // } + // } + if(this.matcher){ + for(const key of this.matcher.keys()){ + this.matcher_str += (this.matcher_str ? "|" : "") + key; + } + } + if(this.stemmer){ + for(const key of this.stemmer.keys()){ + this.stemmer_str += (this.stemmer_str ? "|" : "") + key; + } + } + + // if(SUPPORT_COMPRESSION){ + // this.compression = parse_option(options.compress || options.compression, 0, this.compression); + // if(this.compression && !table){ + // table = new Array(radix); + // for(let i = 0; i < radix; i++) table[i] = i + 33; + // table = String.fromCharCode.apply(null, table); + // } + // } + + return this; +}; + +Encoder.prototype.addMatcher = function(match, replace){ + // regex: + if(typeof match === "object"){ + return this.addReplacer(match, replace); + } + // a single char: + if(match.length < 2){ + return this.addMapper(match, replace); + } + this.matcher || (this.matcher = new Map()); + this.matcher.set(match , replace); + this.matcher_str += (this.matcher_str ? "|" : "") + match; + this.matcher_test = null; //new RegExp("(" + this.matcher_str + ")"); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addStemmer = function(match, replace){ + this.stemmer || (this.stemmer = new Map()); + this.stemmer.set(match, replace); + this.stemmer_str += (this.stemmer_str ? "|" : "") + match; + this.stemmer_test = null; //new RegExp("(" + this.stemmer_str + ")"); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addFilter = function(str){ + this.filter || (this.filter = new Set()); + this.filter.add(str); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addMapper = function(char_match, char_replace){ + // regex: + if(typeof char_match === "object"){ + return this.addReplacer(char_match, char_replace); + } + // not a char: + if(char_match.length > 1){ + return this.addMatcher(char_match, char_replace); + } + this.mapper || (this.mapper = new Map()); + this.mapper.set(char_match, char_replace); + this.cache && this.invalidate(); + return this; +}; + +Encoder.prototype.addReplacer = function(match, replace){ + if(typeof match === "string") match = new RegExp(match, "g"); + this.replacer || (this.replacer = []); + this.replacer.push(match, replace || ""); + this.cache && this.invalidate(); + return this; +}; + +{ + Encoder.prototype.invalidate = function(){ + this.cache_enc.clear(); + this.cache_prt.clear(); + }; +} + +Encoder.prototype.encode = function(str){ + + //if(!str) return str; + // todo remove dupe terms + + if(this.cache && str.length <= this.cache_enc_length){ + if(this.timer){ + if(this.cache_enc.has(str)){ + return this.cache_enc.get(str); + } + } + else { + this.timer = setTimeout(clear$1, 0, this); + } + } + + // 1. apply charset normalization + if(this.normalize){ + if(typeof this.normalize === "function"){ + str = this.normalize(str); + } + else if(normalize){ + str = str.normalize("NFKD").replace(normalize, "").toLowerCase(); + } + else { + str = str.toLowerCase(); + // if(SUPPORT_CHARSET){ + // this.mapper = this.mapper + // // todo replace spread + // ? new Map([.../** @type {!Iterable} */(normalize_mapper), ...this.mapper]) + // : new Map(/** @type {Map} */ (normalize_mapper)); + // } + } + //if(!str) return str; + } + + // 2. apply custom encoder (can replace split) + if(this.prepare){ + str = this.prepare(str); + } + + // 3. split numbers into triplets + if(this.numeric && str.length > 3){ + str = str.replace(numeric_split_prev_char, "$1 $2") + .replace(numeric_split_next_char, "$1 $2") + .replace(numeric_split_length, "$1 "); + } + + // if(this.matcher && (str.length > 1)){ + // this.matcher_test || ( + // this.matcher_test = new RegExp("(" + this.matcher_str + ")", "g") + // ); + // str = str.replace(this.matcher_test, match => this.matcher.get(match)); + // } + // if(this.stemmer){ + // this.stemmer_test || ( + // this.stemmer_test = new RegExp("(?!\\b)(" + this.stemmer_str + ")(\\b|_)", "g") + // ); + // str = str.replace(this.stemmer_test, match => this.stemmer.get(match)); + // } + + const skip = !(this.dedupe || this.mapper || this.filter || this.matcher || this.stemmer || this.replacer); + let final = []; + let words = this.split || this.split === "" + ? str.split(/** @type {string|RegExp} */ (this.split)) + : str; //[str]; + + for(let i = 0, word, base; i < words.length; i++){ + // filter empty entries + if(!(word = base = words[i])){ + continue; + } + if(word.length < this.minlength){ + continue; + } + if(skip) { + final.push(word); + continue; + } + + // 1. pre-filter before cache + if(this.filter && this.filter.has(word)){ + continue; + } + + if(this.cache && word.length <= this.cache_prt_length){ + if(this.timer){ + const tmp = this.cache_prt.get(word); + //if(this.cache_prt.has(word)){ + if(tmp || tmp === ""){ + //word = this.cache_prt.get(word); + tmp && final.push(tmp); + //word ? words[i] = word : words.splice(i--, 1); + continue; + } + } + else { + this.timer = setTimeout(clear$1, 0, this); + } + } + + let postfilter; + + // if(this.normalize === true && normalize){ + // word = word.normalize("NFKD").replace(normalize, ""); + // postfilter = 1; + // } + + // if(this.normalize){ + // if(typeof this.normalize === "function"){ + // word = this.normalize(word); + // } + // else if(normalize){ + // word = word.normalize("NFKD").replace(normalize, "").toLowerCase(); + // } + // else{ + // word = word.toLowerCase(); + // this.mapper = this.mapper + // ? new Map([...normalize_mapper, ...this.mapper]) + // : new Map(/** @type {Map} */ normalize_mapper); + // } + // postfilter = 1; + // //if(!str) return str; + // } + + // 2. apply stemmer after matcher + if(this.stemmer && (word.length > 2)){ + // for(const item of this.stemmer){ + // const key = item[0]; + // const value = item[1]; + // + // if(word.length > key.length && word.endsWith(key)){ + // word = word.substring(0, word.length - key.length) + value; + // break; + // } + // + // // const position = word.length - key.length; + // // if(position > 0 && word.substring(position) === key){ + // // word = word.substring(0, position) + value; + // // break; + // // } + // } + this.stemmer_test || ( + this.stemmer_test = new RegExp("(?!^)(" + this.stemmer_str + ")$") + ); + word = word.replace(this.stemmer_test, match => this.stemmer.get(match)); + postfilter = 1; + } + + // 3. apply matcher + if(this.matcher && (word.length > 1)){ + this.matcher_test || ( + this.matcher_test = new RegExp("(" + this.matcher_str + ")", "g") + ); + word = word.replace(this.matcher_test, match => this.matcher.get(match)); + postfilter = 1; + } + + // 4. post-filter after matcher and stemmer was applied + if(word && postfilter && (word.length < this.minlength || (this.filter && this.filter.has(word)))){ + word = ""; + } + + // 5. apply mapper and collapsing + if(word && (this.mapper || (this.dedupe && word.length > 1))){ + //word = this.replace_dedupe(word); + //word = replace_deduped(word, this.mapper, true); + let final = ""; + for(let i = 0, prev = "", char, tmp; i < word.length; i++){ + char = word.charAt(i); + if(char !== prev || !this.dedupe){ + tmp = this.mapper && this.mapper.get(char); + if(!tmp && tmp !== "") + final += (prev = char); + else if((tmp !== prev || !this.dedupe) && (prev = tmp)) + final += tmp; + } + } + word = final; + } + + // apply custom regex + if(word && this.replacer){ + for(let i = 0; word && (i < this.replacer.length); i+=2){ + word = word.replace(this.replacer[i], this.replacer[i+1]); + } + } + + // slower variants for removing same chars in a row: + //word = word.replace(/([^0-9])\1+/g, "$1"); + //word = word.replace(/(.)\1+/g, "$1"); + //word = word.replace(/(?<=(.))\1+/g, ""); + + // if(word){ + // words[i] = word; + // } + + if(this.cache && base.length <= this.cache_prt_length){ + this.cache_prt.set(base, word); + if(this.cache_prt.size > this.cache_size){ + this.cache_prt.clear(); + this.cache_prt_length = this.cache_prt_length / 1.1 | 0; + } + } + + //word || words.splice(i--, 1); + word && final.push(word); + } + + //words = final; + // else if(this.filter){ + // for(let i = 0, word; i < words.length; i++){ + // if((word = words[i]) && !this.filter.has(word)){ + // //filtered.push(word); + // words.splice(i--, 1); + // } + // } + // } + + if(this.finalize){ + final = this.finalize(final) || final; + } + + if(this.cache && str.length <= this.cache_enc_length){ + this.cache_enc.set(str, final); + if(this.cache_enc.size > this.cache_size){ + this.cache_enc.clear(); + this.cache_enc_length = this.cache_enc_length / 1.1 | 0; + } + } + + return final; +}; + +// Encoder.prototype.compress = function(str) { +// +// //return str; +// //if(!str) return str; +// +// if(SUPPORT_CACHE && this.cache && str.length <= this.cache_prt_length){ +// if(this.timer){ +// if(this.cache_cmp.has(str)){ +// return this.cache_cmp.get(str); +// } +// } +// else{ +// this.timer = setTimeout(clear, 0, this); +// } +// } +// +// const result = typeof this.compression === "function" +// ? this.compression(str) +// : hash(str); //window.hash(str); +// +// if(SUPPORT_CACHE && this.cache && str.length <= this.cache_prt_length){ +// this.cache_cmp.set(str, result); +// this.cache_cmp.size > this.cache_size && +// this.cache_cmp.clear(); +// } +// +// return result; +// }; + +// function hash(str){ +// return str; +// } + +function clear$1(self){ + self.timer = null; + self.cache_enc.clear(); + self.cache_prt.clear(); +} + +/** + * @param {string|Object} query + * @param {number|Object=} limit + * @param {Object=} options + * @this {Index|Document} + * @returns {Array|Promise} + */ + +function searchCache(query, limit, options){ + + query = (typeof query === "object" + ? "" + query.query + : "" + query + ).toLowerCase(); + + //let encoded = this.encoder.encode(query).join(" "); + let cache = this.cache.get(query); + if(!cache){ + cache = this.search(query, limit, options); + if(cache.then){ + const self = this; + cache.then(function(cache){ + self.cache.set(query, cache); + return cache; + }); + } + this.cache.set(query, cache); + } + return cache; +} + +/** + * @param {boolean|number=} limit + * @constructor + */ + +function CacheClass(limit){ + /** @private */ + this.limit = (!limit || limit === true) ? 1000 : limit; + /** @private */ + this.cache = new Map(); + /** @private */ + this.last = ""; +} + +CacheClass.prototype.set = function(key, value){ + //if(!this.cache.has(key)){ + this.cache.set(this.last = key, value); + if(this.cache.size > this.limit){ + this.cache.delete(this.cache.keys().next().value); + } + //} +}; + +CacheClass.prototype.get = function(key){ + const cache = this.cache.get(key); + if(cache && this.last !== key){ + this.cache.delete(key); + this.cache.set(this.last = key, cache); + } + return cache; +}; + +CacheClass.prototype.remove = function(id){ + for(const item of this.cache){ + const key = item[0]; + const value = item[1]; + if(value.includes(id)){ + this.cache.delete(key); + } + } +}; + +CacheClass.prototype.clear = function(){ + this.cache.clear(); + this.last = ""; +}; + +/** @type EncoderOptions */ +const options = { + normalize: function(str){ + return str.toLowerCase(); + }, + dedupe: false +}; + +// import { pipeline } from "../../lang.js"; +// +// const whitespace = /[\p{Z}\p{S}\p{P}\p{C}]+/u; +// +// export const rtl = false; +// export const tokenize = ""; +// export default { +// encode: encode, +// rtl: rtl, +// tokenize: tokenize +// } +// +// /** +// * @param {string|number} str +// */ +// +// export function encode(str){ +// +// return pipeline.call( +// +// this, +// /* string: */ ("" + str).toLowerCase(), +// /* normalize: */ false, +// /* split: */ whitespace, +// /* collapse: */ false +// ); +// } + +// KeystoreObj.prototype.destroy = function(){ +// this.index = null; +// this.keys = null; +// this.proxy = null; +// }; + +function _slice(self, start, end, splice){ + let arr = []; + for(let i = 0, index; i < self.index.length; i++){ + index = self.index[i]; + if(start >= index.length){ + start -= index.length; + } + else { + const tmp = index[splice ? "splice" : "slice"](start, end); + const length = tmp.length; + if(length){ + arr = arr.length + ? arr.concat(tmp) + : tmp; + end -= length; + if(splice) self.length -= length; + if(!end) break; + } + start = 0; + } + } + return arr; +} + +/** + * @param arr + * @constructor + */ + +function KeystoreArray(arr){ + + if(!this){ + return new KeystoreArray(arr); + } + + this.index = arr ? [arr] : []; + this.length = arr ? arr.length : 0; + const self = this; + + return /*this.proxy =*/ new Proxy([], { + get(target, key) { + if(key === "length"){ + return self.length; + } + if(key === "push"){ + return function(value){ + self.index[self.index.length - 1].push(value); + self.length++; + } + } + if(key === "pop"){ + return function(){ + if(self.length){ + self.length--; + return self.index[self.index.length - 1].pop(); + } + } + } + if(key === "indexOf"){ + return function(key){ + let index = 0; + for(let i = 0, arr, tmp; i < self.index.length; i++){ + arr = self.index[i]; + //if(!arr.includes(key)) continue; + tmp = arr.indexOf(key); + if(tmp >= 0) return index + tmp; + index += arr.length; + } + return -1; + } + } + if(key === "includes"){ + return function(key){ + for(let i = 0; i < self.index.length; i++){ + if(self.index[i].includes(key)){ + return true; + } + } + return false; + } + } + if(key === "slice"){ + return function(start, end){ + return _slice( + self, + start || 0, + end || self.length, + false + ); + } + } + if(key === "splice"){ + return function(start, end){ + return _slice( + self, + start || 0, + end || self.length, + // splice: + true + ); + } + } + if(key === "constructor"){ + return Array; + } + if(typeof key === "symbol" /*|| isNaN(key)*/){ + // not supported + return; + } + const index = key / (2**31) | 0; + const arr = self.index[index]; + return arr && arr[key]; + }, + set(target, key, value){ + const index = key / (2**31) | 0; + const arr = self.index[index] || (self.index[index] = []); + arr[key] = value; + self.length++; + return true; + } + }); +} + +KeystoreArray.prototype.clear = function(){ + this.index.length = 0; +}; + +KeystoreArray.prototype.destroy = function(){ + this.index = null; + this.proxy = null; +}; + +KeystoreArray.prototype.push = function(val){}; + +/** + * @param bitlength + * @constructor + */ + +function KeystoreMap(bitlength = 8){ + + if(!this){ + return new KeystoreMap(bitlength); + } + + this.index = create_object(); + this.refs = []; + this.size = 0; + + if(bitlength > 32){ + this.crc = lcg64; + this.bit = BigInt(bitlength); + } + else { + this.crc = lcg$1; + this.bit = bitlength; + } +} + +KeystoreMap.prototype.get = function(key) { + const address = this.crc(key); + const map = this.index[address]; + return map && map.get(key); +}; + +KeystoreMap.prototype.set = function(key, value){ + const address = this.crc(key); + let map = this.index[address]; + if(map){ + let size = map.size; + map.set(key, value); + size -= map.size; + size && this.size++; + } + else { + this.index[address] = map = new Map([[key, value]]); + this.refs.push(map); + } +}; + +/** + * @param bitlength + * @constructor + */ + +function KeystoreSet(bitlength = 8){ + + if(!this){ + return new KeystoreSet(bitlength); + } + + // using plain Object with numeric key access + // just for max performance + this.index = create_object(); + this.refs = []; + + if(bitlength > 32){ + this.crc = lcg64; + this.bit = BigInt(bitlength); + } + else { + this.crc = lcg$1; + this.bit = bitlength; + } +} + +KeystoreSet.prototype.add = function(key){ + const address = this.crc(key); + let set = this.index[address]; + if(set){ + let size = set.size; + set.add(key); + size -= set.size; + size && this.size++; + } + else { + this.index[address] = set = new Set([key]); + this.refs.push(set); + } +}; + +KeystoreMap.prototype.has = +KeystoreSet.prototype.has = function(key) { + const address = this.crc(key); + const map_or_set = this.index[address]; + return map_or_set && map_or_set.has(key); +}; + +/* +KeystoreMap.prototype.size = +KeystoreSet.prototype.size = function(){ + let size = 0; + const values = Object.values(this.index); + for(let i = 0; i < values.length; i++){ + size += values[i].size; + } + return size; +}; +*/ + +KeystoreMap.prototype.delete = +KeystoreSet.prototype.delete = function(key){ + const address = this.crc(key); + const map_or_set = this.index[address]; + // set && (set.size === 1 + // ? this.index.delete(address) + // : set.delete(key)); + map_or_set && + map_or_set.delete(key) && + this.size--; +}; + +KeystoreMap.prototype.clear = +KeystoreSet.prototype.clear = function(){ + this.index = create_object(); + this.refs = []; + this.size = 0; +}; + +// KeystoreMap.prototype.destroy = +// KeystoreSet.prototype.destroy = function(){ +// this.index = null; +// this.refs = null; +// this.proxy = null; +// }; + +KeystoreMap.prototype.values = +KeystoreSet.prototype.values = function*(){ + // alternatively iterate through this.keys[] + //const refs = Object.values(this.index); + for(let i = 0; i < this.refs.length; i++){ + for(let value of this.refs[i].values()){ + yield value; + } + } +}; + +KeystoreMap.prototype.keys = +KeystoreSet.prototype.keys = function*(){ + //const values = Object.values(this.index); + for(let i = 0; i < this.refs.length; i++){ + for(let key of this.refs[i].keys()){ + yield key; + } + } +}; + +KeystoreMap.prototype.entries = +KeystoreSet.prototype.entries = function*(){ + //const values = Object.values(this.index); + for(let i = 0; i < this.refs.length; i++){ + for(let entry of this.refs[i].entries()){ + yield entry; + } + } +}; + +/** + * Linear Congruential Generator (LCG) + * @param str + * @this {KeystoreMap|KeystoreSet} + */ + +function lcg$1(str) { + let range = 2 ** this.bit - 1; + if(typeof str == "number"){ + return str & range; + } + let crc = 0, bit = this.bit + 1; + for(let i = 0; i < str.length; i++) { + crc = (crc * bit ^ str.charCodeAt(i)) & range; + } + // shift Int32 to UInt32 because negative numbers + // extremely slows down key lookup + return this.bit === 32 + ? crc + 2 ** 31 + : crc;// & 0xFFFF; +} + +/** + * @param str + * @this {KeystoreMap|KeystoreSet} + */ + +function lcg64(str) { + let range = BigInt(2) ** /** @type {!BigInt} */ (this.bit) - BigInt(1); + let type = typeof str; + if(type === "bigint"){ + return /** @type {!BigInt} */ (str) & range; + } + if(type === "number"){ + return BigInt(str) & range; + } + let crc = BigInt(0), bit = /** @type {!BigInt} */ (this.bit) + BigInt(1); + for(let i = 0; i < str.length; i++){ + crc = (crc * bit ^ BigInt(str.charCodeAt(i))) & range; + } + return crc;// & 0xFFFFFFFFFFFFFFFF; +} + +// TODO return promises instead of inner await + + +function async(callback, self, field, key, index_doc, index, data, on_done){ + + //setTimeout(function(){ + + const res = callback(field ? field + "." + key : key, JSON.stringify(data)); + + // await isn't supported by ES5 + + if(res && res["then"]){ + + res["then"](function(){ + + self.export(callback, self, field, index_doc, index + 1, on_done); + }); + } + else { + + self.export(callback, self, field, index_doc, index + 1, on_done); + } + //}); +} + +/** + * @param callback + * @param self + * @param field + * @param index_doc + * @param index + * @param on_done + * @this {Index|Document} + */ + +function exportIndex(callback, self, field, index_doc, index, on_done){ + + let return_value = true; + if (typeof on_done === 'undefined') { + return_value = new Promise((resolve) => { + on_done = resolve; + }); + } + + let key, data; + + switch(index || (index = 0)){ + + case 0: + + key = "reg"; + + // fastupdate isn't supported by export + + if(this.fastupdate){ + + data = create_object(); + + for(let key of this.reg.keys()){ + + data[key] = 1; + } + } + else { + + data = this.reg; + } + + break; + + case 1: + + key = "cfg"; + data = { + "doc": 0, + "opt": this.optimize ? 1 : 0 + }; + + break; + + case 2: + + key = "map"; + data = this.map; + break; + + case 3: + + key = "ctx"; + data = this.ctx; + break; + + default: + + if (typeof field === 'undefined' && on_done) { + + on_done(); + } + + return; + } + + async(callback, self || this, field, key, index_doc, index, data, on_done); + + return return_value; +} + +/** + * @this Index + */ + +function importIndex(key, data){ + + if(!data){ + + return; + } + + if(is_string(data)){ + + data = JSON.parse(data); + } + + switch(key){ + + case "cfg": + + this.optimize = !!data["opt"]; + break; + + case "reg": + + // fastupdate isn't supported by import + + this.fastupdate = false; + this.reg = data; + break; + + case "map": + + this.map = data; + break; + + case "ctx": + + this.ctx = data; + break; + } +} + +/** + * @this Document + */ + +function exportDocument(callback, self, field, index_doc, index, on_done){ + + let return_value; + if (typeof on_done === 'undefined') { + return_value = new Promise((resolve) => { + on_done = resolve; + }); + } + + index || (index = 0); + index_doc || (index_doc = 0); + + if(index_doc < this.field.length){ + + const field = this.field[index_doc]; + const idx = this.index[field]; + + self = this; + + //setTimeout(function(){ + + if(!idx.export(callback, self, index ? field/*.replace(":", "-")*/ : "", index_doc, index++, on_done)){ + + index_doc++; + index = 1; + + self.export(callback, self, field, index_doc, index, on_done); + } + //}); + } + else { + + let key, data; + + switch(index){ + + case 1: + + key = "tag"; + data = this.tagindex; + field = null; + break; + + case 2: + + key = "store"; + data = this.store; + field = null; + break; + + // case 3: + // + // key = "reg"; + // data = this.register; + // break; + + default: + + on_done(); + return; + } + + async(callback, this, field, key, index_doc, index, data, on_done); + } + + return return_value +} + +/** + * @this Document + */ + +function importDocument(key, data){ + + if(!data){ + + return; + } + + if(is_string(data)){ + + data = JSON.parse(data); + } + + switch(key){ + + case "tag": + + this.tagindex = data; + break; + + case "reg": + + // fastupdate isn't supported by import + + this.fastupdate = false; + this.reg = data; + + for(let i = 0, index; i < this.field.length; i++){ + + index = this.index[this.field[i]]; + index.reg = data; + index.fastupdate = false; + } + + break; + + case "store": + + this.store = data; + break; + + default: + + key = key.split("."); + const field = key[0]; + key = key[1]; + + if(field && key){ + + this.index[field].import(key, data); + } + } +} + +// COMPILER BLOCK --> + +/** + * @enum {Object} + * @const + */ + +const presets = { + + "memory": { + resolution: 1 + }, + + "performance": { + resolution: 6, + fastupdate: true, + context: { + depth: 1, + resolution: 3 + } + }, + + "match": { + tokenize: "forward" + }, + + "score": { + resolution: 9, + context: { + depth: 2, + resolution: 9 + } + } +}; + +/** + * + * @param {!IndexOptions|string} options + * @return {IndexOptions} + */ + +function apply_preset(options){ + + const preset = is_string(options) + ? options + : options["preset"]; + + if(preset){ + if(!presets[preset]){ + console.warn("Preset not found: " + preset); + } + options = Object.assign({}, presets[preset], /** @type {Object} */ (options)); + } + + return options; +} + +function apply_async(prototype){ + register$1.call(prototype, "add"); + register$1.call(prototype, "append"); + register$1.call(prototype, "search"); + register$1.call(prototype, "update"); + register$1.call(prototype, "remove"); +} + +// let cycle; +// let budget = 0; +// +// function tick(resolve){ +// cycle = null; +// budget = 0; +// resolve(); +// } + +/** + * @param {!string} key + * @this {Index|Document} + */ + +function register$1(key){ + this[key + "Async"] = function(){ + + // // prevent stack overflow of adding too much tasks to the same event loop + // // actually limit stack to 1,000,000 tasks every ~4ms + // cycle || ( + // cycle = new Promise(resolve => setTimeout(tick, 0, resolve)) + // ); + // + // // apply different performance budgets + // if(key === "update" || key === "remove" && this.fastupdate === false){ + // budget += 1000 * this.resolution; + // if(this.depth) + // budget += 1000 * this.resolution_ctx; + // } + // else if(key === "search"){ + // budget++; + // } + // else{ + // budget += 20 * this.resolution; + // if(this.depth) + // budget += 20 * this.resolution_ctx; + // } + // + // // wait for the event loop cycle + // if(budget >= 1e6){ + // await cycle; + // } + + const args = /*[].slice.call*/(arguments); + const arg = args[args.length - 1]; + let callback; + + if(typeof arg === "function"){ + callback = arg; + delete args[args.length - 1]; + } + + this.async = true; + const res = this[key].apply(this, args); + this.async = false; + res.then ? res.then(callback) + : callback(res); + return res; + }; +} + +const data = create_object(); + +/** + * @param {!string} name + */ + +function tick(name){ + + /** @type {!Object} */ + const profiler = data["profiler"] || (data["profiler"] = {}); + + profiler[name] || (profiler[name] = 0); + profiler[name]++; +} + +// COMPILER BLOCK --> +// <-- COMPILER BLOCK + +let table;// = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; +let timer; +const cache = new Map(); + +function toRadix(number, radix = 255) { + + if(!table){ + table = new Array(radix); + // the char code 0 could be a special marker + for(let i = 0; i < radix; i++) table[i] = i + 1; + table = String.fromCharCode.apply(null, table); + } + + let rixit; + let residual = number; + let result = ""; + + while(true){ + rixit = residual % radix; + result = table.charAt(rixit) + result; + residual = residual / radix | 0; + if(!residual) + break; + } + + return result; +} + +function default_compress(str){ + + { + if(timer){ + if(cache.has(str)){ + return cache.get(str); + } + } + else { + timer = setTimeout(clear); + } + } + + /* 2 ** ((level + 1.5) * 1.6 | 0) */ + + const result = toRadix( + typeof str == "number" + ? str + : lcg(str) + ); + + { + cache.size > 2e5 && cache.clear(); + cache.set(str, result); + } + + return result; +} + +function lcg(str) { + let range = 2 ** 32 - 1; + if(typeof str == "number"){ + return str & range; + } + let crc = 0, bit = 32 + 1; + for(let i = 0; i < str.length; i++) { + crc = (crc * bit ^ str.charCodeAt(i)) & range; + } + // shift up from Int32 to UInt32 range 0xFFFFFFFF + return crc + 2 ** 31; +} + +function clear(){ + timer = null; + cache.clear(); +} + +// COMPILER BLOCK --> + +// TODO: +// string + number as text +// boolean, null, undefined as ? + + +/** + * @param {!number|string} id + * @param {!string} content + * @param {boolean=} _append + * @param {boolean=} _skip_update + */ + +Index.prototype.add = function(id, content, _append, _skip_update){ + + if(content && (id || (id === 0))){ + + // todo check skip_update + //_skip_update = false; + + if(!_skip_update && !_append){ + if(this.reg.has(id)){ + return this.update(id, content); + } + } + + // do not force a string as input + // https://github.com/nextapps-de/flexsearch/issues/432 + content = this.encoder.encode(content); + const word_length = content.length; + + if(word_length){ + + // check context dupes to skip all contextual redundancy along a document + + const dupes_ctx = create_object(); + const dupes = create_object(); + const depth = this.depth; + const resolution = this.resolution; + + for(let i = 0; i < word_length; i++){ + + let term = content[this.rtl ? word_length - 1 - i : i]; + let term_length = term.length; + + // skip dupes will break the context chain + + if(term_length /*&& (term_length >= this.minlength)*/ && (depth || !dupes[term])){ + + let score = this.score + ? this.score(content, term, i, null, 0) + : get_score(resolution, word_length, i); + let token = ""; + + switch(this.tokenize){ + + case "full": + if(term_length > 2){ + for(let x = 0; x < term_length; x++){ + for(let y = term_length; y > x; y--){ + + //if((y - x) >= this.minlength){ + token = term.substring(x, y); + const partial_score = this.score + ? this.score(content, term, i, token, x) + : get_score(resolution, word_length, i, term_length, x); + this.push_index(dupes, token, partial_score, id, _append); + //} + } + } + break; + } + // fallthrough to next case when term length < 3 + case "reverse": + // skip last round (this token exist already in "forward") + if(term_length > 1){ + for(let x = term_length - 1; x > 0; x--){ + token = term[x] + token; + //if(token.length >= this.minlength){ + const partial_score = this.score + ? this.score(content, term, i, token, x) + : get_score(resolution, word_length, i, term_length, x); + this.push_index(dupes, token, partial_score, id, _append); + //} + } + token = ""; + } + + // fallthrough to next case to apply forward also + case "forward": + if(term_length > 1){ + for(let x = 0; x < term_length; x++){ + token += term[x]; + //if(token.length >= this.minlength){ + this.push_index(dupes, token, score, id, _append); + //} + } + break; + } + + // fallthrough to next case when token has a length of 1 + default: + // case "strict": + + // todo move boost to search + // if(this.boost){ + // score = Math.min((score / this.boost(content, term, i)) | 0, resolution - 1); + // } + + this.push_index(dupes, term, score, id, _append); + + // context is just supported by tokenizer "strict" + if(depth){ + + if((word_length > 1) && (i < (word_length - 1))){ + + // check inner dupes to skip repeating words in the current context + const dupes_inner = create_object(); + const resolution = this.resolution_ctx; + const keyword = term; + const size = Math.min(depth + 1, word_length - i); + + dupes_inner[keyword] = 1; + + for(let x = 1; x < size; x++){ + + term = content[this.rtl ? word_length - 1 - i - x : i + x]; + + if(term /*&& (term.length >= this.minlength)*/ && !dupes_inner[term]){ + + dupes_inner[term] = 1; + + const context_score = this.score + ? this.score(content, keyword, i, term, x) + : get_score(resolution + ((word_length / 2) > resolution ? 0 : 1), word_length, i, size - 1, x - 1); + const swap = this.bidirectional && (term > keyword); + this.push_index(dupes_ctx, swap ? keyword : term, context_score, id, _append, swap ? term : keyword); + } + } + } + } + } + } + } + + this.fastupdate || this.reg.add(id); + } + else { + content = ""; + } + } + + if(this.db){ + // when the term has no valid content (e.g. empty), + // then it was not added to the ID registry for removal + content || this.commit_task.push({ "del": id }); + this.commit_auto && autoCommit(this); + } + + return this; +}; + +/** + * @private + * @param dupes + * @param term + * @param score + * @param id + * @param {boolean=} append + * @param {string=} keyword + */ + +Index.prototype.push_index = function(dupes, term, score, id, append, keyword){ + + let arr = keyword ? this.ctx : this.map; + let tmp; + + if(!dupes[term] || !keyword || !(tmp = dupes[term])[keyword]){ + + if(keyword){ + + dupes = tmp || (dupes[term] = create_object()); + dupes[keyword] = 1; + + if(this.compress){ + keyword = default_compress(keyword); + } + + tmp = arr.get(keyword); + tmp ? arr = tmp + : arr.set(keyword, arr = new Map()); + } + else { + + dupes[term] = 1; + } + + if(this.compress){ + term = default_compress(term); + } + + tmp = arr.get(term); + tmp ? arr = tmp : arr.set(term, arr = tmp = []); + // the ID array will be upgraded dynamically + arr = arr[score] || (arr[score] = []); + + if(!append || !arr.includes(id)){ + + // auto-upgrade to keystore array if max size exceeded + { + if(arr.length === 2**31-1 /*|| !(arr instanceof KeystoreArray)*/){ + const keystore = new KeystoreArray(arr); + if(this.fastupdate){ + for(let value of this.reg.values()){ + if(value.includes(arr)){ + value[value.indexOf(arr)] = keystore; + } + } + } + tmp[score] = arr = keystore; + } + } + + arr.push(id); + + // add a reference to the register for fast updates + if(this.fastupdate){ + const tmp = this.reg.get(id); + tmp ? tmp.push(arr) + : this.reg.set(id, [arr]); + } + } + } +}; + +/** + * @param {number} resolution + * @param {number} length + * @param {number} i + * @param {number=} term_length + * @param {number=} x + * @returns {number} + */ + +function get_score(resolution, length, i, term_length, x){ + + // console.log("resolution", resolution); + // console.log("length", length); + // console.log("term_length", term_length); + // console.log("i", i); + // console.log("x", x); + // console.log((resolution - 1) / (length + (term_length || 0)) * (i + (x || 0)) + 1); + + // the first resolution slot is reserved for the best match, + // when a query matches the first word(s). + + // also to stretch score to the whole range of resolution, the + // calculation is shift by one and cut the floating point. + // this needs the resolution "1" to be handled additionally. + + // do not stretch the resolution more than the term length will + // improve performance and memory, also it improves scoring in + // most cases between a short document and a long document + + return i && (resolution > 1) ? ( + + (length + (term_length || 0)) <= resolution ? + + i + (x || 0) + : + ((resolution - 1) / (length + (term_length || 0)) * (i + (x || 0)) + 1) | 0 + ): + 0; +} + +/* + from -> res[score][id] + to -> [id] +*/ + +/** + * Aggregate the union of a single raw result + * @param {!Array} result + * @param {!number} limit + * @param {number=} offset + * @param {boolean=} enrich + * @return Array + */ + +function resolve_default(result, limit, offset, enrich){ + + // fast path: when there is just one slot in the result + if(result.length === 1){ + result = result[0]; + result = offset || (result.length > limit) + ? (limit + ? result.slice(offset, offset + limit) + : result.slice(offset) + ) + : result; + return enrich + ? enrich_result(result) + : result; + } + + // this is an optimized workaround instead of + // just doing result = concat(result) + + let final = []; + + for(let i = 0, arr, len; i < result.length; i++){ + if(!(arr = result[i]) || !(len = arr.length)) continue; + + if(offset){ + // forward offset pointer + if(offset >= len){ + offset -= len; + continue; + } + // apply offset pointer when length differs + if(offset < len){ + arr = limit + ? arr.slice(offset, offset + limit) + : arr.slice(offset); + len = arr.length; + offset = 0; + } + } + + if(!final.length){ + // fast path: when limit was reached in first slot + if(len >= limit){ + if(len > limit){ + arr = arr.slice(0, limit); + } + return enrich + ? enrich_result(arr) + : arr; + } + final = [arr]; + } + else { + if(len > limit){ + arr = arr.slice(0, limit); + len = arr.length; + } + final.push(arr); + } + + // reduce limit + limit -= len; + + // todo remove + // if(limit < 0){ + // throw new Error("Impl.Error"); + // } + + // break if limit was reached + if(!limit){ + break; + } + } + + // todo remove + if(!final.length){ + //throw new Error("No results found"); + return final; + } + + final = final.length > 1 + ? concat(final) + : final[0]; + + return enrich + ? enrich_result(final) + : final; +} + +function enrich_result(ids){ + for(let i = 0; i < ids.length; i++){ + ids[i] = { + score: i, + id: ids[i] + }; + } + return ids; +} + +// import xor from "./xor.js"; +// import and from "./and.js"; +// import not from "./not.js"; + +Resolver.prototype.or = function(){ + + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.or.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.or.apply(this, first_argument); + } + } + + // for(let i = 0; i < args.length; i++){ + // if(args[i].result instanceof Promise){ + // return; + // } + // } + + // if(args.length < 2){ + // if(first_argument.index){ + // first_argument.resolve = false; + // const result = first_argument.index.search(first_argument); + // if(result instanceof Promise){ + // result.then(function(result){ + // final = self.result.concat(result); + // self.result = resolver(final, limit, offset, enrich, !resolve); + // return resolve ? self.result : self; + // }); + // } + // else{ + // final = this.result.concat(result); + // this.result = resolver(final, limit, offset, enrich, !resolve); + // return resolve ? this.result : this; + // } + // } + // } + + let final = []; + let promises = []; + let limit = 0, offset = 0, enrich, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.and){ + result = this.and(query.and); + } + else if(query.xor){ + result = this.xor(query.xor); + } + else if(query.not){ + result = this.not(query.not); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + enrich = query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + self.result.length && (final = [self.result].concat(final)); + self.result = resolver(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? self.result : self; + }); + } + + this.result.length && (final = [this.result].concat(final)); + this.result = resolver(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? this.result : this; +}; + +/** + * Aggregate the union of N raw results + * @param result + * @param limit + * @param offset + * @param enrich + * @param resolve + * @param boost + * @return {Array} + */ + +function resolver(result, limit, offset, enrich, resolve, boost){ + + if(!result.length){ + // todo remove + //console.log("Empty Result") + return result; + } + + if(typeof limit === "object"){ + offset = limit.offset || 0; + enrich = limit.enrich || false; + limit = limit.limit || 0; + } + + if(result.length < 2){ + // todo remove + //console.log("Single Result") + if(resolve){ + return resolve_default(result[0], limit, offset, enrich); + } + else { + return result[0]; + } + } + + let final = []; + let count = 0; + let dupe = create_object(); + let maxres = get_max_len(result); + + for(let j = 0, ids; j < maxres; j++){ + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res) continue; + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + if(!dupe[id]){ + dupe[id] = 1; + if(offset){ + offset--; + continue; + } + if(resolve){ + final.push(id); + } + else { + // shift resolution by boost (inverse) + const index = j + (i ? boost : 0); + final[index] || (final[index] = []); + final[index].push(id); + } + if(limit && ++count === limit){ + //this.boost = 0; + return final; + } + } + } + } + } + + //this.boost = 0; + return final; +} + +// import xor from "./xor.js"; +// import or from "./or.js"; +// import not from "./not.js"; + +Resolver.prototype.and = function(){ + if(this.result.length){ + + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.and.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.and.apply(this, first_argument); + } + } + + // for(let i = 0; i < args.length; i++){ + // if(args[i].result instanceof Promise){ + // return; + // } + // } + + // if(args.length < 2){ + // if(first_argument.index){ + // first_argument.resolve = false; + // return first_argument.index.search(first_argument); + // } + // } + + let final = []; + let promises = []; + let limit = 0, offset = 0, enrich, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.or){ + result = this.or(query.or); + } + else if(query.xor){ + result = this.xor(query.xor); + } + else if(query.not){ + result = this.not(query.not); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + enrich = query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + final = [self.result].concat(final); + self.result = intersect$1(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? self.result : self; + }); + } + + final = [this.result].concat(final); + this.result = intersect$1(final, limit, offset, enrich, resolve, self.boostval); + return resolve ? this.result : this; + } + return this; +}; + +/** + * Aggregate the intersection of N raw results + * @param result + * @param limit + * @param offset + * @param enrich + * @param resolve + * @param boost + * @return {Array} + */ + +function intersect$1(result, limit, offset, enrich, resolve, boost){ + + // if(!result.length){ + // // todo remove + // console.log("Empty Result") + // return result; + // } + + if(result.length < 2){ + // todo remove + //console.log("Single Result") + return []; + // if(resolve){ + // return default_resolver(result[0], limit, offset, enrich); + // } + // else{ + // return result[0]; + // } + } + + let final = []; + let count = 0; + + // fast path single slot + // if(result.length < 2){ + // if(limit || offset){ + // let res = result[0]; + // for(let j = 0, ids; j < res.length; j++){ + // ids = res[j]; + // if(!ids) continue; + // for(let k = 0, id; k < ids.length; k++){ + // id = ids[k]; + // if(offset){ + // offset--; + // continue; + // } + // if(resolve){ + // final.push(id); + // } + // else{ + // final[j + this.boost] || (final[j + this.boost] = []); + // final[j + this.boost].push(id); + // } + // if(limit && ++count === limit){ + // this.boost = 0; + // return final; + // } + // } + // } + // } + // this.boost = 0; + // return result[0]; + // } + + let contain = create_object(); + let maxres = get_max_len(result); + if(!maxres) return final; + + // for(let j = 0, ids, res = result[0]; j < res.length; j++){ + // ids = res[j]; + // for(let k = 0; k < ids.length; k++){ + // contain[ids[k]] = 1; + // } + // } + + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res || !res.length) return []; + let contain_new = create_object(); + let match = 0; + let last_round = i === result.length - 1; + + for(let j = 0, ids; j < maxres; j++){ + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id, min; k < ids.length; k++){ + id = ids[k]; + // fill in first round + if(!i){ + // shift resolution +1 + // shift resolution by boost (inverse) + contain_new[id] = j + 1 + (i ? boost : 0); + match = 1; + } + // result in last round + else if(last_round){ + if((min = contain[id])){ + match = 1; + //if(!contain_new[id]){ + if(offset){ + offset--; + continue; + } + if(resolve){ + final.push(id); + } + else { + // reduce resolution -1 + min--; + if(j < min) min = j; + final[min] || (final[min] = []); + final[min].push(id); + } + if(limit && ++count === limit){ + //this.boost = 0; + return final; + } + // shift resolution +1 + //contain_new[id] = min + 1; + //} + } + } + // check for intersection + else if((min = contain[id])){ + // shift resolution +1 + if(j + 1 < min) min = j + 1; + contain_new[id] = min; + match = 1; + } + } + } + + if(!match){ + //this.boost = 0; + return []; + } + + contain = contain_new; + } + + //this.boost = 0; + return final; +} + +// import or from "./or.js"; +// import and from "./and.js"; +// import not from "./not.js"; + +Resolver.prototype.xor = function(){ + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.xor.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.xor.apply(this, first_argument); + } + } + + let final = []; + let promises = []; + let limit = 0, offset = 0, enrich, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.or){ + result = this.or(query.or); + } + else if(query.and){ + result = this.and(query.and); + } + else if(query.not){ + result = this.not(query.not); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + enrich = query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + self.result.length && (final = [self.result].concat(final)); + self.result = exclusive(final, limit, offset, enrich, !resolve, self.boostval); + return resolve ? self.result : self; + }); + } + + this.result.length && (final = [this.result].concat(final)); + this.result = exclusive(final, limit, offset, enrich, !resolve, self.boostval); + return resolve ? this.result : this; +}; + +/** + * @param result + * @param limit + * @param offset + * @param enrich + * @param resolve + * @param boost + * @return {Array} + */ + +function exclusive(result, limit, offset, enrich, resolve, boost){ + + if(!result.length){ + // todo remove + //console.log("Empty Result") + return result; + } + + if(result.length < 2){ + // todo remove + //console.log("Single Result") + if(resolve){ + return resolve_default(result[0], limit, offset, enrich); + } + else { + return result[0]; + } + } + + const final = []; + const check = create_object(); + + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res) continue; + + for(let j = 0, ids; j < res.length; j++){ + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + check[id] + ? check[id]++ + : check[id] = 1; + } + } + } + + for(let i = 0, res; i < result.length; i++){ + res = result[i]; + if(!res) continue; + + for(let j = 0, ids; j < res.length; j++){ + ids = res[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + if(check[id] === 1){ + if(resolve){ + final.push(id); + } + else { + // shift resolution by boost (inverse) + const index = j + (i ? boost : 0); + final[index] || (final[index] = []); + final[index].push(id); + } + } + } + } + } + + //this.boost = 0; + return final; +} + +// import or from "./or.js"; +// import and from "./and.js"; +// import xor from "./xor.js"; + +Resolver.prototype.not = function(){ + const self = this; + let args = arguments; + let first_argument = args[0]; + + if(first_argument.then){ + return first_argument.then(function(){ + return self.not.apply(self, args); + }); + } + + if(first_argument[0]){ + // fix false passed parameter style + if(first_argument[0].index){ + return this.not.apply(this, first_argument); + } + } + + let final = []; + let promises = []; + let limit = 0, offset = 0, resolve; + + for(let i = 0, query; i < args.length; i++){ + if((query = args[i])){ + + let result; + if(query.constructor === Resolver){ + result = query.result; + } + else if(query.constructor === Array){ + result = query; + } + else if(query.index){ + query.resolve = false; + result = query.index.search(query).result; + } + else if(query.or){ + result = this.or(query.or); + } + else if(query.and){ + result = this.and(query.and); + } + else if(query.xor){ + result = this.xor(query.xor); + } + else { + limit = query.limit || 0; + offset = query.offset || 0; + query.enrich; + resolve = query.resolve; + continue; + } + + final[i] = result; + + if(result.then){ + promises.push(result); //{ query, result }; + } + } + } + + if(promises.length){ + return Promise.all(promises).then(function(){ + self.result = exclusion.call(self, final, limit, offset, resolve); + return resolve ? self.result : self; + }); + } + + this.result = exclusion.call(this, final, limit, offset, resolve); + return resolve ? this.result : this; +}; + +/** + * @param result + * @param limit + * @param offset + * @param resolve + * @this Resolver + * @return {Array} + */ + +function exclusion(result, limit, offset, resolve){ + + if(!result.length){ + return this.result; + } + + const final = []; + const exclude = new Set(result.flat().flat()); + + for(let j = 0, ids; j < this.result.length; j++){ + ids = this.result[j]; + if(!ids) continue; + + for(let k = 0, id; k < ids.length; k++){ + id = ids[k]; + if(!exclude.has(id)){ + if(resolve){ + final.push(id); + } + else { + final[j] || (final[j] = []); + final[j].push(id); + } + } + } + } + + return final; +} + +/** + * @param result + * @constructor + */ + +function Resolver(result){ + if(!this){ + return new Resolver(result); + } + if(result && result.index){ + result.resolve = false; + this.index = result.index; + return result.index.search(result); + } + if(result.constructor === Resolver){ + // todo test this branch + //console.log("Resolver Loopback") + return result; + } + this.index = null; + this.result = result || []; + this.boostval = 0; +} + +Resolver.prototype.limit = function(limit){ + if(this.result.length){ + const final = []; + let count = 0; + for(let j = 0, ids; j < this.result.length; j++){ + ids = this.result[j]; + if(ids.length + count < limit){ + final[j] = ids; + count += ids.length; + } + else { + final[j] = ids.slice(0, limit - count); + this.result = final; + break; + } + } + } + return this; +}; + +Resolver.prototype.offset = function(offset){ + if(this.result.length){ + const final = []; + let count = 0; + for(let j = 0, ids; j < this.result.length; j++){ + ids = this.result[j]; + if(ids.length + count < offset){ + count += ids.length; + } + else { + final[j] = ids.slice(offset - count); + count = offset; + } + } + this.result = final; + } + return this; +}; + +Resolver.prototype.boost = function(boost){ + this.boostval += boost; + return this; +}; + +Resolver.prototype.resolve = function(limit, offset, enrich){ + set_resolve(1); + const result = this.result; + this.index = null; + this.result = null; + + if(result.length){ + if(typeof limit === "object"){ + enrich = limit.enrich; + offset = limit.offset; + limit = limit.limit; + } + return resolve_default(result, limit || 100, offset, enrich); + } + + return result; +}; + +/* + + from -> result[ + res[score][id], + res[score][id], + ] + + to -> [id] + + */ + +/** + * Implementation based on Object[key] provides better suggestions + * capabilities and has less performance scaling issues on large indexes. + * + * @param arrays + * @param limit + * @param offset + * @param {boolean|Array=} suggest + * @param {boolean=} resolve + * @returns {Array} + */ + +function intersect(arrays, limit, offset, suggest, resolve) { + + const length = arrays.length; + + // todo remove + // if(length < 2){ + // throw new Error("Not optimized intersect"); + // } + + let result = []; + let size = 0; + let check; + let check_suggest; + let check_new; + let res_arr; + + if(suggest){ + suggest = []; + } + + // 1. a reversed order prioritize the order of words from a query + // because it ends with the first term. + // 2. process terms in reversed order often has advantage for + // the fast path "end reached". + + // alternatively the results could be sorted by length up + //arrays.sort(sort_by_length_up); + + // todo the outer loop should be the res array instead of term array, + // this isn't just simple because the intersection calculation needs to reflect this + //const maxlen = get_max_len(arrays); + + for(let x = length - 1, found; x >= 0; x--){ + //for(let x = 0, found; x < length; x++){ + + res_arr = arrays[x]; + check_new = create_object(); + found = !check; + + // process relevance in forward order (direction is + // important for adding IDs during the last round) + + for(let y = 0, ids; y < res_arr.length; y++){ + + ids = res_arr[y]; + if(!ids || !ids.length) continue; + + for(let z = 0, id; z < ids.length; z++){ + + id = ids[z]; + + // check exists starting from the 2nd slot + if(check){ + if(check[id]){ + + // check if in last round + if(!x){ + //if(x === length - 1){ + + if(offset){ + offset--; + } + else { + + result[size++] = id; + + if(size === limit){ + // fast path "end reached" + return result /*resolve === false + ? { result, suggest } + :*/ + } + } + } + + if(x || suggest){ + //if((x < length - 1) || suggest){ + check_new[id] = 1; + } + + found = true; + } + + if(suggest){ + + if(!check_suggest[id]){ + check_suggest[id] = 1; + const arr = suggest[y] || (suggest[y] = []); + arr.push(id); + } + + // OLD: + // + // check_idx = (check_suggest[id] || 0) + 1; + // check_suggest[id] = check_idx; + // + // // do not adding IDs which are already included in the result (saves one loop) + // // the first intersection match has the check index 2, so shift by -2 + // + // if(check_idx < length){ + // + // const tmp = suggest[check_idx - 2] || (suggest[check_idx - 2] = []); + // tmp[tmp.length] = id; + // } + } + } + else { + + // pre-fill in first round + check_new[id] = 1; + } + } + } + + if(suggest){ + + // re-use the first pre-filled check for suggestions + check || (check_suggest = check_new); + } + else if(!found){ + + return []; + } + + check = check_new; + } + + // return intermediate result + // if(resolve === false){ + // return { result, suggest }; + // } + + if(suggest){ + + // needs to iterate in reverse direction + for(let x = suggest.length - 1, ids, len; x >= 0; x--){ + + ids = suggest[x]; + len = ids.length; + + for(let y = 0, id; y < len; y++){ + + id = ids[y]; + + if(!check[id]){ + + if(offset){ + offset--; + } + else { + + result[size++] = id; + + if(size === limit){ + // fast path "end reached" + return result; + } + } + + check[id] = 1; + } + } + } + } + + return result; +} + +/** + * @param mandatory + * @param arrays + * @returns {Array} + */ + +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.push(id); + } + } + } + } + + return result; +} + + +/** + * Implementation based on Array.includes() provides better performance, + * but it needs at least one word in the query which is less frequent. + * Also on large indexes it does not scale well performance-wise. + * This strategy also lacks of suggestion capabilities (matching & sorting). + * + * @param arrays + * @param limit + * @param offset + * @param {boolean|Array=} suggest + * @returns {Array} + */ + +// export function intersect(arrays, limit, offset, suggest) { +// +// const length = arrays.length; +// let result = []; +// let check; +// +// // determine shortest array and collect results +// // from the sparse relevance arrays +// +// let smallest_size; +// let smallest_arr; +// let smallest_index; +// +// for(let x = 0; x < length; x++){ +// +// const arr = arrays[x]; +// const len = arr.length; +// +// let size = 0; +// +// for(let y = 0, tmp; y < len; y++){ +// +// tmp = arr[y]; +// +// if(tmp){ +// +// size += tmp.length; +// } +// } +// +// if(!smallest_size || (size < smallest_size)){ +// +// smallest_size = size; +// smallest_arr = arr; +// smallest_index = x; +// } +// } +// +// smallest_arr = smallest_arr.length === 1 ? +// +// smallest_arr[0] +// : +// concat(smallest_arr); +// +// if(suggest){ +// +// suggest = [smallest_arr]; +// check = create_object(); +// } +// +// let size = 0; +// let steps = 0; +// +// // process terms in reversed order often results in better performance. +// // the outer loop must be the words array, using the +// // smallest array here disables the "fast fail" optimization. +// +// for(let x = length - 1; x >= 0; x--){ +// +// if(x !== smallest_index){ +// +// steps++; +// +// const word_arr = arrays[x]; +// const word_arr_len = word_arr.length; +// const new_arr = []; +// +// let count = 0; +// +// for(let z = 0, id; z < smallest_arr.length; z++){ +// +// id = smallest_arr[z]; +// +// let found; +// +// // process relevance in forward order (direction is +// // important for adding IDs during the last round) +// +// for(let y = 0; y < word_arr_len; y++){ +// +// const arr = word_arr[y]; +// +// if(arr.length){ +// +// found = arr.includes(id); +// +// if(found){ +// +// // check if in last round +// +// if(steps === length - 1){ +// +// if(offset){ +// +// offset--; +// } +// else{ +// +// result[size++] = id; +// +// if(size === limit){ +// +// // fast path "end reached" +// +// return result; +// } +// } +// +// if(suggest){ +// +// check[id] = 1; +// } +// } +// +// break; +// } +// } +// } +// +// if(found){ +// +// new_arr[count++] = id; +// } +// } +// +// if(suggest){ +// +// suggest[steps] = new_arr; +// } +// else if(!count){ +// +// return []; +// } +// +// smallest_arr = new_arr; +// } +// } +// +// if(suggest){ +// +// // needs to iterate in reverse direction +// +// for(let x = suggest.length - 1, arr, len; x >= 0; x--){ +// +// arr = suggest[x]; +// len = arr && arr.length; +// +// if(len){ +// +// for(let y = 0, id; y < len; y++){ +// +// id = arr[y]; +// +// if(!check[id]){ +// +// check[id] = 1; +// +// if(offset){ +// +// offset--; +// } +// else{ +// +// result[size++] = id; +// +// if(size === limit){ +// +// // fast path "end reached" +// +// return result; +// } +// } +// } +// } +// } +// } +// } +// +// return result; +// } + +// COMPILER BLOCK --> + +let global_resolve = 1; +function set_resolve(resolve){ + global_resolve = resolve; +} + +/** + * @param {string|SearchOptions} query + * @param {number|SearchOptions=} limit + * @param {SearchOptions=} options + * @returns {Array|Resolver|Promise} + */ + +Index.prototype.search = function(query, limit, options){ + + if(!options){ + if(!limit && is_object(query)){ + options = /** @type {!SearchOptions} */ (query); + query = ""; + } + else if(is_object(limit)){ + options = /** @type {!SearchOptions} */ (limit); + limit = 0; + } + } + + let result = []; + let length; + let context, suggest, offset = 0, resolve, enrich, tag; + + if(options){ + query = options.query || query; + limit = options.limit || limit; + offset = options.offset || 0; + context = options.context; + suggest = options.suggest; + resolve = (global_resolve && options.resolve !== false); + resolve || (global_resolve = 0); + enrich = resolve && options.enrich; + tag = this.db && options.tag; + } + else { + resolve = this.resolve || global_resolve; + } + + // todo: term deduplication during encoding when context is disabled + + // do not force a string as input + // https://github.com/nextapps-de/flexsearch/issues/432 + query = /** @type {Array} */ (this.encoder.encode(query)); + length = query.length; + limit || !resolve || (limit = 100); + + // fast path single term + if(length === 1){ + return single_term_query.call( + this, + query[0], // term + "", // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + // TODO: dedupe terms within encoder? + // TODO: deduplication will break the context chain + + context = this.depth && context !== false; + + // fast path single context + if(length === 2 && context && !suggest){ + return single_term_query.call( + this, + query[0], // term + query[1], // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + let maxlength = 0; + let minlength = 0; + + if(length > 1){ + + // term deduplication will break the context chain + // todo add context to dupe check + const dupes = create_object(); + const query_new = []; + + // if(context){ + // keyword = query[0]; + // dupes[keyword] = 1; + // query_new.push(keyword); + // maxlength = minlength = keyword.length; + // i = 1; + // } + + for(let i = 0, term; i < length; i++){ + + term = query[i]; + + if(term && !dupes[term]){ + + // todo add keyword check + // this fast path can't apply to persistent indexes + if(!suggest && !(this.db) && !this.get_array(term/*, keyword*/)){ + + // fast path "not found" + return resolve + ? result + : new Resolver(result); + } + else { + + query_new.push(term); + dupes[term] = 1; + } + + const term_length = term.length; + maxlength = Math.max(maxlength, term_length); + minlength = minlength ? Math.min(minlength, term_length) : term_length; + } + // else if(term && (!this.depth || context === false)){ + // query_new.push(term); + // } + } + + query = query_new; + length = query.length; + } + + // the term length could be changed after deduplication + + if(!length){ + return resolve + ? result + : new Resolver(result); + } + + let index = 0, keyword; + + // fast path single term + if(length === 1){ + return single_term_query.call( + this, + query[0], // term + "", // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + // fast path single context + if(length === 2 && context && !suggest){ + return single_term_query.call( + this, + query[0], // term + query[1], // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + if(length > 1){ + if(context){ + // start with context right away + keyword = query[0]; + index = 1; + } + // todo + else if(maxlength > 9 && (maxlength / minlength) > 3){ + // sorting terms will break the context chain + // bigger terms has less occurrence + // this might also reduce the intersection task + // todo check intersection order + query.sort(sort_by_length_down); + } + } + + if(this.db){ + + if(this.db.search){ + // when the configuration is not supported it returns false + const result = this.db.search(this, query, limit, offset, suggest, resolve, enrich, tag); + if(result !== false) return result; + } + + const self = this; + return (async function(){ + + for(let arr, term; index < length; index++){ + + term = query[index]; + + if(keyword){ + + arr = await self.get_array(term, keyword); + arr = add_result( + arr, + result, + suggest, + self.resolution_ctx, + /** @type {!number} */ (limit), + offset, + length === 2 + /*, term, keyword*/ + ); + + // the context is a moving window where the keyword is going forward like a cursor + // 1. when suggestion enabled just forward keyword if term was found + // 2. as long as the result is empty forward the pointer also + if(!suggest || (arr !== false) || !result.length){ + keyword = term; + } + } + else { + + arr = await self.get_array(term); + arr = add_result( + arr, + result, + suggest, + self.resolution, + /** @type {!number} */ (limit), + offset, + length === 1 + /*, term*/ + ); + } + + // limit reached + if(arr){ + return arr; + } + + // apply suggestions on last loop + if(suggest && (index === length - 1)){ + let length = result.length; + if(!length){ + // fallback to non-contextual search when no result was found + if(keyword){ + keyword = ""; + index = -1; + continue; + } + return result; + } + else if(length === 1){ + return resolve + ? resolve_default(result[0], /** @type {number} */ (limit), offset) + : new Resolver(result[0]); + } + } + } + + return resolve + ? intersect(result, /** @type {number} */ (limit), offset, suggest) + : new Resolver(result[0]) + }()); + } + + for(let arr, term; index < length; index++){ + + term = query[index]; + + if(keyword){ + + arr = this.get_array(term, keyword); + arr = /*this.*/add_result( + arr, + result, + suggest, + this.resolution_ctx, + /** @type {!number} */ (limit), + offset, + length === 2 + /*, term, keyword*/ + ); + + // 1. when suggestion enabled just forward keyword if term was found + // 2. as long as the result is empty forward the pointer also + if(!suggest || (arr !== false) || !result.length){ + keyword = term; + } + } + else { + + arr = this.get_array(term); + arr = /*this.*/add_result( + arr, + result, + suggest, + this.resolution, + /** @type {!number} */ (limit), + offset, + length === 1 + /*, term*/ + ); + } + + // limit reached + if(arr){ + return /** @type {Array} */ (arr); + } + + // apply suggestions on last loop + if(suggest && (index === length - 1)){ + const length = result.length; + if(!length){ + // fallback to non-contextual search when no result was found + if(keyword){ + keyword = ""; + index = -1; + continue; + } + return result; + } + else if(length === 1){ + return resolve + ? resolve_default(result[0], limit, offset) + : new Resolver(result[0]); + } + } + } + + return resolve + ? intersect(result, limit, offset, suggest) + : new Resolver(result[0]); +}; + +/** + * @param term + * @param keyword + * @param limit + * @param offset + * @param resolve + * @param enrich + * @param tag + * @this Index + * @return {Array|Resolver} + */ + +function single_term_query(term, keyword, limit, offset, resolve, enrich, tag){ + + const result = this.get_array(term, keyword, limit, offset, resolve, enrich, tag); + + if(this.db){ + return result.then(function(result){ + if(resolve) return result; + return result && result.length + ? (resolve ? resolve_default(result, limit, offset): new Resolver(result)) + : resolve ? [] : new Resolver([]); + }); + } + + return result && result.length + ? (resolve ? resolve_default(result, limit, offset) : new Resolver(result)) + : resolve ? [] : new Resolver([]); +} + +/** + * Returns a 1-dimensional finalized array when the result is done (fast path return), + * returns false when suggestions is enabled and no result was found, + * or returns nothing when a set was pushed successfully to the results + * + * @private + * @param {Array} arr + * @param {Array} result + * @param {Array} suggest + * @param {number} resolution + * @param {number} limit + * @param {number} offset + * @param {boolean} single_term + * @return {Array|boolean|undefined} + */ + +function add_result(arr, result, suggest, resolution, limit, offset, single_term/*, term, keyword*/){ + + let word_arr = []; + //let arr;// = keyword ? this.ctx : this.map; + //arr = this.get_array(term, keyword); + + if(arr){ + + //const resolution = Math.min(arr.length, keyword ? this.resolution_ctx : this.resolution); + // apply reduced resolution for queries + resolution = Math.min(arr.length, resolution); + + for(let x = 0, size = 0, tmp; x < resolution; x++){ + if((tmp = arr[x])){ + + if(offset){ + // apply offset right here on single terms + if(tmp && single_term){ + if(tmp.length <= offset){ + offset -= tmp.length; + tmp = null; + } + else { + tmp = tmp.slice(offset); + offset = 0; + } + } + } + + if(tmp){ + + // keep score (sparse array): + word_arr[x] = tmp; + // simplified score order: + //word_arr.push(tmp); + + if(single_term){ + size += tmp.length; + if(size >= limit){ + // fast path: + // a single term does not need to pre-collect results + break; + } + } + } + } + } + + if(word_arr.length){ + if(single_term){ + // fast path optimization + // offset was already applied at this point + // return an array will stop the query process immediately + return resolve_default(word_arr, limit, 0); + } + + result.push(word_arr); + // return nothing will continue the query + return; + } + } + + // 1. return an empty array will stop the loop + // 2. return a false value to prevent stop when using suggestions + return !suggest && word_arr; +} + +Index.prototype.get_array = function(term, keyword, limit, offset, resolve, enrich, tag){ + + let arr, swap; + + if(keyword){ + swap = this.bidirectional && (term > keyword); + } + + if(this.compress){ + term = default_compress(term); + keyword && (keyword = default_compress(keyword)); + } + + if(this.db){ + return keyword + ? this.db.get( + swap ? keyword : term, // term + swap ? term : keyword, // ctx + limit, + offset, + resolve, + enrich, + tag + ) + : this.db.get( + term, + "", // ctx + limit, + offset, + resolve, + enrich, + tag + ); + } + + if(keyword){ + // the frequency of the starting letter is slightly less + // on the last half of the alphabet (m-z) in almost every latin language, + // so we sort downwards (https://en.wikipedia.org/wiki/Letter_frequency) + arr = this.ctx.get(swap ? term : keyword); + arr = arr && arr.get(swap ? keyword : term); + } + else { + arr = this.map.get(term); + } + + return arr; +}; + +// COMPILER BLOCK --> + +/** + * @param {boolean=} _skip_deletion + */ + +Index.prototype.remove = function(id, _skip_deletion){ + + const refs = this.reg.size && ( + this.fastupdate + ? this.reg.get(id) + : this.reg.has(id) + ); + + if(refs){ + + if(this.fastupdate){ + + // fast updates did not fully cleanup the key entries + + for(let i = 0, tmp; i < refs.length; i++){ + if((tmp = refs[i])){ + // todo check + //if(tmp.length < 1) throw new Error("invalid length"); + //if(tmp.indexOf(id) < 0) throw new Error("invalid id"); + if(tmp.length < 2){ + tmp.pop(); + } + else { + const index = tmp.indexOf(id); + index === refs.length - 1 + ? tmp.pop() + : tmp.splice(index, 1); + } + } + } + + // todo variation which cleans up, requires to push [ctx, key] instead of arr to the index.reg + // for(let i = 0, arr, term, keyword; i < refs.length; i++){ + // arr = refs[i]; + // if(typeof arr === "string"){ + // arr = this.map.get(term = arr); + // } + // else{ + // arr = this.ctx.get(keyword = arr[0]); + // arr && (arr = arr.get(arr[1])); + // } + // let counter = 0, found; + // if(arr && arr.length){ + // for(let j = 0, tmp; j < arr.length; j++){ + // if((tmp = arr[j])){ + // if(!found && tmp.length){ + // const index = tmp.indexOf(id); + // if(index >= 0){ + // tmp.splice(index, 1); + // // the index [ctx, key]:[res, id] is unique + // found = 1; + // } + // } + // if(tmp.length){ + // counter++; + // if(found){ + // break; + // } + // } + // else{ + // delete arr[j]; + // } + // } + // } + // } + // if(!counter){ + // keyword + // ? this.ctx.delete(keyword) + // : this.map.delete(term); + // } + // } + } + else { + + remove_index(this.map, id/*, this.resolution*/); + this.depth && + remove_index(this.ctx, id/*, this.resolution_ctx*/); + } + + _skip_deletion || this.reg.delete(id); + } + + if(this.db){ + this.commit_task.push({ "del": id }); + this.commit_auto && autoCommit(this); + //return this.db.remove(id); + } + + // the cache could be used outside the InMemory store + if(this.cache){ + this.cache.remove(id); + } + + return this; +}; + +/** + * @param map + * @param id + * @return {number} + */ + +function remove_index(map, id){ + + // a check counter of filled resolution slots + // to prevent removing the field + let count = 0; + + if(is_array(map)){ + for(let x = 0, arr, index; x < map.length; x++){ + if((arr = map[x]) && arr.length){ + index = arr.indexOf(id); + if(index >= 0){ + if(arr.length > 1){ + arr.splice(index, 1); + count++; + } + else { + // remove resolution slot + delete map[x]; + } + // the index key:[res, id] is unique + break; + } + else { + count++; + } + } + } + } + else for(let item of map){ + const key = item[0]; + const value = item[1]; + const tmp = remove_index(value, id); + tmp ? count += tmp + : map.delete(key); + } + + return count; +} + +/**! + * FlexSearch.js + * Author and Copyright: Thomas Wilkerling + * Licence: Apache-2.0 + * Hosted by Nextapps GmbH + * https://github.com/nextapps-de/flexsearch + */ + + +/** + * @constructor + * @param {IndexOptions|string=} options Options or preset as string + * @param {Map|Set|KeystoreSet|KeystoreMap=} _register + */ + +function Index(options$1, _register){ + + if(!this){ + return new Index(options$1); + } + + tick("Index.create"); + + options$1 = options$1 + ? apply_preset(options$1) + : {}; + + /** @type ContextOptions */ + const context = options$1.context || {}; + const encoder = options$1.encode || options$1.encoder || ( + options + ); + /** @type Encoder */ + this.encoder = encoder.encode + ? encoder + : typeof encoder === "object" + ? (new Encoder(encoder) + + ) + : { encode: encoder }; + + { + this.compress = options$1.compress || options$1.compression || false; + } + + let tmp; + this.resolution = options$1.resolution || 9; + this.tokenize = tmp = options$1.tokenize || "strict"; + this.depth = (tmp === "strict" && context.depth) || 0; + this.bidirectional = context.bidirectional !== false; + this.fastupdate = !!options$1.fastupdate; + this.score = options$1.score || null; + + tmp = (options$1.keystore || 0); + tmp && (this.keystore = tmp); + + this.map = tmp && SUPPORT_KEYSTORE ? new KeystoreMap(tmp) : new Map(); + this.ctx = tmp && SUPPORT_KEYSTORE ? new KeystoreMap(tmp) : new Map(); + this.reg = _register || ( + this.fastupdate + ? (tmp && SUPPORT_KEYSTORE ? new KeystoreMap(tmp) : new Map()) + : (tmp && SUPPORT_KEYSTORE ? new KeystoreSet(tmp) : new Set()) + ); + this.resolution_ctx = context.resolution || 1; + this.rtl = (encoder.rtl) || options$1.rtl || false; + + { + this.cache = (tmp = options$1.cache || null) && new CacheClass(tmp); + } + + { + this.resolve = options$1.resolve !== false; + } + + { + if((tmp = options$1.db)){ + this.db = tmp.mount(this); + } + this.commit_auto = options$1.commit !== false; + this.commit_task = []; + this.commit_timer = null; + } +} + +{ + Index.prototype.mount = function(db){ + if(this.commit_timer){ + clearTimeout(this.commit_timer); + this.commit_timer = null; + } + return db.mount(this); + }; + Index.prototype.commit = function(replace, append){ + if(this.commit_timer){ + clearTimeout(this.commit_timer); + this.commit_timer = null; + } + return this.db.commit(this, replace, append); + }; +} + +// if(SUPPORT_RESOLVER){ +// Index.prototype.resolve = function(params){ +// return new Resolver(params); +// }; +// } + +/** + * @param {!Index} self + * @param {boolean=} replace + * @param {boolean=} append + */ + +function autoCommit(self, replace, append){ + if(!self.commit_timer){ + self.commit_timer = setTimeout(function(){ + self.commit_timer = null; + self.db.commit(self, replace, append); + }, 0); + } +} + +Index.prototype.clear = function(){ + + //this.map = new Map(); + //this.ctx = new Map(); + //this.reg = this.fastupdate ? new Map() : new Set(); + this.map.clear(); + this.ctx.clear(); + this.reg.clear(); + + { + this.cache && + this.cache.clear(); + } + + if(this.db){ + this.commit_timer && clearTimeout(this.commit_timer); + this.commit_timer = null; + this.commit_task = [{ "clear": true }]; + //return this.db.clear(); + } + + return this; +}; + +//Index.prototype.pipeline = pipeline; + +/** + * @param {!number|string} id + * @param {!string} content + */ + +Index.prototype.append = function(id, content){ + return this.add(id, content, true); +}; + +Index.prototype.contain = function(id){ + + if(this.db){ + return this.db.has(id); + } + + return this.reg.has(id); +}; + +Index.prototype.update = function(id, content){ + + // todo check the async part + if(this.async /*|| (SUPPORT_PERSISTENT && this.db)*/){ + const self = this; + const res = this.remove(id); + return res.then ? res.then( + () => self.add(id, content) + ) : this.add(id, content); + } + + return this.remove(id).add(id, content); +}; + +/** + * @param map + * @return {number} + */ + +function cleanup_index(map){ + + let count = 0; + + if(is_array(map)){ + for(let i = 0, arr; i < map.length; i++){ + (arr = map[i]) && + (count += arr.length); + } + } + else for(const item of map){ + const key = item[0]; + const value = item[1]; + const tmp = cleanup_index(value); + tmp ? count += tmp + : map.delete(key); + } + + return count; +} + +Index.prototype.cleanup = function(){ + + if(!this.fastupdate){ + console.info("Cleanup the index isn't required when not using \"fastupdate\"."); + return this; + } + + cleanup_index(this.map); + this.depth && + cleanup_index(this.ctx); + + return this; +}; + +{ + + Index.prototype.searchCache = searchCache; +} + +{ + + Index.prototype.export = exportIndex; + Index.prototype.import = importIndex; +} + +{ + + apply_async(Index.prototype); +} + +async function handler(data) { + + data = data["data"]; + + /** @type Index */ + const index = self["_index"]; + const args = data["args"]; + const task = data["task"]; + + switch(task){ + + case "init": + + /** @type IndexOptions */ + let options = data["options"] || {}; + let filepath = options.config; + if(filepath){ + options = filepath; + // will be replaced after build with the line below because + // there is an issue with closure compiler dynamic import + //options = await import(filepath); + } + + // deprecated: + // const encode = options.encode; + // if(encode && (encode.indexOf("function") === 0)){ + // options.encode = Function("return " + encode)(); + // } + + const factory = data["factory"]; + + if(factory){ + + // export the FlexSearch global payload to "self" + Function("return " + factory)()(self); + + /** @type Index */ + self["_index"] = new self["FlexSearch"]["Index"](options); + + // destroy the exported payload + delete self["FlexSearch"]; + } + else { + + self["_index"] = new Index(options); + } + + postMessage({ "id": data["id"] }); + break; + + default: + + const id = data["id"]; + const message = index[task].apply(index, args); + postMessage(task === "search" ? { "id": id, "msg": message } : { "id": id }); + } +} + +//import { promise as Promise } from "../polyfill.js"; + +let pid = 0; + +/** + * @param {IndexOptions=} options + * @constructor + */ + +function WorkerIndex(options){ + + if(!this) { + return new WorkerIndex(options); + } + + if(options); + else { + options = {}; + } + + // the factory is the outer wrapper from the build + // we use "self" as a trap for node.js + + let factory = (self||window)["_factory"]; + if(factory){ + factory = factory.toString(); + } + + const is_node_js = typeof window === "undefined" && self["exports"]; + const _self = this; + + this.worker = create(factory, is_node_js, options.worker); + this.resolver = create_object(); + + if(!this.worker){ + + return; + } + + function onmessage(msg){ + msg = msg["data"] || msg; + const id = msg["id"]; + const res = id && _self.resolver[id]; + if(res){ + res(msg["msg"]); + delete _self.resolver[id]; + } + } + + is_node_js + ? this.worker["on"]("message", onmessage) + : this.worker.onmessage = onmessage; + + if(options["config"]){ + + // when extern configuration needs to be loaded + // it needs to return a promise to await for + return new Promise(function(resolve){ + _self.resolver[++pid] = function(){ + resolve(_self); + }; + _self.worker.postMessage({ + "id": pid, + "task": "init", + "factory": factory, + "options": options + }); + }); + } + + this.worker.postMessage({ + "task": "init", + "factory": factory, + "options": options + }); +} + +register("add"); +register("append"); +register("search"); +register("update"); +register("remove"); + +function register(key){ + + WorkerIndex.prototype[key] = + WorkerIndex.prototype[key + "Async"] = function(){ + + const self = this; + const args = [].slice.call(arguments); + const arg = args[args.length - 1]; + let callback; + + if(is_function(arg)){ + callback = arg; + args.splice(args.length - 1, 1); + } + + const promise = new Promise(function(resolve){ + //setTimeout(function(){ + self.resolver[++pid] = resolve; + self.worker.postMessage({ + "task": key, + "id": pid, + "args": args + }); + //}); + }); + + if(callback){ + promise.then(callback); + return this; + } + else { + + return promise; + } + }; +} + +function create(factory, is_node_js, worker_path){ + + let worker; + + worker = is_node_js ? + // This eval will be removed when compiling, it isn't there in final build + (0, eval)('new (require("worker_threads")["Worker"])(__dirname + "/node/node.js")') + //eval('new (require("worker_threads")["Worker"])(__dirname + "/node/node.js")') + :( + factory ? + new window.Worker(URL.createObjectURL( + new Blob( + ["onmessage=" + handler.toString()], + { "type": "text/javascript" } + ) + )) + : + new window.Worker(is_string(worker_path) ? worker_path : "worker/worker.js", { type: "module" }) + ); + + return worker; +} + +// COMPILER BLOCK --> + +/** + * + * @param id + * @param content + * @param {boolean=} _append + * @returns {Document|Promise} + */ + +Document.prototype.add = function(id, content, _append){ + + if(is_object(id)){ + + content = id; + id = parse_simple(content, this.key); + } + + if(content && (id || (id === 0))){ + + if(!_append && this.reg.has(id)){ + return this.update(id, content); + } + + // this.field does not include db tag indexes + for(let i = 0, tree; i < this.field.length; i++){ + + tree = this.tree[i]; + + const index = this.index.get(this.field[i]); + if(typeof tree === "function"){ + const tmp = tree(content); + if(tmp){ + index.add(id, tmp, /* append: */ false, /* skip update: */ true); + } + } + else { + const filter = tree._filter; + if(filter && !filter(content)){ + continue; + } + if(tree.constructor === String){ + tree = ["" + tree]; + } + else if(is_string(tree)){ + tree = [tree]; + } + add_index(content, tree, this.marker, 0, index, id, tree[0], _append); + } + } + + if(this.tag){ + + //console.log(this.tag, this.tagtree) + + for(let x = 0; x < this.tagtree.length; x++){ + + let tree = this.tagtree[x]; + let field = this.tagfield[x]; + let ref = this.tag.get(field); + let dupes = create_object(); + let tags; + + if(typeof tree === "function"){ + tags = tree(content); + if(!tags) continue; + } + else { + const filter = tree._filter; + if(filter && !filter(content)){ + continue; + } + if(tree.constructor === String){ + tree = "" + tree; + } + tags = parse_simple(content, tree); + } + + if(!ref || !tags){ + ref || (console.warn("Tag '" + field + "' was not found")); + continue; + } + + if(is_string(tags)){ + tags = [tags]; + } + + for(let i = 0, tag, arr; i < tags.length; i++){ + + tag = tags[i]; + //console.log(this.tag, tag, key, field) + + if(!dupes[tag]){ + dupes[tag] = 1; + + let tmp; + tmp = ref.get(tag); + tmp ? arr = tmp : ref.set(tag, arr = []); + + if(!_append || ! /** @type {!Array|KeystoreArray} */(arr).includes(id)){ + + // auto-upgrade to keystore array if max size exceeded + { + if(arr.length === 2**31-1 /*|| !(arr instanceof KeystoreArray)*/){ + const keystore = new KeystoreArray(arr); + if(this.fastupdate){ + for(let value of this.reg.values()){ + if(value.includes(arr)){ + value[value.indexOf(arr)] = keystore; + } + } + } + ref.set(tag, arr = keystore); + } + } + + arr.push(id); + + // add a reference to the register for fast updates + if(this.fastupdate){ + const tmp = this.reg.get(id); + tmp ? tmp.push(arr) + : this.reg.set(id, [arr]); + } + } + } + } + } + } + + if(this.store && (!_append || !this.store.has(id))){ + + let payload; + + if(this.storetree){ + + payload = create_object(); + + for(let i = 0, tree; i < this.storetree.length; i++){ + tree = this.storetree[i]; + + const filter = tree._filter; + if(filter && !filter(content)){ + continue; + } + let custom; + if(typeof tree === "function"){ + custom = tree(content); + if(!custom) continue; + tree = [tree._field]; + } + else if(is_string(tree) || tree.constructor === String){ + payload[tree] = content[tree]; + continue; + } + + store_value(content, payload, tree, 0, tree[0], custom); + } + } + + this.store.set(id, payload || content); + } + } + + return this; +}; + +// TODO support generic function created from string when tree depth > 1 + +/** + * @param obj + * @param store + * @param tree + * @param pos + * @param key + * @param {*=} custom + */ + +function store_value(obj, store, tree, pos, key, custom){ + + obj = obj[key]; + + // reached target field + if(pos === (tree.length - 1)){ + + // store target value + store[key] = custom || obj; + } + else if(obj){ + + if(is_array(obj)){ + + store = store[key] = new Array(obj.length); + + for(let i = 0; i < obj.length; i++){ + // do not increase pos (an array is not a field) + store_value(obj, store, tree, pos, i); + } + } + else { + + store = store[key] || (store[key] = create_object()); + key = tree[++pos]; + store_value(obj, store, tree, pos, key); + } + } +} + +function add_index(obj, tree, marker, pos, index, id, key, _append){ + + if((obj = obj[key])){ + + // reached target field + if(pos === (tree.length - 1)){ + + // handle target value + if(is_array(obj)){ + + // append array contents so each entry gets a new scoring context + if(marker[pos]){ + for(let i = 0; i < obj.length; i++){ + index.add(id, obj[i], /* append: */ true, /* skip update: */ true); + } + return; + } + + // or join array contents and use one scoring context + obj = obj.join(" "); + } + + index.add(id, obj, _append, /* skip_update: */ true); + } + else { + + if(is_array(obj)){ + for(let i = 0; i < obj.length; i++){ + // do not increase index, an array is not a field + add_index(obj, tree, marker, pos, index, id, i, _append); + } + } + else { + key = tree[++pos]; + add_index(obj, tree, marker, pos, index, id, key, _append); + } + } + } + else { + if(index.db){ + index.remove(id); + } + } +} + +// COMPILER BLOCK --> + +/** + * @param {!string|DocumentSearchOptions} query + * @param {number|DocumentSearchOptions=} limit + * @param {DocumentSearchOptions=} options + * @param {Array=} _resolve For internal use only. + * @returns {Promise|Array} + */ + +Document.prototype.search = function(query, limit, options, _resolve){ + + if(!options){ + if(!limit && is_object(query)){ + options = /** @type {DocumentSearchOptions} */ (query); + query = ""; + } + else if(is_object(limit)){ + options = /** @type {DocumentSearchOptions} */ (limit); + limit = 0; + } + } + + let result = [], result_field = []; + let pluck, enrich, merge, suggest; + let field, tag, offset, count = 0; + + if(options){ + + // todo remove support? + if(is_array(options)){ + field = options; + options = null; + } + else { + + query = options.query || query; + pluck = options.pluck; + merge = options.merge; + field = pluck || options.field || options.index; + tag = this.tag && options.tag; + enrich = this.store && options.enrich; + suggest = options.suggest; + limit = options.limit || limit; + offset = options.offset || 0; + limit || (limit = 100); + + if(tag && (!this.db || !_resolve)){ + + if(tag.constructor !== Array){ + tag = [tag]; + } + + let pairs = []; + + for(let i = 0, field; i < tag.length; i++){ + field = tag[i]; + if(is_string(field)){ + throw new Error("A tag option can't be a string, instead it needs a { field: tag } format."); + } + // default array notation + if(field.field && field.tag){ + const value = field.tag; + if(value.constructor === Array){ + for(let k = 0; k < value.length; k++){ + pairs.push(field.field, value[k]); + } + } + else { + pairs.push(field.field, value); + } + } + // shorter object notation + else { + const keys = Object.keys(field); + for(let j = 0, key, value; j < keys.length; j++){ + key = keys[j]; + value = field[key]; + if(value.constructor === Array){ + for(let k = 0; k < value.length; k++){ + pairs.push(key, value[k]); + } + } + else { + pairs.push(key, value); + } + } + } + } + + if(!pairs.length){ + throw new Error("Your tag definition within the search options is probably wrong. No valid tags found."); + } + + // tag used as pairs from this point + tag = pairs; + + // when tags is used and no query was set, + // then just return the tag indexes + if(!query){ + + let promises = []; + if(pairs.length) for(let j = 0; j < pairs.length; j+=2){ + let ids; + if(this.db){ + const index = this.index.get(pairs[j]); + if(!index){ + { + console.warn("Tag '" + pairs[j] + ":" + pairs[j + 1] + "' will be skipped because there is no field '" + pairs[j] + "'."); + } + continue; + } + promises.push(ids = index.db.tag(pairs[j + 1], limit, offset, enrich)); + } + else { + ids = get_tag.call(this, pairs[j], pairs[j + 1], limit, offset, enrich); + } + result.push({ + "field": pairs[j], + "tag": pairs[j + 1], + "result": ids + }); + } + + if(promises.length){ + return Promise.all(promises).then(function(promises){ + for(let j = 0; j < promises.length; j++){ + result[j].result = promises[j]; + } + return result; + }); + } + + return result; + } + } + + // extend to multi field search by default + if(is_string(field)){ + field = [field]; + } + } + } + + field || (field = this.field); + let promises = !_resolve && (this.worker || this.async) && []; + let db_tag_search; + + // multi field search + // field could be a custom set of selected fields by this query + // db tag indexes are also included in this field list + for(let i = 0, res, key, len; i < field.length; i++){ + + key = field[i]; + + if(this.db && this.tag){ + // tree is missing when it is a tag-only index (db) + if(!this.tree[i]){ + continue; + } + } + + let field_options; + + if(!is_string(key)){ + field_options = key; + key = field_options.field; + query = field_options.query || query; + limit = field_options.limit || limit; + //offset = field_options.offset || offset; + suggest = (field_options.suggest || suggest); + //enrich = SUPPORT_STORE && this.store && (field_options.enrich || enrich); + } + + if(_resolve){ + res = _resolve[i]; + } + else { + let opt = field_options || options; + let index = this.index.get(key); + + if(tag){ + if(this.db){ + opt.tag = tag; + db_tag_search = index.db.support_tag_search; + opt.field = field; + } + if(!db_tag_search){ + opt.enrich = false; + } + } + if(promises){ + promises[i] = index.searchAsync(query, limit, opt); + // restore enrich state + opt && enrich && (opt.enrich = enrich); + // just collect and continue + continue; + } + else { + res = index.search(query, limit, opt); + // restore enrich state + opt && enrich && (opt.enrich = enrich); + } + } + + len = res && res.length; + + // todo when no term was matched but tag was retrieved extend suggestion to tags + // every field has to intersect against all selected tag fields + if(tag && len){ + + const arr = []; + let count = 0; + + // tags are only applied in resolve phase when it's a db + if(this.db && _resolve){ + if(!db_tag_search){ + + // retrieve tag results assigned to it's field + for(let y = field.length; y < _resolve.length; y++){ + + let ids = _resolve[y]; + let len = ids && ids.length; + + if(len){ + count++; + arr.push(ids); + } + else if(!suggest){ + // no tags found + return result; + } + } + } + } + else { + + // tag[] are pairs at this line + for(let y = 0, ids, len; y < tag.length; y+=2){ + ids = this.tag.get(tag[y]); + + if(!ids){ + { + console.warn("Tag '" + tag[y] + ":" + tag[y + 1] + "' will be skipped because there is no field '" + tag[y] + "'."); + } + if(suggest){ + continue; + } + else { + return result; + } + } + + ids = ids && ids.get(tag[y + 1]); + len = ids && ids.length; + + if(len){ + count++; + arr.push(ids); + } + else if(!suggest){ + // no tags found + return result; + } + } + } + + if(count){ + res = intersect_union(res, arr); // intersect(arr, limit, offset) + len = res.length; + if(!len && !suggest){ + // nothing matched + return result; + } + // move counter back by 1 + count--; + } + } + + if(len){ + result_field[count] = key; + result.push(res); + count++; + } + else if(field.length === 1){ + // fast path: nothing matched + return result; + } + } + + if(promises){ + if(this.db){ + // todo when a tag index is never a search index this could be extracted + // push tag promises to the end + if(tag && tag.length && !db_tag_search){ + for(let y = 0; y < tag.length; y += 2){ + // it needs to retrieve data from tag pairs + const index = this.index.get(tag[y]); + if(!index){ + { + console.warn("Tag '" + tag[y] + ":" + tag[y + 1] + "' was not found because there is no field '" + tag[y] + "'."); + } + if(suggest){ + continue; + } + else { + return result; + } + } + promises.push(index.db.tag(tag[y + 1], limit, offset, /* enrich */ false)); + } + } + } + + const self = this; + + // TODO unroll this recursion + return Promise.all(promises).then(function(result){ + return result.length + ? self.search(query, limit, options, /* resolve: */ result) + : result; + }); + } + + if(!count){ + return result; + } + if(pluck && (!enrich || !this.store)){ + return result[0]; + } + + promises = []; + + for(let i = 0, res; i < result_field.length; i++){ + + res = result[i]; + + if(enrich && res.length && !res[0].doc){ + if(!this.db){ + if(res.length){ + res = apply_enrich.call(this, res); + } + } + else { + promises.push(res = this.index.get(this.field[0]).db.enrich(res)); + } + } + + if(pluck){ + return res; + } + + result[i] = { + "field": result_field[i], + "result": res + }; + } + + if(enrich && SUPPORT_PERSISTENT && this.db && promises.length){ + return Promise.all(promises).then(function(promises){ + for(let j = 0; j < promises.length; j++){ + result[j].result = promises[j]; + } + return merge + ? merge_fields(result, limit) + : result; + }); + } + + return merge + ? merge_fields(result, limit) + : result; +}; + +// todo support Resolver +// todo when searching through multiple fields each term should +// be found at least by one field to get a valid match without +// using suggestion explicitly + +function merge_fields(fields, limit, offset){ + const final = []; + const set = create_object(); + for(let i = 0, field, res; i < fields.length; i++){ + field = fields[i]; + res = field.result; + for(let j = 0, id, entry, tmp; j < res.length; j++){ + entry = res[j]; + id = entry.id; + tmp = set[id]; + if(!tmp){ + // offset was already applied on field indexes + // if(offset){ + // offset--; + // continue; + // } + // apply limit from last round, because just fields could + // be pushed without adding new results + if(final.length === limit){ + return final; + } + entry.field = set[id] = [field.field]; + final.push(entry); + } + else { + tmp.push(field.field); + } + } + } + return final; +} + +/** + * @this Document + */ + +function get_tag(tag, key, limit, offset, enrich){ + let res = this.tag.get(tag); + if(!res){ + console.warn("Tag '" + tag + "' was not found"); + return []; + } + res = res && res.get(key); + let len = res && (res.length - offset); + + if(len && (len > 0)){ + if((len > limit) || offset){ + res = res.slice(offset, offset + limit); + } + if(enrich){ + res = apply_enrich.call(this, res); + } + return res; + } +} + +/** + * @this Document + */ + +function apply_enrich(res){ + + const arr = new Array(res.length); + + for(let x = 0, id; x < res.length; x++){ + id = res[x]; + arr[x] = { + "id": id, + "doc": this.store.get(id) + }; + } + + return arr; +} + +/**! + * FlexSearch.js + * Author and Copyright: Thomas Wilkerling + * Licence: Apache-2.0 + * Hosted by Nextapps GmbH + * https://github.com/nextapps-de/flexsearch + */ + + +/** + * @constructor + * @param {!DocumentOptions} options + */ + +function Document(options){ + + if(!this) { + return new Document(options); + } + + /** @type DocumentDescriptor */ + const document = options.document || options.doc || options; + let tmp, keystore; + + this.tree = []; + this.field = []; + this.marker = []; + this.key = ((tmp = document.key || document.id) && parse_tree(tmp, this.marker)) || "id"; + + keystore = (options.keystore || 0); + keystore && (this.keystore = keystore); + this.fastupdate = !!options.fastupdate; + this.reg = this.fastupdate + ? (keystore && SUPPORT_KEYSTORE ? new KeystoreMap(keystore) : new Map()) + : (keystore && SUPPORT_KEYSTORE ? new KeystoreSet(keystore) : new Set()); + + { + // todo support custom filter function + this.storetree = (tmp = document.store || null) && tmp !== true && []; + this.store = tmp && ( + keystore && SUPPORT_KEYSTORE + ? new KeystoreMap(keystore) + : new Map() + ); + } + + { + this.cache = (tmp = options.cache || null) && new CacheClass(tmp); + // do not apply cache again for the indexes since .searchCache() + // is just a wrapper over .search() + options.cache = false; + } + + { + this.worker = options.worker; + } + + { + // this switch is used by recall of promise callbacks + this.async = false; + } + + /** @export */ + this.index = parse_descriptor.call(this, options, document); + + { + this.tag = null; + // TODO case-insensitive tags? + if((tmp = document.tag)){ + if(typeof tmp === "string"){ + tmp = [tmp]; + } + if(tmp.length){ + this.tag = new Map(); + this.tagtree = []; + this.tagfield = []; + for(let i = 0, params, field; i < tmp.length; i++){ + params = tmp[i]; + field = params.field || params; + if(!field){ + throw new Error("The tag field from the document descriptor is undefined."); + } + if(params.custom){ + this.tagtree[i] = params.custom; + } + else { + this.tagtree[i] = parse_tree(field, this.marker); + if(params.filter){ + if(typeof this.tagtree[i] === "string"){ + // it needs an object to put a property to it + this.tagtree[i] = new String(this.tagtree[i]); + } + this.tagtree[i]._filter = params.filter; + } + } + // the tag fields needs to be hold by indices + this.tagfield[i] = field; + this.tag.set(field, new Map()); + } + } + } + } + + { + options.db && this.mount(options.db); + } +} + +{ + + Document.prototype.mount = function(db){ + + let fields = this.field; + + if(this.tag){ + // tag indexes are referenced by field + // move tags to their field indexes respectively + for(let i = 0, field; i < this.tagfield.length; i++){ + field = this.tagfield[i]; + let index = this.index.get(field); + if(!index){ + // create raw index when not exists + this.index.set(field, index = new Index({}, this.reg)); + // copy and push to the field selection + if(fields === this.field){ + fields = fields.slice(0); + } + // tag indexes also needs to be upgraded to db instances + fields.push(field); + } + // assign reference + index.tag = this.tag.get(field); + } + } + + const promises = []; + const config = { + db: db.db, + type: db.type, + fastupdate: db.fastupdate + }; + + // upgrade all indexes to db instances + for(let i = 0, index, field; i < fields.length; i++){ + config.field = field = fields[i]; + index = this.index.get(field); + const dbi = new db.constructor(db.id, config); + // take over the storage id + dbi.id = db.id; + promises[i] = dbi.mount(index); + // add an identification property + index.document = true; + if(i){ + // the register has to export just one time + // also it's needed by the index for ID contain check + index.bypass = true; + } + else { + // the datastore has to export one time + index.store = this.store; + } + } + + this.async = true; + this.db = true; + return Promise.all(promises); + }; + + Document.prototype.commit = async function(replace, append){ + // parallel: + const promises = []; + for(const index of this.index.values()){ + promises.push(index.db.commit(index, replace, append)); + } + await Promise.all(promises); + this.reg.clear(); + // queued: + // for(const index of this.index.values()){ + // await index.db.commit(index, replace, append); + // } + // this.reg.clear(); + }; +} + +/** + * @this Document + */ + +function parse_descriptor(options, document){ + + const index = new Map(); + let field = document.index || document.field || document; + + if(is_string(field)){ + field = [field]; + } + + for(let i = 0, key, opt; i < field.length; i++){ + + key = field[i]; + + if(!is_string(key)){ + opt = key; + key = key.field; + } + + opt = /** @type DocumentIndexOptions */ ( + is_object(opt) + ? Object.assign({}, options, opt) + : options + ); + + if(this.worker){ + const worker = new WorkerIndex(opt); + index.set(key, worker); + if(!worker.worker){ + // fallback when not supported + this.worker = false; + } + } + + if(!this.worker){ + index.set(key, new Index(opt, this.reg)); + } + + if(opt.custom){ + this.tree[i] = opt.custom; + } + else { + this.tree[i] = parse_tree(key, this.marker); + if(opt.filter){ + if(typeof this.tree[i] === "string"){ + // it needs an object to put a property to it + this.tree[i] = new String(this.tree[i]); + } + this.tree[i]._filter = opt.filter; + } + } + + this.field[i] = key; + } + + if(this.storetree){ + + let stores = document.store; + if(is_string(stores)) stores = [stores]; + + for(let i = 0, store, field; i < stores.length; i++){ + store = /** @type Array */ (stores[i]); + field = store.field || store; + if(store.custom){ + this.storetree[i] = store.custom; + store.custom._field = field; + } + else { + this.storetree[i] = parse_tree(field, this.marker); + if(store.filter){ + if(typeof this.storetree[i] === "string"){ + // it needs an object to put a property to it + this.storetree[i] = new String(this.storetree[i]); + } + this.storetree[i]._filter = store.filter; + } + } + } + } + + return index; +} + +function parse_tree(key, marker){ + + const tree = key.split(":"); + let count = 0; + + for(let i = 0; i < tree.length; i++){ + key = tree[i]; + // todo apply some indexes e.g. [0], [-1], [0-2] + if(key[key.length - 1] === "]"){ + key = key.substring(0, key.length - 2); + if(key){ + marker[count] = true; + } + } + if(key){ + tree[count++] = key; + } + } + + if(count < tree.length){ + tree.length = count; + } + + return count > 1 ? tree : tree[0]; +} + +Document.prototype.append = function(id, content){ + return this.add(id, content, true); +}; + +Document.prototype.update = function(id, content){ + return this.remove(id).add(id, content); +}; + +Document.prototype.remove = function(id){ + + if(is_object(id)){ + id = parse_simple(id, this.key); + } + + for(const index of this.index.values()){ + index.remove(id, /* skip deletion */ true); + } + + if(this.reg.has(id)){ + + if(this.tag){ + // when fastupdate was enabled all ids are already removed + if(!this.fastupdate){ + for(let field of this.tag.values()){ + for(let item of field){ + const tag = item[0]; + const ids = item[1]; + const pos = ids.indexOf(id); + if(pos > -1){ + ids.length > 1 + ? ids.splice(pos, 1) + : field.delete(tag); + } + } + } + } + } + + if(this.store){ + this.store.delete(id); + } + + this.reg.delete(id); + } + + // the cache could be used outside the InMemory store + if(this.cache){ + this.cache.remove(id); + } + + return this; +}; + +Document.prototype.clear = function(){ + + //const promises = []; + + for(const index of this.index.values()){ + // db index will add clear task + index.clear(); + // const promise = index.clear(); + // if(promise instanceof Promise){ + // promises.push(promise); + // } + } + + if(this.tag){ + for(const tags of this.tag.values()){ + tags.clear(); + } + } + + if(this.store){ + this.store.clear(); + } + + return this; /*promises.length + ? Promise.all(promises) + :*/ +}; + +Document.prototype.contain = function(id){ + + if(this.db){ + return this.index.get(this.field[0]).db.has(id); + } + + return this.reg.has(id); +}; + +Document.prototype.cleanup = function(){ + + for(const index of this.index.values()){ + index.cleanup(); + } + + return this; +}; + +{ + + Document.prototype.get = function(id){ + + if(this.db){ + return this.index.get(this.field[0]).db.enrich(id).then(function(result){ + return result[0] && result[0].doc; + }); + } + + return this.store.get(id); + }; + + Document.prototype.set = function(id, store){ + + this.store.set(id, store); + return this; + }; +} + +{ + // todo mo + Document.prototype.searchCache = searchCache; +} + +{ + + Document.prototype.export = exportDocument; + Document.prototype.import = importDocument; +} + +{ + + apply_async(Document.prototype); +} + +// COMPILER BLOCK --> +const MAXIMUM_QUERY_VARS = 16000; +const fields = ["map", "ctx", "reg", "tag", "cfg"]; +const types = { + "text": "text", + "char": "text", + "varchar": "text", + "string": "text", + "number": "int", + "numeric": "int", + "integer": "int", + "smallint": "int", + "tinyint": "int", + "mediumint": "int", + "int": "int", + "int8": "int", + "uint8": "int", + "int16": "int", + "uint16": "int", + "int32": "int", + "uint32": "bigint", + "int64": "bigint", + "bigint": "bigint" +}; + +function sanitize(str) { + return str.toLowerCase().replace(/[^a-z0-9_]/g, ""); +} + +// global transaction to keep track of database lock +const TRX = Object.create(null); +const DB = Object.create(null); + +/** + * @constructor + * @implements StorageInterface + */ + +function SqliteDB(name, config = {}){ + if(!this){ + return new SqliteDB(name, config); + } + if(typeof name === "object"){ + name = name.name; + config = name; + } + if(!name){ + console.info("Default storage space was used, because a name was not passed."); + } + //field = "Test-456"; + this.id = config.path || ( + name === ":memory:" + ? name + : "flexsearch" + (name ? "-" + sanitize(name) : "") + ".sqlite" + ); + this.field = config.field ? "_" + sanitize(config.field) : ""; + this.support_tag_search = true; + this.db = config.db || DB[this.id] || null; + this.type = config.type ? types[config.type.toLowerCase()] : "string"; + if(!this.type) throw new Error("Unknown type of ID '" + config.type + "'"); +} +SqliteDB.prototype.mount = function(flexsearch){ + if(flexsearch.constructor === Document){ + return flexsearch.mount(this); + } + flexsearch.db = this; + return this.open(); +}; + +SqliteDB.prototype.open = async function(){ + + if(!this.db){ + + if(!(this.db = DB[this.id])){ + + let filepath = this.id; + if(filepath !== ":memory:"){ + // skip absolute path + if(filepath[0] !== "/" && filepath[0] !== "\\"){ + // current working directory + const dir = process.cwd(); + filepath = path.join(dir, this.id); + } + } + + this.db = DB[this.id] = new sqlite3.Database(filepath); + } + } + + const db = this.db; + + for(let i = 0; i < fields.length; i++){ + const exist = await this.promisfy({ + method: "get", + stmt: "SELECT EXISTS(SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?) as exist", + params: [fields[i] + (fields[i] === "reg" ? "" : this.field)] + }); + if(!exist || !exist.exist){ + let stmt, stmt_index; + switch(fields[i]){ + case "map": + stmt = ` + CREATE TABLE IF NOT EXISTS main.map${this.field}( + key TEXT NOT NULL, + res INTEGER NOT NULL, + id ${this.type} NOT NULL + ); + `; + stmt_index = ` + CREATE INDEX IF NOT EXISTS map_key_index${this.field} + ON map${this.field} (key); + CREATE INDEX IF NOT EXISTS map_id_index${this.field} + ON map${this.field} (id); + `; + break; + + case "ctx": + stmt = ` + CREATE TABLE IF NOT EXISTS main.ctx${this.field}( + ctx TEXT NOT NULL, + key TEXT NOT NULL, + res INTEGER NOT NULL, + id ${this.type} NOT NULL + ); + + `; + stmt_index = ` + CREATE INDEX IF NOT EXISTS ctx_key_index${this.field} + ON ctx${this.field} (ctx, key); + CREATE INDEX IF NOT EXISTS ctx_id_index${this.field} + ON ctx${this.field} (id); + `; + break; + + case "tag": + stmt = ` + CREATE TABLE IF NOT EXISTS main.tag${this.field}( + tag TEXT NOT NULL, + id ${this.type} NOT NULL + ); + `; + stmt_index = ` + CREATE INDEX IF NOT EXISTS tag_index${this.field} + ON tag${this.field} (tag); + CREATE INDEX IF NOT EXISTS tag_id_index${this.field} + ON tag${this.field} (id); + `; + break; + + case "reg": + stmt = ` + CREATE TABLE IF NOT EXISTS main.reg( + id ${this.type} NOT NULL + CONSTRAINT reg_pk${this.field} + PRIMARY KEY, + doc TEXT DEFAULT NULL + ); + `; + stmt_index = ` + CREATE INDEX IF NOT EXISTS reg_index + ON reg (id); + `; + break; + + case "cfg": + stmt = ` + CREATE TABLE IF NOT EXISTS main.cfg${this.field} ( + cfg TEXT NOT NULL + ); + `; + break; + } + + await new Promise(function(resolve, reject){ + db.exec(stmt, function(err, rows){ + if(err) return reject(err); + stmt_index + ? db.exec(stmt_index, function(err, rows){ + if(err) return reject(err); + resolve(rows); + }) + : resolve(rows); + }); + }); + } + } + + db.exec("PRAGMA optimize = 0x10002"); + + return db; +}; + +SqliteDB.prototype.close = function(){ + this.db.close(); + this.db = null; + return this; +}; + +SqliteDB.prototype.destroy = async function(){ + await this.transaction(function(){ + this.db.run("DROP TABLE IF EXISTS main.map" + this.field + ";"); + this.db.run("DROP TABLE IF EXISTS main.ctx" + this.field + ";"); + this.db.run("DROP TABLE IF EXISTS main.tag" + this.field + ";"); + this.db.run("DROP TABLE IF EXISTS main.cfg" + this.field + ";"); + this.db.run("DROP TABLE IF EXISTS main.reg;"); + }); + this.close(); +}; + +SqliteDB.prototype.clear = function(){ + return this.transaction(function(){ + this.db.run("DELETE FROM main.map" + this.field + " WHERE 1;"); + this.db.run("DELETE FROM main.ctx" + this.field + " WHERE 1;"); + this.db.run("DELETE FROM main.tag" + this.field + " WHERE 1;"); + this.db.run("DELETE FROM main.cfg" + this.field + " WHERE 1;"); + this.db.run("DELETE FROM main.reg WHERE 1;"); + }); +}; + +function create_result(rows, resolve, enrich){ + if(resolve){ + for(let i = 0; i < rows.length; i++){ + if(enrich){ + if(rows[i].doc){ + rows[i].doc = JSON.parse(rows[i].doc); + } + } + else { + rows[i] = rows[i].id; + } + } + return rows; + } + else { + const arr = []; + for(let i = 0, row; i < rows.length; i++){ + row = rows[i]; + arr[row.res] || (arr[row.res] = []); + arr[row.res].push(enrich + ? row + : row.id + ); + } + return arr; + } +} + +SqliteDB.prototype.get = function(key, ctx, limit = 0, offset = 0, resolve = true, enrich = false, tags){ + let result; + let stmt = ''; + let params = ctx ? [ctx, key] : [key]; + let table = "main." + (ctx ? "ctx" : "map") + this.field; + if(tags){ + for(let i = 0; i < tags.length; i+=2){ + stmt += ` AND ${ table }.id IN (SELECT id FROM main.tag_${ sanitize(tags[i]) } WHERE tag = ?)`; + params.push(tags[i + 1]); + } + } + if(ctx){ + result = this.promisfy({ + method: "all", + stmt: ` + SELECT ${ table }.id + ${ resolve ? "" : ", res" } + ${ enrich ? ", doc" : "" } + FROM ${ table } + ${ enrich ? ` + LEFT JOIN main.reg ON main.reg.id = ${ table }.id + ` : "" } + WHERE ctx = ? AND key = ? ${stmt} + ORDER BY res + ${ limit ? "LIMIT " + limit : "" } + ${ offset ? "OFFSET " + offset : "" } + `, + params + }); + } + else { + result = this.promisfy({ + method: "all", + stmt: ` + SELECT ${ table }.id + ${ resolve ? "" : ", res" } + ${ enrich ? ", doc" : "" } + FROM ${ table } + ${ enrich ? ` + LEFT JOIN main.reg ON main.reg.id = ${ table }.id + ` : "" } + WHERE key = ? ${stmt} + ORDER BY res + ${ limit ? "LIMIT " + limit : "" } + ${ offset ? "OFFSET " + offset : "" } + `, + params + }); + } + return result.then(function(rows){ + return create_result(rows, resolve, enrich); + }); +}; + +SqliteDB.prototype.tag = function(tag, limit = 0, offset = 0, enrich = false){ + const table = "main.tag" + this.field; + const promise = this.promisfy({ + method: "all", + stmt: ` + SELECT ${ table }.id + ${ enrich ? ", doc" : "" } + FROM ${ table } + ${ enrich ? ` + LEFT JOIN main.reg ON main.reg.id = ${ table }.id + ` : "" } + WHERE tag = ? + ${ limit ? "LIMIT " + limit : "" } + ${ offset ? "OFFSET " + offset : "" } + `, + params: [tag] + }); + enrich || promise.then(function(rows){ + return create_result(rows, true, false); + }); + return promise; +}; + +function build_params(length){ + + let stmt = ",?"; + for(let i = 1; i < length;){ + if(i <= (length - i)){ + stmt += stmt; + i *= 2; + } + else { + stmt += stmt.substring(0, (length - i) * 2); + break; + } + } + return stmt.substring(1); +} + +SqliteDB.prototype.enrich = function(ids){ + + const result = []; + const promises = []; + if(typeof ids !== "object"){ + ids = [ids]; + } + + for(let count = 0; count < ids.length;){ + + const chunk = ids.length - count > MAXIMUM_QUERY_VARS + ? ids.slice(count, count + MAXIMUM_QUERY_VARS) + : count ? ids.slice(count) : ids; + count += chunk.length; + + // let stmt = "?"; + // for(let i = 1; i < chunk.length; i++){ + // stmt += ",?"; + // } + + // 10x faster string concatenation + let stmt = build_params(chunk.length); + + promises.push(this.promisfy({ + method: "all", + stmt: `SELECT id, doc FROM main.reg WHERE id IN (${stmt})`, + params: chunk + })); + } + + return Promise.all(promises).then(function(promises){ + + for(let i = 0, res; i < promises.length; i++){ + res = promises[i]; + if(res && res.length){ + for(let i = 0, doc; i < res.length; i++){ + if((doc = res[i].doc)){ + res[i].doc = JSON.parse(doc); + } + } + result.push(res); + } + } + + return result.length === 1 + ? result[0] + : result.length > 1 + ? concat(result) + : result; + }); +}; + +SqliteDB.prototype.has = function(id){ + return this.promisfy({ + method: "get", + stmt: `SELECT EXISTS(SELECT 1 FROM main.reg WHERE id = ?) as exist`, + params: [id] + }).then(function(result){ + return result && result.exist; + }); +}; + +SqliteDB.prototype.search = function(flexsearch, query, limit = 100, offset = 0, suggest = false, resolve = true, enrich = false, tags){ + + let rows; + + if(query.length > 1 && flexsearch.depth){ + + let stmt = ""; + let params = []; + let keyword = query[0]; + let term; + + for(let i = 1; i < query.length; i++){ + term = query[i]; + const swap = flexsearch.bidirectional && (term > keyword); + stmt += (stmt ? " OR " : "") + `(ctx = ? AND key = ?)`; + params.push(swap ? term : keyword, swap ? keyword : term); + keyword = term; + } + + if(tags){ + stmt = "(" + stmt + ")"; + for(let i = 0; i < tags.length; i+=2){ + stmt += ` AND id IN (SELECT id FROM main.tag_${ sanitize(tags[i]) } WHERE tag = ?)`; + params.push(tags[i + 1]); + } + } + + rows = this.promisfy({ + method: "all", + stmt: ` + SELECT r.id + ${ resolve ? "" : ", res" } + ${ enrich ? ", doc" : "" } + FROM ( + SELECT id, count(*) as count, + ${ suggest ? "SUM" : "MIN" }(res) as res + FROM main.ctx${ this.field } + WHERE ${ stmt } + GROUP BY id + ) as r + ${ enrich ? ` + LEFT JOIN main.reg ON main.reg.id = r.id + ` : "" } + ${ suggest ? "" : "WHERE count = " + (query.length - 1) } + ORDER BY ${ suggest ? "count DESC, res" : "res" } + ${ limit ? "LIMIT " + limit : "" } + ${ offset ? "OFFSET " + offset : "" } + `, + params + }); + + // variant 1 + // for(let i = 1; i < query.length; i++){ + // stmt += (stmt ? " UNION ALL " : "") + ` + // SELECT id, res + // FROM main.ctx${this.field} + // WHERE ctx = ? AND key = ? + // `; + // term = query[i]; + // const swap = flexsearch.bidirectional && (term > keyword); + // params.push(swap ? term : keyword, swap ? keyword : term); + // keyword = term; + // } + // + // rows = await this.promisfy({ + // method: "all", + // stmt: ` + // SELECT id/*, res */ + // FROM ( + // SELECT id, ${suggest ? "SUM" : "MIN"}(res) as res, count(*) as count + // FROM (${stmt}) as t + // GROUP BY id + // ORDER BY ${suggest ? "count DESC, res" : "res"} + // LIMIT ${limit} + // OFFSET ${offset} + // ) as r + // ${suggest ? "" : "WHERE count = " + (query.length - 1)} + // `, + // params + // }); + } + else { + + let stmt = ""; + let query_length = query.length; + for(let i = 0; i < query_length; i++){ + stmt += (stmt ? " OR " : "") + `key = ?`; + } + + if(tags){ + stmt = "(" + stmt + ")"; + for(let i = 0; i < tags.length; i+=2){ + stmt += ` AND id IN (SELECT id FROM main.tag_${sanitize(tags[i])} WHERE tag = ?)`; + query.push(tags[i + 1]); + } + } + + rows = this.promisfy({ + method: "all", + stmt: ` + SELECT r.id + ${ resolve ? "" : ", res" } + ${ enrich ? ", doc" : "" } + FROM ( + SELECT id, count(*) as count, + ${ suggest ? "SUM" : "MIN" }(res) as res + FROM main.map${ this.field } + WHERE ${ stmt } + GROUP BY id + ) as r + ${ enrich ? ` + LEFT JOIN main.reg ON main.reg.id = r.id + ` : "" } + ${ suggest ? "" : "WHERE count = " + query_length } + ORDER BY ${ suggest ? "count DESC, res" : "res" } + ${ limit ? "LIMIT " + limit : "" } + ${ offset ? "OFFSET " + offset : "" } + `, + params: query + }); + + // variant 1 + // for(let i = 0; i < query.length; i++){ + // stmt += (stmt ? " UNION ALL " : "") + ` + // SELECT id, res + // FROM main.map${ this.field } + // WHERE key = ? + // `; + // } + // + // rows = await this.promisfy({ + // method: "all", + // stmt: ` + // SELECT id/*, res*/ + // FROM ( + // SELECT id, ${suggest ? "SUM" : "MIN"}(res) as res, count(*) as count + // FROM (${stmt}) as t + // GROUP BY id + // ORDER BY ${suggest ? "count DESC, res" : "res"} + // LIMIT ${limit} + // OFFSET ${offset} + // ) as r + // ${ suggest ? "" : "WHERE count = " + query.length } + // `, + // params: query + // }); + } + + return rows.then(function(rows){ + return create_result(rows, resolve, enrich); + }); +}; + +SqliteDB.prototype.info = function(){ + // todo +}; + +SqliteDB.prototype.transaction = function(task, callback){ + + const self = this; + + if(TRX[this.id]){ + return TRX[this.id].then(function(){ + return self.transaction(task, callback); + }); + } + + const db = this.db; + + return TRX[this.id] = new Promise(function(resolve, reject){ + db.exec("PRAGMA optimize"); + db.exec('PRAGMA busy_timeout = 5000'); + db.exec("BEGIN"); + db.parallelize(function(){ + task.call(self); + }); + db.exec("COMMIT", function(err, rows){ + TRX[self.id] = null; + if(err) return reject(err); + callback && callback(rows); + resolve(rows); + db.exec("PRAGMA shrink_memory"); + }); + }); +}; + +SqliteDB.prototype.commit = async function(flexsearch, _replace, _append){ + + // process cleanup tasks + if(_replace){ + await this.clear(); + // there are just removals in the task queue + flexsearch.commit_task = []; + } + else { + let tasks = flexsearch.commit_task; + flexsearch.commit_task = []; + for(let i = 0, task; i < tasks.length; i++){ + task = tasks[i]; + // there are just removals in the task queue + if(task.clear){ + await this.clear(); + _replace = true; + break; + } + else { + tasks[i] = task.del; + } + } + if(!_replace){ + if(!_append){ + tasks = tasks.concat(toArray(flexsearch.reg)); + } + tasks.length && await this.remove(tasks); + } + } + + if(!flexsearch.reg.size){ + return; + } + + await this.transaction(function(){ + + for(const item of flexsearch.map){ + const key = item[0]; + const arr = item[1]; + + for(let i = 0, ids; i < arr.length; i++){ + if((ids = arr[i]) && ids.length){ + let stmt = ""; + let params = []; + + for(let j = 0; j < ids.length; j++){ + stmt += (stmt ? "," : "") + "(?,?,?)"; + params.push(key, i, ids[j]); + // maximum count of variables supported + if((j === ids.length - 1) || (params.length + 3 > MAXIMUM_QUERY_VARS)){ + this.db.run("INSERT INTO main.map" + this.field + " (key, res, id) VALUES " + stmt, params); + stmt = ""; + params = []; + } + //this.db.run("INSERT INTO map (key, res, id) VALUES (?, ?, ?) ON CONFLICT DO NOTHING", [key, i, ids[j]]); + } + } + } + } + //}); + //await this.transaction(function(){ + + for(const ctx of flexsearch.ctx){ + const ctx_key = ctx[0]; + const ctx_value = ctx[1]; + + for(const item of ctx_value){ + const key = item[0]; + const arr = item[1]; + + for(let i = 0, ids; i < arr.length; i++){ + if((ids = arr[i]) && ids.length){ + let stmt = ""; + let params = []; + + for(let j = 0; j < ids.length; j++){ + stmt += (stmt ? "," : "") + "(?,?,?,?)"; + params.push(ctx_key, key, i, ids[j]); + // maximum count of variables supported + if((j === ids.length - 1) || (params.length + 4 > MAXIMUM_QUERY_VARS)){ + this.db.run("INSERT INTO main.ctx" + this.field + " (ctx, key, res, id) VALUES " + stmt, params); + stmt = ""; + params = []; + } + } + } + } + } + } + //}); + //await this.transaction(function(){ + + if(flexsearch.store){ + let stmt = ""; + let chunk = []; + for(const item of flexsearch.store.entries()){ + const id = item[0]; + const doc = item[1]; + stmt += (stmt ? "," : "") + "(?,?)"; + chunk.push(id, typeof doc === "object" + ? JSON.stringify(doc) + : doc || null + ); + if(chunk.length + 2 > MAXIMUM_QUERY_VARS){ + this.db.run("INSERT INTO main.reg (id, doc) VALUES " + stmt, chunk); + stmt = ""; + chunk = []; + } + } + if(chunk.length){ + this.db.run("INSERT INTO main.reg (id, doc) VALUES " + stmt, chunk); + } + } + else if(!flexsearch.bypass){ + let ids = toArray(flexsearch.reg); + for(let count = 0; count < ids.length;){ + const chunk = ids.length - count > MAXIMUM_QUERY_VARS + ? ids.slice(count, count + MAXIMUM_QUERY_VARS) + : count ? ids.slice(count) : ids; + count += chunk.length; + const stmt = build_params(chunk.length); + this.db.run("INSERT INTO main.reg (id) VALUES (" + stmt + ")", chunk); + } + } + //}); + //await this.transaction(function(){ + + if(flexsearch.tag){ + let stmt = ""; + let chunk = []; + for(const item of flexsearch.tag){ + const tag = item[0]; + const ids = item[1]; + if(!ids.length) continue; + for(let i = 0; i < ids.length; i++){ + stmt += (stmt ? "," : "") + "(?,?)"; + chunk.push(tag, ids[i]); + } + if(chunk.length + 2 > MAXIMUM_QUERY_VARS){ + this.db.run("INSERT INTO main.tag" + this.field + " (tag, id) VALUES " + stmt, chunk); + stmt = ""; + chunk = []; + } + } + if(chunk.length){ + this.db.run("INSERT INTO main.tag" + this.field + " (tag, id) VALUES " + stmt, chunk); + } + } + }); + + // TODO + //await this.transaction(function(){ + // this.db.run("INSERT INTO main.cfg" + this.field + " (cfg) VALUES (?)", [JSON.stringify({ + // "charset": flexsearch.charset, + // "tokenize": flexsearch.tokenize, + // "resolution": flexsearch.resolution, + // "fastupdate": flexsearch.fastupdate, + // "compress": flexsearch.compress, + // "encoder": { + // "minlength": flexsearch.encoder.minlength + // }, + // "context": { + // "depth": flexsearch.depth, + // "bidirectional": flexsearch.bidirectional, + // "resolution": flexsearch.resolution_ctx + // } + // })]); + //}); + + flexsearch.map.clear(); + flexsearch.ctx.clear(); + flexsearch.tag && + flexsearch.tag.clear(); + flexsearch.store && + flexsearch.store.clear(); + flexsearch.document || + flexsearch.reg.clear(); +}; + +SqliteDB.prototype.remove = function(ids){ + + if(typeof ids !== "object"){ + ids = [ids]; + } + + let next; + // maximum count of variables supported + if(ids.length > MAXIMUM_QUERY_VARS){ + next = ids.slice(MAXIMUM_QUERY_VARS); + ids = ids.slice(0, MAXIMUM_QUERY_VARS); + } + + const self = this; + return this.transaction(function(){ + const stmt = build_params(ids.length); + this.db.run("DELETE FROM main.map" + self.field + " WHERE id IN (" + stmt + ")", ids); + this.db.run("DELETE FROM main.ctx" + self.field + " WHERE id IN (" + stmt + ")", ids); + this.db.run("DELETE FROM main.tag" + self.field + " WHERE id IN (" + stmt + ")", ids); + this.db.run("DELETE FROM main.reg WHERE id IN (" + stmt + ")", ids); + }).then(function(res){ + return next + ? self.remove(next) + : res; + }); +}; + +SqliteDB.prototype.promisfy = function(opt){ + const db = this.db; + return new Promise(function(resolve, reject){ + db[opt.method](opt.stmt, opt.params || [], function(err, rows){ + opt.callback && opt.callback(rows); + err ? reject(err) + : resolve(rows); + }); + }); +}; + +module.exports = SqliteDB; diff --git a/dist/flexsearch.bundle.debug.js b/dist/flexsearch.bundle.debug.js index 715b85b..6daaa04 100644 --- a/dist/flexsearch.bundle.debug.js +++ b/dist/flexsearch.bundle.debug.js @@ -80,32 +80,33 @@ function da(a) { } return b; } -;const ea = /[^\p{L}\p{N}]+/u, fa = /(\d{3})/g, ha = /(\D)(\d{3})/g, ia = /(\d{3})(\D)/g, ja = "".normalize && /[\u0300-\u036f]/g, ka = !ja && [["\u00aa", "a"], ["\u00b2", "2"], ["\u00b3", "3"], ["\u00b9", "1"], ["\u00ba", "o"], ["\u00bc", "1\u20444"], ["\u00bd", "1\u20442"], ["\u00be", "3\u20444"], ["\u00e0", "a"], ["\u00e1", "a"], ["\u00e2", "a"], ["\u00e3", "a"], ["\u00e4", "a"], ["\u00e5", "a"], ["\u00e7", "c"], ["\u00e8", "e"], ["\u00e9", "e"], ["\u00ea", "e"], ["\u00eb", "e"], ["\u00ec", "i"], -["\u00ed", "i"], ["\u00ee", "i"], ["\u00ef", "i"], ["\u00f1", "n"], ["\u00f2", "o"], ["\u00f3", "o"], ["\u00f4", "o"], ["\u00f5", "o"], ["\u00f6", "o"], ["\u00f9", "u"], ["\u00fa", "u"], ["\u00fb", "u"], ["\u00fc", "u"], ["\u00fd", "y"], ["\u00ff", "y"], ["\u0101", "a"], ["\u0103", "a"], ["\u0105", "a"], ["\u0107", "c"], ["\u0109", "c"], ["\u010b", "c"], ["\u010d", "c"], ["\u010f", "d"], ["\u0113", "e"], ["\u0115", "e"], ["\u0117", "e"], ["\u0119", "e"], ["\u011b", "e"], ["\u011d", "g"], ["\u011f", -"g"], ["\u0121", "g"], ["\u0123", "g"], ["\u0125", "h"], ["\u0129", "i"], ["\u012b", "i"], ["\u012d", "i"], ["\u012f", "i"], ["\u0133", "ij"], ["\u0135", "j"], ["\u0137", "k"], ["\u013a", "l"], ["\u013c", "l"], ["\u013e", "l"], ["\u0140", "l"], ["\u0144", "n"], ["\u0146", "n"], ["\u0148", "n"], ["\u0149", "n"], ["\u014d", "o"], ["\u014f", "o"], ["\u0151", "o"], ["\u0155", "r"], ["\u0157", "r"], ["\u0159", "r"], ["\u015b", "s"], ["\u015d", "s"], ["\u015f", "s"], ["\u0161", "s"], ["\u0163", "t"], ["\u0165", -"t"], ["\u0169", "u"], ["\u016b", "u"], ["\u016d", "u"], ["\u016f", "u"], ["\u0171", "u"], ["\u0173", "u"], ["\u0175", "w"], ["\u0177", "y"], ["\u017a", "z"], ["\u017c", "z"], ["\u017e", "z"], ["\u017f", "s"], ["\u01a1", "o"], ["\u01b0", "u"], ["\u01c6", "dz"], ["\u01c9", "lj"], ["\u01cc", "nj"], ["\u01ce", "a"], ["\u01d0", "i"], ["\u01d2", "o"], ["\u01d4", "u"], ["\u01d6", "u"], ["\u01d8", "u"], ["\u01da", "u"], ["\u01dc", "u"], ["\u01df", "a"], ["\u01e1", "a"], ["\u01e3", "ae"], ["\u00e6", "ae"], -["\u01fd", "ae"], ["\u01e7", "g"], ["\u01e9", "k"], ["\u01eb", "o"], ["\u01ed", "o"], ["\u01ef", "\u0292"], ["\u01f0", "j"], ["\u01f3", "dz"], ["\u01f5", "g"], ["\u01f9", "n"], ["\u01fb", "a"], ["\u01ff", "\u00f8"], ["\u0201", "a"], ["\u0203", "a"], ["\u0205", "e"], ["\u0207", "e"], ["\u0209", "i"], ["\u020b", "i"], ["\u020d", "o"], ["\u020f", "o"], ["\u0211", "r"], ["\u0213", "r"], ["\u0215", "u"], ["\u0217", "u"], ["\u0219", "s"], ["\u021b", "t"], ["\u021f", "h"], ["\u0227", "a"], ["\u0229", "e"], -["\u022b", "o"], ["\u022d", "o"], ["\u022f", "o"], ["\u0231", "o"], ["\u0233", "y"], ["\u02b0", "h"], ["\u02b1", "h"], ["\u0266", "h"], ["\u02b2", "j"], ["\u02b3", "r"], ["\u02b4", "\u0279"], ["\u02b5", "\u027b"], ["\u02b6", "\u0281"], ["\u02b7", "w"], ["\u02b8", "y"], ["\u02e0", "\u0263"], ["\u02e1", "l"], ["\u02e2", "s"], ["\u02e3", "x"], ["\u02e4", "\u0295"], ["\u0390", "\u03b9"], ["\u03ac", "\u03b1"], ["\u03ad", "\u03b5"], ["\u03ae", "\u03b7"], ["\u03af", "\u03b9"], ["\u03b0", "\u03c5"], ["\u03ca", -"\u03b9"], ["\u03cb", "\u03c5"], ["\u03cc", "\u03bf"], ["\u03cd", "\u03c5"], ["\u03ce", "\u03c9"], ["\u03d0", "\u03b2"], ["\u03d1", "\u03b8"], ["\u03d2", "\u03a5"], ["\u03d3", "\u03a5"], ["\u03d4", "\u03a5"], ["\u03d5", "\u03c6"], ["\u03d6", "\u03c0"], ["\u03f0", "\u03ba"], ["\u03f1", "\u03c1"], ["\u03f2", "\u03c2"], ["\u03f5", "\u03b5"], ["\u0439", "\u0438"], ["\u0450", "\u0435"], ["\u0451", "\u0435"], ["\u0453", "\u0433"], ["\u0457", "\u0456"], ["\u045c", "\u043a"], ["\u045d", "\u0438"], ["\u045e", -"\u0443"], ["\u0477", "\u0475"], ["\u04c2", "\u0436"], ["\u04d1", "\u0430"], ["\u04d3", "\u0430"], ["\u04d7", "\u0435"], ["\u04db", "\u04d9"], ["\u04dd", "\u0436"], ["\u04df", "\u0437"], ["\u04e3", "\u0438"], ["\u04e5", "\u0438"], ["\u04e7", "\u043e"], ["\u04eb", "\u04e9"], ["\u04ed", "\u044d"], ["\u04ef", "\u0443"], ["\u04f1", "\u0443"], ["\u04f3", "\u0443"], ["\u04f5", "\u0447"]]; -function E(a = {}) { - if (!(this instanceof E)) { - return new E(...arguments); +;var ea = [["\u00aa", "a"], ["\u00b2", "2"], ["\u00b3", "3"], ["\u00b9", "1"], ["\u00ba", "o"], ["\u00bc", "1\u20444"], ["\u00bd", "1\u20442"], ["\u00be", "3\u20444"], ["\u00e0", "a"], ["\u00e1", "a"], ["\u00e2", "a"], ["\u00e3", "a"], ["\u00e4", "a"], ["\u00e5", "a"], ["\u00e7", "c"], ["\u00e8", "e"], ["\u00e9", "e"], ["\u00ea", "e"], ["\u00eb", "e"], ["\u00ec", "i"], ["\u00ed", "i"], ["\u00ee", "i"], ["\u00ef", "i"], ["\u00f1", "n"], ["\u00f2", "o"], ["\u00f3", "o"], ["\u00f4", "o"], ["\u00f5", +"o"], ["\u00f6", "o"], ["\u00f9", "u"], ["\u00fa", "u"], ["\u00fb", "u"], ["\u00fc", "u"], ["\u00fd", "y"], ["\u00ff", "y"], ["\u0101", "a"], ["\u0103", "a"], ["\u0105", "a"], ["\u0107", "c"], ["\u0109", "c"], ["\u010b", "c"], ["\u010d", "c"], ["\u010f", "d"], ["\u0113", "e"], ["\u0115", "e"], ["\u0117", "e"], ["\u0119", "e"], ["\u011b", "e"], ["\u011d", "g"], ["\u011f", "g"], ["\u0121", "g"], ["\u0123", "g"], ["\u0125", "h"], ["\u0129", "i"], ["\u012b", "i"], ["\u012d", "i"], ["\u012f", "i"], ["\u0133", +"ij"], ["\u0135", "j"], ["\u0137", "k"], ["\u013a", "l"], ["\u013c", "l"], ["\u013e", "l"], ["\u0140", "l"], ["\u0144", "n"], ["\u0146", "n"], ["\u0148", "n"], ["\u0149", "n"], ["\u014d", "o"], ["\u014f", "o"], ["\u0151", "o"], ["\u0155", "r"], ["\u0157", "r"], ["\u0159", "r"], ["\u015b", "s"], ["\u015d", "s"], ["\u015f", "s"], ["\u0161", "s"], ["\u0163", "t"], ["\u0165", "t"], ["\u0169", "u"], ["\u016b", "u"], ["\u016d", "u"], ["\u016f", "u"], ["\u0171", "u"], ["\u0173", "u"], ["\u0175", "w"], ["\u0177", +"y"], ["\u017a", "z"], ["\u017c", "z"], ["\u017e", "z"], ["\u017f", "s"], ["\u01a1", "o"], ["\u01b0", "u"], ["\u01c6", "dz"], ["\u01c9", "lj"], ["\u01cc", "nj"], ["\u01ce", "a"], ["\u01d0", "i"], ["\u01d2", "o"], ["\u01d4", "u"], ["\u01d6", "u"], ["\u01d8", "u"], ["\u01da", "u"], ["\u01dc", "u"], ["\u01df", "a"], ["\u01e1", "a"], ["\u01e3", "ae"], ["\u00e6", "ae"], ["\u01fd", "ae"], ["\u01e7", "g"], ["\u01e9", "k"], ["\u01eb", "o"], ["\u01ed", "o"], ["\u01ef", "\u0292"], ["\u01f0", "j"], ["\u01f3", +"dz"], ["\u01f5", "g"], ["\u01f9", "n"], ["\u01fb", "a"], ["\u01ff", "\u00f8"], ["\u0201", "a"], ["\u0203", "a"], ["\u0205", "e"], ["\u0207", "e"], ["\u0209", "i"], ["\u020b", "i"], ["\u020d", "o"], ["\u020f", "o"], ["\u0211", "r"], ["\u0213", "r"], ["\u0215", "u"], ["\u0217", "u"], ["\u0219", "s"], ["\u021b", "t"], ["\u021f", "h"], ["\u0227", "a"], ["\u0229", "e"], ["\u022b", "o"], ["\u022d", "o"], ["\u022f", "o"], ["\u0231", "o"], ["\u0233", "y"], ["\u02b0", "h"], ["\u02b1", "h"], ["\u0266", "h"], +["\u02b2", "j"], ["\u02b3", "r"], ["\u02b4", "\u0279"], ["\u02b5", "\u027b"], ["\u02b6", "\u0281"], ["\u02b7", "w"], ["\u02b8", "y"], ["\u02e0", "\u0263"], ["\u02e1", "l"], ["\u02e2", "s"], ["\u02e3", "x"], ["\u02e4", "\u0295"], ["\u0390", "\u03b9"], ["\u03ac", "\u03b1"], ["\u03ad", "\u03b5"], ["\u03ae", "\u03b7"], ["\u03af", "\u03b9"], ["\u03b0", "\u03c5"], ["\u03ca", "\u03b9"], ["\u03cb", "\u03c5"], ["\u03cc", "\u03bf"], ["\u03cd", "\u03c5"], ["\u03ce", "\u03c9"], ["\u03d0", "\u03b2"], ["\u03d1", +"\u03b8"], ["\u03d2", "\u03a5"], ["\u03d3", "\u03a5"], ["\u03d4", "\u03a5"], ["\u03d5", "\u03c6"], ["\u03d6", "\u03c0"], ["\u03f0", "\u03ba"], ["\u03f1", "\u03c1"], ["\u03f2", "\u03c2"], ["\u03f5", "\u03b5"], ["\u0439", "\u0438"], ["\u0450", "\u0435"], ["\u0451", "\u0435"], ["\u0453", "\u0433"], ["\u0457", "\u0456"], ["\u045c", "\u043a"], ["\u045d", "\u0438"], ["\u045e", "\u0443"], ["\u0477", "\u0475"], ["\u04c2", "\u0436"], ["\u04d1", "\u0430"], ["\u04d3", "\u0430"], ["\u04d7", "\u0435"], ["\u04db", +"\u04d9"], ["\u04dd", "\u0436"], ["\u04df", "\u0437"], ["\u04e3", "\u0438"], ["\u04e5", "\u0438"], ["\u04e7", "\u043e"], ["\u04eb", "\u04e9"], ["\u04ed", "\u044d"], ["\u04ef", "\u0443"], ["\u04f1", "\u0443"], ["\u04f3", "\u0443"], ["\u04f5", "\u0447"]]; +const fa = /[^\p{L}\p{N}]+/u, ha = /(\d{3})/g, ia = /(\D)(\d{3})/g, ja = /(\d{3})(\D)/g, ka = "".normalize && /[\u0300-\u036f]/g; +function G(a) { + if (!this) { + return new G(...arguments); } - for (a = 0; a < arguments.length; a++) { - this.assign(arguments[a]); + for (let b = 0; b < arguments.length; b++) { + this.assign(arguments[b]); } } -E.prototype.assign = function(a) { +G.prototype.assign = function(a) { this.normalize = y(a.normalize, !0, this.normalize); - let b = a.ga, c = b || a.na || a.split; + let b = a.include, c = b || a.exclude || a.split; if ("object" === typeof c) { let d = !b, e = ""; - a.ga || (e += "\\p{Z}"); - c.ka && (e += "\\p{L}"); - c.oa && (e += "\\p{N}", d = !!b); - c.qa && (e += "\\p{S}"); - c.pa && (e += "\\p{P}"); + a.include || (e += "\\p{Z}"); + c.letter && (e += "\\p{L}"); + c.number && (e += "\\p{N}", d = !!b); + c.symbol && (e += "\\p{S}"); + c.punctuation && (e += "\\p{P}"); c.control && (e += "\\p{C}"); if (c = c.char) { e += "object" === typeof c ? c.join("") : c; @@ -113,59 +114,59 @@ E.prototype.assign = function(a) { this.split = new RegExp("[" + (b ? "^" : "") + e + "]+", "u"); this.numeric = d; } else { - this.split = y(c, ea, this.split), this.numeric = y(this.numeric, !0); + this.split = y(c, fa, this.split), this.numeric = y(this.numeric, !0); } - this.$ = y(a.$, null, this.$); - this.V = y(a.V, null, this.V); - ka && (this.G = new Map(ka)); + this.prepare = y(a.prepare, null, this.prepare); + this.finalize = y(a.finalize, null, this.finalize); + ka || (this.mapper = new Map(ea)); this.rtl = a.rtl || !1; - this.C = y(a.C, !0, this.C); + this.dedupe = y(a.dedupe, !0, this.dedupe); this.filter = y((c = a.filter) && new Set(c), null, this.filter); - this.J = y((c = a.J) && new Map(c), null, this.J); - this.G = y((c = a.G) && new Map(c), null, this.G); - this.N = y((c = a.N) && new Map(c), null, this.N); - this.K = y(a.K, null, this.K); - this.Z = y(a.Z, 1, this.Z); - this.ha = y(a.ha, 0, this.ha); + this.matcher = y((c = a.matcher) && new Map(c), null, this.matcher); + this.mapper = y((c = a.mapper) && new Map(c), null, this.mapper); + this.stemmer = y((c = a.stemmer) && new Map(c), null, this.stemmer); + this.replacer = y(a.replacer, null, this.replacer); + this.minlength = y(a.minlength, 1, this.minlength); + this.maxlength = y(a.maxlength, 0, this.maxlength); if (this.cache = c = y(a.cache, !0, this.cache)) { - this.X = null, this.ea = "number" === typeof c ? c : 2e5, this.P = new Map(), this.U = new Map(), this.B = this.h = 128; + this.O = null, this.V = "number" === typeof c ? c : 2e5, this.J = new Map(), this.M = new Map(), this.B = this.h = 128; } - this.D = ""; - this.ba = null; - this.Y = ""; - this.ca = null; - if (this.J) { - for (const d of this.J.keys()) { - this.D += (this.D ? "|" : "") + d; + this.C = ""; + this.S = null; + this.P = ""; + this.T = null; + if (this.matcher) { + for (const d of this.matcher.keys()) { + this.C += (this.C ? "|" : "") + d; } } - if (this.N) { - for (const d of this.N.keys()) { - this.Y += (this.Y ? "|" : "") + d; + if (this.stemmer) { + for (const d of this.stemmer.keys()) { + this.P += (this.P ? "|" : "") + d; } } return this; }; -E.prototype.encode = function(a) { +G.prototype.encode = function(a) { if (this.cache && a.length <= this.h) { - if (this.X) { - if (this.P.has(a)) { - return this.P.get(a); + if (this.O) { + if (this.J.has(a)) { + return this.J.get(a); } } else { - this.X = setTimeout(la, 0, this); + this.O = setTimeout(la, 0, this); } } - this.normalize && (a = "function" === typeof this.normalize ? this.normalize(a) : ja ? a.normalize("NFKD").replace(ja, "").toLowerCase() : a.toLowerCase()); - this.$ && (a = this.$(a)); - this.numeric && 3 < a.length && (a = a.replace(ha, "$1 $2").replace(ia, "$1 $2").replace(fa, "$1 ")); - const b = !(this.C || this.G || this.filter || this.J || this.N || this.K); + this.normalize && (a = "function" === typeof this.normalize ? this.normalize(a) : ka ? a.normalize("NFKD").replace(ka, "").toLowerCase() : a.toLowerCase()); + this.prepare && (a = this.prepare(a)); + this.numeric && 3 < a.length && (a = a.replace(ia, "$1 $2").replace(ja, "$1 $2").replace(ha, "$1 ")); + const b = !(this.dedupe || this.mapper || this.filter || this.matcher || this.stemmer || this.replacer); let c = [], d = this.split || "" === this.split ? a.split(this.split) : a; for (let f = 0, g, h; f < d.length; f++) { if (!(g = h = d[f])) { continue; } - if (g.length < this.Z) { + if (g.length < this.minlength) { continue; } if (b) { @@ -176,83 +177,109 @@ E.prototype.encode = function(a) { continue; } if (this.cache && g.length <= this.B) { - if (this.X) { - var e = this.U.get(g); + if (this.O) { + var e = this.M.get(g); if (e || "" === e) { e && c.push(e); continue; } } else { - this.X = setTimeout(la, 0, this); + this.O = setTimeout(la, 0, this); } } let k; - this.N && 2 < g.length && (this.ca || (this.ca = new RegExp("(?!^)(" + this.Y + ")$")), g = g.replace(this.ca, l => this.N.get(l)), k = 1); - this.J && 1 < g.length && (this.ba || (this.ba = new RegExp("(" + this.D + ")", "g")), g = g.replace(this.ba, l => this.J.get(l)), k = 1); - g && k && (g.length < this.Z || this.filter && this.filter.has(g)) && (g = ""); - if (g && (this.G || this.C && 1 < g.length)) { + this.stemmer && 2 < g.length && (this.T || (this.T = new RegExp("(?!^)(" + this.P + ")$")), g = g.replace(this.T, l => this.stemmer.get(l)), k = 1); + this.matcher && 1 < g.length && (this.S || (this.S = new RegExp("(" + this.C + ")", "g")), g = g.replace(this.S, l => this.matcher.get(l)), k = 1); + g && k && (g.length < this.minlength || this.filter && this.filter.has(g)) && (g = ""); + if (g && (this.mapper || this.dedupe && 1 < g.length)) { e = ""; for (let l = 0, m = "", n, q; l < g.length; l++) { - n = g.charAt(l), n === m && this.C || ((q = this.G && this.G.get(n)) || "" === q ? q === m && this.C || !(m = q) || (e += q) : e += m = n); + n = g.charAt(l), n === m && this.dedupe || ((q = this.mapper && this.mapper.get(n)) || "" === q ? q === m && this.dedupe || !(m = q) || (e += q) : e += m = n); } g = e; } - if (g && this.K) { - for (e = 0; g && e < this.K.length; e += 2) { - g = g.replace(this.K[e], this.K[e + 1]); + if (g && this.replacer) { + for (e = 0; g && e < this.replacer.length; e += 2) { + g = g.replace(this.replacer[e], this.replacer[e + 1]); } } - this.cache && h.length <= this.B && (this.U.set(h, g), this.U.size > this.ea && (this.U.clear(), this.B = this.B / 1.1 | 0)); + this.cache && h.length <= this.B && (this.M.set(h, g), this.M.size > this.V && (this.M.clear(), this.B = this.B / 1.1 | 0)); g && c.push(g); } - this.V && (c = this.V(c) || c); - this.cache && a.length <= this.h && (this.P.set(a, c), this.P.size > this.ea && (this.P.clear(), this.h = this.h / 1.1 | 0)); + this.finalize && (c = this.finalize(c) || c); + this.cache && a.length <= this.h && (this.J.set(a, c), this.J.size > this.V && (this.J.clear(), this.h = this.h / 1.1 | 0)); return c; }; function la(a) { - a.X = null; - a.P.clear(); - a.U.clear(); + a.O = null; + a.J.clear(); + a.M.clear(); } ;function ma(a, b, c) { a = ("object" === typeof a ? "" + a.query : a).toLowerCase(); let d = this.cache.get(a); if (!d) { d = this.search(a, b, c); - if (d instanceof Promise) { + if (d.then) { const e = this; d.then(function(f) { e.cache.set(a, f); + return f; }); } this.cache.set(a, d); } return d; } -function F(a) { +function H(a) { this.limit = a && !0 !== a ? a : 1000; this.cache = new Map(); this.h = ""; } -F.prototype.set = function(a, b) { - this.cache.has(a) || (this.cache.set(this.h = a, b), this.limit && this.cache.size > this.limit && this.cache.delete(this.cache.keys().next().value)); +H.prototype.set = function(a, b) { + this.cache.set(this.h = a, b); + this.cache.size > this.limit && this.cache.delete(this.cache.keys().next().value); }; -F.prototype.get = function(a) { +H.prototype.get = function(a) { const b = this.cache.get(a); - b && this.limit && this.h !== a && (this.cache.delete(a), this.cache.set(this.h = a, b)); + b && this.h !== a && (this.cache.delete(a), this.cache.set(this.h = a, b)); return b; }; -F.prototype.remove = function(a) { +H.prototype.remove = function(a) { for (const b of this.cache) { const c = b[0]; b[1].includes(a) && this.cache.delete(c); } }; -F.prototype.clear = function() { +H.prototype.clear = function() { this.cache.clear(); this.h = ""; }; -function na(a, b, c, d) { +const na = {normalize:function(a) { + return a.toLowerCase(); +}, dedupe:!1}; +const oa = new Map([["b", "p"], ["v", "f"], ["w", "f"], ["z", "s"], ["x", "s"], ["d", "t"], ["n", "m"], ["c", "k"], ["g", "k"], ["j", "k"], ["q", "k"], ["i", "e"], ["y", "e"], ["u", "o"]]); +const pa = new Map([["ai", "ei"], ["ae", "a"], ["oe", "o"], ["ue", "u"], ["sh", "s"], ["ch", "c"], ["th", "t"], ["ph", "f"], ["pf", "f"]]), qa = [/([^aeo])h([aeo$])/g, "$1$2", /([aeo])h([^aeo]|$)/g, "$1$2"]; +const ra = {a:"", e:"", i:"", o:"", u:"", y:"", b:1, f:1, p:1, v:1, c:2, g:2, j:2, k:2, q:2, s:2, x:2, z:2, "\u00df":2, d:3, t:3, l:4, m:5, n:5, r:6}; +const sa = /[\x00-\x7F]+/g; +const ta = /[\x00-\x7F]+/g; +const ua = /[\x00-\x7F]+/g; +var va = {LatinExact:{normalize:!1, dedupe:!1}, LatinDefault:na, LatinSimple:{normalize:!0, dedupe:!0}, LatinBalance:{normalize:!0, dedupe:!0, mapper:oa}, LatinAdvanced:{normalize:!0, dedupe:!0, mapper:oa, replacer:qa, matcher:pa}, LatinExtra:{normalize:!0, dedupe:!0, mapper:oa, replacer:qa.concat([/(?!^)[aeoy]/g, ""]), matcher:pa}, LatinSoundex:{normalize:!0, dedupe:!1, include:{letter:!0}, finalize:function(a) { + for (let c = 0; c < a.length; c++) { + var b = a[c]; + let d = b.charAt(0), e = ra[d]; + for (let f = 1, g; f < b.length && (g = b.charAt(f), "h" === g || "w" === g || !(g = ra[g]) || g === e || (d += g, e = g, 4 !== d.length)); f++) { + } + a[c] = d; + } +}}, ArabicDefault:{rtl:!0, normalize:!1, dedupe:!0, prepare:function(a) { + return ("" + a).replace(sa, " "); +}}, CjkDefault:{normalize:!1, dedupe:!0, split:"", prepare:function(a) { + return ("" + a).replace(ta, ""); +}}, CyrillicDefault:{normalize:!1, dedupe:!0, prepare:function(a) { + return ("" + a).replace(ua, " "); +}}}; +function wa(a, b, c, d) { let e = []; for (let f = 0, g; f < a.index.length; f++) { if (g = a.index[f], b >= g.length) { @@ -268,9 +295,9 @@ function na(a, b, c, d) { } return e; } -function I(a) { - if (!(this instanceof I)) { - return new I(a); +function K(a) { + if (!this) { + return new K(a); } this.index = a ? [a] : []; this.length = a ? a.length : 0; @@ -318,12 +345,12 @@ function I(a) { } if ("slice" === d) { return function(e, f) { - return na(b, e || 0, f || b.length, !1); + return wa(b, e || 0, f || b.length, !1); }; } if ("splice" === d) { return function(e, f) { - return na(b, e || 0, f || b.length, !0); + return wa(b, e || 0, f || b.length, !0); }; } if ("constructor" === d) { @@ -339,19 +366,19 @@ function I(a) { return !0; }}); } -I.prototype.clear = function() { +K.prototype.clear = function() { this.index.length = 0; }; -I.prototype.push = function() { +K.prototype.push = function() { }; function L(a = 8) { - if (!(this instanceof L)) { + if (!this) { return new L(a); } this.index = z(); - this.D = []; + this.C = []; this.size = 0; - 32 < a ? (this.h = oa, this.B = BigInt(a)) : (this.h = pa, this.B = a); + 32 < a ? (this.h = xa, this.B = BigInt(a)) : (this.h = ya, this.B = a); } L.prototype.get = function(a) { const b = this.index[this.h(a)]; @@ -360,28 +387,28 @@ L.prototype.get = function(a) { L.prototype.set = function(a, b) { var c = this.h(a); let d = this.index[c]; - d ? (c = d.size, d.set(a, b), (c -= d.size) && this.size++) : (this.index[c] = d = new Map([[a, b]]), this.D.push(d)); + d ? (c = d.size, d.set(a, b), (c -= d.size) && this.size++) : (this.index[c] = d = new Map([[a, b]]), this.C.push(d)); }; function M(a = 8) { - if (!(this instanceof M)) { + if (!this) { return new M(a); } this.index = z(); this.h = []; - 32 < a ? (this.D = oa, this.B = BigInt(a)) : (this.D = pa, this.B = a); + 32 < a ? (this.C = xa, this.B = BigInt(a)) : (this.C = ya, this.B = a); } M.prototype.add = function(a) { - var b = this.D(a); + var b = this.C(a); let c = this.index[b]; c ? (b = c.size, c.add(a), (b -= c.size) && this.size++) : (this.index[b] = c = new Set([a]), this.h.push(c)); }; u = L.prototype; u.has = M.prototype.has = function(a) { - const b = this.index[this.D(a)]; + const b = this.index[this.C(a)]; return b && b.has(a); }; u.delete = M.prototype.delete = function(a) { - const b = this.index[this.D(a)]; + const b = this.index[this.C(a)]; b && b.delete(a) && this.size--; }; u.clear = M.prototype.clear = function() { @@ -410,7 +437,7 @@ u.entries = M.prototype.entries = function*() { } } }; -function pa(a) { +function ya(a) { let b = 2 ** this.B - 1; if ("number" == typeof a) { return a & b; @@ -421,7 +448,7 @@ function pa(a) { } return 32 === this.B ? c + 2 ** 31 : c; } -function oa(a) { +function xa(a) { let b = BigInt(2) ** this.B - BigInt(1); var c = typeof a; if ("bigint" === c) { @@ -437,16 +464,13 @@ function oa(a) { } return c; } -;function qa(a, b, c, d, e, f, g, h) { +;function za(a, b, c, d, e, f, g, h) { (d = a(c ? c + "." + d : d, JSON.stringify(g))) && d.then ? d.then(function() { b.export(a, b, c, e, f + 1, h); }) : b.export(a, b, c, e, f + 1, h); } -;var ra = {normalize:function(a) { - return a.toLowerCase(); -}, C:!1}; -const sa = {memory:{resolution:1}, performance:{resolution:6, fastupdate:!0, context:{depth:1, resolution:3}}, match:{tokenize:"forward"}, score:{resolution:9, context:{depth:2, resolution:9}}}; -function ta(a) { +;const Aa = {memory:{resolution:1}, performance:{resolution:6, fastupdate:!0, context:{depth:1, resolution:3}}, match:{tokenize:"forward"}, score:{resolution:9, context:{depth:2, resolution:9}}}; +function Ba(a) { N.call(a, "add"); N.call(a, "append"); N.call(a, "search"); @@ -508,7 +532,7 @@ O.prototype.add = function(a, b, c, d) { } default: if (Q(this, m, p, f, a, c), n && 1 < d && t < d - 1) { - for (e = z(), g = this.da, f = p, h = Math.min(n + 1, d - t), e[f] = 1, k = 1; k < h; k++) { + for (e = z(), g = this.U, f = p, h = Math.min(n + 1, d - t), e[f] = 1, k = 1; k < h; k++) { if ((p = b[this.rtl ? d - 1 - t - k : t + k]) && !e[p]) { e[p] = 1; const r = this.score ? this.score(b, f, t, p, k) : P(g + (d / 2 > g ? 0 : 1), d, t, h - 1, k - 1), x = this.bidirectional && p > f; @@ -524,15 +548,15 @@ O.prototype.add = function(a, b, c, d) { b = ""; } } - this.db && (b || this.R.push({del:a}), this.fa && ua(this)); + this.db && (b || this.K.push({del:a}), this.W && Ca(this)); return this; }; function Q(a, b, c, d, e, f, g) { - let h = g ? a.I : a.map, k; + let h = g ? a.F : a.map, k; if (!b[c] || !g || !(k = b[c])[g]) { if (g ? (b = k || (b[c] = z()), b[g] = 1, (k = h.get(g)) ? h = k : h.set(g, h = new Map())) : b[c] = 1, (k = h.get(c)) ? h = k : h.set(c, h = k = []), h = h[d] || (h[d] = []), !f || !h.includes(e)) { if (h.length === 2 ** 31 - 1) { - b = new I(h); + b = new K(h); if (a.fastupdate) { for (let l of a.A.values()) { l.includes(h) && (l[l.indexOf(h)] = b); @@ -550,7 +574,7 @@ function P(a, b, c, d, e) { } ;function R(a, b, c, d) { if (1 === a.length) { - return a = a[0], a = c || a.length > b ? b ? a.slice(c, c + b) : a.slice(c) : a, d ? va(a) : a; + return a = a[0], a = c || a.length > b ? b ? a.slice(c, c + b) : a.slice(c) : a, d ? Da(a) : a; } let e = []; for (let f = 0, g, h; f < a.length; f++) { @@ -566,7 +590,7 @@ function P(a, b, c, d, e) { h > b && (g = g.slice(0, b), h = g.length), e.push(g); } else { if (h >= b) { - return h > b && (g = g.slice(0, b)), d ? va(g) : g; + return h > b && (g = g.slice(0, b)), d ? Da(g) : g; } e = [g]; } @@ -580,9 +604,9 @@ function P(a, b, c, d, e) { return e; } e = 1 < e.length ? [].concat.apply([], e) : e[0]; - return d ? va(e) : e; + return d ? Da(e) : e; } -function va(a) { +function Da(a) { for (let b = 0; b < a.length; b++) { a[b] = {score:b, id:a[b]}; } @@ -592,7 +616,7 @@ function va(a) { const a = this; let b = arguments; var c = b[0]; - if (c instanceof Promise) { + if (c.then) { return c.then(function() { return a.or.apply(a, b); }); @@ -606,7 +630,7 @@ function va(a) { for (let k = 0, l; k < b.length; k++) { if (l = b[k]) { let m; - if (l instanceof S) { + if (l.constructor === S) { m = l.result; } else if (l.constructor === Array) { m = l; @@ -616,8 +640,8 @@ function va(a) { m = this.and(l.and); } else if (l.xor) { m = this.xor(l.xor); - } else if (l.F) { - m = this.F(l.F); + } else if (l.not) { + m = this.not(l.not); } else { e = l.limit || 0; f = l.offset || 0; @@ -626,21 +650,21 @@ function va(a) { continue; } d[k] = m; - m instanceof Promise && c.push(m); + m.then && c.push(m); } } if (c.length) { return Promise.all(c).then(function() { a.result.length && (d = [a.result].concat(d)); - a.result = wa(d, e, f, g, h, a.O); + a.result = Ea(d, e, f, g, h, a.G); return h ? a.result : a; }); } this.result.length && (d = [this.result].concat(d)); - this.result = wa(d, e, f, g, h, a.O); + this.result = Ea(d, e, f, g, h, a.G); return h ? this.result : this; }; -function wa(a, b, c, d, e, f) { +function Ea(a, b, c, d, e, f) { if (!a.length) { return a; } @@ -683,7 +707,7 @@ function wa(a, b, c, d, e, f) { const b = this; let c = arguments; var a = c[0]; - if (a instanceof Promise) { + if (a.then) { return a.then(function() { return b.and.apply(b, c); }); @@ -697,7 +721,7 @@ function wa(a, b, c, d, e, f) { for (let h = 0, k; h < c.length; h++) { if (k = c[h]) { let l; - if (k instanceof S) { + if (k.constructor === S) { l = k.result; } else if (k.constructor === Array) { l = k; @@ -707,8 +731,8 @@ function wa(a, b, c, d, e, f) { l = this.or(k.or); } else if (k.xor) { l = this.xor(k.xor); - } else if (k.F) { - l = this.F(k.F); + } else if (k.not) { + l = this.not(k.not); } else { e = k.limit || 0; f = k.offset || 0; @@ -716,23 +740,23 @@ function wa(a, b, c, d, e, f) { continue; } d[h] = l; - l instanceof Promise && a.push(l); + l.then && a.push(l); } } if (a.length) { return Promise.all(a).then(function() { d = [b.result].concat(d); - b.result = xa(d, e, f, g, b.O); + b.result = Fa(d, e, f, g, b.G); return g ? b.result : b; }); } d = [this.result].concat(d); - this.result = xa(d, e, f, g, b.O); + this.result = Fa(d, e, f, g, b.G); return g ? this.result : this; } return this; }; -function xa(a, b, c, d, e) { +function Fa(a, b, c, d, e) { if (2 > a.length) { return []; } @@ -778,7 +802,7 @@ function xa(a, b, c, d, e) { const a = this; let b = arguments; var c = b[0]; - if (c instanceof Promise) { + if (c.then) { return c.then(function() { return a.xor.apply(a, b); }); @@ -792,7 +816,7 @@ function xa(a, b, c, d, e) { for (let k = 0, l; k < b.length; k++) { if (l = b[k]) { let m; - if (l instanceof S) { + if (l.constructor === S) { m = l.result; } else if (l.constructor === Array) { m = l; @@ -802,8 +826,8 @@ function xa(a, b, c, d, e) { m = this.or(l.or); } else if (l.and) { m = this.and(l.and); - } else if (l.F) { - m = this.F(l.F); + } else if (l.not) { + m = this.not(l.not); } else { e = l.limit || 0; f = l.offset || 0; @@ -812,21 +836,21 @@ function xa(a, b, c, d, e) { continue; } d[k] = m; - m instanceof Promise && c.push(m); + m.then && c.push(m); } } if (c.length) { return Promise.all(c).then(function() { a.result.length && (d = [a.result].concat(d)); - a.result = ya(d, e, f, g, !h, a.O); + a.result = Ga(d, e, f, g, !h, a.G); return h ? a.result : a; }); } this.result.length && (d = [this.result].concat(d)); - this.result = ya(d, e, f, g, !h, a.O); + this.result = Ga(d, e, f, g, !h, a.G); return h ? this.result : this; }; -function ya(a, b, c, d, e, f) { +function Ga(a, b, c, d, e, f) { if (!a.length) { return a; } @@ -859,17 +883,17 @@ function ya(a, b, c, d, e, f) { } return b; } -;S.prototype.F = function() { +;S.prototype.not = function() { const a = this; let b = arguments; var c = b[0]; - if (c instanceof Promise) { + if (c.then) { return c.then(function() { - return a.F.apply(a, b); + return a.not.apply(a, b); }); } if (c[0] && c[0].index) { - return this.F.apply(this, c); + return this.not.apply(this, c); } let d = []; c = []; @@ -877,7 +901,7 @@ function ya(a, b, c, d, e, f) { for (let f = 0, g; f < b.length; f++) { if (g = b[f]) { let h; - if (g instanceof S) { + if (g.constructor === S) { h = g.result; } else if (g.constructor === Array) { h = g; @@ -894,19 +918,19 @@ function ya(a, b, c, d, e, f) { continue; } d[f] = h; - h instanceof Promise && c.push(h); + h.then && c.push(h); } } if (c.length) { return Promise.all(c).then(function() { - a.result = za.call(a, d, e); + a.result = Ha.call(a, d, e); return e ? a.result : a; }); } - this.result = za.call(this, d, e); + this.result = Ha.call(this, d, e); return e ? this.result : this; }; -function za(a, b) { +function Ha(a, b) { if (!a.length) { return this.result; } @@ -922,18 +946,18 @@ function za(a, b) { return c; } ;function S(a) { + if (!this) { + return new S(a); + } if (a && a.index) { return a.resolve = !1, this.index = a.index, a.index.search(a); } - if (!(this instanceof S)) { - return new S(a); - } - if (a instanceof S) { + if (a.constructor === S) { return a; } this.index = null; this.result = a || []; - this.O = 0; + this.G = 0; } S.prototype.limit = function(a) { if (this.result.length) { @@ -962,13 +986,17 @@ S.prototype.offset = function(a) { } return this; }; +S.prototype.boost = function(a) { + this.G += a; + return this; +}; S.prototype.resolve = function(a, b, c) { T = 1; const d = this.result; this.result = this.index = null; return d.length ? ("object" === typeof a && (c = a.enrich, b = a.offset, a = a.limit), R(d, a || 100, b, c)) : d; }; -function Aa(a, b, c, d) { +function Ia(a, b, c, d) { var e = a.length; let f = [], g = 0, h, k, l; d && (d = []); @@ -1029,7 +1057,7 @@ function Aa(a, b, c, d) { } return f; } -function Ba(a, b) { +function Ja(a, b) { const c = z(), d = z(), e = []; for (let f = 0; f < a.length; f++) { c[a[f]] = 1; @@ -1063,18 +1091,18 @@ O.prototype.search = function(a, b, c) { e = a.length; b || !h || (b = 100); if (1 === e) { - return Ca.call(this, a[0], "", b, g, h, k, l); + return U.call(this, a[0], "", b, g, h, k, l); } m = this.depth && !1 !== m; if (2 === e && m && !f) { - return Ca.call(this, a[0], a[1], b, g, h, k, l); + return U.call(this, a[0], a[1], b, g, h, k, l); } let n = c = 0; if (1 < e) { const p = z(), r = []; for (let x = 0, A; x < e; x++) { if ((A = a[x]) && !p[A]) { - if (f || this.db || U(this, A)) { + if (f || this.db || V(this, A)) { r.push(A), p[A] = 1; } else { return h ? d : new S(d); @@ -1092,10 +1120,10 @@ O.prototype.search = function(a, b, c) { } let q = 0, t; if (1 === e) { - return Ca.call(this, a[0], "", b, g, h, k, l); + return U.call(this, a[0], "", b, g, h, k, l); } if (2 === e && m && !f) { - return Ca.call(this, a[0], a[1], b, g, h, k, l); + return U.call(this, a[0], a[1], b, g, h, k, l); } 1 < e && (m ? (t = a[0], q = 1) : 9 < c && 3 < c / n && a.sort(aa)); if (this.db) { @@ -1106,7 +1134,7 @@ O.prototype.search = function(a, b, c) { return async function() { for (let r, x; q < e; q++) { x = a[q]; - t ? (r = await U(p, x, t), r = Da(r, d, f, p.da, b, g, 2 === e), f && !1 === r && d.length || (t = x)) : (r = await U(p, x), r = Da(r, d, f, p.resolution, b, g, 1 === e)); + t ? (r = await V(p, x, t), r = Ka(r, d, f, p.U, b, g, 2 === e), f && !1 === r && d.length || (t = x)) : (r = await V(p, x), r = Ka(r, d, f, p.resolution, b, g, 1 === e)); if (r) { return r; } @@ -1125,12 +1153,12 @@ O.prototype.search = function(a, b, c) { } } } - return h ? Aa(d, b, g, f) : new S(d[0]); + return h ? Ia(d, b, g, f) : new S(d[0]); }(); } for (let p, r; q < e; q++) { r = a[q]; - t ? (p = U(this, r, t), p = Da(p, d, f, this.da, b, g, 2 === e), f && !1 === p && d.length || (t = r)) : (p = U(this, r), p = Da(p, d, f, this.resolution, b, g, 1 === e)); + t ? (p = V(this, r, t), p = Ka(p, d, f, this.U, b, g, 2 === e), f && !1 === p && d.length || (t = r)) : (p = V(this, r), p = Ka(p, d, f, this.resolution, b, g, 1 === e)); if (p) { return p; } @@ -1149,15 +1177,15 @@ O.prototype.search = function(a, b, c) { } } } - return h ? Aa(d, b, g, f) : new S(d[0]); + return h ? Ia(d, b, g, f) : new S(d[0]); }; -function Ca(a, b, c, d, e, f, g) { - a = U(this, a, b, c, d, e, f, g); +function U(a, b, c, d, e, f, g) { + a = V(this, a, b, c, d, e, f, g); return this.db ? a.then(function(h) { return e ? h : h && h.length ? e ? R(h, c, d) : new S(h) : e ? [] : new S([]); }) : a && a.length ? e ? R(a, c, d) : new S(a) : e ? [] : new S([]); } -function Da(a, b, c, d, e, f, g) { +function Ka(a, b, c, d, e, f, g) { let h = []; if (a) { d = Math.min(a.length, d); @@ -1178,13 +1206,13 @@ function Da(a, b, c, d, e, f, g) { } return !c && h; } -function U(a, b, c, d, e, f, g, h) { +function V(a, b, c, d, e, f, g, h) { let k; c && (k = a.bidirectional && b > c); if (a.db) { return c ? a.db.get(k ? c : b, k ? b : c, d, e, f, g, h) : a.db.get(b, "", d, e, f, g, h); } - a = c ? (a = a.I.get(k ? b : c)) && a.get(k ? c : b) : a.map.get(b); + a = c ? (a = a.F.get(k ? b : c)) && a.get(k ? c : b) : a.map.get(b); return a; } ;O.prototype.remove = function(a, b) { @@ -1202,15 +1230,15 @@ function U(a, b, c, d, e, f, g, h) { } } } else { - Ea(this.map, a), this.depth && Ea(this.I, a); + La(this.map, a), this.depth && La(this.F, a); } b || this.A.delete(a); } - this.db && (this.R.push({del:a}), this.fa && ua(this)); + this.db && (this.K.push({del:a}), this.W && Ca(this)); this.cache && this.cache.remove(a); return this; }; -function Ea(a, b) { +function La(a, b) { let c = 0; if (a.constructor === Array) { for (let d = 0, e, f; d < a.length; d++) { @@ -1225,25 +1253,25 @@ function Ea(a, b) { } } else { for (let d of a) { - const e = d[0], f = Ea(d[1], b); + const e = d[0], f = La(d[1], b); f ? c += f : a.delete(e); } } return c; } ;function O(a, b) { - if (!(this instanceof O)) { + if (!this) { return new O(a); } if (a) { var c = B(a) ? a : a.preset; - c && (sa[c] || console.warn("Preset not found: " + c), a = Object.assign({}, sa[c], a)); + c && (Aa[c] || console.warn("Preset not found: " + c), a = Object.assign({}, Aa[c], a)); } else { a = {}; } c = a.context || {}; - const d = a.encode || a.encoder || ra; - this.encoder = d.encode ? d : "object" === typeof d ? new E(d) : {encode:d}; + const d = a.encode || a.encoder || na; + this.encoder = d.encode ? d : "object" === typeof d ? new G(d) : {encode:d}; let e; this.resolution = a.resolution || 9; this.tokenize = e = a.tokenize || "strict"; @@ -1253,17 +1281,17 @@ function Ea(a, b) { this.score = a.score || null; (e = a.keystore || 0) && (this.keystore = e); this.map = e ? new L(e) : new Map(); - this.I = e ? new L(e) : new Map(); + this.F = e ? new L(e) : new Map(); this.A = b || (this.fastupdate ? e ? new L(e) : new Map() : e ? new M(e) : new Set()); - this.da = c.resolution || 1; + this.U = c.resolution || 1; this.rtl = d.rtl || a.rtl || !1; - this.cache = (e = a.cache || null) && new F(e); + this.cache = (e = a.cache || null) && new H(e); this.resolve = !1 !== a.resolve; if (e = a.db) { this.db = e.mount(this); } - this.fa = !1 !== a.commit; - this.R = []; + this.W = !1 !== a.commit; + this.K = []; this.h = null; } u = O.prototype; @@ -1275,7 +1303,7 @@ u.commit = function(a, b) { this.h && (clearTimeout(this.h), this.h = null); return this.db.commit(this, a, b); }; -function ua(a) { +function Ca(a) { a.h || (a.h = setTimeout(function() { a.h = null; a.db.commit(a, void 0, void 0); @@ -1283,10 +1311,10 @@ function ua(a) { } u.clear = function() { this.map.clear(); - this.I.clear(); + this.F.clear(); this.A.clear(); this.cache && this.cache.clear(); - this.db && (this.h && clearTimeout(this.h), this.h = null, this.R = [{clear:!0}]); + this.db && (this.h && clearTimeout(this.h), this.h = null, this.K = [{clear:!0}]); return this; }; u.append = function(a, b) { @@ -1302,7 +1330,7 @@ u.update = function(a, b) { } return this.remove(a).add(a, b); }; -function Fa(a) { +function Ma(a) { let b = 0; if (a.constructor === Array) { for (let c = 0, d; c < a.length; c++) { @@ -1310,7 +1338,7 @@ function Fa(a) { } } else { for (const c of a) { - const d = c[0], e = Fa(c[1]); + const d = c[0], e = Ma(c[1]); e ? b += e : a.delete(d); } } @@ -1320,8 +1348,8 @@ u.cleanup = function() { if (!this.fastupdate) { return console.info('Cleanup the index isn\'t required when not using "fastupdate".'), this; } - Fa(this.map); - this.depth && Fa(this.I); + Ma(this.map); + this.depth && Ma(this.F); return this; }; u.searchCache = ma; @@ -1353,13 +1381,13 @@ u.export = function(a, b, c, d, e, f) { break; case 3: h = "ctx"; - k = this.I; + k = this.F; break; default: "undefined" === typeof c && f && f(); return; } - qa(a, b || this, c, h, d, e, k, f); + za(a, b || this, c, h, d, e, k, f); return g; }; u.import = function(a, b) { @@ -1376,12 +1404,12 @@ u.import = function(a, b) { this.map = b; break; case "ctx": - this.I = b; + this.F = b; } } }; -ta(O.prototype); -async function Ga(a) { +Ba(O.prototype); +async function Na(a) { a = a.data; var b = self._index; const c = a.args; @@ -1397,77 +1425,77 @@ async function Ga(a) { a = a.id, b = b[d].apply(b, c), postMessage("search" === d ? {id:a, msg:b} : {id:a}); } } -;let Ha = 0; -function V(a) { +;let Oa = 0; +function W(a) { function b(f) { f = f.data || f; const g = f.id, h = g && e.h[g]; h && (h(f.msg), delete e.h[g]); } - if (!(this instanceof V)) { - return new V(a); + if (!this) { + return new W(a); } a || (a = {}); let c = (self || window)._factory; c && (c = c.toString()); const d = "undefined" === typeof window && self.exports, e = this; - this.worker = Ia(c, d, a.worker); + this.worker = Pa(c, d, a.worker); this.h = z(); if (this.worker) { d ? this.worker.on("message", b) : this.worker.onmessage = b; if (a.config) { return new Promise(function(f) { - e.h[++Ha] = function() { + e.h[++Oa] = function() { f(e); }; - e.worker.postMessage({id:Ha, task:"init", factory:c, options:a}); + e.worker.postMessage({id:Oa, task:"init", factory:c, options:a}); }); } this.worker.postMessage({task:"init", factory:c, options:a}); } } -W("add"); -W("append"); -W("search"); -W("update"); -W("remove"); -function W(a) { - V.prototype[a] = V.prototype[a + "Async"] = function() { +X("add"); +X("append"); +X("search"); +X("update"); +X("remove"); +function X(a) { + W.prototype[a] = W.prototype[a + "Async"] = function() { const b = this, c = [].slice.call(arguments); var d = c[c.length - 1]; let e; "function" === typeof d && (e = d, c.splice(c.length - 1, 1)); d = new Promise(function(f) { - b.h[++Ha] = f; - b.worker.postMessage({task:a, id:Ha, args:c}); + b.h[++Oa] = f; + b.worker.postMessage({task:a, id:Oa, args:c}); }); return e ? (d.then(e), this) : d; }; } -function Ia(a, b, c) { - return b ? new (require("worker_threads")["Worker"])(__dirname + "/node/node.js") : a ? new window.Worker(URL.createObjectURL(new Blob(["onmessage=" + Ga.toString()], {type:"text/javascript"}))) : new window.Worker(B(c) ? c : "worker/worker.js", {type:"module"}); +function Pa(a, b, c) { + return b ? new (require("worker_threads")["Worker"])(__dirname + "/node/node.js") : a ? new window.Worker(URL.createObjectURL(new Blob(["onmessage=" + Na.toString()], {type:"text/javascript"}))) : new window.Worker(B(c) ? c : "worker/worker.js", {type:"module"}); } -;X.prototype.add = function(a, b, c) { +;Y.prototype.add = function(a, b, c) { C(a) && (b = a, a = ca(b, this.key)); if (b && (a || 0 === a)) { if (!c && this.A.has(a)) { return this.update(a, b); } for (let h = 0, k; h < this.field.length; h++) { - k = this.M[h]; + k = this.I[h]; var d = this.index.get(this.field[h]); if ("function" === typeof k) { var e = k(b); e && d.add(a, e, !1, !0); } else { - if (e = k.T, !e || e(b)) { - k instanceof String ? k = ["" + k] : B(k) && (k = [k]), Ja(b, k, this.W, 0, d, a, k[0], c); + if (e = k.L, !e || e(b)) { + k.constructor === String ? k = ["" + k] : B(k) && (k = [k]), Qa(b, k, this.N, 0, d, a, k[0], c); } } } if (this.tag) { - for (d = 0; d < this.L.length; d++) { - var f = this.L[d], g = this.aa[d]; + for (d = 0; d < this.H.length; d++) { + var f = this.H[d], g = this.R[d]; e = this.tag.get(g); let h = z(); if ("function" === typeof f) { @@ -1475,11 +1503,11 @@ function Ia(a, b, c) { continue; } } else { - const k = f.T; + const k = f.L; if (k && !k(b)) { continue; } - f instanceof String && (f = "" + f); + f.constructor === String && (f = "" + f); f = ca(b, f); } if (e && f) { @@ -1487,7 +1515,7 @@ function Ia(a, b, c) { for (let k = 0, l, m; k < f.length; k++) { if (l = f[k], !h[l] && (h[l] = 1, (g = e.get(l)) ? m = g : e.set(l, m = []), !c || !m.includes(a))) { if (m.length === 2 ** 31 - 1) { - g = new I(m); + g = new K(m); if (this.fastupdate) { for (let n of this.A.values()) { n.includes(m) && (n[n.indexOf(m)] = g); @@ -1506,11 +1534,11 @@ function Ia(a, b, c) { } if (this.store && (!c || !this.store.has(a))) { let h; - if (this.H) { + if (this.D) { h = z(); - for (let k = 0, l; k < this.H.length; k++) { - l = this.H[k]; - if ((c = l.T) && !c(b)) { + for (let k = 0, l; k < this.D.length; k++) { + l = this.D[k]; + if ((c = l.L) && !c(b)) { continue; } let m; @@ -1519,12 +1547,12 @@ function Ia(a, b, c) { if (!m) { continue; } - l = [l.ia]; - } else if (B(l) || l instanceof String) { + l = [l.X]; + } else if (B(l) || l.constructor === String) { h[l] = b[l]; continue; } - Ka(b, h, l, 0, l[0], m); + Ra(b, h, l, 0, l[0], m); } } this.store.set(a, h || b); @@ -1532,21 +1560,21 @@ function Ia(a, b, c) { } return this; }; -function Ka(a, b, c, d, e, f) { +function Ra(a, b, c, d, e, f) { a = a[e]; if (d === c.length - 1) { b[e] = f || a; } else if (a) { if (a.constructor === Array) { for (b = b[e] = Array(a.length), e = 0; e < a.length; e++) { - Ka(a, b, c, d, e); + Ra(a, b, c, d, e); } } else { - b = b[e] || (b[e] = z()), e = c[++d], Ka(a, b, c, d, e); + b = b[e] || (b[e] = z()), e = c[++d], Ra(a, b, c, d, e); } } } -function Ja(a, b, c, d, e, f, g, h) { +function Qa(a, b, c, d, e, f, g, h) { if (a = a[g]) { if (d === b.length - 1) { if (a.constructor === Array) { @@ -1562,17 +1590,17 @@ function Ja(a, b, c, d, e, f, g, h) { } else { if (a.constructor === Array) { for (g = 0; g < a.length; g++) { - Ja(a, b, c, d, e, f, g, h); + Qa(a, b, c, d, e, f, g, h); } } else { - g = b[++d], Ja(a, b, c, d, e, f, g, h); + g = b[++d], Qa(a, b, c, d, e, f, g, h); } } } else { e.db && e.remove(f); } } -;X.prototype.search = function(a, b, c, d) { +;Y.prototype.search = function(a, b, c, d) { c || (!b && C(a) ? (c = a, a = "") : C(b) && (c = b, b = 0)); let e = [], f = [], g; let h; @@ -1611,13 +1639,13 @@ function Ja(a, b, c, d, e, f, g, h) { } } else { r = Object.keys(v); - for (let J = 0, K, D; J < r.length; J++) { - if (K = r[J], D = v[K], D.constructor === Array) { + for (let I = 0, J, D; I < r.length; I++) { + if (J = r[I], D = v[J], D.constructor === Array) { for (x = 0; x < D.length; x++) { - p.push(K, D[x]); + p.push(J, D[x]); } } else { - p.push(K, D); + p.push(J, D); } } } @@ -1638,7 +1666,7 @@ function Ja(a, b, c, d, e, f, g, h) { } t.push(d = d.db.tag(p[n + 1], b, l, q)); } else { - d = La.call(this, p[n], p[n + 1], b, l, q); + d = Sa.call(this, p[n], p[n + 1], b, l, q); } e.push({field:p[n], tag:p[n + 1], result:d}); } @@ -1657,17 +1685,17 @@ function Ja(a, b, c, d, e, f, g, h) { k || (k = this.field); p = !d && (this.worker || this.async) && []; let A; - for (let w = 0, v, J, K; w < k.length; w++) { - J = k[w]; - if (this.db && this.tag && !this.M[w]) { + for (let w = 0, v, I, J; w < k.length; w++) { + I = k[w]; + if (this.db && this.tag && !this.I[w]) { continue; } let D; - B(J) || (D = J, J = D.field, a = D.query || a, b = D.limit || b, t = D.suggest || t); + B(I) || (D = I, I = D.field, a = D.query || a, b = D.limit || b, t = D.suggest || t); if (d) { v = d[w]; } else { - if (r = D || c, x = this.index.get(J), n && (this.db && (r.tag = n, A = x.db.la, r.field = k), A || (r.enrich = !1)), p) { + if (r = D || c, x = this.index.get(I), n && (this.db && (r.tag = n, A = x.db.Z, r.field = k), A || (r.enrich = !1)), p) { p[w] = x.searchAsync(a, b, r); r && q && (r.enrich = q); continue; @@ -1675,49 +1703,49 @@ function Ja(a, b, c, d, e, f, g, h) { v = x.search(a, b, r), r && q && (r.enrich = q); } } - K = v && v.length; - if (n && K) { + J = v && v.length; + if (n && J) { r = []; x = 0; if (this.db && d) { if (!A) { - for (let G = k.length; G < d.length; G++) { - let H = d[G]; - if (H && H.length) { - x++, r.push(H); + for (let E = k.length; E < d.length; E++) { + let F = d[E]; + if (F && F.length) { + x++, r.push(F); } else if (!t) { return e; } } } } else { - for (let G = 0, H, Za; G < n.length; G += 2) { - H = this.tag.get(n[G]); - if (!H) { - if (console.warn("Tag '" + n[G] + ":" + n[G + 1] + "' will be skipped because there is no field '" + n[G] + "'."), t) { + for (let E = 0, F, bb; E < n.length; E += 2) { + F = this.tag.get(n[E]); + if (!F) { + if (console.warn("Tag '" + n[E] + ":" + n[E + 1] + "' will be skipped because there is no field '" + n[E] + "'."), t) { continue; } else { return e; } } - if (Za = (H = H && H.get(n[G + 1])) && H.length) { - x++, r.push(H); + if (bb = (F = F && F.get(n[E + 1])) && F.length) { + x++, r.push(F); } else if (!t) { return e; } } } if (x) { - v = Ba(v, r); - K = v.length; - if (!K && !t) { + v = Ja(v, r); + J = v.length; + if (!J && !t) { return e; } x--; } } - if (K) { - f[m] = J, e.push(v), m++; + if (J) { + f[m] = I, e.push(v), m++; } else if (1 === k.length) { return e; } @@ -1750,7 +1778,7 @@ function Ja(a, b, c, d, e, f, g, h) { p = []; for (let w = 0, v; w < f.length; w++) { v = e[w]; - q && v.length && !v[0].doc && (this.db ? p.push(v = this.index.get(this.field[0]).db.enrich(v)) : v.length && (v = Ma.call(this, v))); + q && v.length && !v[0].doc && (this.db ? p.push(v = this.index.get(this.field[0]).db.enrich(v)) : v.length && (v = Ta.call(this, v))); if (g) { return v; } @@ -1760,10 +1788,10 @@ function Ja(a, b, c, d, e, f, g, h) { for (let v = 0; v < w.length; v++) { e[v].result = w[v]; } - return h ? Na(e, b) : e; - }) : h ? Na(e, b) : e; + return h ? Ua(e, b) : e; + }) : h ? Ua(e, b) : e; }; -function Na(a, b) { +function Ua(a, b) { const c = [], d = z(); for (let e = 0, f, g; e < a.length; e++) { f = a[e]; @@ -1782,7 +1810,7 @@ function Na(a, b) { } return c; } -function La(a, b, c, d, e) { +function Sa(a, b, c, d, e) { let f = this.tag.get(a); if (!f) { return console.warn("Tag '" + a + "' was not found"), []; @@ -1791,32 +1819,32 @@ function La(a, b, c, d, e) { if (a > c || d) { f = f.slice(d, d + c); } - e && (f = Ma.call(this, f)); + e && (f = Ta.call(this, f)); return f; } } -function Ma(a) { +function Ta(a) { const b = Array(a.length); for (let c = 0, d; c < a.length; c++) { d = a[c], b[c] = {id:d, doc:this.store.get(d)}; } return b; } -;function X(a) { - if (!(this instanceof X)) { - return new X(a); +;function Y(a) { + if (!this) { + return new Y(a); } const b = a.document || a.doc || a; var c, d; - this.M = []; + this.I = []; this.field = []; - this.W = []; - this.key = (c = b.key || b.id) && Oa(c, this.W) || "id"; + this.N = []; + this.key = (c = b.key || b.id) && Va(c, this.N) || "id"; (d = a.keystore || 0) && (this.keystore = d); this.A = (this.fastupdate = !!a.fastupdate) ? d ? new L(d) : new Map() : d ? new M(d) : new Set(); - this.H = (c = b.store || null) && !0 !== c && []; + this.D = (c = b.store || null) && !0 !== c && []; this.store = c && (d ? new L(d) : new Map()); - this.cache = (c = a.cache || null) && new F(c); + this.cache = (c = a.cache || null) && new H(c); a.cache = !1; this.worker = a.worker; this.async = !1; @@ -1828,19 +1856,19 @@ function Ma(a) { B(f) || (g = f, f = f.field); g = C(g) ? Object.assign({}, a, g) : a; if (this.worker) { - const h = new V(g); + const h = new W(g); c.set(f, h); h.worker || (this.worker = !1); } this.worker || c.set(f, new O(g, this.A)); - g.S ? this.M[e] = g.S : (this.M[e] = Oa(f, this.W), g.filter && ("string" === typeof this.M[e] && (this.M[e] = new String(this.M[e])), this.M[e].T = g.filter)); + g.custom ? this.I[e] = g.custom : (this.I[e] = Va(f, this.N), g.filter && ("string" === typeof this.I[e] && (this.I[e] = new String(this.I[e])), this.I[e].L = g.filter)); this.field[e] = f; } - if (this.H) { + if (this.D) { d = b.store; B(d) && (d = [d]); for (let e = 0, f, g; e < d.length; e++) { - f = d[e], g = f.field || f, f.S ? (this.H[e] = f.S, f.S.ia = g) : (this.H[e] = Oa(g, this.W), f.filter && ("string" === typeof this.H[e] && (this.H[e] = new String(this.H[e])), this.H[e].T = f.filter)); + f = d[e], g = f.field || f, f.custom ? (this.D[e] = f.custom, f.custom.X = g) : (this.D[e] = Va(g, this.N), f.filter && ("string" === typeof this.D[e] && (this.D[e] = new String(this.D[e])), this.D[e].L = f.filter)); } } this.index = c; @@ -1848,28 +1876,28 @@ function Ma(a) { if (c = b.tag) { if ("string" === typeof c && (c = [c]), c.length) { this.tag = new Map(); - this.L = []; - this.aa = []; + this.H = []; + this.R = []; for (let e = 0, f, g; e < c.length; e++) { f = c[e]; g = f.field || f; if (!g) { throw Error("The tag field from the document descriptor is undefined."); } - f.S ? this.L[e] = f.S : (this.L[e] = Oa(g, this.W), f.filter && ("string" === typeof this.L[e] && (this.L[e] = new String(this.L[e])), this.L[e].T = f.filter)); - this.aa[e] = g; + f.custom ? this.H[e] = f.custom : (this.H[e] = Va(g, this.N), f.filter && ("string" === typeof this.H[e] && (this.H[e] = new String(this.H[e])), this.H[e].L = f.filter)); + this.R[e] = g; this.tag.set(g, new Map()); } } } a.db && this.mount(a.db); } -u = X.prototype; +u = Y.prototype; u.mount = function(a) { let b = this.field; if (this.tag) { - for (let e = 0, f; e < this.aa.length; e++) { - f = this.aa[e]; + for (let e = 0, f; e < this.R.length; e++) { + f = this.R[e]; var c = this.index.get(f); c || (this.index.set(f, c = new O({}, this.A)), b === this.field && (b = b.slice(0)), b.push(f)); c.tag = this.tag.get(f); @@ -1884,7 +1912,7 @@ u.mount = function(a) { h.id = a.id; c[e] = h.mount(f); f.document = !0; - e ? f.ja = !0 : f.store = this.store; + e ? f.Y = !0 : f.store = this.store; } this.db = this.async = !0; return Promise.all(c); @@ -1897,7 +1925,7 @@ u.commit = async function(a, b) { await Promise.all(c); this.A.clear(); }; -function Oa(a, b) { +function Va(a, b) { const c = a.split(":"); let d = 0; for (let e = 0; e < c.length; e++) { @@ -1992,7 +2020,7 @@ u.export = function(a, b, c, d, e, f) { f(); return; } - qa(a, this, c, b, d, e, h, f); + za(a, this, c, b, d, e, h, f); } return g; }; @@ -2020,35 +2048,35 @@ u.import = function(a, b) { } } }; -ta(X.prototype); -const Pa = "undefined" !== typeof window && (window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB), Qa = ["map", "ctx", "tag", "reg", "cfg"]; -function Ra(a, b = {}) { - if (!(this instanceof Ra)) { - return new Ra(a, b); +Ba(Y.prototype); +const Wa = "undefined" !== typeof window && (window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB), Xa = ["map", "ctx", "tag", "reg", "cfg"]; +function Ya(a, b = {}) { + if (!this) { + return new Ya(a, b); } "object" === typeof a && (b = a = a.name); a || console.info("Default storage space was used, because a name was not passed."); this.id = "flexsearch" + (a ? ":" + a.toLowerCase().replace(/[^a-z0-9_\-]/g, "") : ""); this.field = b.field ? b.field.toLowerCase().replace(/[^a-z0-9_\-]/g, "") : ""; - this.la = !1; + this.Z = !1; this.db = null; this.h = {}; } -u = Ra.prototype; +u = Ya.prototype; u.mount = function(a) { - if (a instanceof X) { + if (a.constructor === Y) { return a.mount(this); } a.db = this; - return Sa(this); + return Za(this); }; -function Sa(a) { +function Za(a) { navigator.storage && navigator.storage.persist(); return a.db || new Promise(function(b, c) { - const d = Pa.open(a.id + (a.field ? ":" + a.field : ""), 1); + const d = Wa.open(a.id + (a.field ? ":" + a.field : ""), 1); d.onupgradeneeded = function() { const e = a.db = this.result; - Qa.forEach(f => { + Xa.forEach(f => { e.objectStoreNames.contains(f) || e.createObjectStore(f); }); }; @@ -2074,16 +2102,16 @@ u.close = function() { this.db = null; }; u.clear = function() { - const a = this.db.transaction(Qa, "readwrite"); - for (let b = 0; b < Qa.length; b++) { - a.objectStore(Qa[b]).clear(); + const a = this.db.transaction(Xa, "readwrite"); + for (let b = 0; b < Xa.length; b++) { + a.objectStore(Xa[b]).clear(); } - return Y(a); + return Z(a); }; u.get = function(a, b, c = 0, d = 0, e = !0, f = !1) { a = this.db.transaction(b ? "ctx" : "map", "readonly").objectStore(b ? "ctx" : "map").get(b ? b + ":" + a : a); const g = this; - return Y(a).then(function(h) { + return Z(a).then(function(h) { let k = []; if (!h || !h.length) { return k; @@ -2116,7 +2144,7 @@ u.get = function(a, b, c = 0, d = 0, e = !0, f = !1) { u.tag = function(a, b = 0, c = 0, d = !1) { a = this.db.transaction("tag", "readonly").objectStore("tag").get(a); const e = this; - return Y(a).then(function(f) { + return Z(a).then(function(f) { if (!f || !f.length || c >= f.length) { return []; } @@ -2131,7 +2159,7 @@ u.enrich = function(a) { "object" !== typeof a && (a = [a]); const b = this.db.transaction("reg", "readonly").objectStore("reg"), c = []; for (let d = 0; d < a.length; d++) { - c[d] = Y(b.get(a[d])); + c[d] = Z(b.get(a[d])); } return Promise.all(c).then(function(d) { for (let e = 0; e < d.length; e++) { @@ -2142,7 +2170,7 @@ u.enrich = function(a) { }; u.has = function(a) { a = this.db.transaction("reg", "readonly").objectStore("reg").getKey(a); - return Y(a); + return Z(a); }; u.search = null; u.info = function() { @@ -2170,17 +2198,17 @@ u.transaction = function(a, b, c) { }; u.commit = async function(a, b, c) { if (b) { - await this.clear(), a.R = []; + await this.clear(), a.K = []; } else { - let d = a.R; - a.R = []; + let d = a.K; + a.K = []; for (let e = 0, f; e < d.length; e++) { if (f = d[e], f.clear) { await this.clear(); b = !0; break; } else { - d[e] = f.ma; + d[e] = f.$; } } b || (c || (d = d.concat(ba(a.A))), d.length && await this.remove(d)); @@ -2212,7 +2240,7 @@ u.commit = async function(a, b, c) { }); } }), await this.transaction("ctx", "readwrite", function(d) { - for (const e of a.I) { + for (const e of a.F) { const f = e[0], g = e[1]; for (const h of g) { const k = h[0], l = h[1]; @@ -2245,7 +2273,7 @@ u.commit = async function(a, b, c) { const f = e[0], g = e[1]; d.put("object" === typeof g ? JSON.stringify(g) : 1, f); } - }) : a.ja || await this.transaction("reg", "readwrite", function(d) { + }) : a.Y || await this.transaction("reg", "readwrite", function(d) { for (const e of a.A.keys()) { d.put(1, e); } @@ -2258,9 +2286,9 @@ u.commit = async function(a, b, c) { d.put(h, f); }); } - }), a.map.clear(), a.I.clear(), a.tag && a.tag.clear(), a.store && a.store.clear(), a.document || a.A.clear()); + }), a.map.clear(), a.F.clear(), a.tag && a.tag.clear(), a.store && a.store.clear(), a.document || a.A.clear()); }; -function Ta(a, b, c) { +function $a(a, b, c) { const d = a.value; let e, f, g = 0; for (let h = 0, k; h < d.length; h++) { @@ -2289,17 +2317,17 @@ u.remove = function(a) { return Promise.all([this.transaction("map", "readwrite", function(b) { b.openCursor().onsuccess = function() { const c = this.result; - c && Ta(c, a); + c && $a(c, a); }; }), this.transaction("ctx", "readwrite", function(b) { b.openCursor().onsuccess = function() { const c = this.result; - c && Ta(c, a); + c && $a(c, a); }; }), this.transaction("tag", "readwrite", function(b) { b.openCursor().onsuccess = function() { const c = this.result; - c && Ta(c, a, !0); + c && $a(c, a, !0); }; }), this.transaction("reg", "readwrite", function(b) { for (let c = 0; c < a.length; c++) { @@ -2307,7 +2335,7 @@ u.remove = function(a) { } })]); }; -function Y(a) { +function Z(a) { return new Promise((b, c) => { a.onsuccess = function() { b(this.result); @@ -2319,31 +2347,9 @@ function Y(a) { a = null; }); } -;const Z = z(); -const Ua = new Map([["b", "p"], ["v", "f"], ["w", "f"], ["z", "s"], ["x", "s"], ["d", "t"], ["n", "m"], ["c", "k"], ["g", "k"], ["j", "k"], ["q", "k"], ["i", "e"], ["y", "e"], ["u", "o"]]); -var Va = {normalize:!0, C:!0, G:Ua}; -const Wa = new Map([["ai", "ei"], ["ae", "a"], ["oe", "o"], ["ue", "u"], ["sh", "s"], ["ch", "c"], ["th", "t"], ["ph", "f"], ["pf", "f"]]), Xa = [/([^aeo])h([aeo$])/g, "$1$2", /([aeo])h([^aeo]|$)/g, "$1$2"]; -var Ya = {normalize:!0, C:!0, G:Ua, K:Xa, J:Wa}; -var $a = {normalize:!0, C:!0, G:Ua, K:Xa.concat([/(?!^)[aeoy]/g, ""]), J:Wa}; -const ab = {a:"", e:"", i:"", o:"", u:"", y:"", b:1, f:1, p:1, v:1, c:2, g:2, j:2, k:2, q:2, s:2, x:2, z:2, "\u00df":2, d:3, t:3, l:4, m:5, n:5, r:6}; -Z["latin:exact"] = {normalize:!1, C:!1}; -Z["latin:default"] = ra; -Z["latin:simple"] = {normalize:!0, C:!0}; -Z["latin:balance"] = Va; -Z["latin:advanced"] = Ya; -Z["latin:extra"] = $a; -Z["latin:soundex"] = {normalize:!0, C:!1, ga:{ka:!0}, V:function(a) { - for (let c = 0; c < a.length; c++) { - var b = a[c]; - let d = b.charAt(0), e = ab[d]; - for (let f = 1, g; f < b.length && (g = b.charAt(f), "h" === g || "w" === g || !(g = ab[g]) || g === e || (d += g, e = g, 4 !== d.length)); f++) { - } - a[c] = d; - } -}}; -const bb = {Index:O, Charset:Z, Encoder:E, Document:X, Worker:V, Resolver:S, IndexedDB:Ra}, cb = self; +;const ab = {Index:O, Charset:va, Encoder:G, Document:Y, Worker:W, Resolver:S, IndexedDB:Ya, Language:{}}, cb = self; let db; (db = cb.define) && db.amd ? db([], function() { - return bb; -}) : "object" === typeof cb.exports ? cb.exports = bb : cb.FlexSearch = bb; + return ab; +}) : "object" === typeof cb.exports ? cb.exports = ab : cb.FlexSearch = ab; }(this)); diff --git a/dist/flexsearch.bundle.min.js b/dist/flexsearch.bundle.min.js index 7f8ebea..2bcee53 100644 --- a/dist/flexsearch.bundle.min.js +++ b/dist/flexsearch.bundle.min.js @@ -6,84 +6,83 @@ * https://github.com/nextapps-de/flexsearch */ (function _f(self){'use strict';try{if(module)self=module}catch(e){}self._factory=_f;var u;function y(a,b,c){const d=typeof c,e=typeof a;if("undefined"!==d){if("undefined"!==e){if(c){if("function"===e&&d===e)return function(h){return a(c(h))};b=a.constructor;if(b===c.constructor){if(b===Array)return c.concat(a);if(b===Map){var f=new Map(c);for(var g of a)f.set(g[0],g[1]);return f}if(b===Set){g=new Set(c);for(f of a.values())g.add(f);return g}}}return a}return c}return"undefined"===e?b:a}function z(){return Object.create(null)}function aa(a,b){return b.length-a.length} -function B(a){return"string"===typeof a}function C(a){return"object"===typeof a}function ba(a){const b=[];for(const c of a.keys())b.push(c);return b}function ca(a,b){if(B(b))a=a[b];else for(let c=0;a&&cthis.N.get(l)),k=1);this.J&&1this.J.get(l)),k=1);g&&k&&(g.lengththis.ea&&(this.U.clear(),this.B=this.B/1.1|0));g&&c.push(g)}this.V&&(c= -this.V(c)||c);this.cache&&a.length<=this.h&&(this.P.set(a,c),this.P.size>this.ea&&(this.P.clear(),this.h=this.h/1.1|0));return c};function la(a){a.X=null;a.P.clear();a.U.clear()};function ma(a,b,c){a=("object"===typeof a?""+a.query:a).toLowerCase();let d=this.cache.get(a);if(!d){d=this.search(a,b,c);if(d instanceof Promise){const e=this;d.then(function(f){e.cache.set(a,f)})}this.cache.set(a,d)}return d}function F(a){this.limit=a&&!0!==a?a:1E3;this.cache=new Map;this.h=""}F.prototype.set=function(a,b){this.cache.has(a)||(this.cache.set(this.h=a,b),this.limit&&this.cache.size>this.limit&&this.cache.delete(this.cache.keys().next().value))}; -F.prototype.get=function(a){const b=this.cache.get(a);b&&this.limit&&this.h!==a&&(this.cache.delete(a),this.cache.set(this.h=a,b));return b};F.prototype.remove=function(a){for(const b of this.cache){const c=b[0];b[1].includes(a)&&this.cache.delete(c)}};F.prototype.clear=function(){this.cache.clear();this.h=""};function na(a,b,c,d){let e=[];for(let f=0,g;f=g.length)b-=g.length;else{b=g[d?"splice":"slice"](b,c);const h=b.length;if(h&&(e=e.length?e.concat(b):b,c-=h,d&&(a.length-=h),!c))break;b=0}return e} -function H(a){if(!(this instanceof H))return new H(a);this.index=a?[a]:[];this.length=a?a.length:0;const b=this;return new Proxy([],{get(c,d){if("length"===d)return b.length;if("push"===d)return function(e){b.index[b.index.length-1].push(e);b.length++};if("pop"===d)return function(){if(b.length)return b.length--,b.index[b.index.length-1].pop()};if("indexOf"===d)return function(e){let f=0;for(let g=0,h,k;gf;h--){g=p.substring(f,h);var k=this.score?this.score(b,p,t,g,f):P(q,d,t,e,f);Q(this,m,g,k,a,c)}break}case "reverse":if(1< -e){for(h=e-1;0g?0:1),d,t,h-1,k-1),x=this.bidirectional&&p>f;Q(this,l,x?f:p,r,a,c,x?p:f)}}}}this.fastupdate||this.A.add(a)}else b=""}this.db&& -(b||this.R.push({del:a}),this.fa&&ua(this));return this};function Q(a,b,c,d,e,f,g){let h=g?a.I:a.map,k;if(!b[c]||!g||!(k=b[c])[g])if(g?(b=k||(b[c]=z()),b[g]=1,(k=h.get(g))?h=k:h.set(g,h=new Map)):b[c]=1,(k=h.get(c))?h=k:h.set(c,h=k=[]),h=h[d]||(h[d]=[]),!f||!h.includes(e)){if(h.length===2**31-1){b=new H(h);if(a.fastupdate)for(let l of a.A.values())l.includes(h)&&(l[l.indexOf(h)]=b);k[d]=h=b}h.push(e);a.fastupdate&&((d=a.A.get(e))?d.push(h):a.A.set(e,[h]))}} -function P(a,b,c,d,e){return c&&1b?b?a.slice(c,c+b):a.slice(c):a,d?va(a):a;let e=[];for(let f=0,g,h;f=h){c-=h;continue}cb&&(g=g.slice(0,b),h=g.length),e.push(g);else{if(h>=b)return h>b&&(g=g.slice(0,b)),d?va(g):g;e=[g]}b-=h;if(!b)break}if(!e.length)return e;e=1a.length)return e?R(a[0],b,c,d):a[0];d=[];let g=0,h=z(),k=da(a);for(let l=0,m;la.length)return[];let f=[],g=0,h=z(),k=da(a);if(!k)return f;for(let l=0,m;la.length)return e?R(a[0],b,c,d):a[0];b=[];c=z();for(let g=0,h;g=e)))break;if(h.length){if(g)return R(h,e,0);b.push(h);return}}return!c&&h}function U(a,b,c,d,e,f,g,h){let k;c&&(k=a.bidirectional&&b>c);if(a.db)return c?a.db.get(k?c:b,k?b:c,d,e,f,g,h):a.db.get(b,"",d,e,f,g,h);a=c?(a=a.I.get(k?b:c))&&a.get(k?c:b):a.map.get(b);return a};O.prototype.remove=function(a,b){const c=this.A.size&&(this.fastupdate?this.A.get(a):this.A.has(a));if(c){if(this.fastupdate)for(let d=0,e;de.length)e.pop();else{const f=e.indexOf(a);f===c.length-1?e.pop():e.splice(f,1)}}else Ea(this.map,a),this.depth&&Ea(this.I,a);b||this.A.delete(a)}this.db&&(this.R.push({del:a}),this.fa&&ua(this));this.cache&&this.cache.remove(a);return this}; -function Ea(a,b){let c=0;if(a.constructor===Array)for(let d=0,e,f;dc.add(a,b)):this.add(a,b)}return this.remove(a).add(a,b)};function Fa(a){let b=0;if(a.constructor===Array)for(let c=0,d;c{f=l}));let h,k;switch(e||(e=0)){case 0:h="reg";if(this.fastupdate){k=z();for(let l of this.A.keys())k[l]=1}else k=this.A;break;case 1:h="cfg";k={doc:0,opt:this.B?1:0};break;case 2:h="map";k=this.map;break;case 3:h="ctx";k=this.I;break;default:"undefined"===typeof c&&f&&f();return}qa(a,b||this,c,h,d,e,k,f);return g}; -u.import=function(a,b){if(b)switch(B(b)&&(b=JSON.parse(b)),a){case "cfg":this.B=!!b.opt;break;case "reg":this.fastupdate=!1;this.A=b;break;case "map":this.map=b;break;case "ctx":this.I=b}};ta(O.prototype);async function Ga(a){a=a.data;var b=self._index;const c=a.args;var d=a.task;switch(d){case "init":d=a.options||{};(b=d.config)&&(d=await import(b));(b=a.factory)?(Function("return "+b)()(self),self._index=new self.FlexSearch.Index(d),delete self.FlexSearch):self._index=new O(d);postMessage({id:a.id});break;default:a=a.id,b=b[d].apply(b,c),postMessage("search"===d?{id:a,msg:b}:{id:a})}};let Ha=0; -function V(a){function b(f){f=f.data||f;const g=f.id,h=g&&e.h[g];h&&(h(f.msg),delete e.h[g])}if(!(this instanceof V))return new V(a);a||(a={});let c=(self||window)._factory;c&&(c=c.toString());const d="undefined"===typeof window&&self.exports,e=this;this.worker=Ia(c,d,a.worker);this.h=z();if(this.worker){d?this.worker.on("message",b):this.worker.onmessage=b;if(a.config)return new Promise(function(f){e.h[++Ha]=function(){f(e)};e.worker.postMessage({id:Ha,task:"init",factory:c,options:a})});this.worker.postMessage({task:"init", -factory:c,options:a})}}W("add");W("append");W("search");W("update");W("remove");function W(a){V.prototype[a]=V.prototype[a+"Async"]=function(){const b=this,c=[].slice.call(arguments);var d=c[c.length-1];let e;"function"===typeof d&&(e=d,c.splice(c.length-1,1));d=new Promise(function(f){b.h[++Ha]=f;b.worker.postMessage({task:a,id:Ha,args:c})});return e?(d.then(e),this):d}} -function Ia(a,b,c){return b?new (require("worker_threads")["Worker"])(__dirname + "/node/node.js"):a?new window.Worker(URL.createObjectURL(new Blob(["onmessage="+Ga.toString()],{type:"text/javascript"}))):new window.Worker(B(c)?c:"worker/worker.js",{type:"module"})};X.prototype.add=function(a,b,c){C(a)&&(b=a,a=ca(b,this.key));if(b&&(a||0===a)){if(!c&&this.A.has(a))return this.update(a,b);for(let h=0,k;hc||d)a=a.slice(d,d+c);e&&(a=Ma.call(this,a));return a}} -function Ma(a){const b=Array(a.length);for(let c=0,d;cthis.stemmer.get(l)),k=1);this.matcher&& +1this.matcher.get(l)),k=1);g&&k&&(g.lengththis.V&&(this.M.clear(),this.B=this.B/1.1|0));g&&c.push(g)}this.finalize&&(c=this.finalize(c)||c);this.cache&&a.length<=this.h&&(this.J.set(a,c),this.J.size>this.V&&(this.J.clear(),this.h=this.h/1.1|0));return c};function la(a){a.O=null;a.J.clear();a.M.clear()};function ma(a,b,c){a=("object"===typeof a?""+a.query:a).toLowerCase();let d=this.cache.get(a);if(!d){d=this.search(a,b,c);if(d.then){const e=this;d.then(function(f){e.cache.set(a,f);return f})}this.cache.set(a,d)}return d}function G(a){this.limit=a&&!0!==a?a:1E3;this.cache=new Map;this.h=""}G.prototype.set=function(a,b){this.cache.set(this.h=a,b);this.cache.size>this.limit&&this.cache.delete(this.cache.keys().next().value)}; +G.prototype.get=function(a){const b=this.cache.get(a);b&&this.h!==a&&(this.cache.delete(a),this.cache.set(this.h=a,b));return b};G.prototype.remove=function(a){for(const b of this.cache){const c=b[0];b[1].includes(a)&&this.cache.delete(c)}};G.prototype.clear=function(){this.cache.clear();this.h=""};const na={normalize:function(a){return a.toLowerCase()},dedupe:!1};const oa=new Map([["b","p"],["v","f"],["w","f"],["z","s"],["x","s"],["d","t"],["n","m"],["c","k"],["g","k"],["j","k"],["q","k"],["i","e"],["y","e"],["u","o"]]);const pa=new Map([["ai","ei"],["ae","a"],["oe","o"],["ue","u"],["sh","s"],["ch","c"],["th","t"],["ph","f"],["pf","f"]]),qa=[/([^aeo])h([aeo$])/g,"$1$2",/([aeo])h([^aeo]|$)/g,"$1$2"];const ra={a:"",e:"",i:"",o:"",u:"",y:"",b:1,f:1,p:1,v:1,c:2,g:2,j:2,k:2,q:2,s:2,x:2,z:2,"\u00df":2,d:3,t:3,l:4,m:5,n:5,r:6};const sa=/[\x00-\x7F]+/g;const ta=/[\x00-\x7F]+/g;const ua=/[\x00-\x7F]+/g;var va={LatinExact:{normalize:!1,dedupe:!1},LatinDefault:na,LatinSimple:{normalize:!0,dedupe:!0},LatinBalance:{normalize:!0,dedupe:!0,mapper:oa},LatinAdvanced:{normalize:!0,dedupe:!0,mapper:oa,replacer:qa,matcher:pa},LatinExtra:{normalize:!0,dedupe:!0,mapper:oa,replacer:qa.concat([/(?!^)[aeoy]/g,""]),matcher:pa},LatinSoundex:{normalize:!0,dedupe:!1,include:{letter:!0},finalize:function(a){for(let c=0;c=g.length)b-=g.length;else{b=g[d?"splice":"slice"](b,c);const h=b.length;if(h&&(e=e.length?e.concat(b):b,c-=h,d&&(a.length-=h),!c))break;b=0}return e} +function J(a){if(!this)return new J(a);this.index=a?[a]:[];this.length=a?a.length:0;const b=this;return new Proxy([],{get(c,d){if("length"===d)return b.length;if("push"===d)return function(e){b.index[b.index.length-1].push(e);b.length++};if("pop"===d)return function(){if(b.length)return b.length--,b.index[b.index.length-1].pop()};if("indexOf"===d)return function(e){let f=0;for(let g=0,h,k;gf;h--){g=p.substring(f,h);var k=this.score?this.score(b,p,t,g,f):P(q,d,t,e,f);Q(this,m,g,k,a,c)}break}case "reverse":if(1< +e){for(h=e-1;0g?0:1),d,t,h-1,k-1),x=this.bidirectional&&p>f;Q(this,l,x?f:p,r,a,c,x?p:f)}}}}this.fastupdate||this.A.add(a)}else b=""}this.db&&(b|| +this.K.push({del:a}),this.W&&Ca(this));return this};function Q(a,b,c,d,e,f,g){let h=g?a.F:a.map,k;if(!b[c]||!g||!(k=b[c])[g])if(g?(b=k||(b[c]=z()),b[g]=1,(k=h.get(g))?h=k:h.set(g,h=new Map)):b[c]=1,(k=h.get(c))?h=k:h.set(c,h=k=[]),h=h[d]||(h[d]=[]),!f||!h.includes(e)){if(h.length===2**31-1){b=new J(h);if(a.fastupdate)for(let l of a.A.values())l.includes(h)&&(l[l.indexOf(h)]=b);k[d]=h=b}h.push(e);a.fastupdate&&((d=a.A.get(e))?d.push(h):a.A.set(e,[h]))}} +function P(a,b,c,d,e){return c&&1b?b?a.slice(c,c+b):a.slice(c):a,d?Da(a):a;let e=[];for(let f=0,g,h;f=h){c-=h;continue}cb&&(g=g.slice(0,b),h=g.length),e.push(g);else{if(h>=b)return h>b&&(g=g.slice(0,b)),d?Da(g):g;e=[g]}b-=h;if(!b)break}if(!e.length)return e;e=1a.length)return e?R(a[0],b,c,d):a[0];d=[];let g=0,h=z(),k=da(a);for(let l=0,m;la.length)return[];let f=[],g=0,h=z(),k=da(a);if(!k)return f;for(let l=0,m;la.length)return e?R(a[0],b,c,d):a[0];b=[];c=z();for(let g=0,h;g=e)))break;if(h.length){if(g)return R(h,e,0);b.push(h);return}}return!c&&h}function V(a,b,c,d,e,f,g,h){let k;c&&(k=a.bidirectional&&b>c);if(a.db)return c?a.db.get(k?c:b,k?b:c,d,e,f,g,h):a.db.get(b,"",d,e,f,g,h);a=c?(a=a.F.get(k?b:c))&&a.get(k?c:b):a.map.get(b);return a};O.prototype.remove=function(a,b){const c=this.A.size&&(this.fastupdate?this.A.get(a):this.A.has(a));if(c){if(this.fastupdate)for(let d=0,e;de.length)e.pop();else{const f=e.indexOf(a);f===c.length-1?e.pop():e.splice(f,1)}}else La(this.map,a),this.depth&&La(this.F,a);b||this.A.delete(a)}this.db&&(this.K.push({del:a}),this.W&&Ca(this));this.cache&&this.cache.remove(a);return this}; +function La(a,b){let c=0;if(a.constructor===Array)for(let d=0,e,f;dc.add(a,b)):this.add(a,b)}return this.remove(a).add(a,b)};function Ma(a){let b=0;if(a.constructor===Array)for(let c=0,d;c{f=l}));let h,k;switch(e||(e=0)){case 0:h="reg";if(this.fastupdate){k=z();for(let l of this.A.keys())k[l]=1}else k=this.A;break;case 1:h="cfg";k={doc:0,opt:this.B?1:0};break;case 2:h="map";k=this.map;break;case 3:h="ctx";k=this.F;break;default:"undefined"===typeof c&&f&&f();return}za(a,b||this,c,h,d,e,k,f);return g}; +u.import=function(a,b){if(b)switch(B(b)&&(b=JSON.parse(b)),a){case "cfg":this.B=!!b.opt;break;case "reg":this.fastupdate=!1;this.A=b;break;case "map":this.map=b;break;case "ctx":this.F=b}};Ba(O.prototype);async function Na(a){a=a.data;var b=self._index;const c=a.args;var d=a.task;switch(d){case "init":d=a.options||{};(b=d.config)&&(d=await import(b));(b=a.factory)?(Function("return "+b)()(self),self._index=new self.FlexSearch.Index(d),delete self.FlexSearch):self._index=new O(d);postMessage({id:a.id});break;default:a=a.id,b=b[d].apply(b,c),postMessage("search"===d?{id:a,msg:b}:{id:a})}};let Oa=0; +function W(a){function b(f){f=f.data||f;const g=f.id,h=g&&e.h[g];h&&(h(f.msg),delete e.h[g])}if(!this)return new W(a);a||(a={});let c=(self||window)._factory;c&&(c=c.toString());const d="undefined"===typeof window&&self.exports,e=this;this.worker=Pa(c,d,a.worker);this.h=z();if(this.worker){d?this.worker.on("message",b):this.worker.onmessage=b;if(a.config)return new Promise(function(f){e.h[++Oa]=function(){f(e)};e.worker.postMessage({id:Oa,task:"init",factory:c,options:a})});this.worker.postMessage({task:"init",factory:c, +options:a})}}X("add");X("append");X("search");X("update");X("remove");function X(a){W.prototype[a]=W.prototype[a+"Async"]=function(){const b=this,c=[].slice.call(arguments);var d=c[c.length-1];let e;"function"===typeof d&&(e=d,c.splice(c.length-1,1));d=new Promise(function(f){b.h[++Oa]=f;b.worker.postMessage({task:a,id:Oa,args:c})});return e?(d.then(e),this):d}} +function Pa(a,b,c){return b?new (require("worker_threads")["Worker"])(__dirname + "/node/node.js"):a?new window.Worker(URL.createObjectURL(new Blob(["onmessage="+Na.toString()],{type:"text/javascript"}))):new window.Worker(B(c)?c:"worker/worker.js",{type:"module"})};Y.prototype.add=function(a,b,c){C(a)&&(b=a,a=ca(b,this.key));if(b&&(a||0===a)){if(!c&&this.A.has(a))return this.update(a,b);for(let h=0,k;hc||d)a=a.slice(d,d+c);e&&(a=Ta.call(this,a));return a}} +function Ta(a){const b=Array(a.length);for(let c=0,d;c{f=k}));e||(e=0);d||(d=0);if(d{e.objectStoreNames.contains(f)||e.createObjectStore(f)})};d.onblocked=function(e){console.error("blocked",e);c()};d.onerror=function(e){console.error(this.error,e);c()};d.onsuccess=function(){a.db=this.result;a.db.onversionchange=function(){a.close()};b(a)}})} -u.close=function(){this.db.close();this.db=null};u.clear=function(){const a=this.db.transaction(Qa,"readwrite");for(let b=0;b=m.length){d-=m.length;continue}const n=c?d+Math.min(m.length-d,c):m.length;for(let q=d;q=f.length)return[];if(!b&&!c)return f;f=f.slice(c,c+b);return d?e.enrich(f):f})}; -u.enrich=function(a){"object"!==typeof a&&(a=[a]);const b=this.db.transaction("reg","readonly").objectStore("reg"),c=[];for(let d=0;d{f=k}));e||(e=0);d||(d=0);if(d{e.objectStoreNames.contains(f)||e.createObjectStore(f)})};d.onblocked=function(e){console.error("blocked",e);c()};d.onerror=function(e){console.error(this.error,e);c()};d.onsuccess=function(){a.db=this.result;a.db.onversionchange=function(){a.close()};b(a)}})} +u.close=function(){this.db.close();this.db=null};u.clear=function(){const a=this.db.transaction(Xa,"readwrite");for(let b=0;b=m.length){d-=m.length;continue}const n=c?d+Math.min(m.length-d,c):m.length;for(let q=d;q=f.length)return[];if(!b&&!c)return f;f=f.slice(c,c+b);return d?e.enrich(f):f})}; +u.enrich=function(a){"object"!==typeof a&&(a=[a]);const b=this.db.transaction("reg","readonly").objectStore("reg"),c=[];for(let d=0;d{e.onerror=h=>{this.h[a+":"+b]=null;e.abort();e=d=null;g(h)};e.oncomplete=h=>{e=d=this.h[a+":"+b]=null;f(h||!0)};return c.call(this,d)})}; -u.commit=async function(a,b,c){if(b)await this.clear(),a.R=[];else{let d=a.R;a.R=[];for(let e=0,f;em&&!f&&"string"===typeof n&&!isNaN(n)&&(m=k.indexOf(parseInt(n,10)))&&(f=1),0<=m)if(e=1,1{a.onsuccess=function(){b(this.result)};a.oncomplete=function(){b(this.result)};a.onerror=c;a=null})};const Z=z();const Ua=new Map([["b","p"],["v","f"],["w","f"],["z","s"],["x","s"],["d","t"],["n","m"],["c","k"],["g","k"],["j","k"],["q","k"],["i","e"],["y","e"],["u","o"]]);var Va={normalize:!0,C:!0,G:Ua};const Wa=new Map([["ai","ei"],["ae","a"],["oe","o"],["ue","u"],["sh","s"],["ch","c"],["th","t"],["ph","f"],["pf","f"]]),Xa=[/([^aeo])h([aeo$])/g,"$1$2",/([aeo])h([^aeo]|$)/g,"$1$2"];var Ya={normalize:!0,C:!0,G:Ua,K:Xa,J:Wa};var $a={normalize:!0,C:!0,G:Ua,K:Xa.concat([/(?!^)[aeoy]/g,""]),J:Wa};const ab={a:"",e:"",i:"",o:"",u:"",y:"",b:1,f:1,p:1,v:1,c:2,g:2,j:2,k:2,q:2,s:2,x:2,z:2,"\u00df":2,d:3,t:3,l:4,m:5,n:5,r:6};Z["latin:exact"]={normalize:!1,C:!1};Z["latin:default"]=ra;Z["latin:simple"]={normalize:!0,C:!0};Z["latin:balance"]=Va;Z["latin:advanced"]=Ya;Z["latin:extra"]=$a;Z["latin:soundex"]={normalize:!0,C:!1,ga:{ka:!0},V:function(a){for(let c=0;cm&&!f&&"string"===typeof n&&!isNaN(n)&&(m=k.indexOf(parseInt(n,10)))&&(f=1),0<=m)if(e=1,1{a.onsuccess=function(){b(this.result)};a.oncomplete=function(){b(this.result)};a.onerror=c;a=null})};const ab={Index:O,Charset:va,Encoder:F,Document:Y,Worker:W,Resolver:S,IndexedDB:Ya,Language:{}},cb=self;let db;(db=cb.define)&&db.amd?db([],function(){return ab}):"object"===typeof cb.exports?cb.exports=ab:cb.FlexSearch=ab;}(this)); diff --git a/dist/flexsearch.bundle.module.debug.js b/dist/flexsearch.bundle.module.debug.js index 1db6941..177734b 100644 --- a/dist/flexsearch.bundle.module.debug.js +++ b/dist/flexsearch.bundle.module.debug.js @@ -79,32 +79,33 @@ function da(a) { } return b; } -;const ea = /[^\p{L}\p{N}]+/u, fa = /(\d{3})/g, ha = /(\D)(\d{3})/g, ia = /(\d{3})(\D)/g, ja = "".normalize && /[\u0300-\u036f]/g, ka = !ja && [["\u00aa", "a"], ["\u00b2", "2"], ["\u00b3", "3"], ["\u00b9", "1"], ["\u00ba", "o"], ["\u00bc", "1\u20444"], ["\u00bd", "1\u20442"], ["\u00be", "3\u20444"], ["\u00e0", "a"], ["\u00e1", "a"], ["\u00e2", "a"], ["\u00e3", "a"], ["\u00e4", "a"], ["\u00e5", "a"], ["\u00e7", "c"], ["\u00e8", "e"], ["\u00e9", "e"], ["\u00ea", "e"], ["\u00eb", "e"], ["\u00ec", "i"], -["\u00ed", "i"], ["\u00ee", "i"], ["\u00ef", "i"], ["\u00f1", "n"], ["\u00f2", "o"], ["\u00f3", "o"], ["\u00f4", "o"], ["\u00f5", "o"], ["\u00f6", "o"], ["\u00f9", "u"], ["\u00fa", "u"], ["\u00fb", "u"], ["\u00fc", "u"], ["\u00fd", "y"], ["\u00ff", "y"], ["\u0101", "a"], ["\u0103", "a"], ["\u0105", "a"], ["\u0107", "c"], ["\u0109", "c"], ["\u010b", "c"], ["\u010d", "c"], ["\u010f", "d"], ["\u0113", "e"], ["\u0115", "e"], ["\u0117", "e"], ["\u0119", "e"], ["\u011b", "e"], ["\u011d", "g"], ["\u011f", -"g"], ["\u0121", "g"], ["\u0123", "g"], ["\u0125", "h"], ["\u0129", "i"], ["\u012b", "i"], ["\u012d", "i"], ["\u012f", "i"], ["\u0133", "ij"], ["\u0135", "j"], ["\u0137", "k"], ["\u013a", "l"], ["\u013c", "l"], ["\u013e", "l"], ["\u0140", "l"], ["\u0144", "n"], ["\u0146", "n"], ["\u0148", "n"], ["\u0149", "n"], ["\u014d", "o"], ["\u014f", "o"], ["\u0151", "o"], ["\u0155", "r"], ["\u0157", "r"], ["\u0159", "r"], ["\u015b", "s"], ["\u015d", "s"], ["\u015f", "s"], ["\u0161", "s"], ["\u0163", "t"], ["\u0165", -"t"], ["\u0169", "u"], ["\u016b", "u"], ["\u016d", "u"], ["\u016f", "u"], ["\u0171", "u"], ["\u0173", "u"], ["\u0175", "w"], ["\u0177", "y"], ["\u017a", "z"], ["\u017c", "z"], ["\u017e", "z"], ["\u017f", "s"], ["\u01a1", "o"], ["\u01b0", "u"], ["\u01c6", "dz"], ["\u01c9", "lj"], ["\u01cc", "nj"], ["\u01ce", "a"], ["\u01d0", "i"], ["\u01d2", "o"], ["\u01d4", "u"], ["\u01d6", "u"], ["\u01d8", "u"], ["\u01da", "u"], ["\u01dc", "u"], ["\u01df", "a"], ["\u01e1", "a"], ["\u01e3", "ae"], ["\u00e6", "ae"], -["\u01fd", "ae"], ["\u01e7", "g"], ["\u01e9", "k"], ["\u01eb", "o"], ["\u01ed", "o"], ["\u01ef", "\u0292"], ["\u01f0", "j"], ["\u01f3", "dz"], ["\u01f5", "g"], ["\u01f9", "n"], ["\u01fb", "a"], ["\u01ff", "\u00f8"], ["\u0201", "a"], ["\u0203", "a"], ["\u0205", "e"], ["\u0207", "e"], ["\u0209", "i"], ["\u020b", "i"], ["\u020d", "o"], ["\u020f", "o"], ["\u0211", "r"], ["\u0213", "r"], ["\u0215", "u"], ["\u0217", "u"], ["\u0219", "s"], ["\u021b", "t"], ["\u021f", "h"], ["\u0227", "a"], ["\u0229", "e"], -["\u022b", "o"], ["\u022d", "o"], ["\u022f", "o"], ["\u0231", "o"], ["\u0233", "y"], ["\u02b0", "h"], ["\u02b1", "h"], ["\u0266", "h"], ["\u02b2", "j"], ["\u02b3", "r"], ["\u02b4", "\u0279"], ["\u02b5", "\u027b"], ["\u02b6", "\u0281"], ["\u02b7", "w"], ["\u02b8", "y"], ["\u02e0", "\u0263"], ["\u02e1", "l"], ["\u02e2", "s"], ["\u02e3", "x"], ["\u02e4", "\u0295"], ["\u0390", "\u03b9"], ["\u03ac", "\u03b1"], ["\u03ad", "\u03b5"], ["\u03ae", "\u03b7"], ["\u03af", "\u03b9"], ["\u03b0", "\u03c5"], ["\u03ca", -"\u03b9"], ["\u03cb", "\u03c5"], ["\u03cc", "\u03bf"], ["\u03cd", "\u03c5"], ["\u03ce", "\u03c9"], ["\u03d0", "\u03b2"], ["\u03d1", "\u03b8"], ["\u03d2", "\u03a5"], ["\u03d3", "\u03a5"], ["\u03d4", "\u03a5"], ["\u03d5", "\u03c6"], ["\u03d6", "\u03c0"], ["\u03f0", "\u03ba"], ["\u03f1", "\u03c1"], ["\u03f2", "\u03c2"], ["\u03f5", "\u03b5"], ["\u0439", "\u0438"], ["\u0450", "\u0435"], ["\u0451", "\u0435"], ["\u0453", "\u0433"], ["\u0457", "\u0456"], ["\u045c", "\u043a"], ["\u045d", "\u0438"], ["\u045e", -"\u0443"], ["\u0477", "\u0475"], ["\u04c2", "\u0436"], ["\u04d1", "\u0430"], ["\u04d3", "\u0430"], ["\u04d7", "\u0435"], ["\u04db", "\u04d9"], ["\u04dd", "\u0436"], ["\u04df", "\u0437"], ["\u04e3", "\u0438"], ["\u04e5", "\u0438"], ["\u04e7", "\u043e"], ["\u04eb", "\u04e9"], ["\u04ed", "\u044d"], ["\u04ef", "\u0443"], ["\u04f1", "\u0443"], ["\u04f3", "\u0443"], ["\u04f5", "\u0447"]]; -function E(a = {}) { - if (!(this instanceof E)) { - return new E(...arguments); +;var ea = [["\u00aa", "a"], ["\u00b2", "2"], ["\u00b3", "3"], ["\u00b9", "1"], ["\u00ba", "o"], ["\u00bc", "1\u20444"], ["\u00bd", "1\u20442"], ["\u00be", "3\u20444"], ["\u00e0", "a"], ["\u00e1", "a"], ["\u00e2", "a"], ["\u00e3", "a"], ["\u00e4", "a"], ["\u00e5", "a"], ["\u00e7", "c"], ["\u00e8", "e"], ["\u00e9", "e"], ["\u00ea", "e"], ["\u00eb", "e"], ["\u00ec", "i"], ["\u00ed", "i"], ["\u00ee", "i"], ["\u00ef", "i"], ["\u00f1", "n"], ["\u00f2", "o"], ["\u00f3", "o"], ["\u00f4", "o"], ["\u00f5", +"o"], ["\u00f6", "o"], ["\u00f9", "u"], ["\u00fa", "u"], ["\u00fb", "u"], ["\u00fc", "u"], ["\u00fd", "y"], ["\u00ff", "y"], ["\u0101", "a"], ["\u0103", "a"], ["\u0105", "a"], ["\u0107", "c"], ["\u0109", "c"], ["\u010b", "c"], ["\u010d", "c"], ["\u010f", "d"], ["\u0113", "e"], ["\u0115", "e"], ["\u0117", "e"], ["\u0119", "e"], ["\u011b", "e"], ["\u011d", "g"], ["\u011f", "g"], ["\u0121", "g"], ["\u0123", "g"], ["\u0125", "h"], ["\u0129", "i"], ["\u012b", "i"], ["\u012d", "i"], ["\u012f", "i"], ["\u0133", +"ij"], ["\u0135", "j"], ["\u0137", "k"], ["\u013a", "l"], ["\u013c", "l"], ["\u013e", "l"], ["\u0140", "l"], ["\u0144", "n"], ["\u0146", "n"], ["\u0148", "n"], ["\u0149", "n"], ["\u014d", "o"], ["\u014f", "o"], ["\u0151", "o"], ["\u0155", "r"], ["\u0157", "r"], ["\u0159", "r"], ["\u015b", "s"], ["\u015d", "s"], ["\u015f", "s"], ["\u0161", "s"], ["\u0163", "t"], ["\u0165", "t"], ["\u0169", "u"], ["\u016b", "u"], ["\u016d", "u"], ["\u016f", "u"], ["\u0171", "u"], ["\u0173", "u"], ["\u0175", "w"], ["\u0177", +"y"], ["\u017a", "z"], ["\u017c", "z"], ["\u017e", "z"], ["\u017f", "s"], ["\u01a1", "o"], ["\u01b0", "u"], ["\u01c6", "dz"], ["\u01c9", "lj"], ["\u01cc", "nj"], ["\u01ce", "a"], ["\u01d0", "i"], ["\u01d2", "o"], ["\u01d4", "u"], ["\u01d6", "u"], ["\u01d8", "u"], ["\u01da", "u"], ["\u01dc", "u"], ["\u01df", "a"], ["\u01e1", "a"], ["\u01e3", "ae"], ["\u00e6", "ae"], ["\u01fd", "ae"], ["\u01e7", "g"], ["\u01e9", "k"], ["\u01eb", "o"], ["\u01ed", "o"], ["\u01ef", "\u0292"], ["\u01f0", "j"], ["\u01f3", +"dz"], ["\u01f5", "g"], ["\u01f9", "n"], ["\u01fb", "a"], ["\u01ff", "\u00f8"], ["\u0201", "a"], ["\u0203", "a"], ["\u0205", "e"], ["\u0207", "e"], ["\u0209", "i"], ["\u020b", "i"], ["\u020d", "o"], ["\u020f", "o"], ["\u0211", "r"], ["\u0213", "r"], ["\u0215", "u"], ["\u0217", "u"], ["\u0219", "s"], ["\u021b", "t"], ["\u021f", "h"], ["\u0227", "a"], ["\u0229", "e"], ["\u022b", "o"], ["\u022d", "o"], ["\u022f", "o"], ["\u0231", "o"], ["\u0233", "y"], ["\u02b0", "h"], ["\u02b1", "h"], ["\u0266", "h"], +["\u02b2", "j"], ["\u02b3", "r"], ["\u02b4", "\u0279"], ["\u02b5", "\u027b"], ["\u02b6", "\u0281"], ["\u02b7", "w"], ["\u02b8", "y"], ["\u02e0", "\u0263"], ["\u02e1", "l"], ["\u02e2", "s"], ["\u02e3", "x"], ["\u02e4", "\u0295"], ["\u0390", "\u03b9"], ["\u03ac", "\u03b1"], ["\u03ad", "\u03b5"], ["\u03ae", "\u03b7"], ["\u03af", "\u03b9"], ["\u03b0", "\u03c5"], ["\u03ca", "\u03b9"], ["\u03cb", "\u03c5"], ["\u03cc", "\u03bf"], ["\u03cd", "\u03c5"], ["\u03ce", "\u03c9"], ["\u03d0", "\u03b2"], ["\u03d1", +"\u03b8"], ["\u03d2", "\u03a5"], ["\u03d3", "\u03a5"], ["\u03d4", "\u03a5"], ["\u03d5", "\u03c6"], ["\u03d6", "\u03c0"], ["\u03f0", "\u03ba"], ["\u03f1", "\u03c1"], ["\u03f2", "\u03c2"], ["\u03f5", "\u03b5"], ["\u0439", "\u0438"], ["\u0450", "\u0435"], ["\u0451", "\u0435"], ["\u0453", "\u0433"], ["\u0457", "\u0456"], ["\u045c", "\u043a"], ["\u045d", "\u0438"], ["\u045e", "\u0443"], ["\u0477", "\u0475"], ["\u04c2", "\u0436"], ["\u04d1", "\u0430"], ["\u04d3", "\u0430"], ["\u04d7", "\u0435"], ["\u04db", +"\u04d9"], ["\u04dd", "\u0436"], ["\u04df", "\u0437"], ["\u04e3", "\u0438"], ["\u04e5", "\u0438"], ["\u04e7", "\u043e"], ["\u04eb", "\u04e9"], ["\u04ed", "\u044d"], ["\u04ef", "\u0443"], ["\u04f1", "\u0443"], ["\u04f3", "\u0443"], ["\u04f5", "\u0447"]]; +const fa = /[^\p{L}\p{N}]+/u, ha = /(\d{3})/g, ia = /(\D)(\d{3})/g, ja = /(\d{3})(\D)/g, ka = "".normalize && /[\u0300-\u036f]/g; +function G(a) { + if (!this) { + return new G(...arguments); } - for (a = 0; a < arguments.length; a++) { - this.assign(arguments[a]); + for (let b = 0; b < arguments.length; b++) { + this.assign(arguments[b]); } } -E.prototype.assign = function(a) { +G.prototype.assign = function(a) { this.normalize = y(a.normalize, !0, this.normalize); - let b = a.ga, c = b || a.na || a.split; + let b = a.include, c = b || a.exclude || a.split; if ("object" === typeof c) { let d = !b, e = ""; - a.ga || (e += "\\p{Z}"); - c.ka && (e += "\\p{L}"); - c.oa && (e += "\\p{N}", d = !!b); - c.qa && (e += "\\p{S}"); - c.pa && (e += "\\p{P}"); + a.include || (e += "\\p{Z}"); + c.letter && (e += "\\p{L}"); + c.number && (e += "\\p{N}", d = !!b); + c.symbol && (e += "\\p{S}"); + c.punctuation && (e += "\\p{P}"); c.control && (e += "\\p{C}"); if (c = c.char) { e += "object" === typeof c ? c.join("") : c; @@ -112,59 +113,59 @@ E.prototype.assign = function(a) { this.split = new RegExp("[" + (b ? "^" : "") + e + "]+", "u"); this.numeric = d; } else { - this.split = y(c, ea, this.split), this.numeric = y(this.numeric, !0); + this.split = y(c, fa, this.split), this.numeric = y(this.numeric, !0); } - this.$ = y(a.$, null, this.$); - this.V = y(a.V, null, this.V); - ka && (this.G = new Map(ka)); + this.prepare = y(a.prepare, null, this.prepare); + this.finalize = y(a.finalize, null, this.finalize); + ka || (this.mapper = new Map(ea)); this.rtl = a.rtl || !1; - this.C = y(a.C, !0, this.C); + this.dedupe = y(a.dedupe, !0, this.dedupe); this.filter = y((c = a.filter) && new Set(c), null, this.filter); - this.J = y((c = a.J) && new Map(c), null, this.J); - this.G = y((c = a.G) && new Map(c), null, this.G); - this.N = y((c = a.N) && new Map(c), null, this.N); - this.K = y(a.K, null, this.K); - this.Z = y(a.Z, 1, this.Z); - this.ha = y(a.ha, 0, this.ha); + this.matcher = y((c = a.matcher) && new Map(c), null, this.matcher); + this.mapper = y((c = a.mapper) && new Map(c), null, this.mapper); + this.stemmer = y((c = a.stemmer) && new Map(c), null, this.stemmer); + this.replacer = y(a.replacer, null, this.replacer); + this.minlength = y(a.minlength, 1, this.minlength); + this.maxlength = y(a.maxlength, 0, this.maxlength); if (this.cache = c = y(a.cache, !0, this.cache)) { - this.X = null, this.ea = "number" === typeof c ? c : 2e5, this.P = new Map(), this.U = new Map(), this.B = this.h = 128; + this.O = null, this.V = "number" === typeof c ? c : 2e5, this.J = new Map(), this.M = new Map(), this.B = this.h = 128; } - this.D = ""; - this.ba = null; - this.Y = ""; - this.ca = null; - if (this.J) { - for (const d of this.J.keys()) { - this.D += (this.D ? "|" : "") + d; + this.C = ""; + this.S = null; + this.P = ""; + this.T = null; + if (this.matcher) { + for (const d of this.matcher.keys()) { + this.C += (this.C ? "|" : "") + d; } } - if (this.N) { - for (const d of this.N.keys()) { - this.Y += (this.Y ? "|" : "") + d; + if (this.stemmer) { + for (const d of this.stemmer.keys()) { + this.P += (this.P ? "|" : "") + d; } } return this; }; -E.prototype.encode = function(a) { +G.prototype.encode = function(a) { if (this.cache && a.length <= this.h) { - if (this.X) { - if (this.P.has(a)) { - return this.P.get(a); + if (this.O) { + if (this.J.has(a)) { + return this.J.get(a); } } else { - this.X = setTimeout(la, 0, this); + this.O = setTimeout(la, 0, this); } } - this.normalize && (a = "function" === typeof this.normalize ? this.normalize(a) : ja ? a.normalize("NFKD").replace(ja, "").toLowerCase() : a.toLowerCase()); - this.$ && (a = this.$(a)); - this.numeric && 3 < a.length && (a = a.replace(ha, "$1 $2").replace(ia, "$1 $2").replace(fa, "$1 ")); - const b = !(this.C || this.G || this.filter || this.J || this.N || this.K); + this.normalize && (a = "function" === typeof this.normalize ? this.normalize(a) : ka ? a.normalize("NFKD").replace(ka, "").toLowerCase() : a.toLowerCase()); + this.prepare && (a = this.prepare(a)); + this.numeric && 3 < a.length && (a = a.replace(ia, "$1 $2").replace(ja, "$1 $2").replace(ha, "$1 ")); + const b = !(this.dedupe || this.mapper || this.filter || this.matcher || this.stemmer || this.replacer); let c = [], d = this.split || "" === this.split ? a.split(this.split) : a; for (let f = 0, g, h; f < d.length; f++) { if (!(g = h = d[f])) { continue; } - if (g.length < this.Z) { + if (g.length < this.minlength) { continue; } if (b) { @@ -175,83 +176,109 @@ E.prototype.encode = function(a) { continue; } if (this.cache && g.length <= this.B) { - if (this.X) { - var e = this.U.get(g); + if (this.O) { + var e = this.M.get(g); if (e || "" === e) { e && c.push(e); continue; } } else { - this.X = setTimeout(la, 0, this); + this.O = setTimeout(la, 0, this); } } let k; - this.N && 2 < g.length && (this.ca || (this.ca = new RegExp("(?!^)(" + this.Y + ")$")), g = g.replace(this.ca, l => this.N.get(l)), k = 1); - this.J && 1 < g.length && (this.ba || (this.ba = new RegExp("(" + this.D + ")", "g")), g = g.replace(this.ba, l => this.J.get(l)), k = 1); - g && k && (g.length < this.Z || this.filter && this.filter.has(g)) && (g = ""); - if (g && (this.G || this.C && 1 < g.length)) { + this.stemmer && 2 < g.length && (this.T || (this.T = new RegExp("(?!^)(" + this.P + ")$")), g = g.replace(this.T, l => this.stemmer.get(l)), k = 1); + this.matcher && 1 < g.length && (this.S || (this.S = new RegExp("(" + this.C + ")", "g")), g = g.replace(this.S, l => this.matcher.get(l)), k = 1); + g && k && (g.length < this.minlength || this.filter && this.filter.has(g)) && (g = ""); + if (g && (this.mapper || this.dedupe && 1 < g.length)) { e = ""; for (let l = 0, m = "", n, q; l < g.length; l++) { - n = g.charAt(l), n === m && this.C || ((q = this.G && this.G.get(n)) || "" === q ? q === m && this.C || !(m = q) || (e += q) : e += m = n); + n = g.charAt(l), n === m && this.dedupe || ((q = this.mapper && this.mapper.get(n)) || "" === q ? q === m && this.dedupe || !(m = q) || (e += q) : e += m = n); } g = e; } - if (g && this.K) { - for (e = 0; g && e < this.K.length; e += 2) { - g = g.replace(this.K[e], this.K[e + 1]); + if (g && this.replacer) { + for (e = 0; g && e < this.replacer.length; e += 2) { + g = g.replace(this.replacer[e], this.replacer[e + 1]); } } - this.cache && h.length <= this.B && (this.U.set(h, g), this.U.size > this.ea && (this.U.clear(), this.B = this.B / 1.1 | 0)); + this.cache && h.length <= this.B && (this.M.set(h, g), this.M.size > this.V && (this.M.clear(), this.B = this.B / 1.1 | 0)); g && c.push(g); } - this.V && (c = this.V(c) || c); - this.cache && a.length <= this.h && (this.P.set(a, c), this.P.size > this.ea && (this.P.clear(), this.h = this.h / 1.1 | 0)); + this.finalize && (c = this.finalize(c) || c); + this.cache && a.length <= this.h && (this.J.set(a, c), this.J.size > this.V && (this.J.clear(), this.h = this.h / 1.1 | 0)); return c; }; function la(a) { - a.X = null; - a.P.clear(); - a.U.clear(); + a.O = null; + a.J.clear(); + a.M.clear(); } ;function ma(a, b, c) { a = ("object" === typeof a ? "" + a.query : a).toLowerCase(); let d = this.cache.get(a); if (!d) { d = this.search(a, b, c); - if (d instanceof Promise) { + if (d.then) { const e = this; d.then(function(f) { e.cache.set(a, f); + return f; }); } this.cache.set(a, d); } return d; } -function F(a) { +function H(a) { this.limit = a && !0 !== a ? a : 1000; this.cache = new Map(); this.h = ""; } -F.prototype.set = function(a, b) { - this.cache.has(a) || (this.cache.set(this.h = a, b), this.limit && this.cache.size > this.limit && this.cache.delete(this.cache.keys().next().value)); +H.prototype.set = function(a, b) { + this.cache.set(this.h = a, b); + this.cache.size > this.limit && this.cache.delete(this.cache.keys().next().value); }; -F.prototype.get = function(a) { +H.prototype.get = function(a) { const b = this.cache.get(a); - b && this.limit && this.h !== a && (this.cache.delete(a), this.cache.set(this.h = a, b)); + b && this.h !== a && (this.cache.delete(a), this.cache.set(this.h = a, b)); return b; }; -F.prototype.remove = function(a) { +H.prototype.remove = function(a) { for (const b of this.cache) { const c = b[0]; b[1].includes(a) && this.cache.delete(c); } }; -F.prototype.clear = function() { +H.prototype.clear = function() { this.cache.clear(); this.h = ""; }; -function na(a, b, c, d) { +const na = {normalize:function(a) { + return a.toLowerCase(); +}, dedupe:!1}; +const oa = new Map([["b", "p"], ["v", "f"], ["w", "f"], ["z", "s"], ["x", "s"], ["d", "t"], ["n", "m"], ["c", "k"], ["g", "k"], ["j", "k"], ["q", "k"], ["i", "e"], ["y", "e"], ["u", "o"]]); +const pa = new Map([["ai", "ei"], ["ae", "a"], ["oe", "o"], ["ue", "u"], ["sh", "s"], ["ch", "c"], ["th", "t"], ["ph", "f"], ["pf", "f"]]), qa = [/([^aeo])h([aeo$])/g, "$1$2", /([aeo])h([^aeo]|$)/g, "$1$2"]; +const ra = {a:"", e:"", i:"", o:"", u:"", y:"", b:1, f:1, p:1, v:1, c:2, g:2, j:2, k:2, q:2, s:2, x:2, z:2, "\u00df":2, d:3, t:3, l:4, m:5, n:5, r:6}; +const sa = /[\x00-\x7F]+/g; +const ta = /[\x00-\x7F]+/g; +const ua = /[\x00-\x7F]+/g; +var va = {LatinExact:{normalize:!1, dedupe:!1}, LatinDefault:na, LatinSimple:{normalize:!0, dedupe:!0}, LatinBalance:{normalize:!0, dedupe:!0, mapper:oa}, LatinAdvanced:{normalize:!0, dedupe:!0, mapper:oa, replacer:qa, matcher:pa}, LatinExtra:{normalize:!0, dedupe:!0, mapper:oa, replacer:qa.concat([/(?!^)[aeoy]/g, ""]), matcher:pa}, LatinSoundex:{normalize:!0, dedupe:!1, include:{letter:!0}, finalize:function(a) { + for (let c = 0; c < a.length; c++) { + var b = a[c]; + let d = b.charAt(0), e = ra[d]; + for (let f = 1, g; f < b.length && (g = b.charAt(f), "h" === g || "w" === g || !(g = ra[g]) || g === e || (d += g, e = g, 4 !== d.length)); f++) { + } + a[c] = d; + } +}}, ArabicDefault:{rtl:!0, normalize:!1, dedupe:!0, prepare:function(a) { + return ("" + a).replace(sa, " "); +}}, CjkDefault:{normalize:!1, dedupe:!0, split:"", prepare:function(a) { + return ("" + a).replace(ta, ""); +}}, CyrillicDefault:{normalize:!1, dedupe:!0, prepare:function(a) { + return ("" + a).replace(ua, " "); +}}}; +function wa(a, b, c, d) { let e = []; for (let f = 0, g; f < a.index.length; f++) { if (g = a.index[f], b >= g.length) { @@ -267,9 +294,9 @@ function na(a, b, c, d) { } return e; } -function I(a) { - if (!(this instanceof I)) { - return new I(a); +function K(a) { + if (!this) { + return new K(a); } this.index = a ? [a] : []; this.length = a ? a.length : 0; @@ -317,12 +344,12 @@ function I(a) { } if ("slice" === d) { return function(e, f) { - return na(b, e || 0, f || b.length, !1); + return wa(b, e || 0, f || b.length, !1); }; } if ("splice" === d) { return function(e, f) { - return na(b, e || 0, f || b.length, !0); + return wa(b, e || 0, f || b.length, !0); }; } if ("constructor" === d) { @@ -338,19 +365,19 @@ function I(a) { return !0; }}); } -I.prototype.clear = function() { +K.prototype.clear = function() { this.index.length = 0; }; -I.prototype.push = function() { +K.prototype.push = function() { }; function L(a = 8) { - if (!(this instanceof L)) { + if (!this) { return new L(a); } this.index = z(); - this.D = []; + this.C = []; this.size = 0; - 32 < a ? (this.h = oa, this.B = BigInt(a)) : (this.h = pa, this.B = a); + 32 < a ? (this.h = xa, this.B = BigInt(a)) : (this.h = ya, this.B = a); } L.prototype.get = function(a) { const b = this.index[this.h(a)]; @@ -359,28 +386,28 @@ L.prototype.get = function(a) { L.prototype.set = function(a, b) { var c = this.h(a); let d = this.index[c]; - d ? (c = d.size, d.set(a, b), (c -= d.size) && this.size++) : (this.index[c] = d = new Map([[a, b]]), this.D.push(d)); + d ? (c = d.size, d.set(a, b), (c -= d.size) && this.size++) : (this.index[c] = d = new Map([[a, b]]), this.C.push(d)); }; function M(a = 8) { - if (!(this instanceof M)) { + if (!this) { return new M(a); } this.index = z(); this.h = []; - 32 < a ? (this.D = oa, this.B = BigInt(a)) : (this.D = pa, this.B = a); + 32 < a ? (this.C = xa, this.B = BigInt(a)) : (this.C = ya, this.B = a); } M.prototype.add = function(a) { - var b = this.D(a); + var b = this.C(a); let c = this.index[b]; c ? (b = c.size, c.add(a), (b -= c.size) && this.size++) : (this.index[b] = c = new Set([a]), this.h.push(c)); }; u = L.prototype; u.has = M.prototype.has = function(a) { - const b = this.index[this.D(a)]; + const b = this.index[this.C(a)]; return b && b.has(a); }; u.delete = M.prototype.delete = function(a) { - const b = this.index[this.D(a)]; + const b = this.index[this.C(a)]; b && b.delete(a) && this.size--; }; u.clear = M.prototype.clear = function() { @@ -409,7 +436,7 @@ u.entries = M.prototype.entries = function*() { } } }; -function pa(a) { +function ya(a) { let b = 2 ** this.B - 1; if ("number" == typeof a) { return a & b; @@ -420,7 +447,7 @@ function pa(a) { } return 32 === this.B ? c + 2 ** 31 : c; } -function oa(a) { +function xa(a) { let b = BigInt(2) ** this.B - BigInt(1); var c = typeof a; if ("bigint" === c) { @@ -436,16 +463,13 @@ function oa(a) { } return c; } -;function qa(a, b, c, d, e, f, g, h) { +;function za(a, b, c, d, e, f, g, h) { (d = a(c ? c + "." + d : d, JSON.stringify(g))) && d.then ? d.then(function() { b.export(a, b, c, e, f + 1, h); }) : b.export(a, b, c, e, f + 1, h); } -;var ra = {normalize:function(a) { - return a.toLowerCase(); -}, C:!1}; -const sa = {memory:{resolution:1}, performance:{resolution:6, fastupdate:!0, context:{depth:1, resolution:3}}, match:{tokenize:"forward"}, score:{resolution:9, context:{depth:2, resolution:9}}}; -function ta(a) { +;const Aa = {memory:{resolution:1}, performance:{resolution:6, fastupdate:!0, context:{depth:1, resolution:3}}, match:{tokenize:"forward"}, score:{resolution:9, context:{depth:2, resolution:9}}}; +function Ba(a) { N.call(a, "add"); N.call(a, "append"); N.call(a, "search"); @@ -507,7 +531,7 @@ O.prototype.add = function(a, b, c, d) { } default: if (Q(this, m, p, f, a, c), n && 1 < d && t < d - 1) { - for (e = z(), g = this.da, f = p, h = Math.min(n + 1, d - t), e[f] = 1, k = 1; k < h; k++) { + for (e = z(), g = this.U, f = p, h = Math.min(n + 1, d - t), e[f] = 1, k = 1; k < h; k++) { if ((p = b[this.rtl ? d - 1 - t - k : t + k]) && !e[p]) { e[p] = 1; const r = this.score ? this.score(b, f, t, p, k) : P(g + (d / 2 > g ? 0 : 1), d, t, h - 1, k - 1), x = this.bidirectional && p > f; @@ -523,15 +547,15 @@ O.prototype.add = function(a, b, c, d) { b = ""; } } - this.db && (b || this.R.push({del:a}), this.fa && ua(this)); + this.db && (b || this.K.push({del:a}), this.W && Ca(this)); return this; }; function Q(a, b, c, d, e, f, g) { - let h = g ? a.I : a.map, k; + let h = g ? a.F : a.map, k; if (!b[c] || !g || !(k = b[c])[g]) { if (g ? (b = k || (b[c] = z()), b[g] = 1, (k = h.get(g)) ? h = k : h.set(g, h = new Map())) : b[c] = 1, (k = h.get(c)) ? h = k : h.set(c, h = k = []), h = h[d] || (h[d] = []), !f || !h.includes(e)) { if (h.length === 2 ** 31 - 1) { - b = new I(h); + b = new K(h); if (a.fastupdate) { for (let l of a.A.values()) { l.includes(h) && (l[l.indexOf(h)] = b); @@ -549,7 +573,7 @@ function P(a, b, c, d, e) { } ;function R(a, b, c, d) { if (1 === a.length) { - return a = a[0], a = c || a.length > b ? b ? a.slice(c, c + b) : a.slice(c) : a, d ? va(a) : a; + return a = a[0], a = c || a.length > b ? b ? a.slice(c, c + b) : a.slice(c) : a, d ? Da(a) : a; } let e = []; for (let f = 0, g, h; f < a.length; f++) { @@ -565,7 +589,7 @@ function P(a, b, c, d, e) { h > b && (g = g.slice(0, b), h = g.length), e.push(g); } else { if (h >= b) { - return h > b && (g = g.slice(0, b)), d ? va(g) : g; + return h > b && (g = g.slice(0, b)), d ? Da(g) : g; } e = [g]; } @@ -579,9 +603,9 @@ function P(a, b, c, d, e) { return e; } e = 1 < e.length ? [].concat.apply([], e) : e[0]; - return d ? va(e) : e; + return d ? Da(e) : e; } -function va(a) { +function Da(a) { for (let b = 0; b < a.length; b++) { a[b] = {score:b, id:a[b]}; } @@ -591,7 +615,7 @@ function va(a) { const a = this; let b = arguments; var c = b[0]; - if (c instanceof Promise) { + if (c.then) { return c.then(function() { return a.or.apply(a, b); }); @@ -605,7 +629,7 @@ function va(a) { for (let k = 0, l; k < b.length; k++) { if (l = b[k]) { let m; - if (l instanceof S) { + if (l.constructor === S) { m = l.result; } else if (l.constructor === Array) { m = l; @@ -615,8 +639,8 @@ function va(a) { m = this.and(l.and); } else if (l.xor) { m = this.xor(l.xor); - } else if (l.F) { - m = this.F(l.F); + } else if (l.not) { + m = this.not(l.not); } else { e = l.limit || 0; f = l.offset || 0; @@ -625,21 +649,21 @@ function va(a) { continue; } d[k] = m; - m instanceof Promise && c.push(m); + m.then && c.push(m); } } if (c.length) { return Promise.all(c).then(function() { a.result.length && (d = [a.result].concat(d)); - a.result = wa(d, e, f, g, h, a.O); + a.result = Ea(d, e, f, g, h, a.G); return h ? a.result : a; }); } this.result.length && (d = [this.result].concat(d)); - this.result = wa(d, e, f, g, h, a.O); + this.result = Ea(d, e, f, g, h, a.G); return h ? this.result : this; }; -function wa(a, b, c, d, e, f) { +function Ea(a, b, c, d, e, f) { if (!a.length) { return a; } @@ -682,7 +706,7 @@ function wa(a, b, c, d, e, f) { const b = this; let c = arguments; var a = c[0]; - if (a instanceof Promise) { + if (a.then) { return a.then(function() { return b.and.apply(b, c); }); @@ -696,7 +720,7 @@ function wa(a, b, c, d, e, f) { for (let h = 0, k; h < c.length; h++) { if (k = c[h]) { let l; - if (k instanceof S) { + if (k.constructor === S) { l = k.result; } else if (k.constructor === Array) { l = k; @@ -706,8 +730,8 @@ function wa(a, b, c, d, e, f) { l = this.or(k.or); } else if (k.xor) { l = this.xor(k.xor); - } else if (k.F) { - l = this.F(k.F); + } else if (k.not) { + l = this.not(k.not); } else { e = k.limit || 0; f = k.offset || 0; @@ -715,23 +739,23 @@ function wa(a, b, c, d, e, f) { continue; } d[h] = l; - l instanceof Promise && a.push(l); + l.then && a.push(l); } } if (a.length) { return Promise.all(a).then(function() { d = [b.result].concat(d); - b.result = xa(d, e, f, g, b.O); + b.result = Fa(d, e, f, g, b.G); return g ? b.result : b; }); } d = [this.result].concat(d); - this.result = xa(d, e, f, g, b.O); + this.result = Fa(d, e, f, g, b.G); return g ? this.result : this; } return this; }; -function xa(a, b, c, d, e) { +function Fa(a, b, c, d, e) { if (2 > a.length) { return []; } @@ -777,7 +801,7 @@ function xa(a, b, c, d, e) { const a = this; let b = arguments; var c = b[0]; - if (c instanceof Promise) { + if (c.then) { return c.then(function() { return a.xor.apply(a, b); }); @@ -791,7 +815,7 @@ function xa(a, b, c, d, e) { for (let k = 0, l; k < b.length; k++) { if (l = b[k]) { let m; - if (l instanceof S) { + if (l.constructor === S) { m = l.result; } else if (l.constructor === Array) { m = l; @@ -801,8 +825,8 @@ function xa(a, b, c, d, e) { m = this.or(l.or); } else if (l.and) { m = this.and(l.and); - } else if (l.F) { - m = this.F(l.F); + } else if (l.not) { + m = this.not(l.not); } else { e = l.limit || 0; f = l.offset || 0; @@ -811,21 +835,21 @@ function xa(a, b, c, d, e) { continue; } d[k] = m; - m instanceof Promise && c.push(m); + m.then && c.push(m); } } if (c.length) { return Promise.all(c).then(function() { a.result.length && (d = [a.result].concat(d)); - a.result = ya(d, e, f, g, !h, a.O); + a.result = Ga(d, e, f, g, !h, a.G); return h ? a.result : a; }); } this.result.length && (d = [this.result].concat(d)); - this.result = ya(d, e, f, g, !h, a.O); + this.result = Ga(d, e, f, g, !h, a.G); return h ? this.result : this; }; -function ya(a, b, c, d, e, f) { +function Ga(a, b, c, d, e, f) { if (!a.length) { return a; } @@ -858,17 +882,17 @@ function ya(a, b, c, d, e, f) { } return b; } -;S.prototype.F = function() { +;S.prototype.not = function() { const a = this; let b = arguments; var c = b[0]; - if (c instanceof Promise) { + if (c.then) { return c.then(function() { - return a.F.apply(a, b); + return a.not.apply(a, b); }); } if (c[0] && c[0].index) { - return this.F.apply(this, c); + return this.not.apply(this, c); } let d = []; c = []; @@ -876,7 +900,7 @@ function ya(a, b, c, d, e, f) { for (let f = 0, g; f < b.length; f++) { if (g = b[f]) { let h; - if (g instanceof S) { + if (g.constructor === S) { h = g.result; } else if (g.constructor === Array) { h = g; @@ -893,19 +917,19 @@ function ya(a, b, c, d, e, f) { continue; } d[f] = h; - h instanceof Promise && c.push(h); + h.then && c.push(h); } } if (c.length) { return Promise.all(c).then(function() { - a.result = za.call(a, d, e); + a.result = Ha.call(a, d, e); return e ? a.result : a; }); } - this.result = za.call(this, d, e); + this.result = Ha.call(this, d, e); return e ? this.result : this; }; -function za(a, b) { +function Ha(a, b) { if (!a.length) { return this.result; } @@ -921,18 +945,18 @@ function za(a, b) { return c; } ;function S(a) { + if (!this) { + return new S(a); + } if (a && a.index) { return a.resolve = !1, this.index = a.index, a.index.search(a); } - if (!(this instanceof S)) { - return new S(a); - } - if (a instanceof S) { + if (a.constructor === S) { return a; } this.index = null; this.result = a || []; - this.O = 0; + this.G = 0; } S.prototype.limit = function(a) { if (this.result.length) { @@ -961,13 +985,17 @@ S.prototype.offset = function(a) { } return this; }; +S.prototype.boost = function(a) { + this.G += a; + return this; +}; S.prototype.resolve = function(a, b, c) { T = 1; const d = this.result; this.result = this.index = null; return d.length ? ("object" === typeof a && (c = a.enrich, b = a.offset, a = a.limit), R(d, a || 100, b, c)) : d; }; -function Aa(a, b, c, d) { +function Ia(a, b, c, d) { var e = a.length; let f = [], g = 0, h, k, l; d && (d = []); @@ -1028,7 +1056,7 @@ function Aa(a, b, c, d) { } return f; } -function Ba(a, b) { +function Ja(a, b) { const c = z(), d = z(), e = []; for (let f = 0; f < a.length; f++) { c[a[f]] = 1; @@ -1062,18 +1090,18 @@ O.prototype.search = function(a, b, c) { e = a.length; b || !h || (b = 100); if (1 === e) { - return Ca.call(this, a[0], "", b, g, h, k, l); + return U.call(this, a[0], "", b, g, h, k, l); } m = this.depth && !1 !== m; if (2 === e && m && !f) { - return Ca.call(this, a[0], a[1], b, g, h, k, l); + return U.call(this, a[0], a[1], b, g, h, k, l); } let n = c = 0; if (1 < e) { const p = z(), r = []; for (let x = 0, A; x < e; x++) { if ((A = a[x]) && !p[A]) { - if (f || this.db || U(this, A)) { + if (f || this.db || V(this, A)) { r.push(A), p[A] = 1; } else { return h ? d : new S(d); @@ -1091,10 +1119,10 @@ O.prototype.search = function(a, b, c) { } let q = 0, t; if (1 === e) { - return Ca.call(this, a[0], "", b, g, h, k, l); + return U.call(this, a[0], "", b, g, h, k, l); } if (2 === e && m && !f) { - return Ca.call(this, a[0], a[1], b, g, h, k, l); + return U.call(this, a[0], a[1], b, g, h, k, l); } 1 < e && (m ? (t = a[0], q = 1) : 9 < c && 3 < c / n && a.sort(aa)); if (this.db) { @@ -1105,7 +1133,7 @@ O.prototype.search = function(a, b, c) { return async function() { for (let r, x; q < e; q++) { x = a[q]; - t ? (r = await U(p, x, t), r = Da(r, d, f, p.da, b, g, 2 === e), f && !1 === r && d.length || (t = x)) : (r = await U(p, x), r = Da(r, d, f, p.resolution, b, g, 1 === e)); + t ? (r = await V(p, x, t), r = Ka(r, d, f, p.U, b, g, 2 === e), f && !1 === r && d.length || (t = x)) : (r = await V(p, x), r = Ka(r, d, f, p.resolution, b, g, 1 === e)); if (r) { return r; } @@ -1124,12 +1152,12 @@ O.prototype.search = function(a, b, c) { } } } - return h ? Aa(d, b, g, f) : new S(d[0]); + return h ? Ia(d, b, g, f) : new S(d[0]); }(); } for (let p, r; q < e; q++) { r = a[q]; - t ? (p = U(this, r, t), p = Da(p, d, f, this.da, b, g, 2 === e), f && !1 === p && d.length || (t = r)) : (p = U(this, r), p = Da(p, d, f, this.resolution, b, g, 1 === e)); + t ? (p = V(this, r, t), p = Ka(p, d, f, this.U, b, g, 2 === e), f && !1 === p && d.length || (t = r)) : (p = V(this, r), p = Ka(p, d, f, this.resolution, b, g, 1 === e)); if (p) { return p; } @@ -1148,15 +1176,15 @@ O.prototype.search = function(a, b, c) { } } } - return h ? Aa(d, b, g, f) : new S(d[0]); + return h ? Ia(d, b, g, f) : new S(d[0]); }; -function Ca(a, b, c, d, e, f, g) { - a = U(this, a, b, c, d, e, f, g); +function U(a, b, c, d, e, f, g) { + a = V(this, a, b, c, d, e, f, g); return this.db ? a.then(function(h) { return e ? h : h && h.length ? e ? R(h, c, d) : new S(h) : e ? [] : new S([]); }) : a && a.length ? e ? R(a, c, d) : new S(a) : e ? [] : new S([]); } -function Da(a, b, c, d, e, f, g) { +function Ka(a, b, c, d, e, f, g) { let h = []; if (a) { d = Math.min(a.length, d); @@ -1177,13 +1205,13 @@ function Da(a, b, c, d, e, f, g) { } return !c && h; } -function U(a, b, c, d, e, f, g, h) { +function V(a, b, c, d, e, f, g, h) { let k; c && (k = a.bidirectional && b > c); if (a.db) { return c ? a.db.get(k ? c : b, k ? b : c, d, e, f, g, h) : a.db.get(b, "", d, e, f, g, h); } - a = c ? (a = a.I.get(k ? b : c)) && a.get(k ? c : b) : a.map.get(b); + a = c ? (a = a.F.get(k ? b : c)) && a.get(k ? c : b) : a.map.get(b); return a; } ;O.prototype.remove = function(a, b) { @@ -1201,15 +1229,15 @@ function U(a, b, c, d, e, f, g, h) { } } } else { - Ea(this.map, a), this.depth && Ea(this.I, a); + La(this.map, a), this.depth && La(this.F, a); } b || this.A.delete(a); } - this.db && (this.R.push({del:a}), this.fa && ua(this)); + this.db && (this.K.push({del:a}), this.W && Ca(this)); this.cache && this.cache.remove(a); return this; }; -function Ea(a, b) { +function La(a, b) { let c = 0; if (a.constructor === Array) { for (let d = 0, e, f; d < a.length; d++) { @@ -1224,25 +1252,25 @@ function Ea(a, b) { } } else { for (let d of a) { - const e = d[0], f = Ea(d[1], b); + const e = d[0], f = La(d[1], b); f ? c += f : a.delete(e); } } return c; } ;function O(a, b) { - if (!(this instanceof O)) { + if (!this) { return new O(a); } if (a) { var c = B(a) ? a : a.preset; - c && (sa[c] || console.warn("Preset not found: " + c), a = Object.assign({}, sa[c], a)); + c && (Aa[c] || console.warn("Preset not found: " + c), a = Object.assign({}, Aa[c], a)); } else { a = {}; } c = a.context || {}; - const d = a.encode || a.encoder || ra; - this.encoder = d.encode ? d : "object" === typeof d ? new E(d) : {encode:d}; + const d = a.encode || a.encoder || na; + this.encoder = d.encode ? d : "object" === typeof d ? new G(d) : {encode:d}; let e; this.resolution = a.resolution || 9; this.tokenize = e = a.tokenize || "strict"; @@ -1252,17 +1280,17 @@ function Ea(a, b) { this.score = a.score || null; (e = a.keystore || 0) && (this.keystore = e); this.map = e ? new L(e) : new Map(); - this.I = e ? new L(e) : new Map(); + this.F = e ? new L(e) : new Map(); this.A = b || (this.fastupdate ? e ? new L(e) : new Map() : e ? new M(e) : new Set()); - this.da = c.resolution || 1; + this.U = c.resolution || 1; this.rtl = d.rtl || a.rtl || !1; - this.cache = (e = a.cache || null) && new F(e); + this.cache = (e = a.cache || null) && new H(e); this.resolve = !1 !== a.resolve; if (e = a.db) { this.db = e.mount(this); } - this.fa = !1 !== a.commit; - this.R = []; + this.W = !1 !== a.commit; + this.K = []; this.h = null; } u = O.prototype; @@ -1274,7 +1302,7 @@ u.commit = function(a, b) { this.h && (clearTimeout(this.h), this.h = null); return this.db.commit(this, a, b); }; -function ua(a) { +function Ca(a) { a.h || (a.h = setTimeout(function() { a.h = null; a.db.commit(a, void 0, void 0); @@ -1282,10 +1310,10 @@ function ua(a) { } u.clear = function() { this.map.clear(); - this.I.clear(); + this.F.clear(); this.A.clear(); this.cache && this.cache.clear(); - this.db && (this.h && clearTimeout(this.h), this.h = null, this.R = [{clear:!0}]); + this.db && (this.h && clearTimeout(this.h), this.h = null, this.K = [{clear:!0}]); return this; }; u.append = function(a, b) { @@ -1301,7 +1329,7 @@ u.update = function(a, b) { } return this.remove(a).add(a, b); }; -function Fa(a) { +function Ma(a) { let b = 0; if (a.constructor === Array) { for (let c = 0, d; c < a.length; c++) { @@ -1309,7 +1337,7 @@ function Fa(a) { } } else { for (const c of a) { - const d = c[0], e = Fa(c[1]); + const d = c[0], e = Ma(c[1]); e ? b += e : a.delete(d); } } @@ -1319,8 +1347,8 @@ u.cleanup = function() { if (!this.fastupdate) { return console.info('Cleanup the index isn\'t required when not using "fastupdate".'), this; } - Fa(this.map); - this.depth && Fa(this.I); + Ma(this.map); + this.depth && Ma(this.F); return this; }; u.searchCache = ma; @@ -1352,13 +1380,13 @@ u.export = function(a, b, c, d, e, f) { break; case 3: h = "ctx"; - k = this.I; + k = this.F; break; default: "undefined" === typeof c && f && f(); return; } - qa(a, b || this, c, h, d, e, k, f); + za(a, b || this, c, h, d, e, k, f); return g; }; u.import = function(a, b) { @@ -1375,12 +1403,12 @@ u.import = function(a, b) { this.map = b; break; case "ctx": - this.I = b; + this.F = b; } } }; -ta(O.prototype); -async function Ga(a) { +Ba(O.prototype); +async function Na(a) { a = a.data; var b = self._index; const c = a.args; @@ -1396,77 +1424,77 @@ async function Ga(a) { a = a.id, b = b[d].apply(b, c), postMessage("search" === d ? {id:a, msg:b} : {id:a}); } } -;let Ha = 0; -function V(a) { +;let Oa = 0; +function W(a) { function b(f) { f = f.data || f; const g = f.id, h = g && e.h[g]; h && (h(f.msg), delete e.h[g]); } - if (!(this instanceof V)) { - return new V(a); + if (!this) { + return new W(a); } a || (a = {}); let c = (self || window)._factory; c && (c = c.toString()); const d = "undefined" === typeof window && self.exports, e = this; - this.worker = Ia(c, d, a.worker); + this.worker = Pa(c, d, a.worker); this.h = z(); if (this.worker) { d ? this.worker.on("message", b) : this.worker.onmessage = b; if (a.config) { return new Promise(function(f) { - e.h[++Ha] = function() { + e.h[++Oa] = function() { f(e); }; - e.worker.postMessage({id:Ha, task:"init", factory:c, options:a}); + e.worker.postMessage({id:Oa, task:"init", factory:c, options:a}); }); } this.worker.postMessage({task:"init", factory:c, options:a}); } } -W("add"); -W("append"); -W("search"); -W("update"); -W("remove"); -function W(a) { - V.prototype[a] = V.prototype[a + "Async"] = function() { +X("add"); +X("append"); +X("search"); +X("update"); +X("remove"); +function X(a) { + W.prototype[a] = W.prototype[a + "Async"] = function() { const b = this, c = [].slice.call(arguments); var d = c[c.length - 1]; let e; "function" === typeof d && (e = d, c.splice(c.length - 1, 1)); d = new Promise(function(f) { - b.h[++Ha] = f; - b.worker.postMessage({task:a, id:Ha, args:c}); + b.h[++Oa] = f; + b.worker.postMessage({task:a, id:Oa, args:c}); }); return e ? (d.then(e), this) : d; }; } -function Ia(a, b, c) { - return b ? new (require("worker_threads")["Worker"])(__dirname + "/node/node.js") : a ? new window.Worker(URL.createObjectURL(new Blob(["onmessage=" + Ga.toString()], {type:"text/javascript"}))) : new window.Worker(B(c) ? c : "worker/worker.js", {type:"module"}); +function Pa(a, b, c) { + return b ? new (require("worker_threads")["Worker"])(__dirname + "/node/node.js") : a ? new window.Worker(URL.createObjectURL(new Blob(["onmessage=" + Na.toString()], {type:"text/javascript"}))) : new window.Worker(B(c) ? c : "worker/worker.js", {type:"module"}); } -;X.prototype.add = function(a, b, c) { +;Y.prototype.add = function(a, b, c) { C(a) && (b = a, a = ca(b, this.key)); if (b && (a || 0 === a)) { if (!c && this.A.has(a)) { return this.update(a, b); } for (let h = 0, k; h < this.field.length; h++) { - k = this.M[h]; + k = this.I[h]; var d = this.index.get(this.field[h]); if ("function" === typeof k) { var e = k(b); e && d.add(a, e, !1, !0); } else { - if (e = k.T, !e || e(b)) { - k instanceof String ? k = ["" + k] : B(k) && (k = [k]), Ja(b, k, this.W, 0, d, a, k[0], c); + if (e = k.L, !e || e(b)) { + k.constructor === String ? k = ["" + k] : B(k) && (k = [k]), Qa(b, k, this.N, 0, d, a, k[0], c); } } } if (this.tag) { - for (d = 0; d < this.L.length; d++) { - var f = this.L[d], g = this.aa[d]; + for (d = 0; d < this.H.length; d++) { + var f = this.H[d], g = this.R[d]; e = this.tag.get(g); let h = z(); if ("function" === typeof f) { @@ -1474,11 +1502,11 @@ function Ia(a, b, c) { continue; } } else { - const k = f.T; + const k = f.L; if (k && !k(b)) { continue; } - f instanceof String && (f = "" + f); + f.constructor === String && (f = "" + f); f = ca(b, f); } if (e && f) { @@ -1486,7 +1514,7 @@ function Ia(a, b, c) { for (let k = 0, l, m; k < f.length; k++) { if (l = f[k], !h[l] && (h[l] = 1, (g = e.get(l)) ? m = g : e.set(l, m = []), !c || !m.includes(a))) { if (m.length === 2 ** 31 - 1) { - g = new I(m); + g = new K(m); if (this.fastupdate) { for (let n of this.A.values()) { n.includes(m) && (n[n.indexOf(m)] = g); @@ -1505,11 +1533,11 @@ function Ia(a, b, c) { } if (this.store && (!c || !this.store.has(a))) { let h; - if (this.H) { + if (this.D) { h = z(); - for (let k = 0, l; k < this.H.length; k++) { - l = this.H[k]; - if ((c = l.T) && !c(b)) { + for (let k = 0, l; k < this.D.length; k++) { + l = this.D[k]; + if ((c = l.L) && !c(b)) { continue; } let m; @@ -1518,12 +1546,12 @@ function Ia(a, b, c) { if (!m) { continue; } - l = [l.ia]; - } else if (B(l) || l instanceof String) { + l = [l.X]; + } else if (B(l) || l.constructor === String) { h[l] = b[l]; continue; } - Ka(b, h, l, 0, l[0], m); + Ra(b, h, l, 0, l[0], m); } } this.store.set(a, h || b); @@ -1531,21 +1559,21 @@ function Ia(a, b, c) { } return this; }; -function Ka(a, b, c, d, e, f) { +function Ra(a, b, c, d, e, f) { a = a[e]; if (d === c.length - 1) { b[e] = f || a; } else if (a) { if (a.constructor === Array) { for (b = b[e] = Array(a.length), e = 0; e < a.length; e++) { - Ka(a, b, c, d, e); + Ra(a, b, c, d, e); } } else { - b = b[e] || (b[e] = z()), e = c[++d], Ka(a, b, c, d, e); + b = b[e] || (b[e] = z()), e = c[++d], Ra(a, b, c, d, e); } } } -function Ja(a, b, c, d, e, f, g, h) { +function Qa(a, b, c, d, e, f, g, h) { if (a = a[g]) { if (d === b.length - 1) { if (a.constructor === Array) { @@ -1561,17 +1589,17 @@ function Ja(a, b, c, d, e, f, g, h) { } else { if (a.constructor === Array) { for (g = 0; g < a.length; g++) { - Ja(a, b, c, d, e, f, g, h); + Qa(a, b, c, d, e, f, g, h); } } else { - g = b[++d], Ja(a, b, c, d, e, f, g, h); + g = b[++d], Qa(a, b, c, d, e, f, g, h); } } } else { e.db && e.remove(f); } } -;X.prototype.search = function(a, b, c, d) { +;Y.prototype.search = function(a, b, c, d) { c || (!b && C(a) ? (c = a, a = "") : C(b) && (c = b, b = 0)); let e = [], f = [], g; let h; @@ -1610,13 +1638,13 @@ function Ja(a, b, c, d, e, f, g, h) { } } else { r = Object.keys(v); - for (let J = 0, K, D; J < r.length; J++) { - if (K = r[J], D = v[K], D.constructor === Array) { + for (let I = 0, J, D; I < r.length; I++) { + if (J = r[I], D = v[J], D.constructor === Array) { for (x = 0; x < D.length; x++) { - p.push(K, D[x]); + p.push(J, D[x]); } } else { - p.push(K, D); + p.push(J, D); } } } @@ -1637,7 +1665,7 @@ function Ja(a, b, c, d, e, f, g, h) { } t.push(d = d.db.tag(p[n + 1], b, l, q)); } else { - d = La.call(this, p[n], p[n + 1], b, l, q); + d = Sa.call(this, p[n], p[n + 1], b, l, q); } e.push({field:p[n], tag:p[n + 1], result:d}); } @@ -1656,17 +1684,17 @@ function Ja(a, b, c, d, e, f, g, h) { k || (k = this.field); p = !d && (this.worker || this.async) && []; let A; - for (let w = 0, v, J, K; w < k.length; w++) { - J = k[w]; - if (this.db && this.tag && !this.M[w]) { + for (let w = 0, v, I, J; w < k.length; w++) { + I = k[w]; + if (this.db && this.tag && !this.I[w]) { continue; } let D; - B(J) || (D = J, J = D.field, a = D.query || a, b = D.limit || b, t = D.suggest || t); + B(I) || (D = I, I = D.field, a = D.query || a, b = D.limit || b, t = D.suggest || t); if (d) { v = d[w]; } else { - if (r = D || c, x = this.index.get(J), n && (this.db && (r.tag = n, A = x.db.la, r.field = k), A || (r.enrich = !1)), p) { + if (r = D || c, x = this.index.get(I), n && (this.db && (r.tag = n, A = x.db.Z, r.field = k), A || (r.enrich = !1)), p) { p[w] = x.searchAsync(a, b, r); r && q && (r.enrich = q); continue; @@ -1674,49 +1702,49 @@ function Ja(a, b, c, d, e, f, g, h) { v = x.search(a, b, r), r && q && (r.enrich = q); } } - K = v && v.length; - if (n && K) { + J = v && v.length; + if (n && J) { r = []; x = 0; if (this.db && d) { if (!A) { - for (let G = k.length; G < d.length; G++) { - let H = d[G]; - if (H && H.length) { - x++, r.push(H); + for (let E = k.length; E < d.length; E++) { + let F = d[E]; + if (F && F.length) { + x++, r.push(F); } else if (!t) { return e; } } } } else { - for (let G = 0, H, Wa; G < n.length; G += 2) { - H = this.tag.get(n[G]); - if (!H) { - if (console.warn("Tag '" + n[G] + ":" + n[G + 1] + "' will be skipped because there is no field '" + n[G] + "'."), t) { + for (let E = 0, F, Za; E < n.length; E += 2) { + F = this.tag.get(n[E]); + if (!F) { + if (console.warn("Tag '" + n[E] + ":" + n[E + 1] + "' will be skipped because there is no field '" + n[E] + "'."), t) { continue; } else { return e; } } - if (Wa = (H = H && H.get(n[G + 1])) && H.length) { - x++, r.push(H); + if (Za = (F = F && F.get(n[E + 1])) && F.length) { + x++, r.push(F); } else if (!t) { return e; } } } if (x) { - v = Ba(v, r); - K = v.length; - if (!K && !t) { + v = Ja(v, r); + J = v.length; + if (!J && !t) { return e; } x--; } } - if (K) { - f[m] = J, e.push(v), m++; + if (J) { + f[m] = I, e.push(v), m++; } else if (1 === k.length) { return e; } @@ -1749,7 +1777,7 @@ function Ja(a, b, c, d, e, f, g, h) { p = []; for (let w = 0, v; w < f.length; w++) { v = e[w]; - q && v.length && !v[0].doc && (this.db ? p.push(v = this.index.get(this.field[0]).db.enrich(v)) : v.length && (v = Ma.call(this, v))); + q && v.length && !v[0].doc && (this.db ? p.push(v = this.index.get(this.field[0]).db.enrich(v)) : v.length && (v = Ta.call(this, v))); if (g) { return v; } @@ -1759,10 +1787,10 @@ function Ja(a, b, c, d, e, f, g, h) { for (let v = 0; v < w.length; v++) { e[v].result = w[v]; } - return h ? Na(e, b) : e; - }) : h ? Na(e, b) : e; + return h ? Ua(e, b) : e; + }) : h ? Ua(e, b) : e; }; -function Na(a, b) { +function Ua(a, b) { const c = [], d = z(); for (let e = 0, f, g; e < a.length; e++) { f = a[e]; @@ -1781,7 +1809,7 @@ function Na(a, b) { } return c; } -function La(a, b, c, d, e) { +function Sa(a, b, c, d, e) { let f = this.tag.get(a); if (!f) { return console.warn("Tag '" + a + "' was not found"), []; @@ -1790,32 +1818,32 @@ function La(a, b, c, d, e) { if (a > c || d) { f = f.slice(d, d + c); } - e && (f = Ma.call(this, f)); + e && (f = Ta.call(this, f)); return f; } } -function Ma(a) { +function Ta(a) { const b = Array(a.length); for (let c = 0, d; c < a.length; c++) { d = a[c], b[c] = {id:d, doc:this.store.get(d)}; } return b; } -;function X(a) { - if (!(this instanceof X)) { - return new X(a); +;function Y(a) { + if (!this) { + return new Y(a); } const b = a.document || a.doc || a; var c, d; - this.M = []; + this.I = []; this.field = []; - this.W = []; - this.key = (c = b.key || b.id) && Oa(c, this.W) || "id"; + this.N = []; + this.key = (c = b.key || b.id) && Va(c, this.N) || "id"; (d = a.keystore || 0) && (this.keystore = d); this.A = (this.fastupdate = !!a.fastupdate) ? d ? new L(d) : new Map() : d ? new M(d) : new Set(); - this.H = (c = b.store || null) && !0 !== c && []; + this.D = (c = b.store || null) && !0 !== c && []; this.store = c && (d ? new L(d) : new Map()); - this.cache = (c = a.cache || null) && new F(c); + this.cache = (c = a.cache || null) && new H(c); a.cache = !1; this.worker = a.worker; this.async = !1; @@ -1827,19 +1855,19 @@ function Ma(a) { B(f) || (g = f, f = f.field); g = C(g) ? Object.assign({}, a, g) : a; if (this.worker) { - const h = new V(g); + const h = new W(g); c.set(f, h); h.worker || (this.worker = !1); } this.worker || c.set(f, new O(g, this.A)); - g.S ? this.M[e] = g.S : (this.M[e] = Oa(f, this.W), g.filter && ("string" === typeof this.M[e] && (this.M[e] = new String(this.M[e])), this.M[e].T = g.filter)); + g.custom ? this.I[e] = g.custom : (this.I[e] = Va(f, this.N), g.filter && ("string" === typeof this.I[e] && (this.I[e] = new String(this.I[e])), this.I[e].L = g.filter)); this.field[e] = f; } - if (this.H) { + if (this.D) { d = b.store; B(d) && (d = [d]); for (let e = 0, f, g; e < d.length; e++) { - f = d[e], g = f.field || f, f.S ? (this.H[e] = f.S, f.S.ia = g) : (this.H[e] = Oa(g, this.W), f.filter && ("string" === typeof this.H[e] && (this.H[e] = new String(this.H[e])), this.H[e].T = f.filter)); + f = d[e], g = f.field || f, f.custom ? (this.D[e] = f.custom, f.custom.X = g) : (this.D[e] = Va(g, this.N), f.filter && ("string" === typeof this.D[e] && (this.D[e] = new String(this.D[e])), this.D[e].L = f.filter)); } } this.index = c; @@ -1847,28 +1875,28 @@ function Ma(a) { if (c = b.tag) { if ("string" === typeof c && (c = [c]), c.length) { this.tag = new Map(); - this.L = []; - this.aa = []; + this.H = []; + this.R = []; for (let e = 0, f, g; e < c.length; e++) { f = c[e]; g = f.field || f; if (!g) { throw Error("The tag field from the document descriptor is undefined."); } - f.S ? this.L[e] = f.S : (this.L[e] = Oa(g, this.W), f.filter && ("string" === typeof this.L[e] && (this.L[e] = new String(this.L[e])), this.L[e].T = f.filter)); - this.aa[e] = g; + f.custom ? this.H[e] = f.custom : (this.H[e] = Va(g, this.N), f.filter && ("string" === typeof this.H[e] && (this.H[e] = new String(this.H[e])), this.H[e].L = f.filter)); + this.R[e] = g; this.tag.set(g, new Map()); } } } a.db && this.mount(a.db); } -u = X.prototype; +u = Y.prototype; u.mount = function(a) { let b = this.field; if (this.tag) { - for (let e = 0, f; e < this.aa.length; e++) { - f = this.aa[e]; + for (let e = 0, f; e < this.R.length; e++) { + f = this.R[e]; var c = this.index.get(f); c || (this.index.set(f, c = new O({}, this.A)), b === this.field && (b = b.slice(0)), b.push(f)); c.tag = this.tag.get(f); @@ -1883,7 +1911,7 @@ u.mount = function(a) { h.id = a.id; c[e] = h.mount(f); f.document = !0; - e ? f.ja = !0 : f.store = this.store; + e ? f.Y = !0 : f.store = this.store; } this.db = this.async = !0; return Promise.all(c); @@ -1896,7 +1924,7 @@ u.commit = async function(a, b) { await Promise.all(c); this.A.clear(); }; -function Oa(a, b) { +function Va(a, b) { const c = a.split(":"); let d = 0; for (let e = 0; e < c.length; e++) { @@ -1991,7 +2019,7 @@ u.export = function(a, b, c, d, e, f) { f(); return; } - qa(a, this, c, b, d, e, h, f); + za(a, this, c, b, d, e, h, f); } return g; }; @@ -2019,35 +2047,35 @@ u.import = function(a, b) { } } }; -ta(X.prototype); -const Pa = "undefined" !== typeof window && (window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB), Qa = ["map", "ctx", "tag", "reg", "cfg"]; -function Ra(a, b = {}) { - if (!(this instanceof Ra)) { - return new Ra(a, b); +Ba(Y.prototype); +const Wa = "undefined" !== typeof window && (window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB), Xa = ["map", "ctx", "tag", "reg", "cfg"]; +function Ya(a, b = {}) { + if (!this) { + return new Ya(a, b); } "object" === typeof a && (b = a = a.name); a || console.info("Default storage space was used, because a name was not passed."); this.id = "flexsearch" + (a ? ":" + a.toLowerCase().replace(/[^a-z0-9_\-]/g, "") : ""); this.field = b.field ? b.field.toLowerCase().replace(/[^a-z0-9_\-]/g, "") : ""; - this.la = !1; + this.Z = !1; this.db = null; this.h = {}; } -u = Ra.prototype; +u = Ya.prototype; u.mount = function(a) { - if (a instanceof X) { + if (a.constructor === Y) { return a.mount(this); } a.db = this; - return Sa(this); + return $a(this); }; -function Sa(a) { +function $a(a) { navigator.storage && navigator.storage.persist(); return a.db || new Promise(function(b, c) { - const d = Pa.open(a.id + (a.field ? ":" + a.field : ""), 1); + const d = Wa.open(a.id + (a.field ? ":" + a.field : ""), 1); d.onupgradeneeded = function() { const e = a.db = this.result; - Qa.forEach(f => { + Xa.forEach(f => { e.objectStoreNames.contains(f) || e.createObjectStore(f); }); }; @@ -2073,16 +2101,16 @@ u.close = function() { this.db = null; }; u.clear = function() { - const a = this.db.transaction(Qa, "readwrite"); - for (let b = 0; b < Qa.length; b++) { - a.objectStore(Qa[b]).clear(); + const a = this.db.transaction(Xa, "readwrite"); + for (let b = 0; b < Xa.length; b++) { + a.objectStore(Xa[b]).clear(); } - return Y(a); + return Z(a); }; u.get = function(a, b, c = 0, d = 0, e = !0, f = !1) { a = this.db.transaction(b ? "ctx" : "map", "readonly").objectStore(b ? "ctx" : "map").get(b ? b + ":" + a : a); const g = this; - return Y(a).then(function(h) { + return Z(a).then(function(h) { let k = []; if (!h || !h.length) { return k; @@ -2115,7 +2143,7 @@ u.get = function(a, b, c = 0, d = 0, e = !0, f = !1) { u.tag = function(a, b = 0, c = 0, d = !1) { a = this.db.transaction("tag", "readonly").objectStore("tag").get(a); const e = this; - return Y(a).then(function(f) { + return Z(a).then(function(f) { if (!f || !f.length || c >= f.length) { return []; } @@ -2130,7 +2158,7 @@ u.enrich = function(a) { "object" !== typeof a && (a = [a]); const b = this.db.transaction("reg", "readonly").objectStore("reg"), c = []; for (let d = 0; d < a.length; d++) { - c[d] = Y(b.get(a[d])); + c[d] = Z(b.get(a[d])); } return Promise.all(c).then(function(d) { for (let e = 0; e < d.length; e++) { @@ -2141,7 +2169,7 @@ u.enrich = function(a) { }; u.has = function(a) { a = this.db.transaction("reg", "readonly").objectStore("reg").getKey(a); - return Y(a); + return Z(a); }; u.search = null; u.info = function() { @@ -2169,17 +2197,17 @@ u.transaction = function(a, b, c) { }; u.commit = async function(a, b, c) { if (b) { - await this.clear(), a.R = []; + await this.clear(), a.K = []; } else { - let d = a.R; - a.R = []; + let d = a.K; + a.K = []; for (let e = 0, f; e < d.length; e++) { if (f = d[e], f.clear) { await this.clear(); b = !0; break; } else { - d[e] = f.ma; + d[e] = f.$; } } b || (c || (d = d.concat(ba(a.A))), d.length && await this.remove(d)); @@ -2211,7 +2239,7 @@ u.commit = async function(a, b, c) { }); } }), await this.transaction("ctx", "readwrite", function(d) { - for (const e of a.I) { + for (const e of a.F) { const f = e[0], g = e[1]; for (const h of g) { const k = h[0], l = h[1]; @@ -2244,7 +2272,7 @@ u.commit = async function(a, b, c) { const f = e[0], g = e[1]; d.put("object" === typeof g ? JSON.stringify(g) : 1, f); } - }) : a.ja || await this.transaction("reg", "readwrite", function(d) { + }) : a.Y || await this.transaction("reg", "readwrite", function(d) { for (const e of a.A.keys()) { d.put(1, e); } @@ -2257,9 +2285,9 @@ u.commit = async function(a, b, c) { d.put(h, f); }); } - }), a.map.clear(), a.I.clear(), a.tag && a.tag.clear(), a.store && a.store.clear(), a.document || a.A.clear()); + }), a.map.clear(), a.F.clear(), a.tag && a.tag.clear(), a.store && a.store.clear(), a.document || a.A.clear()); }; -function Ta(a, b, c) { +function ab(a, b, c) { const d = a.value; let e, f, g = 0; for (let h = 0, k; h < d.length; h++) { @@ -2288,17 +2316,17 @@ u.remove = function(a) { return Promise.all([this.transaction("map", "readwrite", function(b) { b.openCursor().onsuccess = function() { const c = this.result; - c && Ta(c, a); + c && ab(c, a); }; }), this.transaction("ctx", "readwrite", function(b) { b.openCursor().onsuccess = function() { const c = this.result; - c && Ta(c, a); + c && ab(c, a); }; }), this.transaction("tag", "readwrite", function(b) { b.openCursor().onsuccess = function() { const c = this.result; - c && Ta(c, a, !0); + c && ab(c, a, !0); }; }), this.transaction("reg", "readwrite", function(b) { for (let c = 0; c < a.length; c++) { @@ -2306,7 +2334,7 @@ u.remove = function(a) { } })]); }; -function Y(a) { +function Z(a) { return new Promise((b, c) => { a.onsuccess = function() { b(this.result); @@ -2318,28 +2346,6 @@ function Y(a) { a = null; }); } -;const Z = z(); -const Ua = new Map([["b", "p"], ["v", "f"], ["w", "f"], ["z", "s"], ["x", "s"], ["d", "t"], ["n", "m"], ["c", "k"], ["g", "k"], ["j", "k"], ["q", "k"], ["i", "e"], ["y", "e"], ["u", "o"]]); -var Va = {normalize:!0, C:!0, G:Ua}; -const Xa = new Map([["ai", "ei"], ["ae", "a"], ["oe", "o"], ["ue", "u"], ["sh", "s"], ["ch", "c"], ["th", "t"], ["ph", "f"], ["pf", "f"]]), Ya = [/([^aeo])h([aeo$])/g, "$1$2", /([aeo])h([^aeo]|$)/g, "$1$2"]; -var Za = {normalize:!0, C:!0, G:Ua, K:Ya, J:Xa}; -var $a = {normalize:!0, C:!0, G:Ua, K:Ya.concat([/(?!^)[aeoy]/g, ""]), J:Xa}; -const ab = {a:"", e:"", i:"", o:"", u:"", y:"", b:1, f:1, p:1, v:1, c:2, g:2, j:2, k:2, q:2, s:2, x:2, z:2, "\u00df":2, d:3, t:3, l:4, m:5, n:5, r:6}; -Z["latin:exact"] = {normalize:!1, C:!1}; -Z["latin:default"] = ra; -Z["latin:simple"] = {normalize:!0, C:!0}; -Z["latin:balance"] = Va; -Z["latin:advanced"] = Za; -Z["latin:extra"] = $a; -Z["latin:soundex"] = {normalize:!0, C:!1, ga:{ka:!0}, V:function(a) { - for (let c = 0; c < a.length; c++) { - var b = a[c]; - let d = b.charAt(0), e = ab[d]; - for (let f = 1, g; f < b.length && (g = b.charAt(f), "h" === g || "w" === g || !(g = ab[g]) || g === e || (d += g, e = g, 4 !== d.length)); f++) { - } - a[c] = d; - } -}}; -export default {Index:O, Charset:Z, Encoder:E, Document:X, Worker:V, Resolver:S, IndexedDB:Ra}; +;export default {Index:O, Charset:va, Encoder:G, Document:Y, Worker:W, Resolver:S, IndexedDB:Ya, Language:{}}; -export const Index=O;export const Charset=Z;export const Encoder=E;export const Document=X;export const Worker=V;export const Resolver=S;export const IndexedDB=Ra; \ No newline at end of file +export const Index=O;export const Charset=va;export const Encoder=G;export const Document=Y;export const Worker=W;export const Resolver=S;export const IndexedDB=Ya;export const Language={}; \ No newline at end of file diff --git a/dist/flexsearch.bundle.module.min.js b/dist/flexsearch.bundle.module.min.js index 54c62df..eabd2a5 100644 --- a/dist/flexsearch.bundle.module.min.js +++ b/dist/flexsearch.bundle.module.min.js @@ -6,84 +6,84 @@ * https://github.com/nextapps-de/flexsearch */ var u;function y(a,b,c){const d=typeof c,e=typeof a;if("undefined"!==d){if("undefined"!==e){if(c){if("function"===e&&d===e)return function(h){return a(c(h))};b=a.constructor;if(b===c.constructor){if(b===Array)return c.concat(a);if(b===Map){var f=new Map(c);for(var g of a)f.set(g[0],g[1]);return f}if(b===Set){g=new Set(c);for(f of a.values())g.add(f);return g}}}return a}return c}return"undefined"===e?b:a}function z(){return Object.create(null)}function aa(a,b){return b.length-a.length} -function B(a){return"string"===typeof a}function C(a){return"object"===typeof a}function ba(a){const b=[];for(const c of a.keys())b.push(c);return b}function ca(a,b){if(B(b))a=a[b];else for(let c=0;a&&cthis.N.get(l)),k=1);this.J&&1this.J.get(l)),k=1);g&&k&&(g.lengththis.ea&&(this.U.clear(),this.B=this.B/1.1|0));g&&c.push(g)}this.V&&(c= -this.V(c)||c);this.cache&&a.length<=this.h&&(this.P.set(a,c),this.P.size>this.ea&&(this.P.clear(),this.h=this.h/1.1|0));return c};function la(a){a.X=null;a.P.clear();a.U.clear()};function ma(a,b,c){a=("object"===typeof a?""+a.query:a).toLowerCase();let d=this.cache.get(a);if(!d){d=this.search(a,b,c);if(d instanceof Promise){const e=this;d.then(function(f){e.cache.set(a,f)})}this.cache.set(a,d)}return d}function F(a){this.limit=a&&!0!==a?a:1E3;this.cache=new Map;this.h=""}F.prototype.set=function(a,b){this.cache.has(a)||(this.cache.set(this.h=a,b),this.limit&&this.cache.size>this.limit&&this.cache.delete(this.cache.keys().next().value))}; -F.prototype.get=function(a){const b=this.cache.get(a);b&&this.limit&&this.h!==a&&(this.cache.delete(a),this.cache.set(this.h=a,b));return b};F.prototype.remove=function(a){for(const b of this.cache){const c=b[0];b[1].includes(a)&&this.cache.delete(c)}};F.prototype.clear=function(){this.cache.clear();this.h=""};function na(a,b,c,d){let e=[];for(let f=0,g;f=g.length)b-=g.length;else{b=g[d?"splice":"slice"](b,c);const h=b.length;if(h&&(e=e.length?e.concat(b):b,c-=h,d&&(a.length-=h),!c))break;b=0}return e} -function H(a){if(!(this instanceof H))return new H(a);this.index=a?[a]:[];this.length=a?a.length:0;const b=this;return new Proxy([],{get(c,d){if("length"===d)return b.length;if("push"===d)return function(e){b.index[b.index.length-1].push(e);b.length++};if("pop"===d)return function(){if(b.length)return b.length--,b.index[b.index.length-1].pop()};if("indexOf"===d)return function(e){let f=0;for(let g=0,h,k;gf;h--){g=p.substring(f,h);var k=this.score?this.score(b,p,t,g,f):P(q,d,t,e,f);Q(this,m,g,k,a,c)}break}case "reverse":if(1< -e){for(h=e-1;0g?0:1),d,t,h-1,k-1),x=this.bidirectional&&p>f;Q(this,l,x?f:p,r,a,c,x?p:f)}}}}this.fastupdate||this.A.add(a)}else b=""}this.db&& -(b||this.R.push({del:a}),this.fa&&ua(this));return this};function Q(a,b,c,d,e,f,g){let h=g?a.I:a.map,k;if(!b[c]||!g||!(k=b[c])[g])if(g?(b=k||(b[c]=z()),b[g]=1,(k=h.get(g))?h=k:h.set(g,h=new Map)):b[c]=1,(k=h.get(c))?h=k:h.set(c,h=k=[]),h=h[d]||(h[d]=[]),!f||!h.includes(e)){if(h.length===2**31-1){b=new H(h);if(a.fastupdate)for(let l of a.A.values())l.includes(h)&&(l[l.indexOf(h)]=b);k[d]=h=b}h.push(e);a.fastupdate&&((d=a.A.get(e))?d.push(h):a.A.set(e,[h]))}} -function P(a,b,c,d,e){return c&&1b?b?a.slice(c,c+b):a.slice(c):a,d?va(a):a;let e=[];for(let f=0,g,h;f=h){c-=h;continue}cb&&(g=g.slice(0,b),h=g.length),e.push(g);else{if(h>=b)return h>b&&(g=g.slice(0,b)),d?va(g):g;e=[g]}b-=h;if(!b)break}if(!e.length)return e;e=1a.length)return e?R(a[0],b,c,d):a[0];d=[];let g=0,h=z(),k=da(a);for(let l=0,m;la.length)return[];let f=[],g=0,h=z(),k=da(a);if(!k)return f;for(let l=0,m;la.length)return e?R(a[0],b,c,d):a[0];b=[];c=z();for(let g=0,h;g=e)))break;if(h.length){if(g)return R(h,e,0);b.push(h);return}}return!c&&h}function U(a,b,c,d,e,f,g,h){let k;c&&(k=a.bidirectional&&b>c);if(a.db)return c?a.db.get(k?c:b,k?b:c,d,e,f,g,h):a.db.get(b,"",d,e,f,g,h);a=c?(a=a.I.get(k?b:c))&&a.get(k?c:b):a.map.get(b);return a};O.prototype.remove=function(a,b){const c=this.A.size&&(this.fastupdate?this.A.get(a):this.A.has(a));if(c){if(this.fastupdate)for(let d=0,e;de.length)e.pop();else{const f=e.indexOf(a);f===c.length-1?e.pop():e.splice(f,1)}}else Ea(this.map,a),this.depth&&Ea(this.I,a);b||this.A.delete(a)}this.db&&(this.R.push({del:a}),this.fa&&ua(this));this.cache&&this.cache.remove(a);return this}; -function Ea(a,b){let c=0;if(a.constructor===Array)for(let d=0,e,f;dc.add(a,b)):this.add(a,b)}return this.remove(a).add(a,b)};function Fa(a){let b=0;if(a.constructor===Array)for(let c=0,d;c{f=l}));let h,k;switch(e||(e=0)){case 0:h="reg";if(this.fastupdate){k=z();for(let l of this.A.keys())k[l]=1}else k=this.A;break;case 1:h="cfg";k={doc:0,opt:this.B?1:0};break;case 2:h="map";k=this.map;break;case 3:h="ctx";k=this.I;break;default:"undefined"===typeof c&&f&&f();return}qa(a,b||this,c,h,d,e,k,f);return g}; -u.import=function(a,b){if(b)switch(B(b)&&(b=JSON.parse(b)),a){case "cfg":this.B=!!b.opt;break;case "reg":this.fastupdate=!1;this.A=b;break;case "map":this.map=b;break;case "ctx":this.I=b}};ta(O.prototype);async function Ga(a){a=a.data;var b=self._index;const c=a.args;var d=a.task;switch(d){case "init":d=a.options||{};(b=d.config)&&(d=await import(b));(b=a.factory)?(Function("return "+b)()(self),self._index=new self.FlexSearch.Index(d),delete self.FlexSearch):self._index=new O(d);postMessage({id:a.id});break;default:a=a.id,b=b[d].apply(b,c),postMessage("search"===d?{id:a,msg:b}:{id:a})}};let Ha=0; -function V(a){function b(f){f=f.data||f;const g=f.id,h=g&&e.h[g];h&&(h(f.msg),delete e.h[g])}if(!(this instanceof V))return new V(a);a||(a={});let c=(self||window)._factory;c&&(c=c.toString());const d="undefined"===typeof window&&self.exports,e=this;this.worker=Ia(c,d,a.worker);this.h=z();if(this.worker){d?this.worker.on("message",b):this.worker.onmessage=b;if(a.config)return new Promise(function(f){e.h[++Ha]=function(){f(e)};e.worker.postMessage({id:Ha,task:"init",factory:c,options:a})});this.worker.postMessage({task:"init", -factory:c,options:a})}}W("add");W("append");W("search");W("update");W("remove");function W(a){V.prototype[a]=V.prototype[a+"Async"]=function(){const b=this,c=[].slice.call(arguments);var d=c[c.length-1];let e;"function"===typeof d&&(e=d,c.splice(c.length-1,1));d=new Promise(function(f){b.h[++Ha]=f;b.worker.postMessage({task:a,id:Ha,args:c})});return e?(d.then(e),this):d}} -function Ia(a,b,c){return b?new (require("worker_threads")["Worker"])(__dirname + "/node/node.js"):a?new window.Worker(URL.createObjectURL(new Blob(["onmessage="+Ga.toString()],{type:"text/javascript"}))):new window.Worker(B(c)?c:"worker/worker.js",{type:"module"})};X.prototype.add=function(a,b,c){C(a)&&(b=a,a=ca(b,this.key));if(b&&(a||0===a)){if(!c&&this.A.has(a))return this.update(a,b);for(let h=0,k;hc||d)a=a.slice(d,d+c);e&&(a=Ma.call(this,a));return a}} -function Ma(a){const b=Array(a.length);for(let c=0,d;cthis.stemmer.get(l)),k=1);this.matcher&& +1this.matcher.get(l)),k=1);g&&k&&(g.lengththis.V&&(this.M.clear(),this.B=this.B/1.1|0));g&&c.push(g)}this.finalize&&(c=this.finalize(c)||c);this.cache&&a.length<=this.h&&(this.J.set(a,c),this.J.size>this.V&&(this.J.clear(),this.h=this.h/1.1|0));return c};function la(a){a.O=null;a.J.clear();a.M.clear()};function ma(a,b,c){a=("object"===typeof a?""+a.query:a).toLowerCase();let d=this.cache.get(a);if(!d){d=this.search(a,b,c);if(d.then){const e=this;d.then(function(f){e.cache.set(a,f);return f})}this.cache.set(a,d)}return d}function G(a){this.limit=a&&!0!==a?a:1E3;this.cache=new Map;this.h=""}G.prototype.set=function(a,b){this.cache.set(this.h=a,b);this.cache.size>this.limit&&this.cache.delete(this.cache.keys().next().value)}; +G.prototype.get=function(a){const b=this.cache.get(a);b&&this.h!==a&&(this.cache.delete(a),this.cache.set(this.h=a,b));return b};G.prototype.remove=function(a){for(const b of this.cache){const c=b[0];b[1].includes(a)&&this.cache.delete(c)}};G.prototype.clear=function(){this.cache.clear();this.h=""};const na={normalize:function(a){return a.toLowerCase()},dedupe:!1};const oa=new Map([["b","p"],["v","f"],["w","f"],["z","s"],["x","s"],["d","t"],["n","m"],["c","k"],["g","k"],["j","k"],["q","k"],["i","e"],["y","e"],["u","o"]]);const pa=new Map([["ai","ei"],["ae","a"],["oe","o"],["ue","u"],["sh","s"],["ch","c"],["th","t"],["ph","f"],["pf","f"]]),qa=[/([^aeo])h([aeo$])/g,"$1$2",/([aeo])h([^aeo]|$)/g,"$1$2"];const ra={a:"",e:"",i:"",o:"",u:"",y:"",b:1,f:1,p:1,v:1,c:2,g:2,j:2,k:2,q:2,s:2,x:2,z:2,"\u00df":2,d:3,t:3,l:4,m:5,n:5,r:6};const sa=/[\x00-\x7F]+/g;const ta=/[\x00-\x7F]+/g;const ua=/[\x00-\x7F]+/g;var va={LatinExact:{normalize:!1,dedupe:!1},LatinDefault:na,LatinSimple:{normalize:!0,dedupe:!0},LatinBalance:{normalize:!0,dedupe:!0,mapper:oa},LatinAdvanced:{normalize:!0,dedupe:!0,mapper:oa,replacer:qa,matcher:pa},LatinExtra:{normalize:!0,dedupe:!0,mapper:oa,replacer:qa.concat([/(?!^)[aeoy]/g,""]),matcher:pa},LatinSoundex:{normalize:!0,dedupe:!1,include:{letter:!0},finalize:function(a){for(let c=0;c=g.length)b-=g.length;else{b=g[d?"splice":"slice"](b,c);const h=b.length;if(h&&(e=e.length?e.concat(b):b,c-=h,d&&(a.length-=h),!c))break;b=0}return e} +function J(a){if(!this)return new J(a);this.index=a?[a]:[];this.length=a?a.length:0;const b=this;return new Proxy([],{get(c,d){if("length"===d)return b.length;if("push"===d)return function(e){b.index[b.index.length-1].push(e);b.length++};if("pop"===d)return function(){if(b.length)return b.length--,b.index[b.index.length-1].pop()};if("indexOf"===d)return function(e){let f=0;for(let g=0,h,k;gf;h--){g=p.substring(f,h);var k=this.score?this.score(b,p,t,g,f):P(q,d,t,e,f);Q(this,m,g,k,a,c)}break}case "reverse":if(1< +e){for(h=e-1;0g?0:1),d,t,h-1,k-1),x=this.bidirectional&&p>f;Q(this,l,x?f:p,r,a,c,x?p:f)}}}}this.fastupdate||this.A.add(a)}else b=""}this.db&&(b|| +this.K.push({del:a}),this.W&&Ca(this));return this};function Q(a,b,c,d,e,f,g){let h=g?a.F:a.map,k;if(!b[c]||!g||!(k=b[c])[g])if(g?(b=k||(b[c]=z()),b[g]=1,(k=h.get(g))?h=k:h.set(g,h=new Map)):b[c]=1,(k=h.get(c))?h=k:h.set(c,h=k=[]),h=h[d]||(h[d]=[]),!f||!h.includes(e)){if(h.length===2**31-1){b=new J(h);if(a.fastupdate)for(let l of a.A.values())l.includes(h)&&(l[l.indexOf(h)]=b);k[d]=h=b}h.push(e);a.fastupdate&&((d=a.A.get(e))?d.push(h):a.A.set(e,[h]))}} +function P(a,b,c,d,e){return c&&1b?b?a.slice(c,c+b):a.slice(c):a,d?Da(a):a;let e=[];for(let f=0,g,h;f=h){c-=h;continue}cb&&(g=g.slice(0,b),h=g.length),e.push(g);else{if(h>=b)return h>b&&(g=g.slice(0,b)),d?Da(g):g;e=[g]}b-=h;if(!b)break}if(!e.length)return e;e=1a.length)return e?R(a[0],b,c,d):a[0];d=[];let g=0,h=z(),k=da(a);for(let l=0,m;la.length)return[];let f=[],g=0,h=z(),k=da(a);if(!k)return f;for(let l=0,m;la.length)return e?R(a[0],b,c,d):a[0];b=[];c=z();for(let g=0,h;g=e)))break;if(h.length){if(g)return R(h,e,0);b.push(h);return}}return!c&&h}function V(a,b,c,d,e,f,g,h){let k;c&&(k=a.bidirectional&&b>c);if(a.db)return c?a.db.get(k?c:b,k?b:c,d,e,f,g,h):a.db.get(b,"",d,e,f,g,h);a=c?(a=a.F.get(k?b:c))&&a.get(k?c:b):a.map.get(b);return a};O.prototype.remove=function(a,b){const c=this.A.size&&(this.fastupdate?this.A.get(a):this.A.has(a));if(c){if(this.fastupdate)for(let d=0,e;de.length)e.pop();else{const f=e.indexOf(a);f===c.length-1?e.pop():e.splice(f,1)}}else La(this.map,a),this.depth&&La(this.F,a);b||this.A.delete(a)}this.db&&(this.K.push({del:a}),this.W&&Ca(this));this.cache&&this.cache.remove(a);return this}; +function La(a,b){let c=0;if(a.constructor===Array)for(let d=0,e,f;dc.add(a,b)):this.add(a,b)}return this.remove(a).add(a,b)};function Ma(a){let b=0;if(a.constructor===Array)for(let c=0,d;c{f=l}));let h,k;switch(e||(e=0)){case 0:h="reg";if(this.fastupdate){k=z();for(let l of this.A.keys())k[l]=1}else k=this.A;break;case 1:h="cfg";k={doc:0,opt:this.B?1:0};break;case 2:h="map";k=this.map;break;case 3:h="ctx";k=this.F;break;default:"undefined"===typeof c&&f&&f();return}za(a,b||this,c,h,d,e,k,f);return g}; +u.import=function(a,b){if(b)switch(B(b)&&(b=JSON.parse(b)),a){case "cfg":this.B=!!b.opt;break;case "reg":this.fastupdate=!1;this.A=b;break;case "map":this.map=b;break;case "ctx":this.F=b}};Ba(O.prototype);async function Na(a){a=a.data;var b=self._index;const c=a.args;var d=a.task;switch(d){case "init":d=a.options||{};(b=d.config)&&(d=await import(b));(b=a.factory)?(Function("return "+b)()(self),self._index=new self.FlexSearch.Index(d),delete self.FlexSearch):self._index=new O(d);postMessage({id:a.id});break;default:a=a.id,b=b[d].apply(b,c),postMessage("search"===d?{id:a,msg:b}:{id:a})}};let Oa=0; +function W(a){function b(f){f=f.data||f;const g=f.id,h=g&&e.h[g];h&&(h(f.msg),delete e.h[g])}if(!this)return new W(a);a||(a={});let c=(self||window)._factory;c&&(c=c.toString());const d="undefined"===typeof window&&self.exports,e=this;this.worker=Pa(c,d,a.worker);this.h=z();if(this.worker){d?this.worker.on("message",b):this.worker.onmessage=b;if(a.config)return new Promise(function(f){e.h[++Oa]=function(){f(e)};e.worker.postMessage({id:Oa,task:"init",factory:c,options:a})});this.worker.postMessage({task:"init",factory:c, +options:a})}}X("add");X("append");X("search");X("update");X("remove");function X(a){W.prototype[a]=W.prototype[a+"Async"]=function(){const b=this,c=[].slice.call(arguments);var d=c[c.length-1];let e;"function"===typeof d&&(e=d,c.splice(c.length-1,1));d=new Promise(function(f){b.h[++Oa]=f;b.worker.postMessage({task:a,id:Oa,args:c})});return e?(d.then(e),this):d}} +function Pa(a,b,c){return b?new (require("worker_threads")["Worker"])(__dirname + "/node/node.js"):a?new window.Worker(URL.createObjectURL(new Blob(["onmessage="+Na.toString()],{type:"text/javascript"}))):new window.Worker(B(c)?c:"worker/worker.js",{type:"module"})};Y.prototype.add=function(a,b,c){C(a)&&(b=a,a=ca(b,this.key));if(b&&(a||0===a)){if(!c&&this.A.has(a))return this.update(a,b);for(let h=0,k;hc||d)a=a.slice(d,d+c);e&&(a=Ta.call(this,a));return a}} +function Ta(a){const b=Array(a.length);for(let c=0,d;c{f=k}));e||(e=0);d||(d=0);if(d{e.objectStoreNames.contains(f)||e.createObjectStore(f)})};d.onblocked=function(e){console.error("blocked",e);c()};d.onerror=function(e){console.error(this.error,e);c()};d.onsuccess=function(){a.db=this.result;a.db.onversionchange=function(){a.close()};b(a)}})} -u.close=function(){this.db.close();this.db=null};u.clear=function(){const a=this.db.transaction(Qa,"readwrite");for(let b=0;b=m.length){d-=m.length;continue}const n=c?d+Math.min(m.length-d,c):m.length;for(let q=d;q=f.length)return[];if(!b&&!c)return f;f=f.slice(c,c+b);return d?e.enrich(f):f})}; -u.enrich=function(a){"object"!==typeof a&&(a=[a]);const b=this.db.transaction("reg","readonly").objectStore("reg"),c=[];for(let d=0;d{f=k}));e||(e=0);d||(d=0);if(d{e.objectStoreNames.contains(f)||e.createObjectStore(f)})};d.onblocked=function(e){console.error("blocked",e);c()};d.onerror=function(e){console.error(this.error,e);c()};d.onsuccess=function(){a.db=this.result;a.db.onversionchange=function(){a.close()};b(a)}})} +u.close=function(){this.db.close();this.db=null};u.clear=function(){const a=this.db.transaction(Xa,"readwrite");for(let b=0;b=m.length){d-=m.length;continue}const n=c?d+Math.min(m.length-d,c):m.length;for(let q=d;q=f.length)return[];if(!b&&!c)return f;f=f.slice(c,c+b);return d?e.enrich(f):f})}; +u.enrich=function(a){"object"!==typeof a&&(a=[a]);const b=this.db.transaction("reg","readonly").objectStore("reg"),c=[];for(let d=0;d{e.onerror=h=>{this.h[a+":"+b]=null;e.abort();e=d=null;g(h)};e.oncomplete=h=>{e=d=this.h[a+":"+b]=null;f(h||!0)};return c.call(this,d)})}; -u.commit=async function(a,b,c){if(b)await this.clear(),a.R=[];else{let d=a.R;a.R=[];for(let e=0,f;em&&!f&&"string"===typeof n&&!isNaN(n)&&(m=k.indexOf(parseInt(n,10)))&&(f=1),0<=m)if(e=1,1{a.onsuccess=function(){b(this.result)};a.oncomplete=function(){b(this.result)};a.onerror=c;a=null})};const Z=z();const Ua=new Map([["b","p"],["v","f"],["w","f"],["z","s"],["x","s"],["d","t"],["n","m"],["c","k"],["g","k"],["j","k"],["q","k"],["i","e"],["y","e"],["u","o"]]);var Va={normalize:!0,C:!0,G:Ua};const Xa=new Map([["ai","ei"],["ae","a"],["oe","o"],["ue","u"],["sh","s"],["ch","c"],["th","t"],["ph","f"],["pf","f"]]),Ya=[/([^aeo])h([aeo$])/g,"$1$2",/([aeo])h([^aeo]|$)/g,"$1$2"];var Za={normalize:!0,C:!0,G:Ua,K:Ya,J:Xa};var $a={normalize:!0,C:!0,G:Ua,K:Ya.concat([/(?!^)[aeoy]/g,""]),J:Xa};const ab={a:"",e:"",i:"",o:"",u:"",y:"",b:1,f:1,p:1,v:1,c:2,g:2,j:2,k:2,q:2,s:2,x:2,z:2,"\u00df":2,d:3,t:3,l:4,m:5,n:5,r:6};Z["latin:exact"]={normalize:!1,C:!1};Z["latin:default"]=ra;Z["latin:simple"]={normalize:!0,C:!0};Z["latin:balance"]=Va;Z["latin:advanced"]=Za;Z["latin:extra"]=$a;Z["latin:soundex"]={normalize:!0,C:!1,ga:{ka:!0},V:function(a){for(let c=0;cm&&!f&&"string"===typeof n&&!isNaN(n)&&(m=k.indexOf(parseInt(n,10)))&&(f=1),0<=m)if(e=1,1{a.onsuccess=function(){b(this.result)};a.oncomplete=function(){b(this.result)};a.onerror=c;a=null})};export default {Index:O,Charset:va,Encoder:F,Document:Y,Worker:W,Resolver:S,IndexedDB:Ya,Language:{}}; +export const Index=O;export const Charset=va;export const Encoder=F;export const Document=Y;export const Worker=W;export const Resolver=S;export const IndexedDB=Ya;export const Language={}; \ No newline at end of file diff --git a/dist/flexsearch.compact.debug.js b/dist/flexsearch.compact.debug.js index 23f24b0..2fff863 100644 --- a/dist/flexsearch.compact.debug.js +++ b/dist/flexsearch.compact.debug.js @@ -66,32 +66,33 @@ function F(a, c) { } return a; } -;const ba = /[^\p{L}\p{N}]+/u, ca = /(\d{3})/g, da = /(\D)(\d{3})/g, ea = /(\d{3})(\D)/g, G = "".normalize && /[\u0300-\u036f]/g, fa = !G && [["\u00aa", "a"], ["\u00b2", "2"], ["\u00b3", "3"], ["\u00b9", "1"], ["\u00ba", "o"], ["\u00bc", "1\u20444"], ["\u00bd", "1\u20442"], ["\u00be", "3\u20444"], ["\u00e0", "a"], ["\u00e1", "a"], ["\u00e2", "a"], ["\u00e3", "a"], ["\u00e4", "a"], ["\u00e5", "a"], ["\u00e7", "c"], ["\u00e8", "e"], ["\u00e9", "e"], ["\u00ea", "e"], ["\u00eb", "e"], ["\u00ec", "i"], -["\u00ed", "i"], ["\u00ee", "i"], ["\u00ef", "i"], ["\u00f1", "n"], ["\u00f2", "o"], ["\u00f3", "o"], ["\u00f4", "o"], ["\u00f5", "o"], ["\u00f6", "o"], ["\u00f9", "u"], ["\u00fa", "u"], ["\u00fb", "u"], ["\u00fc", "u"], ["\u00fd", "y"], ["\u00ff", "y"], ["\u0101", "a"], ["\u0103", "a"], ["\u0105", "a"], ["\u0107", "c"], ["\u0109", "c"], ["\u010b", "c"], ["\u010d", "c"], ["\u010f", "d"], ["\u0113", "e"], ["\u0115", "e"], ["\u0117", "e"], ["\u0119", "e"], ["\u011b", "e"], ["\u011d", "g"], ["\u011f", -"g"], ["\u0121", "g"], ["\u0123", "g"], ["\u0125", "h"], ["\u0129", "i"], ["\u012b", "i"], ["\u012d", "i"], ["\u012f", "i"], ["\u0133", "ij"], ["\u0135", "j"], ["\u0137", "k"], ["\u013a", "l"], ["\u013c", "l"], ["\u013e", "l"], ["\u0140", "l"], ["\u0144", "n"], ["\u0146", "n"], ["\u0148", "n"], ["\u0149", "n"], ["\u014d", "o"], ["\u014f", "o"], ["\u0151", "o"], ["\u0155", "r"], ["\u0157", "r"], ["\u0159", "r"], ["\u015b", "s"], ["\u015d", "s"], ["\u015f", "s"], ["\u0161", "s"], ["\u0163", "t"], ["\u0165", -"t"], ["\u0169", "u"], ["\u016b", "u"], ["\u016d", "u"], ["\u016f", "u"], ["\u0171", "u"], ["\u0173", "u"], ["\u0175", "w"], ["\u0177", "y"], ["\u017a", "z"], ["\u017c", "z"], ["\u017e", "z"], ["\u017f", "s"], ["\u01a1", "o"], ["\u01b0", "u"], ["\u01c6", "dz"], ["\u01c9", "lj"], ["\u01cc", "nj"], ["\u01ce", "a"], ["\u01d0", "i"], ["\u01d2", "o"], ["\u01d4", "u"], ["\u01d6", "u"], ["\u01d8", "u"], ["\u01da", "u"], ["\u01dc", "u"], ["\u01df", "a"], ["\u01e1", "a"], ["\u01e3", "ae"], ["\u00e6", "ae"], -["\u01fd", "ae"], ["\u01e7", "g"], ["\u01e9", "k"], ["\u01eb", "o"], ["\u01ed", "o"], ["\u01ef", "\u0292"], ["\u01f0", "j"], ["\u01f3", "dz"], ["\u01f5", "g"], ["\u01f9", "n"], ["\u01fb", "a"], ["\u01ff", "\u00f8"], ["\u0201", "a"], ["\u0203", "a"], ["\u0205", "e"], ["\u0207", "e"], ["\u0209", "i"], ["\u020b", "i"], ["\u020d", "o"], ["\u020f", "o"], ["\u0211", "r"], ["\u0213", "r"], ["\u0215", "u"], ["\u0217", "u"], ["\u0219", "s"], ["\u021b", "t"], ["\u021f", "h"], ["\u0227", "a"], ["\u0229", "e"], -["\u022b", "o"], ["\u022d", "o"], ["\u022f", "o"], ["\u0231", "o"], ["\u0233", "y"], ["\u02b0", "h"], ["\u02b1", "h"], ["\u0266", "h"], ["\u02b2", "j"], ["\u02b3", "r"], ["\u02b4", "\u0279"], ["\u02b5", "\u027b"], ["\u02b6", "\u0281"], ["\u02b7", "w"], ["\u02b8", "y"], ["\u02e0", "\u0263"], ["\u02e1", "l"], ["\u02e2", "s"], ["\u02e3", "x"], ["\u02e4", "\u0295"], ["\u0390", "\u03b9"], ["\u03ac", "\u03b1"], ["\u03ad", "\u03b5"], ["\u03ae", "\u03b7"], ["\u03af", "\u03b9"], ["\u03b0", "\u03c5"], ["\u03ca", -"\u03b9"], ["\u03cb", "\u03c5"], ["\u03cc", "\u03bf"], ["\u03cd", "\u03c5"], ["\u03ce", "\u03c9"], ["\u03d0", "\u03b2"], ["\u03d1", "\u03b8"], ["\u03d2", "\u03a5"], ["\u03d3", "\u03a5"], ["\u03d4", "\u03a5"], ["\u03d5", "\u03c6"], ["\u03d6", "\u03c0"], ["\u03f0", "\u03ba"], ["\u03f1", "\u03c1"], ["\u03f2", "\u03c2"], ["\u03f5", "\u03b5"], ["\u0439", "\u0438"], ["\u0450", "\u0435"], ["\u0451", "\u0435"], ["\u0453", "\u0433"], ["\u0457", "\u0456"], ["\u045c", "\u043a"], ["\u045d", "\u0438"], ["\u045e", -"\u0443"], ["\u0477", "\u0475"], ["\u04c2", "\u0436"], ["\u04d1", "\u0430"], ["\u04d3", "\u0430"], ["\u04d7", "\u0435"], ["\u04db", "\u04d9"], ["\u04dd", "\u0436"], ["\u04df", "\u0437"], ["\u04e3", "\u0438"], ["\u04e5", "\u0438"], ["\u04e7", "\u043e"], ["\u04eb", "\u04e9"], ["\u04ed", "\u044d"], ["\u04ef", "\u0443"], ["\u04f1", "\u0443"], ["\u04f3", "\u0443"], ["\u04f5", "\u0447"]]; -function H(a = {}) { - if (!(this instanceof H)) { - return new H(...arguments); +;var ba = [["\u00aa", "a"], ["\u00b2", "2"], ["\u00b3", "3"], ["\u00b9", "1"], ["\u00ba", "o"], ["\u00bc", "1\u20444"], ["\u00bd", "1\u20442"], ["\u00be", "3\u20444"], ["\u00e0", "a"], ["\u00e1", "a"], ["\u00e2", "a"], ["\u00e3", "a"], ["\u00e4", "a"], ["\u00e5", "a"], ["\u00e7", "c"], ["\u00e8", "e"], ["\u00e9", "e"], ["\u00ea", "e"], ["\u00eb", "e"], ["\u00ec", "i"], ["\u00ed", "i"], ["\u00ee", "i"], ["\u00ef", "i"], ["\u00f1", "n"], ["\u00f2", "o"], ["\u00f3", "o"], ["\u00f4", "o"], ["\u00f5", +"o"], ["\u00f6", "o"], ["\u00f9", "u"], ["\u00fa", "u"], ["\u00fb", "u"], ["\u00fc", "u"], ["\u00fd", "y"], ["\u00ff", "y"], ["\u0101", "a"], ["\u0103", "a"], ["\u0105", "a"], ["\u0107", "c"], ["\u0109", "c"], ["\u010b", "c"], ["\u010d", "c"], ["\u010f", "d"], ["\u0113", "e"], ["\u0115", "e"], ["\u0117", "e"], ["\u0119", "e"], ["\u011b", "e"], ["\u011d", "g"], ["\u011f", "g"], ["\u0121", "g"], ["\u0123", "g"], ["\u0125", "h"], ["\u0129", "i"], ["\u012b", "i"], ["\u012d", "i"], ["\u012f", "i"], ["\u0133", +"ij"], ["\u0135", "j"], ["\u0137", "k"], ["\u013a", "l"], ["\u013c", "l"], ["\u013e", "l"], ["\u0140", "l"], ["\u0144", "n"], ["\u0146", "n"], ["\u0148", "n"], ["\u0149", "n"], ["\u014d", "o"], ["\u014f", "o"], ["\u0151", "o"], ["\u0155", "r"], ["\u0157", "r"], ["\u0159", "r"], ["\u015b", "s"], ["\u015d", "s"], ["\u015f", "s"], ["\u0161", "s"], ["\u0163", "t"], ["\u0165", "t"], ["\u0169", "u"], ["\u016b", "u"], ["\u016d", "u"], ["\u016f", "u"], ["\u0171", "u"], ["\u0173", "u"], ["\u0175", "w"], ["\u0177", +"y"], ["\u017a", "z"], ["\u017c", "z"], ["\u017e", "z"], ["\u017f", "s"], ["\u01a1", "o"], ["\u01b0", "u"], ["\u01c6", "dz"], ["\u01c9", "lj"], ["\u01cc", "nj"], ["\u01ce", "a"], ["\u01d0", "i"], ["\u01d2", "o"], ["\u01d4", "u"], ["\u01d6", "u"], ["\u01d8", "u"], ["\u01da", "u"], ["\u01dc", "u"], ["\u01df", "a"], ["\u01e1", "a"], ["\u01e3", "ae"], ["\u00e6", "ae"], ["\u01fd", "ae"], ["\u01e7", "g"], ["\u01e9", "k"], ["\u01eb", "o"], ["\u01ed", "o"], ["\u01ef", "\u0292"], ["\u01f0", "j"], ["\u01f3", +"dz"], ["\u01f5", "g"], ["\u01f9", "n"], ["\u01fb", "a"], ["\u01ff", "\u00f8"], ["\u0201", "a"], ["\u0203", "a"], ["\u0205", "e"], ["\u0207", "e"], ["\u0209", "i"], ["\u020b", "i"], ["\u020d", "o"], ["\u020f", "o"], ["\u0211", "r"], ["\u0213", "r"], ["\u0215", "u"], ["\u0217", "u"], ["\u0219", "s"], ["\u021b", "t"], ["\u021f", "h"], ["\u0227", "a"], ["\u0229", "e"], ["\u022b", "o"], ["\u022d", "o"], ["\u022f", "o"], ["\u0231", "o"], ["\u0233", "y"], ["\u02b0", "h"], ["\u02b1", "h"], ["\u0266", "h"], +["\u02b2", "j"], ["\u02b3", "r"], ["\u02b4", "\u0279"], ["\u02b5", "\u027b"], ["\u02b6", "\u0281"], ["\u02b7", "w"], ["\u02b8", "y"], ["\u02e0", "\u0263"], ["\u02e1", "l"], ["\u02e2", "s"], ["\u02e3", "x"], ["\u02e4", "\u0295"], ["\u0390", "\u03b9"], ["\u03ac", "\u03b1"], ["\u03ad", "\u03b5"], ["\u03ae", "\u03b7"], ["\u03af", "\u03b9"], ["\u03b0", "\u03c5"], ["\u03ca", "\u03b9"], ["\u03cb", "\u03c5"], ["\u03cc", "\u03bf"], ["\u03cd", "\u03c5"], ["\u03ce", "\u03c9"], ["\u03d0", "\u03b2"], ["\u03d1", +"\u03b8"], ["\u03d2", "\u03a5"], ["\u03d3", "\u03a5"], ["\u03d4", "\u03a5"], ["\u03d5", "\u03c6"], ["\u03d6", "\u03c0"], ["\u03f0", "\u03ba"], ["\u03f1", "\u03c1"], ["\u03f2", "\u03c2"], ["\u03f5", "\u03b5"], ["\u0439", "\u0438"], ["\u0450", "\u0435"], ["\u0451", "\u0435"], ["\u0453", "\u0433"], ["\u0457", "\u0456"], ["\u045c", "\u043a"], ["\u045d", "\u0438"], ["\u045e", "\u0443"], ["\u0477", "\u0475"], ["\u04c2", "\u0436"], ["\u04d1", "\u0430"], ["\u04d3", "\u0430"], ["\u04d7", "\u0435"], ["\u04db", +"\u04d9"], ["\u04dd", "\u0436"], ["\u04df", "\u0437"], ["\u04e3", "\u0438"], ["\u04e5", "\u0438"], ["\u04e7", "\u043e"], ["\u04eb", "\u04e9"], ["\u04ed", "\u044d"], ["\u04ef", "\u0443"], ["\u04f1", "\u0443"], ["\u04f3", "\u0443"], ["\u04f5", "\u0447"]]; +const ca = /[^\p{L}\p{N}]+/u, da = /(\d{3})/g, ea = /(\D)(\d{3})/g, fa = /(\d{3})(\D)/g, I = "".normalize && /[\u0300-\u036f]/g; +function J(a) { + if (!this) { + return new J(...arguments); } - for (a = 0; a < arguments.length; a++) { - this.assign(arguments[a]); + for (let c = 0; c < arguments.length; c++) { + this.assign(arguments[c]); } } -H.prototype.assign = function(a) { +J.prototype.assign = function(a) { this.normalize = x(a.normalize, !0, this.normalize); - let c = a.aa, b = c || a.ga || a.split; + let c = a.include, b = c || a.exclude || a.split; if ("object" === typeof b) { let e = !c, d = ""; - a.aa || (d += "\\p{Z}"); - b.fa && (d += "\\p{L}"); - b.ha && (d += "\\p{N}", e = !!c); - b.ja && (d += "\\p{S}"); - b.ia && (d += "\\p{P}"); + a.include || (d += "\\p{Z}"); + b.letter && (d += "\\p{L}"); + b.number && (d += "\\p{N}", e = !!c); + b.symbol && (d += "\\p{S}"); + b.punctuation && (d += "\\p{P}"); b.control && (d += "\\p{C}"); if (b = b.char) { d += "object" === typeof b ? b.join("") : b; @@ -99,59 +100,59 @@ H.prototype.assign = function(a) { this.split = new RegExp("[" + (c ? "^" : "") + d + "]+", "u"); this.numeric = e; } else { - this.split = x(b, ba, this.split), this.numeric = x(this.numeric, !0); + this.split = x(b, ca, this.split), this.numeric = x(this.numeric, !0); } - this.X = x(a.X, null, this.X); - this.R = x(a.R, null, this.R); - fa && (this.C = new Map(fa)); + this.prepare = x(a.prepare, null, this.prepare); + this.finalize = x(a.finalize, null, this.finalize); + I || (this.mapper = new Map(ba)); this.rtl = a.rtl || !1; - this.A = x(a.A, !0, this.A); + this.dedupe = x(a.dedupe, !0, this.dedupe); this.filter = x((b = a.filter) && new Set(b), null, this.filter); - this.F = x((b = a.F) && new Map(b), null, this.F); - this.C = x((b = a.C) && new Map(b), null, this.C); - this.J = x((b = a.J) && new Map(b), null, this.J); - this.G = x(a.G, null, this.G); - this.W = x(a.W, 1, this.W); - this.ba = x(a.ba, 0, this.ba); + this.matcher = x((b = a.matcher) && new Map(b), null, this.matcher); + this.mapper = x((b = a.mapper) && new Map(b), null, this.mapper); + this.stemmer = x((b = a.stemmer) && new Map(b), null, this.stemmer); + this.replacer = x(a.replacer, null, this.replacer); + this.minlength = x(a.minlength, 1, this.minlength); + this.maxlength = x(a.maxlength, 0, this.maxlength); if (this.cache = b = x(a.cache, !0, this.cache)) { - this.T = null, this.$ = "number" === typeof b ? b : 2e5, this.M = new Map(), this.P = new Map(), this.L = this.B = 128; + this.L = null, this.R = "number" === typeof b ? b : 2e5, this.H = new Map(), this.J = new Map(), this.G = this.A = 128; } - this.U = ""; - this.Y = null; - this.V = ""; - this.Z = null; - if (this.F) { - for (const e of this.F.keys()) { - this.U += (this.U ? "|" : "") + e; + this.M = ""; + this.O = null; + this.N = ""; + this.P = null; + if (this.matcher) { + for (const e of this.matcher.keys()) { + this.M += (this.M ? "|" : "") + e; } } - if (this.J) { - for (const e of this.J.keys()) { - this.V += (this.V ? "|" : "") + e; + if (this.stemmer) { + for (const e of this.stemmer.keys()) { + this.N += (this.N ? "|" : "") + e; } } return this; }; -H.prototype.encode = function(a) { - if (this.cache && a.length <= this.B) { - if (this.T) { - if (this.M.has(a)) { - return this.M.get(a); +J.prototype.encode = function(a) { + if (this.cache && a.length <= this.A) { + if (this.L) { + if (this.H.has(a)) { + return this.H.get(a); } } else { - this.T = setTimeout(ha, 0, this); + this.L = setTimeout(ha, 0, this); } } - this.normalize && (a = "function" === typeof this.normalize ? this.normalize(a) : G ? a.normalize("NFKD").replace(G, "").toLowerCase() : a.toLowerCase()); - this.X && (a = this.X(a)); - this.numeric && 3 < a.length && (a = a.replace(da, "$1 $2").replace(ea, "$1 $2").replace(ca, "$1 ")); - const c = !(this.A || this.C || this.filter || this.F || this.J || this.G); + this.normalize && (a = "function" === typeof this.normalize ? this.normalize(a) : I ? a.normalize("NFKD").replace(I, "").toLowerCase() : a.toLowerCase()); + this.prepare && (a = this.prepare(a)); + this.numeric && 3 < a.length && (a = a.replace(ea, "$1 $2").replace(fa, "$1 $2").replace(da, "$1 ")); + const c = !(this.dedupe || this.mapper || this.filter || this.matcher || this.stemmer || this.replacer); let b = [], e = this.split || "" === this.split ? a.split(this.split) : a; for (let f = 0, g, h; f < e.length; f++) { if (!(g = h = e[f])) { continue; } - if (g.length < this.W) { + if (g.length < this.minlength) { continue; } if (c) { @@ -161,54 +162,55 @@ H.prototype.encode = function(a) { if (this.filter && this.filter.has(g)) { continue; } - if (this.cache && g.length <= this.L) { - if (this.T) { - var d = this.P.get(g); + if (this.cache && g.length <= this.G) { + if (this.L) { + var d = this.J.get(g); if (d || "" === d) { d && b.push(d); continue; } } else { - this.T = setTimeout(ha, 0, this); + this.L = setTimeout(ha, 0, this); } } let k; - this.J && 2 < g.length && (this.Z || (this.Z = new RegExp("(?!^)(" + this.V + ")$")), g = g.replace(this.Z, m => this.J.get(m)), k = 1); - this.F && 1 < g.length && (this.Y || (this.Y = new RegExp("(" + this.U + ")", "g")), g = g.replace(this.Y, m => this.F.get(m)), k = 1); - g && k && (g.length < this.W || this.filter && this.filter.has(g)) && (g = ""); - if (g && (this.C || this.A && 1 < g.length)) { + this.stemmer && 2 < g.length && (this.P || (this.P = new RegExp("(?!^)(" + this.N + ")$")), g = g.replace(this.P, m => this.stemmer.get(m)), k = 1); + this.matcher && 1 < g.length && (this.O || (this.O = new RegExp("(" + this.M + ")", "g")), g = g.replace(this.O, m => this.matcher.get(m)), k = 1); + g && k && (g.length < this.minlength || this.filter && this.filter.has(g)) && (g = ""); + if (g && (this.mapper || this.dedupe && 1 < g.length)) { d = ""; for (let m = 0, l = "", r, t; m < g.length; m++) { - r = g.charAt(m), r === l && this.A || ((t = this.C && this.C.get(r)) || "" === t ? t === l && this.A || !(l = t) || (d += t) : d += l = r); + r = g.charAt(m), r === l && this.dedupe || ((t = this.mapper && this.mapper.get(r)) || "" === t ? t === l && this.dedupe || !(l = t) || (d += t) : d += l = r); } g = d; } - if (g && this.G) { - for (d = 0; g && d < this.G.length; d += 2) { - g = g.replace(this.G[d], this.G[d + 1]); + if (g && this.replacer) { + for (d = 0; g && d < this.replacer.length; d += 2) { + g = g.replace(this.replacer[d], this.replacer[d + 1]); } } - this.cache && h.length <= this.L && (this.P.set(h, g), this.P.size > this.$ && (this.P.clear(), this.L = this.L / 1.1 | 0)); + this.cache && h.length <= this.G && (this.J.set(h, g), this.J.size > this.R && (this.J.clear(), this.G = this.G / 1.1 | 0)); g && b.push(g); } - this.R && (b = this.R(b) || b); - this.cache && a.length <= this.B && (this.M.set(a, b), this.M.size > this.$ && (this.M.clear(), this.B = this.B / 1.1 | 0)); + this.finalize && (b = this.finalize(b) || b); + this.cache && a.length <= this.A && (this.H.set(a, b), this.H.size > this.R && (this.H.clear(), this.A = this.A / 1.1 | 0)); return b; }; function ha(a) { - a.T = null; - a.M.clear(); - a.P.clear(); + a.L = null; + a.H.clear(); + a.J.clear(); } ;function ia(a, c, b) { a = ("object" === typeof a ? "" + a.query : a).toLowerCase(); let e = this.cache.get(a); if (!e) { e = this.search(a, c, b); - if (e instanceof Promise) { + if (e.then) { const d = this; e.then(function(f) { d.cache.set(a, f); + return f; }); } this.cache.set(a, e); @@ -218,14 +220,15 @@ function ha(a) { function K(a) { this.limit = a && !0 !== a ? a : 1000; this.cache = new Map(); - this.B = ""; + this.A = ""; } K.prototype.set = function(a, c) { - this.cache.has(a) || (this.cache.set(this.B = a, c), this.limit && this.cache.size > this.limit && this.cache.delete(this.cache.keys().next().value)); + this.cache.set(this.A = a, c); + this.cache.size > this.limit && this.cache.delete(this.cache.keys().next().value); }; K.prototype.get = function(a) { const c = this.cache.get(a); - c && this.limit && this.B !== a && (this.cache.delete(a), this.cache.set(this.B = a, c)); + c && this.A !== a && (this.cache.delete(a), this.cache.set(this.A = a, c)); return c; }; K.prototype.remove = function(a) { @@ -236,25 +239,46 @@ K.prototype.remove = function(a) { }; K.prototype.clear = function() { this.cache.clear(); - this.B = ""; + this.A = ""; }; -function ja(a, c, b, e, d, f, g, h) { +const ja = {normalize:function(a) { + return a.toLowerCase(); +}, dedupe:!1}; +const L = new Map([["b", "p"], ["v", "f"], ["w", "f"], ["z", "s"], ["x", "s"], ["d", "t"], ["n", "m"], ["c", "k"], ["g", "k"], ["j", "k"], ["q", "k"], ["i", "e"], ["y", "e"], ["u", "o"]]); +const ka = new Map([["ai", "ei"], ["ae", "a"], ["oe", "o"], ["ue", "u"], ["sh", "s"], ["ch", "c"], ["th", "t"], ["ph", "f"], ["pf", "f"]]), la = [/([^aeo])h([aeo$])/g, "$1$2", /([aeo])h([^aeo]|$)/g, "$1$2"]; +const ma = {a:"", e:"", i:"", o:"", u:"", y:"", b:1, f:1, p:1, v:1, c:2, g:2, j:2, k:2, q:2, s:2, x:2, z:2, "\u00df":2, d:3, t:3, l:4, m:5, n:5, r:6}; +const na = /[\x00-\x7F]+/g; +const oa = /[\x00-\x7F]+/g; +const pa = /[\x00-\x7F]+/g; +var qa = {LatinExact:{normalize:!1, dedupe:!1}, LatinDefault:ja, LatinSimple:{normalize:!0, dedupe:!0}, LatinBalance:{normalize:!0, dedupe:!0, mapper:L}, LatinAdvanced:{normalize:!0, dedupe:!0, mapper:L, replacer:la, matcher:ka}, LatinExtra:{normalize:!0, dedupe:!0, mapper:L, replacer:la.concat([/(?!^)[aeoy]/g, ""]), matcher:ka}, LatinSoundex:{normalize:!0, dedupe:!1, include:{letter:!0}, finalize:function(a) { + for (let b = 0; b < a.length; b++) { + var c = a[b]; + let e = c.charAt(0), d = ma[e]; + for (let f = 1, g; f < c.length && (g = c.charAt(f), "h" === g || "w" === g || !(g = ma[g]) || g === d || (e += g, d = g, 4 !== e.length)); f++) { + } + a[b] = e; + } +}}, ArabicDefault:{rtl:!0, normalize:!1, dedupe:!0, prepare:function(a) { + return ("" + a).replace(na, " "); +}}, CjkDefault:{normalize:!1, dedupe:!0, split:"", prepare:function(a) { + return ("" + a).replace(oa, ""); +}}, CyrillicDefault:{normalize:!1, dedupe:!0, prepare:function(a) { + return ("" + a).replace(pa, " "); +}}}; +function ra(a, c, b, e, d, f, g, h) { (e = a(b ? b + "." + e : e, JSON.stringify(g))) && e.then ? e.then(function() { c.export(a, c, b, d, f + 1, h); }) : c.export(a, c, b, d, f + 1, h); } -;var ka = {normalize:function(a) { - return a.toLowerCase(); -}, A:!1}; -const la = {memory:{resolution:1}, performance:{resolution:6, fastupdate:!0, context:{depth:1, resolution:3}}, match:{tokenize:"forward"}, score:{resolution:9, context:{depth:2, resolution:9}}}; -function ma(a) { - L.call(a, "add"); - L.call(a, "append"); - L.call(a, "search"); - L.call(a, "update"); - L.call(a, "remove"); +;const sa = {memory:{resolution:1}, performance:{resolution:6, fastupdate:!0, context:{depth:1, resolution:3}}, match:{tokenize:"forward"}, score:{resolution:9, context:{depth:2, resolution:9}}}; +function ta(a) { + M.call(a, "add"); + M.call(a, "append"); + M.call(a, "search"); + M.call(a, "update"); + M.call(a, "remove"); } -function L(a) { +function M(a) { this[a + "Async"] = function() { var c = arguments; const b = c[c.length - 1]; @@ -268,7 +292,7 @@ function L(a) { }; } ;y(); -M.prototype.add = function(a, c, b, e) { +N.prototype.add = function(a, c, b, e) { if (c && (a || 0 === a)) { if (!e && !b && this.h.has(a)) { return this.update(a, c); @@ -280,15 +304,15 @@ M.prototype.add = function(a, c, b, e) { let n = c[this.rtl ? e - 1 - p : p]; var d = n.length; if (d && (r || !l[n])) { - var f = this.score ? this.score(c, n, p, null, 0) : N(t, e, p), g = ""; + var f = this.score ? this.score(c, n, p, null, 0) : O(t, e, p), g = ""; switch(this.tokenize) { case "full": if (2 < d) { for (f = 0; f < d; f++) { for (var h = d; h > f; h--) { g = n.substring(f, h); - var k = this.score ? this.score(c, n, p, g, f) : N(t, e, p, d, f); - O(this, l, g, k, a, b); + var k = this.score ? this.score(c, n, p, g, f) : O(t, e, p, d, f); + P(this, l, g, k, a, b); } } break; @@ -296,24 +320,24 @@ M.prototype.add = function(a, c, b, e) { case "reverse": if (1 < d) { for (h = d - 1; 0 < h; h--) { - g = n[h] + g, k = this.score ? this.score(c, n, p, g, h) : N(t, e, p, d, h), O(this, l, g, k, a, b); + g = n[h] + g, k = this.score ? this.score(c, n, p, g, h) : O(t, e, p, d, h), P(this, l, g, k, a, b); } g = ""; } case "forward": if (1 < d) { for (h = 0; h < d; h++) { - g += n[h], O(this, l, g, f, a, b); + g += n[h], P(this, l, g, f, a, b); } break; } default: - if (O(this, l, n, f, a, b), r && 1 < e && p < e - 1) { - for (d = y(), g = this.ca, f = n, h = Math.min(r + 1, e - p), d[f] = 1, k = 1; k < h; k++) { + if (P(this, l, n, f, a, b), r && 1 < e && p < e - 1) { + for (d = y(), g = this.S, f = n, h = Math.min(r + 1, e - p), d[f] = 1, k = 1; k < h; k++) { if ((n = c[this.rtl ? e - 1 - p - k : p + k]) && !d[n]) { d[n] = 1; - const u = this.score ? this.score(c, f, p, n, k) : N(g + (e / 2 > g ? 0 : 1), e, p, h - 1, k - 1), B = this.bidirectional && n > f; - O(this, m, B ? f : n, u, a, b, B ? n : f); + const u = this.score ? this.score(c, f, p, n, k) : O(g + (e / 2 > g ? 0 : 1), e, p, h - 1, k - 1), B = this.bidirectional && n > f; + P(this, m, B ? f : n, u, a, b, B ? n : f); } } } @@ -325,14 +349,14 @@ M.prototype.add = function(a, c, b, e) { } return this; }; -function O(a, c, b, e, d, f, g) { - let h = g ? a.I : a.map, k; +function P(a, c, b, e, d, f, g) { + let h = g ? a.D : a.map, k; c[b] && g && (k = c[b])[g] || (g ? (c = k || (c[b] = y()), c[g] = 1, (k = h.get(g)) ? h = k : h.set(g, h = new Map())) : c[b] = 1, (k = h.get(b)) ? h = k : h.set(b, h = []), h = h[e] || (h[e] = []), f && h.includes(d) || (h.push(d), a.fastupdate && ((c = a.h.get(d)) ? c.push(h) : a.h.set(d, [h])))); } -function N(a, c, b, e, d) { +function O(a, c, b, e, d) { return b && 1 < a ? c + (e || 0) <= a ? b + (d || 0) : (a - 1) / (c + (e || 0)) * (b + (d || 0)) + 1 | 0 : 0; } -;function P(a, c, b) { +;function Q(a, c, b) { if (1 === a.length) { return a = a[0], a = b || a.length > c ? c ? a.slice(b, b + c) : a.slice(b) : a; } @@ -362,7 +386,7 @@ function N(a, c, b, e, d) { } return e.length ? e = 1 < e.length ? [].concat.apply([], e) : e[0] : e; } -;function na(a, c, b, e) { +;function ua(a, c, b, e) { var d = a.length; let f = [], g = 0, h, k, m; e && (e = []); @@ -423,7 +447,7 @@ function N(a, c, b, e, d) { } return f; } -function oa(a, c) { +function va(a, c) { const b = y(), e = y(), d = []; for (let f = 0; f < a.length; f++) { b[a[f]] = 1; @@ -436,7 +460,7 @@ function oa(a, c) { } return d; } -;M.prototype.search = function(a, c, b) { +;N.prototype.search = function(a, c, b) { b || (!c && E(a) ? (b = a, a = "") : E(c) && (b = c, c = 0)); let e = []; let d, f = 0; @@ -451,11 +475,11 @@ function oa(a, c) { b = a.length; c || (c = 100); if (1 === b) { - return Q.call(this, a[0], "", c, f); + return R.call(this, a[0], "", c, f); } g = this.depth && !1 !== g; if (2 === b && g && !d) { - return Q.call(this, a[0], a[1], c, f); + return R.call(this, a[0], a[1], c, f); } let h = 0, k = 0; if (1 < b) { @@ -463,7 +487,7 @@ function oa(a, c) { const r = []; for (let t = 0, p; t < b; t++) { if ((p = a[t]) && !m[p]) { - if (d || R(this, p)) { + if (d || S(this, p)) { r.push(p), m[p] = 1; } else { return e; @@ -482,15 +506,15 @@ function oa(a, c) { m = 0; let l; if (1 === b) { - return Q.call(this, a[0], "", c, f); + return R.call(this, a[0], "", c, f); } if (2 === b && g && !d) { - return Q.call(this, a[0], a[1], c, f); + return R.call(this, a[0], a[1], c, f); } 1 < b && (g ? (l = a[0], m = 1) : 9 < h && 3 < h / k && a.sort(aa)); for (let r, t; m < b; m++) { t = a[m]; - l ? (r = R(this, t, l), r = pa(r, e, d, this.ca, c, f, 2 === b), d && !1 === r && e.length || (l = t)) : (r = R(this, t), r = pa(r, e, d, this.resolution, c, f, 1 === b)); + l ? (r = S(this, t, l), r = wa(r, e, d, this.S, c, f, 2 === b), d && !1 === r && e.length || (l = t)) : (r = S(this, t), r = wa(r, e, d, this.resolution, c, f, 1 === b)); if (r) { return r; } @@ -505,16 +529,16 @@ function oa(a, c) { return e; } if (1 === g) { - return P(e[0], c, f); + return Q(e[0], c, f); } } } - return na(e, c, f, d); + return ua(e, c, f, d); }; -function Q(a, c, b, e) { - return (a = R(this, a, c)) && a.length ? P(a, b, e) : []; +function R(a, c, b, e) { + return (a = S(this, a, c)) && a.length ? Q(a, b, e) : []; } -function pa(a, c, b, e, d, f, g) { +function wa(a, c, b, e, d, f, g) { let h = []; if (a) { e = Math.min(a.length, e); @@ -527,7 +551,7 @@ function pa(a, c, b, e, d, f, g) { } if (h.length) { if (g) { - return P(h, d, 0); + return Q(h, d, 0); } c.push(h); return; @@ -535,13 +559,13 @@ function pa(a, c, b, e, d, f, g) { } return !b && h; } -function R(a, c, b) { +function S(a, c, b) { let e; b && (e = a.bidirectional && c > b); - a = b ? (a = a.I.get(e ? c : b)) && a.get(e ? b : c) : a.map.get(c); + a = b ? (a = a.D.get(e ? c : b)) && a.get(e ? b : c) : a.map.get(c); return a; } -;M.prototype.remove = function(a, c) { +;N.prototype.remove = function(a, c) { const b = this.h.size && (this.fastupdate ? this.h.get(a) : this.h.has(a)); if (b) { if (this.fastupdate) { @@ -556,14 +580,14 @@ function R(a, c, b) { } } } else { - S(this.map, a), this.depth && S(this.I, a); + T(this.map, a), this.depth && T(this.D, a); } c || this.h.delete(a); } this.cache && this.cache.remove(a); return this; }; -function S(a, c) { +function T(a, c) { let b = 0; if (a.constructor === Array) { for (let e = 0, d, f; e < a.length; e++) { @@ -578,25 +602,25 @@ function S(a, c) { } } else { for (let e of a) { - const d = e[0], f = S(e[1], c); + const d = e[0], f = T(e[1], c); f ? b += f : a.delete(d); } } return b; } -;function M(a, c) { - if (!(this instanceof M)) { - return new M(a); +;function N(a, c) { + if (!this) { + return new N(a); } if (a) { var b = z(a) ? a : a.preset; - b && (la[b] || console.warn("Preset not found: " + b), a = Object.assign({}, la[b], a)); + b && (sa[b] || console.warn("Preset not found: " + b), a = Object.assign({}, sa[b], a)); } else { a = {}; } b = a.context || {}; - const e = a.encode || a.encoder || ka; - this.encoder = e.encode ? e : "object" === typeof e ? new H(e) : {encode:e}; + const e = a.encode || a.encoder || ja; + this.encoder = e.encode ? e : "object" === typeof e ? new J(e) : {encode:e}; let d; this.resolution = a.resolution || 9; this.tokenize = d = a.tokenize || "strict"; @@ -606,16 +630,16 @@ function S(a, c) { this.score = a.score || null; d = !1; this.map = new Map(); - this.I = new Map(); + this.D = new Map(); this.h = c || (this.fastupdate ? new Map() : new Set()); - this.ca = b.resolution || 1; + this.S = b.resolution || 1; this.rtl = e.rtl || a.rtl || !1; this.cache = (d = a.cache || null) && new K(d); } -v = M.prototype; +v = N.prototype; v.clear = function() { this.map.clear(); - this.I.clear(); + this.D.clear(); this.h.clear(); this.cache && this.cache.clear(); return this; @@ -633,7 +657,7 @@ v.update = function(a, c) { } return this.remove(a).add(a, c); }; -function T(a) { +function U(a) { let c = 0; if (a.constructor === Array) { for (let b = 0, e; b < a.length; b++) { @@ -641,7 +665,7 @@ function T(a) { } } else { for (const b of a) { - const e = b[0], d = T(b[1]); + const e = b[0], d = U(b[1]); d ? c += d : a.delete(e); } } @@ -651,8 +675,8 @@ v.cleanup = function() { if (!this.fastupdate) { return console.info('Cleanup the index isn\'t required when not using "fastupdate".'), this; } - T(this.map); - this.depth && T(this.I); + U(this.map); + this.depth && U(this.D); return this; }; v.searchCache = ia; @@ -676,7 +700,7 @@ v.export = function(a, c, b, e, d, f) { break; case 1: h = "cfg"; - k = {doc:0, opt:this.B ? 1 : 0}; + k = {doc:0, opt:this.A ? 1 : 0}; break; case 2: h = "map"; @@ -684,20 +708,20 @@ v.export = function(a, c, b, e, d, f) { break; case 3: h = "ctx"; - k = this.I; + k = this.D; break; default: "undefined" === typeof b && f && f(); return; } - ja(a, c || this, b, h, e, d, k, f); + ra(a, c || this, b, h, e, d, k, f); return g; }; v.import = function(a, c) { if (c) { switch(z(c) && (c = JSON.parse(c)), a) { case "cfg": - this.B = !!c.opt; + this.A = !!c.opt; break; case "reg": this.fastupdate = !1; @@ -707,32 +731,32 @@ v.import = function(a, c) { this.map = c; break; case "ctx": - this.I = c; + this.D = c; } } }; -ma(M.prototype); -U.prototype.add = function(a, c, b) { +ta(N.prototype); +V.prototype.add = function(a, c, b) { E(a) && (c = a, a = F(c, this.key)); if (c && (a || 0 === a)) { if (!b && this.h.has(a)) { return this.update(a, c); } for (let h = 0, k; h < this.field.length; h++) { - k = this.K[h]; + k = this.F[h]; var e = this.index.get(this.field[h]); if ("function" === typeof k) { var d = k(c); d && e.add(a, d, !1, !0); } else { - if (d = k.O, !d || d(c)) { - k instanceof String ? k = ["" + k] : z(k) && (k = [k]), V(c, k, this.S, 0, e, a, k[0], b); + if (d = k.I, !d || d(c)) { + k.constructor === String ? k = ["" + k] : z(k) && (k = [k]), W(c, k, this.K, 0, e, a, k[0], b); } } } if (this.tag) { - for (e = 0; e < this.H.length; e++) { - var f = this.H[e], g = this.da[e]; + for (e = 0; e < this.C.length; e++) { + var f = this.C[e], g = this.T[e]; d = this.tag.get(g); let h = y(); if ("function" === typeof f) { @@ -740,11 +764,11 @@ U.prototype.add = function(a, c, b) { continue; } } else { - const k = f.O; + const k = f.I; if (k && !k(c)) { continue; } - f instanceof String && (f = "" + f); + f.constructor === String && (f = "" + f); f = F(c, f); } if (d && f) { @@ -759,11 +783,11 @@ U.prototype.add = function(a, c, b) { } if (this.store && (!b || !this.store.has(a))) { let h; - if (this.D) { + if (this.B) { h = y(); - for (let k = 0, m; k < this.D.length; k++) { - m = this.D[k]; - if ((b = m.O) && !b(c)) { + for (let k = 0, m; k < this.B.length; k++) { + m = this.B[k]; + if ((b = m.I) && !b(c)) { continue; } let l; @@ -772,12 +796,12 @@ U.prototype.add = function(a, c, b) { if (!l) { continue; } - m = [m.ea]; - } else if (z(m) || m instanceof String) { + m = [m.U]; + } else if (z(m) || m.constructor === String) { h[m] = c[m]; continue; } - W(c, h, m, 0, m[0], l); + X(c, h, m, 0, m[0], l); } } this.store.set(a, h || c); @@ -785,21 +809,21 @@ U.prototype.add = function(a, c, b) { } return this; }; -function W(a, c, b, e, d, f) { +function X(a, c, b, e, d, f) { a = a[d]; if (e === b.length - 1) { c[d] = f || a; } else if (a) { if (a.constructor === Array) { for (c = c[d] = Array(a.length), d = 0; d < a.length; d++) { - W(a, c, b, e, d); + X(a, c, b, e, d); } } else { - c = c[d] || (c[d] = y()), d = b[++e], W(a, c, b, e, d); + c = c[d] || (c[d] = y()), d = b[++e], X(a, c, b, e, d); } } } -function V(a, c, b, e, d, f, g, h) { +function W(a, c, b, e, d, f, g, h) { if (a = a[g]) { if (e === c.length - 1) { if (a.constructor === Array) { @@ -815,15 +839,15 @@ function V(a, c, b, e, d, f, g, h) { } else { if (a.constructor === Array) { for (g = 0; g < a.length; g++) { - V(a, c, b, e, d, f, g, h); + W(a, c, b, e, d, f, g, h); } } else { - g = c[++e], V(a, c, b, e, d, f, g, h); + g = c[++e], W(a, c, b, e, d, f, g, h); } } } } -;U.prototype.search = function(a, c, b, e) { +;V.prototype.search = function(a, c, b, e) { b || (!c && E(a) ? (b = a, a = "") : E(c) && (b = c, c = 0)); let d = []; var f = []; @@ -881,7 +905,7 @@ function V(a, c, b, e, d, f, g, h) { e = []; if (n.length) { for (f = 0; f < n.length; f += 2) { - t = qa.call(this, n[f], n[f + 1], c, p, g), d.push({field:n[f], tag:n[f + 1], result:t}); + t = xa.call(this, n[f], n[f + 1], c, p, g), d.push({field:n[f], tag:n[f + 1], result:t}); } } return e.length ? Promise.all(e).then(function(w) { @@ -916,23 +940,23 @@ function V(a, c, b, e, d, f, g, h) { if (l && D) { n = []; u = 0; - for (let I = 0, J, ya; I < l.length; I += 2) { - J = this.tag.get(l[I]); - if (!J) { - if (console.warn("Tag '" + l[I] + ":" + l[I + 1] + "' will be skipped because there is no field '" + l[I] + "'."), k) { + for (let G = 0, H, Ba; G < l.length; G += 2) { + H = this.tag.get(l[G]); + if (!H) { + if (console.warn("Tag '" + l[G] + ":" + l[G + 1] + "' will be skipped because there is no field '" + l[G] + "'."), k) { continue; } else { return d; } } - if (ya = (J = J && J.get(l[I + 1])) && J.length) { - u++, n.push(J); + if (Ba = (H = H && H.get(l[G + 1])) && H.length) { + u++, n.push(H); } else if (!k) { return d; } } if (u) { - q = oa(q, n); + q = va(q, n); D = q.length; if (!D && !k) { return d; @@ -961,15 +985,15 @@ function V(a, c, b, e, d, f, g, h) { p = []; for (let w = 0, q; w < f.length; w++) { q = d[w]; - g && q.length && !q[0].doc && q.length && (q = ra.call(this, q)); + g && q.length && !q[0].doc && q.length && (q = ya.call(this, q)); if (t) { return q; } d[w] = {field:f[w], result:q}; } - return h ? sa(d, c) : d; + return h ? za(d, c) : d; }; -function sa(a, c) { +function za(a, c) { const b = [], e = y(); for (let d = 0, f, g; d < a.length; d++) { f = a[d]; @@ -988,7 +1012,7 @@ function sa(a, c) { } return b; } -function qa(a, c, b, e, d) { +function xa(a, c, b, e, d) { let f = this.tag.get(a); if (!f) { return console.warn("Tag '" + a + "' was not found"), []; @@ -997,29 +1021,29 @@ function qa(a, c, b, e, d) { if (a > b || e) { f = f.slice(e, e + b); } - d && (f = ra.call(this, f)); + d && (f = ya.call(this, f)); return f; } } -function ra(a) { +function ya(a) { const c = Array(a.length); for (let b = 0, e; b < a.length; b++) { e = a[b], c[b] = {id:e, doc:this.store.get(e)}; } return c; } -;function U(a) { - if (!(this instanceof U)) { - return new U(a); +;function V(a) { + if (!this) { + return new V(a); } const c = a.document || a.doc || a; var b; - this.K = []; + this.F = []; this.field = []; - this.S = []; - this.key = (b = c.key || c.id) && X(b, this.S) || "id"; + this.K = []; + this.key = (b = c.key || c.id) && Y(b, this.K) || "id"; this.h = (this.fastupdate = !!a.fastupdate) ? new Map() : new Set(); - this.D = (b = c.store || null) && !0 !== b && []; + this.B = (b = c.store || null) && !0 !== b && []; this.store = b && new Map(); this.cache = (b = a.cache || null) && new K(b); this.async = a.cache = !1; @@ -1027,13 +1051,13 @@ function ra(a) { let e = c.index || c.field || c; z(e) && (e = [e]); for (let d = 0, f, g; d < e.length; d++) { - f = e[d], z(f) || (g = f, f = f.field), g = E(g) ? Object.assign({}, a, g) : a, b.set(f, new M(g, this.h)), g.N ? this.K[d] = g.N : (this.K[d] = X(f, this.S), g.filter && ("string" === typeof this.K[d] && (this.K[d] = new String(this.K[d])), this.K[d].O = g.filter)), this.field[d] = f; + f = e[d], z(f) || (g = f, f = f.field), g = E(g) ? Object.assign({}, a, g) : a, b.set(f, new N(g, this.h)), g.custom ? this.F[d] = g.custom : (this.F[d] = Y(f, this.K), g.filter && ("string" === typeof this.F[d] && (this.F[d] = new String(this.F[d])), this.F[d].I = g.filter)), this.field[d] = f; } - if (this.D) { + if (this.B) { a = c.store; z(a) && (a = [a]); for (let d = 0, f, g; d < a.length; d++) { - f = a[d], g = f.field || f, f.N ? (this.D[d] = f.N, f.N.ea = g) : (this.D[d] = X(g, this.S), f.filter && ("string" === typeof this.D[d] && (this.D[d] = new String(this.D[d])), this.D[d].O = f.filter)); + f = a[d], g = f.field || f, f.custom ? (this.B[d] = f.custom, f.custom.U = g) : (this.B[d] = Y(g, this.K), f.filter && ("string" === typeof this.B[d] && (this.B[d] = new String(this.B[d])), this.B[d].I = f.filter)); } } this.index = b; @@ -1041,22 +1065,22 @@ function ra(a) { if (b = c.tag) { if ("string" === typeof b && (b = [b]), b.length) { this.tag = new Map(); - this.H = []; - this.da = []; + this.C = []; + this.T = []; for (let d = 0, f, g; d < b.length; d++) { f = b[d]; g = f.field || f; if (!g) { throw Error("The tag field from the document descriptor is undefined."); } - f.N ? this.H[d] = f.N : (this.H[d] = X(g, this.S), f.filter && ("string" === typeof this.H[d] && (this.H[d] = new String(this.H[d])), this.H[d].O = f.filter)); - this.da[d] = g; + f.custom ? this.C[d] = f.custom : (this.C[d] = Y(g, this.K), f.filter && ("string" === typeof this.C[d] && (this.C[d] = new String(this.C[d])), this.C[d].I = f.filter)); + this.T[d] = g; this.tag.set(g, new Map()); } } } } -function X(a, c) { +function Y(a, c) { const b = a.split(":"); let e = 0; for (let d = 0; d < b.length; d++) { @@ -1065,7 +1089,7 @@ function X(a, c) { e < b.length && (b.length = e); return 1 < e ? b : b[0]; } -v = U.prototype; +v = V.prototype; v.append = function(a, c) { return this.add(a, c, !0); }; @@ -1138,7 +1162,7 @@ v.export = function(a, c, b, e, d, f) { switch(d) { case 1: c = "tag"; - h = this.L; + h = this.G; b = null; break; case 2: @@ -1150,7 +1174,7 @@ v.export = function(a, c, b, e, d, f) { f(); return; } - ja(a, this, b, c, e, d, h, f); + ra(a, this, b, c, e, d, h, f); } return g; }; @@ -1158,7 +1182,7 @@ v.import = function(a, c) { if (c) { switch(z(c) && (c = JSON.parse(c)), a) { case "tag": - this.L = c; + this.G = c; break; case "reg": this.fastupdate = !1; @@ -1178,32 +1202,10 @@ v.import = function(a, c) { } } }; -ma(U.prototype); -const Y = y(); -const ta = new Map([["b", "p"], ["v", "f"], ["w", "f"], ["z", "s"], ["x", "s"], ["d", "t"], ["n", "m"], ["c", "k"], ["g", "k"], ["j", "k"], ["q", "k"], ["i", "e"], ["y", "e"], ["u", "o"]]); -var ua = {normalize:!0, A:!0, C:ta}; -const va = new Map([["ai", "ei"], ["ae", "a"], ["oe", "o"], ["ue", "u"], ["sh", "s"], ["ch", "c"], ["th", "t"], ["ph", "f"], ["pf", "f"]]), wa = [/([^aeo])h([aeo$])/g, "$1$2", /([aeo])h([^aeo]|$)/g, "$1$2"]; -var xa = {normalize:!0, A:!0, C:ta, G:wa, F:va}; -var za = {normalize:!0, A:!0, C:ta, G:wa.concat([/(?!^)[aeoy]/g, ""]), F:va}; -const Aa = {a:"", e:"", i:"", o:"", u:"", y:"", b:1, f:1, p:1, v:1, c:2, g:2, j:2, k:2, q:2, s:2, x:2, z:2, "\u00df":2, d:3, t:3, l:4, m:5, n:5, r:6}; -Y["latin:exact"] = {normalize:!1, A:!1}; -Y["latin:default"] = ka; -Y["latin:simple"] = {normalize:!0, A:!0}; -Y["latin:balance"] = ua; -Y["latin:advanced"] = xa; -Y["latin:extra"] = za; -Y["latin:soundex"] = {normalize:!0, A:!1, aa:{fa:!0}, R:function(a) { - for (let b = 0; b < a.length; b++) { - var c = a[b]; - let e = c.charAt(0), d = Aa[e]; - for (let f = 1, g; f < c.length && (g = c.charAt(f), "h" === g || "w" === g || !(g = Aa[g]) || g === d || (e += g, d = g, 4 !== e.length)); f++) { - } - a[b] = e; - } -}}; -const Ba = {Index:M, Charset:Y, Encoder:H, Document:U, Worker:null, Resolver:null, IndexedDB:null}, Z = self; +ta(V.prototype); +const Aa = {Index:N, Charset:qa, Encoder:J, Document:V, Worker:null, Resolver:null, IndexedDB:null, Language:{}}, Z = self; let Ca; (Ca = Z.define) && Ca.amd ? Ca([], function() { - return Ba; -}) : "object" === typeof Z.exports ? Z.exports = Ba : Z.FlexSearch = Ba; + return Aa; +}) : "object" === typeof Z.exports ? Z.exports = Aa : Z.FlexSearch = Aa; }(this)); diff --git a/dist/flexsearch.compact.min.js b/dist/flexsearch.compact.min.js index 4e23f64..113a146 100644 --- a/dist/flexsearch.compact.min.js +++ b/dist/flexsearch.compact.min.js @@ -6,45 +6,44 @@ * https://github.com/nextapps-de/flexsearch */ (function(self){'use strict';var v;function x(a,c,b){const e=typeof b,d=typeof a;if("undefined"!==e){if("undefined"!==d){if(b){if("function"===d&&e===d)return function(h){return a(b(h))};c=a.constructor;if(c===b.constructor){if(c===Array)return b.concat(a);if(c===Map){var f=new Map(b);for(var g of a)f.set(g[0],g[1]);return f}if(c===Set){g=new Set(b);for(f of a.values())g.add(f);return g}}}return a}return b}return"undefined"===d?c:a}function y(){return Object.create(null)}function aa(a,c){return c.length-a.length} -function z(a){return"string"===typeof a}function E(a){return"object"===typeof a}function F(a,c){if(z(c))a=a[c];else for(let b=0;a&&bthis.J.get(m)),k=1);this.F&&1this.F.get(m)), -k=1);g&&k&&(g.lengththis.$&&(this.P.clear(),this.L=this.L/1.1|0));g&&b.push(g)}this.R&&(b=this.R(b)||b);this.cache&&a.length<=this.B&& -(this.M.set(a,b),this.M.size>this.$&&(this.M.clear(),this.B=this.B/1.1|0));return b};function ha(a){a.T=null;a.M.clear();a.P.clear()};function ia(a,c,b){a=("object"===typeof a?""+a.query:a).toLowerCase();let e=this.cache.get(a);if(!e){e=this.search(a,c,b);if(e instanceof Promise){const d=this;e.then(function(f){d.cache.set(a,f)})}this.cache.set(a,e)}return e}function J(a){this.limit=a&&!0!==a?a:1E3;this.cache=new Map;this.B=""}J.prototype.set=function(a,c){this.cache.has(a)||(this.cache.set(this.B=a,c),this.limit&&this.cache.size>this.limit&&this.cache.delete(this.cache.keys().next().value))}; -J.prototype.get=function(a){const c=this.cache.get(a);c&&this.limit&&this.B!==a&&(this.cache.delete(a),this.cache.set(this.B=a,c));return c};J.prototype.remove=function(a){for(const c of this.cache){const b=c[0];c[1].includes(a)&&this.cache.delete(b)}};J.prototype.clear=function(){this.cache.clear();this.B=""};function ja(a,c,b,e,d,f,g,h){(e=a(b?b+"."+e:e,JSON.stringify(g)))&&e.then?e.then(function(){c.export(a,c,b,d,f+1,h)}):c.export(a,c,b,d,f+1,h)};var ka={normalize:function(a){return a.toLowerCase()},A:!1};const la={memory:{resolution:1},performance:{resolution:6,fastupdate:!0,context:{depth:1,resolution:3}},match:{tokenize:"forward"},score:{resolution:9,context:{depth:2,resolution:9}}};function ma(a){K.call(a,"add");K.call(a,"append");K.call(a,"search");K.call(a,"update");K.call(a,"remove")}function K(a){this[a+"Async"]=function(){var c=arguments;const b=c[c.length-1];let e;"function"===typeof b&&(e=b,delete c[c.length-1]);this.async=!0;c=this[a].apply(this,c);this.async=!1;c.then?c.then(e):e(c);return c}};y();L.prototype.add=function(a,c,b,e){if(c&&(a||0===a)){if(!e&&!b&&this.h.has(a))return this.update(a,c);c=this.encoder.encode(c);if(e=c.length){const m=y(),l=y(),r=this.depth,t=this.resolution;for(let p=0;pf;h--){g=n.substring(f,h);var k=this.score?this.score(c,n,p,g,f):M(t,e,p,d,f);N(this,l,g,k,a,b)}break}case "reverse":if(1< -d){for(h=d-1;0g?0:1),e,p,h-1,k-1),B=this.bidirectional&&n>f;N(this,m,B?f:n,u,a,b,B?n:f)}}}}this.fastupdate||this.h.add(a)}}return this}; -function N(a,c,b,e,d,f,g){let h=g?a.I:a.map,k;c[b]&&g&&(k=c[b])[g]||(g?(c=k||(c[b]=y()),c[g]=1,(k=h.get(g))?h=k:h.set(g,h=new Map)):c[b]=1,(k=h.get(b))?h=k:h.set(b,h=[]),h=h[e]||(h[e]=[]),f&&h.includes(d)||(h.push(d),a.fastupdate&&((c=a.h.get(d))?c.push(h):a.h.set(d,[h]))))}function M(a,c,b,e,d){return b&&1c?c?a.slice(b,b+c):a.slice(b):a;let e=[];for(let d=0,f,g;d=g){b-=g;continue}bc&&(f=f.slice(0,c),g=f.length),e.push(f);else{if(g>=c)return g>c&&(f=f.slice(0,c)),f;e=[f]}c-=g;if(!c)break}return e.length?e=1=d)))break;if(h.length){if(g)return O(h,d,0);c.push(h);return}}return!b&&h}function R(a,c,b){let e;b&&(e=a.bidirectional&&c>b);a=b?(a=a.I.get(e?c:b))&&a.get(e?b:c):a.map.get(c);return a};L.prototype.remove=function(a,c){const b=this.h.size&&(this.fastupdate?this.h.get(a):this.h.has(a));if(b){if(this.fastupdate)for(let e=0,d;ed.length)d.pop();else{const f=d.indexOf(a);f===b.length-1?d.pop():d.splice(f,1)}}else S(this.map,a),this.depth&&S(this.I,a);c||this.h.delete(a)}this.cache&&this.cache.remove(a);return this}; -function S(a,c){let b=0;if(a.constructor===Array)for(let e=0,d,f;eb.add(a,c)):this.add(a,c)}return this.remove(a).add(a,c)}; -function T(a){let c=0;if(a.constructor===Array)for(let b=0,e;b{f=m}));let h,k;switch(d||(d=0)){case 0:h="reg";if(this.fastupdate){k=y();for(let m of this.h.keys())k[m]=1}else k=this.h;break;case 1:h="cfg";k={doc:0,opt:this.B?1:0};break;case 2:h="map";k=this.map;break;case 3:h="ctx";k=this.I;break;default:"undefined"===typeof b&&f&&f();return}ja(a,c||this,b,h,e,d,k,f);return g}; -v.import=function(a,c){if(c)switch(z(c)&&(c=JSON.parse(c)),a){case "cfg":this.B=!!c.opt;break;case "reg":this.fastupdate=!1;this.h=c;break;case "map":this.map=c;break;case "ctx":this.I=c}};ma(L.prototype);U.prototype.add=function(a,c,b){E(a)&&(c=a,a=F(c,this.key));if(c&&(a||0===a)){if(!b&&this.h.has(a))return this.update(a,c);for(let h=0,k;hb||e)a=a.slice(e,e+b);d&&(a=ra.call(this,a));return a}} -function ra(a){const c=Array(a.length);for(let b=0,e;bthis.stemmer.get(m)),k=1);this.matcher&& +1this.matcher.get(m)),k=1);g&&k&&(g.lengththis.R&&(this.J.clear(),this.G=this.G/1.1|0));g&&b.push(g)}this.finalize&&(b=this.finalize(b)||b);this.cache&&a.length<=this.A&&(this.H.set(a,b),this.H.size>this.R&&(this.H.clear(),this.A=this.A/1.1|0));return b};function ha(a){a.L=null;a.H.clear();a.J.clear()};function ia(a,c,b){a=("object"===typeof a?""+a.query:a).toLowerCase();let e=this.cache.get(a);if(!e){e=this.search(a,c,b);if(e.then){const d=this;e.then(function(f){d.cache.set(a,f);return f})}this.cache.set(a,e)}return e}function J(a){this.limit=a&&!0!==a?a:1E3;this.cache=new Map;this.A=""}J.prototype.set=function(a,c){this.cache.set(this.A=a,c);this.cache.size>this.limit&&this.cache.delete(this.cache.keys().next().value)}; +J.prototype.get=function(a){const c=this.cache.get(a);c&&this.A!==a&&(this.cache.delete(a),this.cache.set(this.A=a,c));return c};J.prototype.remove=function(a){for(const c of this.cache){const b=c[0];c[1].includes(a)&&this.cache.delete(b)}};J.prototype.clear=function(){this.cache.clear();this.A=""};const ja={normalize:function(a){return a.toLowerCase()},dedupe:!1};const K=new Map([["b","p"],["v","f"],["w","f"],["z","s"],["x","s"],["d","t"],["n","m"],["c","k"],["g","k"],["j","k"],["q","k"],["i","e"],["y","e"],["u","o"]]);const ka=new Map([["ai","ei"],["ae","a"],["oe","o"],["ue","u"],["sh","s"],["ch","c"],["th","t"],["ph","f"],["pf","f"]]),la=[/([^aeo])h([aeo$])/g,"$1$2",/([aeo])h([^aeo]|$)/g,"$1$2"];const ma={a:"",e:"",i:"",o:"",u:"",y:"",b:1,f:1,p:1,v:1,c:2,g:2,j:2,k:2,q:2,s:2,x:2,z:2,"\u00df":2,d:3,t:3,l:4,m:5,n:5,r:6};const na=/[\x00-\x7F]+/g;const oa=/[\x00-\x7F]+/g;const pa=/[\x00-\x7F]+/g;var qa={LatinExact:{normalize:!1,dedupe:!1},LatinDefault:ja,LatinSimple:{normalize:!0,dedupe:!0},LatinBalance:{normalize:!0,dedupe:!0,mapper:K},LatinAdvanced:{normalize:!0,dedupe:!0,mapper:K,replacer:la,matcher:ka},LatinExtra:{normalize:!0,dedupe:!0,mapper:K,replacer:la.concat([/(?!^)[aeoy]/g,""]),matcher:ka},LatinSoundex:{normalize:!0,dedupe:!1,include:{letter:!0},finalize:function(a){for(let b=0;bf;h--){g=n.substring(f,h);var k=this.score?this.score(c,n,p,g,f):N(t,e,p,d,f);O(this,l,g,k,a,b)}break}case "reverse":if(1< +d){for(h=d-1;0g?0:1),e,p,h-1,k-1),B=this.bidirectional&&n>f;O(this,m,B?f:n,u,a,b,B?n:f)}}}}this.fastupdate||this.h.add(a)}}return this}; +function O(a,c,b,e,d,f,g){let h=g?a.D:a.map,k;c[b]&&g&&(k=c[b])[g]||(g?(c=k||(c[b]=y()),c[g]=1,(k=h.get(g))?h=k:h.set(g,h=new Map)):c[b]=1,(k=h.get(b))?h=k:h.set(b,h=[]),h=h[e]||(h[e]=[]),f&&h.includes(d)||(h.push(d),a.fastupdate&&((c=a.h.get(d))?c.push(h):a.h.set(d,[h]))))}function N(a,c,b,e,d){return b&&1c?c?a.slice(b,b+c):a.slice(b):a;let e=[];for(let d=0,f,g;d=g){b-=g;continue}bc&&(f=f.slice(0,c),g=f.length),e.push(f);else{if(g>=c)return g>c&&(f=f.slice(0,c)),f;e=[f]}c-=g;if(!c)break}return e.length?e=1=d)))break;if(h.length){if(g)return Q(h,d,0);c.push(h);return}}return!b&&h}function S(a,c,b){let e;b&&(e=a.bidirectional&&c>b);a=b?(a=a.D.get(e?c:b))&&a.get(e?b:c):a.map.get(c);return a};M.prototype.remove=function(a,c){const b=this.h.size&&(this.fastupdate?this.h.get(a):this.h.has(a));if(b){if(this.fastupdate)for(let e=0,d;ed.length)d.pop();else{const f=d.indexOf(a);f===b.length-1?d.pop():d.splice(f,1)}}else T(this.map,a),this.depth&&T(this.D,a);c||this.h.delete(a)}this.cache&&this.cache.remove(a);return this}; +function T(a,c){let b=0;if(a.constructor===Array)for(let e=0,d,f;eb.add(a,c)):this.add(a,c)}return this.remove(a).add(a,c)}; +function U(a){let c=0;if(a.constructor===Array)for(let b=0,e;b{f=m}));let h,k;switch(d||(d=0)){case 0:h="reg";if(this.fastupdate){k=y();for(let m of this.h.keys())k[m]=1}else k=this.h;break;case 1:h="cfg";k={doc:0,opt:this.A?1:0};break;case 2:h="map";k=this.map;break;case 3:h="ctx";k=this.D;break;default:"undefined"===typeof b&&f&&f();return}ra(a,c||this,b,h,e,d,k,f);return g}; +v.import=function(a,c){if(c)switch(z(c)&&(c=JSON.parse(c)),a){case "cfg":this.A=!!c.opt;break;case "reg":this.fastupdate=!1;this.h=c;break;case "map":this.map=c;break;case "ctx":this.D=c}};ta(M.prototype);V.prototype.add=function(a,c,b){E(a)&&(c=a,a=F(c,this.key));if(c&&(a||0===a)){if(!b&&this.h.has(a))return this.update(a,c);for(let h=0,k;hb||e)a=a.slice(e,e+b);d&&(a=ya.call(this,a));return a}} +function ya(a){const c=Array(a.length);for(let b=0,e;b{f=k}));d||(d=0);e||(e=0);if(e{f=k}));d||(d=0);e||(e=0);if(e this.H.get(h)), g = 1); - this.D && 1 < d.length && (this.R || (this.R = new RegExp("(" + this.K + ")", "g")), d = d.replace(this.R, h => this.D.get(h)), g = 1); - d && g && (d.length < this.O || this.filter && this.filter.has(d)) && (d = ""); - if (d && (this.B || this.A && 1 < d.length)) { + this.stemmer && 2 < d.length && (this.I || (this.I = new RegExp("(?!^)(" + this.D + ")$")), d = d.replace(this.I, h => this.stemmer.get(h)), g = 1); + this.matcher && 1 < d.length && (this.H || (this.H = new RegExp("(" + this.F + ")", "g")), d = d.replace(this.H, h => this.matcher.get(h)), g = 1); + d && g && (d.length < this.minlength || this.filter && this.filter.has(d)) && (d = ""); + if (d && (this.mapper || this.dedupe && 1 < d.length)) { var e = ""; for (let h = 0, k = "", n, l; h < d.length; h++) { - n = d.charAt(h), n === k && this.A || ((l = this.B && this.B.get(n)) || "" === l ? l === k && this.A || !(k = l) || (e += l) : e += k = n); + n = d.charAt(h), n === k && this.dedupe || ((l = this.mapper && this.mapper.get(n)) || "" === l ? l === k && this.dedupe || !(k = l) || (e += l) : e += k = n); } d = e; } - if (d && this.F) { - for (e = 0; d && e < this.F.length; e += 2) { - d = d.replace(this.F[e], this.F[e + 1]); + if (d && this.replacer) { + for (e = 0; d && e < this.replacer.length; e += 2) { + d = d.replace(this.replacer[e], this.replacer[e + 1]); } } d && b.push(d); } - this.L && (b = this.L(b) || b); + this.finalize && (b = this.finalize(b) || b); return b; }; -function fa(a, c, b, e, f, d, g, h) { +const K = {normalize:function(a) { + return a.toLowerCase(); +}, dedupe:!1}; +const L = new Map([["b", "p"], ["v", "f"], ["w", "f"], ["z", "s"], ["x", "s"], ["d", "t"], ["n", "m"], ["c", "k"], ["g", "k"], ["j", "k"], ["q", "k"], ["i", "e"], ["y", "e"], ["u", "o"]]); +const M = new Map([["ai", "ei"], ["ae", "a"], ["oe", "o"], ["ue", "u"], ["sh", "s"], ["ch", "c"], ["th", "t"], ["ph", "f"], ["pf", "f"]]), ha = [/([^aeo])h([aeo$])/g, "$1$2", /([aeo])h([^aeo]|$)/g, "$1$2"]; +const ia = {a:"", e:"", i:"", o:"", u:"", y:"", b:1, f:1, p:1, v:1, c:2, g:2, j:2, k:2, q:2, s:2, x:2, z:2, "\u00df":2, d:3, t:3, l:4, m:5, n:5, r:6}; +const ja = /[\x00-\x7F]+/g; +const ka = /[\x00-\x7F]+/g; +const la = /[\x00-\x7F]+/g; +var ma = {LatinExact:{normalize:!1, dedupe:!1}, LatinDefault:K, LatinSimple:{normalize:!0, dedupe:!0}, LatinBalance:{normalize:!0, dedupe:!0, mapper:L}, LatinAdvanced:{normalize:!0, dedupe:!0, mapper:L, replacer:ha, matcher:M}, LatinExtra:{normalize:!0, dedupe:!0, mapper:L, replacer:ha.concat([/(?!^)[aeoy]/g, ""]), matcher:M}, LatinSoundex:{normalize:!0, dedupe:!1, include:{letter:!0}, finalize:function(a) { + for (let b = 0; b < a.length; b++) { + var c = a[b]; + let e = c.charAt(0), f = ia[e]; + for (let d = 1, g; d < c.length && (g = c.charAt(d), "h" === g || "w" === g || !(g = ia[g]) || g === f || (e += g, f = g, 4 !== e.length)); d++) { + } + a[b] = e; + } +}}, ArabicDefault:{rtl:!0, normalize:!1, dedupe:!0, prepare:function(a) { + return ("" + a).replace(ja, " "); +}}, CjkDefault:{normalize:!1, dedupe:!0, split:"", prepare:function(a) { + return ("" + a).replace(ka, ""); +}}, CyrillicDefault:{normalize:!1, dedupe:!0, prepare:function(a) { + return ("" + a).replace(la, " "); +}}}; +function na(a, c, b, e, f, d, g, h) { (e = a(b ? b + "." + e : e, JSON.stringify(g))) && e.then ? e.then(function() { c.export(a, c, b, f, d + 1, h); }) : c.export(a, c, b, f, d + 1, h); } -;var ha = {normalize:function(a) { - return a.toLowerCase(); -}, A:!1}; -const ia = {memory:{resolution:1}, performance:{resolution:6, fastupdate:!0, context:{depth:1, resolution:3}}, match:{tokenize:"forward"}, score:{resolution:9, context:{depth:2, resolution:9}}}; -function ja(a) { - L.call(a, "add"); - L.call(a, "append"); - L.call(a, "search"); - L.call(a, "update"); - L.call(a, "remove"); +;const oa = {memory:{resolution:1}, performance:{resolution:6, fastupdate:!0, context:{depth:1, resolution:3}}, match:{tokenize:"forward"}, score:{resolution:9, context:{depth:2, resolution:9}}}; +function pa(a) { + N.call(a, "add"); + N.call(a, "append"); + N.call(a, "search"); + N.call(a, "update"); + N.call(a, "remove"); } -function L(a) { +function N(a) { this[a + "Async"] = function() { var c = arguments; const b = c[c.length - 1]; @@ -200,7 +222,7 @@ function L(a) { }; } ;y(); -M.prototype.add = function(a, c, b, e) { +O.prototype.add = function(a, c, b, e) { if (c && (a || 0 === a)) { if (!e && !b && this.h.has(a)) { return this.update(a, c); @@ -212,15 +234,15 @@ M.prototype.add = function(a, c, b, e) { let m = c[this.rtl ? e - 1 - p : p]; var f = m.length; if (f && (r || !l[m])) { - var d = this.score ? this.score(c, m, p, null, 0) : N(t, e, p), g = ""; + var d = this.score ? this.score(c, m, p, null, 0) : P(t, e, p), g = ""; switch(this.tokenize) { case "full": if (2 < f) { for (d = 0; d < f; d++) { for (var h = f; h > d; h--) { g = m.substring(d, h); - var k = this.score ? this.score(c, m, p, g, d) : N(t, e, p, f, d); - O(this, l, g, k, a, b); + var k = this.score ? this.score(c, m, p, g, d) : P(t, e, p, f, d); + Q(this, l, g, k, a, b); } } break; @@ -228,24 +250,24 @@ M.prototype.add = function(a, c, b, e) { case "reverse": if (1 < f) { for (h = f - 1; 0 < h; h--) { - g = m[h] + g, k = this.score ? this.score(c, m, p, g, h) : N(t, e, p, f, h), O(this, l, g, k, a, b); + g = m[h] + g, k = this.score ? this.score(c, m, p, g, h) : P(t, e, p, f, h), Q(this, l, g, k, a, b); } g = ""; } case "forward": if (1 < f) { for (h = 0; h < f; h++) { - g += m[h], O(this, l, g, d, a, b); + g += m[h], Q(this, l, g, d, a, b); } break; } default: - if (O(this, l, m, d, a, b), r && 1 < e && p < e - 1) { - for (f = y(), g = this.W, d = m, h = Math.min(r + 1, e - p), f[d] = 1, k = 1; k < h; k++) { + if (Q(this, l, m, d, a, b), r && 1 < e && p < e - 1) { + for (f = y(), g = this.K, d = m, h = Math.min(r + 1, e - p), f[d] = 1, k = 1; k < h; k++) { if ((m = c[this.rtl ? e - 1 - p - k : p + k]) && !f[m]) { f[m] = 1; - const u = this.score ? this.score(c, d, p, m, k) : N(g + (e / 2 > g ? 0 : 1), e, p, h - 1, k - 1), B = this.bidirectional && m > d; - O(this, n, B ? d : m, u, a, b, B ? m : d); + const u = this.score ? this.score(c, d, p, m, k) : P(g + (e / 2 > g ? 0 : 1), e, p, h - 1, k - 1), B = this.bidirectional && m > d; + Q(this, n, B ? d : m, u, a, b, B ? m : d); } } } @@ -257,14 +279,14 @@ M.prototype.add = function(a, c, b, e) { } return this; }; -function O(a, c, b, e, f, d, g) { - let h = g ? a.G : a.map, k; +function Q(a, c, b, e, f, d, g) { + let h = g ? a.B : a.map, k; c[b] && g && (k = c[b])[g] || (g ? (c = k || (c[b] = y()), c[g] = 1, (k = h.get(g)) ? h = k : h.set(g, h = new Map())) : c[b] = 1, (k = h.get(b)) ? h = k : h.set(b, h = []), h = h[e] || (h[e] = []), d && h.includes(f) || (h.push(f), a.fastupdate && ((c = a.h.get(f)) ? c.push(h) : a.h.set(f, [h])))); } -function N(a, c, b, e, f) { +function P(a, c, b, e, f) { return b && 1 < a ? c + (e || 0) <= a ? b + (f || 0) : (a - 1) / (c + (e || 0)) * (b + (f || 0)) + 1 | 0 : 0; } -;function P(a, c, b) { +;function R(a, c, b) { if (1 === a.length) { return a = a[0], a = b || a.length > c ? c ? a.slice(b, b + c) : a.slice(b) : a; } @@ -294,7 +316,7 @@ function N(a, c, b, e, f) { } return e.length ? e = 1 < e.length ? [].concat.apply([], e) : e[0] : e; } -;function ka(a, c, b, e) { +;function qa(a, c, b, e) { var f = a.length; let d = [], g = 0, h, k, n; e && (e = []); @@ -355,7 +377,7 @@ function N(a, c, b, e, f) { } return d; } -function la(a, c) { +function ra(a, c) { const b = y(), e = y(), f = []; for (let d = 0; d < a.length; d++) { b[a[d]] = 1; @@ -368,7 +390,7 @@ function la(a, c) { } return f; } -;M.prototype.search = function(a, c, b) { +;O.prototype.search = function(a, c, b) { b || (!c && E(a) ? (b = a, a = "") : E(c) && (b = c, c = 0)); let e = []; let f, d = 0; @@ -383,11 +405,11 @@ function la(a, c) { b = a.length; c || (c = 100); if (1 === b) { - return Q.call(this, a[0], "", c, d); + return S.call(this, a[0], "", c, d); } g = this.depth && !1 !== g; if (2 === b && g && !f) { - return Q.call(this, a[0], a[1], c, d); + return S.call(this, a[0], a[1], c, d); } let h = 0, k = 0; if (1 < b) { @@ -395,7 +417,7 @@ function la(a, c) { const r = []; for (let t = 0, p; t < b; t++) { if ((p = a[t]) && !n[p]) { - if (f || R(this, p)) { + if (f || T(this, p)) { r.push(p), n[p] = 1; } else { return e; @@ -414,15 +436,15 @@ function la(a, c) { n = 0; let l; if (1 === b) { - return Q.call(this, a[0], "", c, d); + return S.call(this, a[0], "", c, d); } if (2 === b && g && !f) { - return Q.call(this, a[0], a[1], c, d); + return S.call(this, a[0], a[1], c, d); } 1 < b && (g ? (l = a[0], n = 1) : 9 < h && 3 < h / k && a.sort(aa)); for (let r, t; n < b; n++) { t = a[n]; - l ? (r = R(this, t, l), r = ma(r, e, f, this.W, c, d, 2 === b), f && !1 === r && e.length || (l = t)) : (r = R(this, t), r = ma(r, e, f, this.resolution, c, d, 1 === b)); + l ? (r = T(this, t, l), r = sa(r, e, f, this.K, c, d, 2 === b), f && !1 === r && e.length || (l = t)) : (r = T(this, t), r = sa(r, e, f, this.resolution, c, d, 1 === b)); if (r) { return r; } @@ -437,16 +459,16 @@ function la(a, c) { return e; } if (1 === g) { - return P(e[0], c, d); + return R(e[0], c, d); } } } - return ka(e, c, d, f); + return qa(e, c, d, f); }; -function Q(a, c, b, e) { - return (a = R(this, a, c)) && a.length ? P(a, b, e) : []; +function S(a, c, b, e) { + return (a = T(this, a, c)) && a.length ? R(a, b, e) : []; } -function ma(a, c, b, e, f, d, g) { +function sa(a, c, b, e, f, d, g) { let h = []; if (a) { e = Math.min(a.length, e); @@ -459,7 +481,7 @@ function ma(a, c, b, e, f, d, g) { } if (h.length) { if (g) { - return P(h, f, 0); + return R(h, f, 0); } c.push(h); return; @@ -467,13 +489,13 @@ function ma(a, c, b, e, f, d, g) { } return !b && h; } -function R(a, c, b) { +function T(a, c, b) { let e; b && (e = a.bidirectional && c > b); - a = b ? (a = a.G.get(e ? c : b)) && a.get(e ? b : c) : a.map.get(c); + a = b ? (a = a.B.get(e ? c : b)) && a.get(e ? b : c) : a.map.get(c); return a; } -;M.prototype.remove = function(a, c) { +;O.prototype.remove = function(a, c) { const b = this.h.size && (this.fastupdate ? this.h.get(a) : this.h.has(a)); if (b) { if (this.fastupdate) { @@ -488,13 +510,13 @@ function R(a, c, b) { } } } else { - S(this.map, a), this.depth && S(this.G, a); + U(this.map, a), this.depth && U(this.B, a); } c || this.h.delete(a); } return this; }; -function S(a, c) { +function U(a, c) { let b = 0; if (a.constructor === Array) { for (let e = 0, f, d; e < a.length; e++) { @@ -509,25 +531,25 @@ function S(a, c) { } } else { for (let e of a) { - const f = e[0], d = S(e[1], c); + const f = e[0], d = U(e[1], c); d ? b += d : a.delete(f); } } return b; } -;function M(a, c) { - if (!(this instanceof M)) { - return new M(a); +;function O(a, c) { + if (!this) { + return new O(a); } if (a) { var b = z(a) ? a : a.preset; - b && (ia[b] || console.warn("Preset not found: " + b), a = Object.assign({}, ia[b], a)); + b && (oa[b] || console.warn("Preset not found: " + b), a = Object.assign({}, oa[b], a)); } else { a = {}; } b = a.context || {}; - const e = a.encode || a.encoder || ha; - this.encoder = e.encode ? e : "object" === typeof e ? new K(e) : {encode:e}; + const e = a.encode || a.encoder || K; + this.encoder = e.encode ? e : "object" === typeof e ? new J(e) : {encode:e}; let f; this.resolution = a.resolution || 9; this.tokenize = f = a.tokenize || "strict"; @@ -536,15 +558,15 @@ function S(a, c) { this.fastupdate = !!a.fastupdate; this.score = a.score || null; this.map = new Map(); - this.G = new Map(); + this.B = new Map(); this.h = c || (this.fastupdate ? new Map() : new Set()); - this.W = b.resolution || 1; + this.K = b.resolution || 1; this.rtl = e.rtl || a.rtl || !1; } -w = M.prototype; +w = O.prototype; w.clear = function() { this.map.clear(); - this.G.clear(); + this.B.clear(); this.h.clear(); return this; }; @@ -561,7 +583,7 @@ w.update = function(a, c) { } return this.remove(a).add(a, c); }; -function T(a) { +function V(a) { let c = 0; if (a.constructor === Array) { for (let b = 0, e; b < a.length; b++) { @@ -569,7 +591,7 @@ function T(a) { } } else { for (const b of a) { - const e = b[0], f = T(b[1]); + const e = b[0], f = V(b[1]); f ? c += f : a.delete(e); } } @@ -579,8 +601,8 @@ w.cleanup = function() { if (!this.fastupdate) { return console.info('Cleanup the index isn\'t required when not using "fastupdate".'), this; } - T(this.map); - this.depth && T(this.G); + V(this.map); + this.depth && V(this.B); return this; }; w.export = function(a, c, b, e, f, d) { @@ -603,7 +625,7 @@ w.export = function(a, c, b, e, f, d) { break; case 1: h = "cfg"; - k = {doc:0, opt:this.K ? 1 : 0}; + k = {doc:0, opt:this.F ? 1 : 0}; break; case 2: h = "map"; @@ -611,20 +633,20 @@ w.export = function(a, c, b, e, f, d) { break; case 3: h = "ctx"; - k = this.G; + k = this.B; break; default: "undefined" === typeof b && d && d(); return; } - fa(a, c || this, b, h, e, f, k, d); + na(a, c || this, b, h, e, f, k, d); return g; }; w.import = function(a, c) { if (c) { switch(z(c) && (c = JSON.parse(c)), a) { case "cfg": - this.K = !!c.opt; + this.F = !!c.opt; break; case "reg": this.fastupdate = !1; @@ -634,36 +656,36 @@ w.import = function(a, c) { this.map = c; break; case "ctx": - this.G = c; + this.B = c; } } }; -ja(M.prototype); -U.prototype.add = function(a, c, b) { +pa(O.prototype); +W.prototype.add = function(a, c, b) { E(a) && (c = a, a = F(c, this.key)); if (c && (a || 0 === a)) { if (!b && this.h.has(a)) { return this.update(a, c); } for (let f = 0, d; f < this.field.length; f++) { - d = this.I[f]; + d = this.C[f]; const g = this.index.get(this.field[f]); if ("function" === typeof d) { var e = d(c); e && g.add(a, e, !1, !0); } else { - if (e = d.T, !e || e(c)) { - d instanceof String ? d = ["" + d] : z(d) && (d = [d]), V(c, d, this.N, 0, g, a, d[0], b); + if (e = d.J, !e || e(c)) { + d.constructor === String ? d = ["" + d] : z(d) && (d = [d]), X(c, d, this.G, 0, g, a, d[0], b); } } } if (this.store && (!b || !this.store.has(a))) { let f; - if (this.C) { + if (this.A) { f = y(); - for (let d = 0, g; d < this.C.length; d++) { - g = this.C[d]; - if ((b = g.T) && !b(c)) { + for (let d = 0, g; d < this.A.length; d++) { + g = this.A[d]; + if ((b = g.J) && !b(c)) { continue; } let h; @@ -672,12 +694,12 @@ U.prototype.add = function(a, c, b) { if (!h) { continue; } - g = [g.X]; - } else if (z(g) || g instanceof String) { + g = [g.L]; + } else if (z(g) || g.constructor === String) { f[g] = c[g]; continue; } - W(c, f, g, 0, g[0], h); + Y(c, f, g, 0, g[0], h); } } this.store.set(a, f || c); @@ -685,21 +707,21 @@ U.prototype.add = function(a, c, b) { } return this; }; -function W(a, c, b, e, f, d) { +function Y(a, c, b, e, f, d) { a = a[f]; if (e === b.length - 1) { c[f] = d || a; } else if (a) { if (a.constructor === Array) { for (c = c[f] = Array(a.length), f = 0; f < a.length; f++) { - W(a, c, b, e, f); + Y(a, c, b, e, f); } } else { - c = c[f] || (c[f] = y()), f = b[++e], W(a, c, b, e, f); + c = c[f] || (c[f] = y()), f = b[++e], Y(a, c, b, e, f); } } } -function V(a, c, b, e, f, d, g, h) { +function X(a, c, b, e, f, d, g, h) { if (a = a[g]) { if (e === c.length - 1) { if (a.constructor === Array) { @@ -715,15 +737,15 @@ function V(a, c, b, e, f, d, g, h) { } else { if (a.constructor === Array) { for (g = 0; g < a.length; g++) { - V(a, c, b, e, f, d, g, h); + X(a, c, b, e, f, d, g, h); } } else { - g = c[++e], V(a, c, b, e, f, d, g, h); + g = c[++e], X(a, c, b, e, f, d, g, h); } } } } -;U.prototype.search = function(a, c, b, e) { +;W.prototype.search = function(a, c, b, e) { b || (!c && E(a) ? (b = a, a = "") : E(c) && (b = c, c = 0)); let f = []; var d = []; @@ -781,7 +803,7 @@ function V(a, c, b, e, f, d, g, h) { e = []; if (m.length) { for (d = 0; d < m.length; d += 2) { - t = na.call(this, m[d], m[d + 1], c, p, g), f.push({field:m[d], tag:m[d + 1], result:t}); + t = ta.call(this, m[d], m[d + 1], c, p, g), f.push({field:m[d], tag:m[d + 1], result:t}); } } return e.length ? Promise.all(e).then(function(v) { @@ -816,23 +838,23 @@ function V(a, c, b, e, f, d, g, h) { if (l && D) { m = []; u = 0; - for (let I = 0, J, sa; I < l.length; I += 2) { - J = this.tag.get(l[I]); - if (!J) { - if (console.warn("Tag '" + l[I] + ":" + l[I + 1] + "' will be skipped because there is no field '" + l[I] + "'."), k) { + for (let G = 0, H, va; G < l.length; G += 2) { + H = this.tag.get(l[G]); + if (!H) { + if (console.warn("Tag '" + l[G] + ":" + l[G + 1] + "' will be skipped because there is no field '" + l[G] + "'."), k) { continue; } else { return f; } } - if (sa = (J = J && J.get(l[I + 1])) && J.length) { - u++, m.push(J); + if (va = (H = H && H.get(l[G + 1])) && H.length) { + u++, m.push(H); } else if (!k) { return f; } } if (u) { - q = la(q, m); + q = ra(q, m); D = q.length; if (!D && !k) { return f; @@ -861,15 +883,15 @@ function V(a, c, b, e, f, d, g, h) { p = []; for (let v = 0, q; v < d.length; v++) { q = f[v]; - g && q.length && !q[0].doc && q.length && (q = oa.call(this, q)); + g && q.length && !q[0].doc && q.length && (q = ua.call(this, q)); if (t) { return q; } f[v] = {field:d[v], result:q}; } - return h ? pa(f, c) : f; + return h ? wa(f, c) : f; }; -function pa(a, c) { +function wa(a, c) { const b = [], e = y(); for (let f = 0, d, g; f < a.length; f++) { d = a[f]; @@ -888,7 +910,7 @@ function pa(a, c) { } return b; } -function na(a, c, b, e, f) { +function ta(a, c, b, e, f) { let d = this.tag.get(a); if (!d) { return console.warn("Tag '" + a + "' was not found"), []; @@ -897,47 +919,47 @@ function na(a, c, b, e, f) { if (a > b || e) { d = d.slice(e, e + b); } - f && (d = oa.call(this, d)); + f && (d = ua.call(this, d)); return d; } } -function oa(a) { +function ua(a) { const c = Array(a.length); for (let b = 0, e; b < a.length; b++) { e = a[b], c[b] = {id:e, doc:this.store.get(e)}; } return c; } -;function U(a) { - if (!(this instanceof U)) { - return new U(a); +;function W(a) { + if (!this) { + return new W(a); } const c = a.document || a.doc || a; var b; - this.I = []; + this.C = []; this.field = []; - this.N = []; - this.key = (b = c.key || c.id) && X(b, this.N) || "id"; + this.G = []; + this.key = (b = c.key || c.id) && Z(b, this.G) || "id"; this.h = (this.fastupdate = !!a.fastupdate) ? new Map() : new Set(); - this.C = (b = c.store || null) && !0 !== b && []; + this.A = (b = c.store || null) && !0 !== b && []; this.store = b && new Map(); this.async = !1; b = new Map(); let e = c.index || c.field || c; z(e) && (e = [e]); for (let f = 0, d, g; f < e.length; f++) { - d = e[f], z(d) || (g = d, d = d.field), g = E(g) ? Object.assign({}, a, g) : a, b.set(d, new M(g, this.h)), g.M ? this.I[f] = g.M : (this.I[f] = X(d, this.N), g.filter && ("string" === typeof this.I[f] && (this.I[f] = new String(this.I[f])), this.I[f].T = g.filter)), this.field[f] = d; + d = e[f], z(d) || (g = d, d = d.field), g = E(g) ? Object.assign({}, a, g) : a, b.set(d, new O(g, this.h)), g.custom ? this.C[f] = g.custom : (this.C[f] = Z(d, this.G), g.filter && ("string" === typeof this.C[f] && (this.C[f] = new String(this.C[f])), this.C[f].J = g.filter)), this.field[f] = d; } - if (this.C) { + if (this.A) { a = c.store; z(a) && (a = [a]); for (let f = 0, d, g; f < a.length; f++) { - d = a[f], g = d.field || d, d.M ? (this.C[f] = d.M, d.M.X = g) : (this.C[f] = X(g, this.N), d.filter && ("string" === typeof this.C[f] && (this.C[f] = new String(this.C[f])), this.C[f].T = d.filter)); + d = a[f], g = d.field || d, d.custom ? (this.A[f] = d.custom, d.custom.L = g) : (this.A[f] = Z(g, this.G), d.filter && ("string" === typeof this.A[f] && (this.A[f] = new String(this.A[f])), this.A[f].J = d.filter)); } } this.index = b; } -function X(a, c) { +function Z(a, c) { const b = a.split(":"); let e = 0; for (let f = 0; f < b.length; f++) { @@ -946,7 +968,7 @@ function X(a, c) { e < b.length && (b.length = e); return 1 < e ? b : b[0]; } -w = U.prototype; +w = W.prototype; w.append = function(a, c) { return this.add(a, c, !0); }; @@ -1000,7 +1022,7 @@ w.export = function(a, c, b, e, f, d) { switch(f) { case 1: c = "tag"; - h = this.J; + h = this.D; b = null; break; case 2: @@ -1012,7 +1034,7 @@ w.export = function(a, c, b, e, f, d) { d(); return; } - fa(a, this, b, c, e, f, h, d); + na(a, this, b, c, e, f, h, d); } return g; }; @@ -1020,7 +1042,7 @@ w.import = function(a, c) { if (c) { switch(z(c) && (c = JSON.parse(c)), a) { case "tag": - this.J = c; + this.D = c; break; case "reg": this.fastupdate = !1; @@ -1040,29 +1062,7 @@ w.import = function(a, c) { } } }; -ja(U.prototype); -const Y = y(); -const Z = new Map([["b", "p"], ["v", "f"], ["w", "f"], ["z", "s"], ["x", "s"], ["d", "t"], ["n", "m"], ["c", "k"], ["g", "k"], ["j", "k"], ["q", "k"], ["i", "e"], ["y", "e"], ["u", "o"]]); -var qa = {normalize:!0, A:!0, B:Z}; -const ra = new Map([["ai", "ei"], ["ae", "a"], ["oe", "o"], ["ue", "u"], ["sh", "s"], ["ch", "c"], ["th", "t"], ["ph", "f"], ["pf", "f"]]), ta = [/([^aeo])h([aeo$])/g, "$1$2", /([aeo])h([^aeo]|$)/g, "$1$2"]; -var ua = {normalize:!0, A:!0, B:Z, F:ta, D:ra}; -var va = {normalize:!0, A:!0, B:Z, F:ta.concat([/(?!^)[aeoy]/g, ""]), D:ra}; -const wa = {a:"", e:"", i:"", o:"", u:"", y:"", b:1, f:1, p:1, v:1, c:2, g:2, j:2, k:2, q:2, s:2, x:2, z:2, "\u00df":2, d:3, t:3, l:4, m:5, n:5, r:6}; -Y["latin:exact"] = {normalize:!1, A:!1}; -Y["latin:default"] = ha; -Y["latin:simple"] = {normalize:!0, A:!0}; -Y["latin:balance"] = qa; -Y["latin:advanced"] = ua; -Y["latin:extra"] = va; -Y["latin:soundex"] = {normalize:!0, A:!1, U:{Y:!0}, L:function(a) { - for (let b = 0; b < a.length; b++) { - var c = a[b]; - let e = c.charAt(0), f = wa[e]; - for (let d = 1, g; d < c.length && (g = c.charAt(d), "h" === g || "w" === g || !(g = wa[g]) || g === f || (e += g, f = g, 4 !== e.length)); d++) { - } - a[b] = e; - } -}}; -export default {Index:M, Charset:Y, Encoder:K, Document:U, Worker:null, Resolver:null, IndexedDB:null}; +pa(W.prototype); +export default {Index:O, Charset:ma, Encoder:J, Document:W, Worker:null, Resolver:null, IndexedDB:null, Language:{}}; -export const Index=M;export const Charset=Y;export const Encoder=K;export const Document=U;export const Worker=null;export const Resolver=null;export const IndexedDB=null; \ No newline at end of file +export const Index=O;export const Charset=ma;export const Encoder=J;export const Document=W;export const Worker=null;export const Resolver=null;export const IndexedDB=null;export const Language={}; \ No newline at end of file diff --git a/dist/flexsearch.compact.module.min.js b/dist/flexsearch.compact.module.min.js index 85c9e92..d9c6ba3 100644 --- a/dist/flexsearch.compact.module.min.js +++ b/dist/flexsearch.compact.module.min.js @@ -6,39 +6,40 @@ * https://github.com/nextapps-de/flexsearch */ var w;function x(a,b,c){const d=typeof c,e=typeof a;if("undefined"!==d){if("undefined"!==e){if(c){if("function"===e&&d===e)return function(h){return a(c(h))};b=a.constructor;if(b===c.constructor){if(b===Array)return c.concat(a);if(b===Map){var f=new Map(c);for(var g of a)f.set(g[0],g[1]);return f}if(b===Set){g=new Set(c);for(f of a.values())g.add(f);return g}}}return a}return c}return"undefined"===e?b:a}function y(){return Object.create(null)}function aa(a,b){return b.length-a.length} -function A(a){return"string"===typeof a}function E(a){return"object"===typeof a}function F(a,b){if(A(b))a=a[b];else for(let c=0;a&&cthis.H.get(h)),g=1);this.D&&1this.D.get(h)),g=1);f&&g&&(f.lengthf;h--){g=m.substring(f,h);var k=this.score?this.score(b,m,p,g,f):M(t,d,p,e,f);N(this,l,g,k,a,c)}break}case "reverse":if(1< -e){for(h=e-1;0g?0:1),d,p,h-1,k-1),B=this.bidirectional&&m>f;N(this,n,B?f:m,u,a,c,B?m:f)}}}}this.fastupdate||this.h.add(a)}}return this}; -function N(a,b,c,d,e,f,g){let h=g?a.G:a.map,k;b[c]&&g&&(k=b[c])[g]||(g?(b=k||(b[c]=y()),b[g]=1,(k=h.get(g))?h=k:h.set(g,h=new Map)):b[c]=1,(k=h.get(c))?h=k:h.set(c,h=[]),h=h[d]||(h[d]=[]),f&&h.includes(e)||(h.push(e),a.fastupdate&&((b=a.h.get(e))?b.push(h):a.h.set(e,[h]))))}function M(a,b,c,d,e){return c&&1b?b?a.slice(c,c+b):a.slice(c):a;let d=[];for(let e=0,f,g;e=g){c-=g;continue}cb&&(f=f.slice(0,b),g=f.length),d.push(f);else{if(g>=b)return g>b&&(f=f.slice(0,b)),f;d=[f]}b-=g;if(!b)break}return d.length?d=1=e)))break;if(h.length){if(g)return O(h,e,0);b.push(h);return}}return!c&&h}function R(a,b,c){let d;c&&(d=a.bidirectional&&b>c);a=c?(a=a.G.get(d?b:c))&&a.get(d?c:b):a.map.get(b);return a};L.prototype.remove=function(a,b){const c=this.h.size&&(this.fastupdate?this.h.get(a):this.h.has(a));if(c){if(this.fastupdate)for(let d=0,e;de.length)e.pop();else{const f=e.indexOf(a);f===c.length-1?e.pop():e.splice(f,1)}}else S(this.map,a),this.depth&&S(this.G,a);b||this.h.delete(a)}return this}; -function S(a,b){let c=0;if(a.constructor===Array)for(let d=0,e,f;dc.add(a,b)):this.add(a,b)}return this.remove(a).add(a,b)}; -function T(a){let b=0;if(a.constructor===Array)for(let c=0,d;c{f=n}));let h,k;switch(e||(e=0)){case 0:h="reg";if(this.fastupdate){k=y();for(let n of this.h.keys())k[n]=1}else k=this.h;break;case 1:h="cfg";k={doc:0,opt:this.K?1:0};break;case 2:h="map";k=this.map;break;case 3:h="ctx";k=this.G;break;default:"undefined"===typeof c&&f&&f();return}fa(a,b||this,c,h,d,e,k,f);return g}; -w.import=function(a,b){if(b)switch(A(b)&&(b=JSON.parse(b)),a){case "cfg":this.K=!!b.opt;break;case "reg":this.fastupdate=!1;this.h=b;break;case "map":this.map=b;break;case "ctx":this.G=b}};ja(L.prototype);U.prototype.add=function(a,b,c){E(a)&&(b=a,a=F(b,this.key));if(b&&(a||0===a)){if(!c&&this.h.has(a))return this.update(a,b);for(let e=0,f;ec||d)a=a.slice(d,d+c);e&&(a=oa.call(this,a));return a}} -function oa(a){const b=Array(a.length);for(let c=0,d;cthis.stemmer.get(h)),g=1);this.matcher&&1this.matcher.get(h)),g=1);f&&g&&(f.lengthf;h--){g=m.substring(f,h);var k=this.score?this.score(b,m,p,g,f):P(t,d,p,e,f);Q(this,l,g,k,a,c)}break}case "reverse":if(1< +e){for(h=e-1;0g?0:1),d,p,h-1,k-1),B=this.bidirectional&&m>f;Q(this,n,B?f:m,u,a,c,B?m:f)}}}}this.fastupdate||this.h.add(a)}}return this}; +function Q(a,b,c,d,e,f,g){let h=g?a.B:a.map,k;b[c]&&g&&(k=b[c])[g]||(g?(b=k||(b[c]=y()),b[g]=1,(k=h.get(g))?h=k:h.set(g,h=new Map)):b[c]=1,(k=h.get(c))?h=k:h.set(c,h=[]),h=h[d]||(h[d]=[]),f&&h.includes(e)||(h.push(e),a.fastupdate&&((b=a.h.get(e))?b.push(h):a.h.set(e,[h]))))}function P(a,b,c,d,e){return c&&1b?b?a.slice(c,c+b):a.slice(c):a;let d=[];for(let e=0,f,g;e=g){c-=g;continue}cb&&(f=f.slice(0,b),g=f.length),d.push(f);else{if(g>=b)return g>b&&(f=f.slice(0,b)),f;d=[f]}b-=g;if(!b)break}return d.length?d=1=e)))break;if(h.length){if(g)return R(h,e,0);b.push(h);return}}return!c&&h}function T(a,b,c){let d;c&&(d=a.bidirectional&&b>c);a=c?(a=a.B.get(d?b:c))&&a.get(d?c:b):a.map.get(b);return a};N.prototype.remove=function(a,b){const c=this.h.size&&(this.fastupdate?this.h.get(a):this.h.has(a));if(c){if(this.fastupdate)for(let d=0,e;de.length)e.pop();else{const f=e.indexOf(a);f===c.length-1?e.pop():e.splice(f,1)}}else U(this.map,a),this.depth&&U(this.B,a);b||this.h.delete(a)}return this}; +function U(a,b){let c=0;if(a.constructor===Array)for(let d=0,e,f;dc.add(a,b)):this.add(a,b)}return this.remove(a).add(a,b)}; +function V(a){let b=0;if(a.constructor===Array)for(let c=0,d;c{f=n}));let h,k;switch(e||(e=0)){case 0:h="reg";if(this.fastupdate){k=y();for(let n of this.h.keys())k[n]=1}else k=this.h;break;case 1:h="cfg";k={doc:0,opt:this.F?1:0};break;case 2:h="map";k=this.map;break;case 3:h="ctx";k=this.B;break;default:"undefined"===typeof c&&f&&f();return}na(a,b||this,c,h,d,e,k,f);return g}; +w.import=function(a,b){if(b)switch(A(b)&&(b=JSON.parse(b)),a){case "cfg":this.F=!!b.opt;break;case "reg":this.fastupdate=!1;this.h=b;break;case "map":this.map=b;break;case "ctx":this.B=b}};pa(N.prototype);W.prototype.add=function(a,b,c){E(a)&&(b=a,a=F(b,this.key));if(b&&(a||0===a)){if(!c&&this.h.has(a))return this.update(a,b);for(let e=0,f;ec||d)a=a.slice(d,d+c);e&&(a=ua.call(this,a));return a}} +function ua(a){const b=Array(a.length);for(let c=0,d;c{f=k}));e||(e=0);d||(d=0);if(d{f=k}));e||(e=0);d||(d=0);if(d>> 0) + "_", e = 0; return b; }); -B("Symbol.iterator", function(a) { +C("Symbol.iterator", function(a) { if (a) { return a; } @@ -235,24 +235,24 @@ B("Symbol.iterator", function(a) { for (var b = "Array Int8Array Uint8Array Uint8ClampedArray Int16Array Uint16Array Int32Array Uint32Array Float32Array Float64Array".split(" "), c = 0; c < b.length; c++) { var d = z[b[c]]; "function" === typeof d && "function" != typeof d.prototype[a] && ba(d.prototype, a, {configurable:!0, writable:!0, value:function() { - return ua(aa(this)); + return ta(aa(this)); }}); } return a; }); -function ua(a) { +function ta(a) { a = {next:a}; a[Symbol.iterator] = function() { return this; }; return a; } -B("Promise", function(a) { +C("Promise", function(a) { function b(f) { this.A = 0; this.C = void 0; this.h = []; - this.R = !1; + this.L = !1; var h = this.D(); try { f(h.resolve, h.reject); @@ -313,14 +313,14 @@ B("Promise", function(a) { }; } var h = this, k = !1; - return {resolve:f(this.wa), reject:f(this.G)}; + return {resolve:f(this.ka), reject:f(this.G)}; }; - b.prototype.wa = function(f) { + b.prototype.ka = function(f) { if (f === this) { this.G(new TypeError("A Promise cannot resolve to itself")); } else { if (f instanceof b) { - this.ya(f); + this.ma(f); } else { a: { switch(typeof f) { @@ -334,11 +334,11 @@ B("Promise", function(a) { h = !1; } } - h ? this.va(f) : this.J(f); + h ? this.ja(f) : this.H(f); } } }; - b.prototype.va = function(f) { + b.prototype.ja = function(f) { var h = void 0; try { h = f.then; @@ -346,34 +346,34 @@ B("Promise", function(a) { this.G(k); return; } - "function" == typeof h ? this.za(h, f) : this.J(f); + "function" == typeof h ? this.na(h, f) : this.H(f); }; b.prototype.G = function(f) { - this.pa(2, f); + this.fa(2, f); }; - b.prototype.J = function(f) { - this.pa(1, f); + b.prototype.H = function(f) { + this.fa(1, f); }; - b.prototype.pa = function(f, h) { + b.prototype.fa = function(f, h) { if (0 != this.A) { throw Error("Cannot settle(" + f + ", " + h + "): Promise already settled in state" + this.A); } this.A = f; this.C = h; - 2 === this.A && this.xa(); - this.ta(); + 2 === this.A && this.la(); + this.ha(); }; - b.prototype.xa = function() { + b.prototype.la = function() { var f = this; e(function() { - if (f.ua()) { + if (f.ia()) { var h = z.console; "undefined" !== typeof h && h.error(f.C); } }, 1); }; - b.prototype.ua = function() { - if (this.R) { + b.prototype.ia = function() { + if (this.L) { return !1; } var f = z.CustomEvent, h = z.Event, k = z.dispatchEvent; @@ -385,7 +385,7 @@ B("Promise", function(a) { f.reason = this.C; return k(f); }; - b.prototype.ta = function() { + b.prototype.ha = function() { if (null != this.h) { for (var f = 0; f < this.h.length; ++f) { g.A(this.h[f]); @@ -394,11 +394,11 @@ B("Promise", function(a) { } }; var g = new c(); - b.prototype.ya = function(f) { + b.prototype.ma = function(f) { var h = this.D(); - f.fa(h.resolve, h.reject); + f.Y(h.resolve, h.reject); }; - b.prototype.za = function(f, h) { + b.prototype.na = function(f, h) { var k = this.D(); try { f.call(h, k.resolve, k.reject); @@ -420,13 +420,13 @@ B("Promise", function(a) { l = p; m = q; }); - this.fa(k(f, l), k(h, m)); + this.Y(k(f, l), k(h, m)); return n; }; b.prototype.catch = function(f) { return this.then(void 0, f); }; - b.prototype.fa = function(f, h) { + b.prototype.Y = function(f, h) { function k() { switch(l.A) { case 1: @@ -441,7 +441,7 @@ B("Promise", function(a) { } var l = this; null == this.h ? g.A(k) : this.h.push(k); - this.R = !0; + this.L = !0; }; b.resolve = d; b.reject = function(f) { @@ -452,7 +452,7 @@ B("Promise", function(a) { b.race = function(f) { return new b(function(h, k) { for (var l = y(f), m = l.next(); !m.done; m = l.next()) { - d(m.value).fa(h, k); + d(m.value).Y(h, k); } }); }; @@ -468,13 +468,13 @@ B("Promise", function(a) { } var p = [], q = 0; do { - p.push(void 0), q++, d(k.value).fa(n(p.length - 1), m), k = h.next(); + p.push(void 0), q++, d(k.value).Y(n(p.length - 1), m), k = h.next(); } while (!k.done); }); }; return b; }); -function va(a, b) { +function ua(a, b) { a instanceof String && (a += ""); var c = 0, d = !1, e = {next:function() { if (!d && c < a.length) { @@ -489,17 +489,17 @@ function va(a, b) { }; return e; } -B("Array.prototype.values", function(a) { +C("Array.prototype.values", function(a) { return a ? a : function() { - return va(this, function(b, c) { + return ua(this, function(b, c) { return c; }); }; }); -function G(a, b) { +function H(a, b) { return Object.prototype.hasOwnProperty.call(a, b); } -B("WeakMap", function(a) { +C("WeakMap", function(a) { function b(k) { this.h = (h += Math.random() + 1).toString(); if (k) { @@ -516,7 +516,7 @@ B("WeakMap", function(a) { return "object" === l && null !== k || "function" === l; } function e(k) { - if (!G(k, f)) { + if (!H(k, f)) { var l = new c(); ba(k, f, {value:l}); } @@ -559,34 +559,34 @@ B("WeakMap", function(a) { throw Error("Invalid WeakMap key"); } e(k); - if (!G(k, f)) { + if (!H(k, f)) { throw Error("WeakMap key fail: " + k); } k[f][this.h] = l; return this; }; b.prototype.get = function(k) { - return d(k) && G(k, f) ? k[f][this.h] : void 0; + return d(k) && H(k, f) ? k[f][this.h] : void 0; }; b.prototype.has = function(k) { - return d(k) && G(k, f) && G(k[f], this.h); + return d(k) && H(k, f) && H(k[f], this.h); }; b.prototype.delete = function(k) { - return d(k) && G(k, f) && G(k[f], this.h) ? delete k[f][this.h] : !1; + return d(k) && H(k, f) && H(k[f], this.h) ? delete k[f][this.h] : !1; }; return b; }); -B("Map", function(a) { +C("Map", function(a) { function b() { var h = {}; - return h.O = h.next = h.head = h; + return h.K = h.next = h.head = h; } function c(h, k) { var l = h[1]; - return ua(function() { + return ta(function() { if (l) { for (; l.head != h[1];) { - l = l.O; + l = l.K; } for (; l.next != l.head;) { return l = l.next, {done:!1, value:k(l)}; @@ -600,7 +600,7 @@ B("Map", function(a) { var l = k && typeof k; "object" == l || "function" == l ? g.has(k) ? l = g.get(k) : (l = "" + ++f, g.set(k, l)) : l = "p_" + k; var m = h[0][l]; - if (m && G(h[0], l)) { + if (m && H(h[0], l)) { for (h = 0; h < m.length; h++) { var n = m[h]; if (k !== k && n.key !== n.key || k === n.key) { @@ -647,16 +647,16 @@ B("Map", function(a) { h = 0 === h ? 0 : h; var l = d(this, h); l.list || (l.list = this[0][l.id] = []); - l.F ? l.F.value = k : (l.F = {next:this[1], O:this[1].O, head:this[1], key:h, value:k}, l.list.push(l.F), this[1].O.next = l.F, this[1].O = l.F, this.size++); + l.F ? l.F.value = k : (l.F = {next:this[1], K:this[1].K, head:this[1], key:h, value:k}, l.list.push(l.F), this[1].K.next = l.F, this[1].K = l.F, this.size++); return this; }; e.prototype.delete = function(h) { h = d(this, h); - return h.F && h.list ? (h.list.splice(h.index, 1), h.list.length || delete this[0][h.id], h.F.O.next = h.F.next, h.F.next.O = h.F.O, h.F.head = null, this.size--, !0) : !1; + return h.F && h.list ? (h.list.splice(h.index, 1), h.list.length || delete this[0][h.id], h.F.K.next = h.F.next, h.F.next.K = h.F.K, h.F.head = null, this.size--, !0) : !1; }; e.prototype.clear = function() { this[0] = {}; - this[1] = this[1].O = b(); + this[1] = this[1].K = b(); this.size = 0; }; e.prototype.has = function(h) { @@ -689,14 +689,14 @@ B("Map", function(a) { var f = 0; return e; }); -B("Array.prototype.keys", function(a) { +C("Array.prototype.keys", function(a) { return a ? a : function() { - return va(this, function(b) { + return ua(this, function(b) { return b; }); }; }); -B("Set", function(a) { +C("Set", function(a) { function b(c) { this.h = new Map(); if (c) { @@ -762,12 +762,12 @@ B("Set", function(a) { }; return b; }); -B("Object.is", function(a) { +C("Object.is", function(a) { return a ? a : function(b, c) { return b === c ? 0 !== b || 1 / b === 1 / c : b !== b && c !== c; }; }); -B("Array.prototype.includes", function(a) { +C("Array.prototype.includes", function(a) { return a ? a : function(b, c) { var d = this; d instanceof String && (d = String(d)); @@ -782,7 +782,7 @@ B("Array.prototype.includes", function(a) { return !1; }; }); -B("String.prototype.includes", function(a) { +C("String.prototype.includes", function(a) { return a ? a : function(b, c) { if (null == this) { throw new TypeError("The 'this' value for String.prototype.includes must not be null or undefined"); @@ -793,28 +793,28 @@ B("String.prototype.includes", function(a) { return -1 !== this.indexOf(b, c || 0); }; }); -B("Array.prototype.entries", function(a) { +C("Array.prototype.entries", function(a) { return a ? a : function() { - return va(this, function(b, c) { + return ua(this, function(b, c) { return [b, c]; }); }; }); -var wa = "function" == typeof Object.assign ? Object.assign : function(a, b) { +var va = "function" == typeof Object.assign ? Object.assign : function(a, b) { for (var c = 1; c < arguments.length; c++) { var d = arguments[c]; if (d) { for (var e in d) { - G(d, e) && (a[e] = d[e]); + H(d, e) && (a[e] = d[e]); } } } return a; }; -B("Object.assign", function(a) { - return a || wa; +C("Object.assign", function(a) { + return a || va; }); -B("Array.prototype.flat", function(a) { +C("Array.prototype.flat", function(a) { return a ? a : function(b) { b = void 0 === b ? 1 : b; var c = []; @@ -824,7 +824,7 @@ B("Array.prototype.flat", function(a) { return c; }; }); -function H(a, b, c) { +function I(a, b, c) { var d = typeof c, e = typeof a; if ("undefined" !== d) { if ("undefined" !== e) { @@ -863,19 +863,19 @@ function H(a, b, c) { } return "undefined" === e ? b : a; } -function I() { +function K() { return Object.create(null); } -function xa(a, b) { +function wa(a, b) { return b.length - a.length; } -function J(a) { +function L(a) { return "string" === typeof a; } -function K(a) { +function M(a) { return "object" === typeof a; } -function ya(a) { +function xa(a) { var b = []; a = y(a.keys()); for (var c = a.next(); !c.done; c = a.next()) { @@ -883,8 +883,8 @@ function ya(a) { } return b; } -function za(a, b) { - if (J(b)) { +function ya(a, b) { + if (L(b)) { a = a[b]; } else { for (var c = 0; a && c < b.length; c++) { @@ -893,23 +893,23 @@ function za(a, b) { } return a; } -function Aa(a) { +function za(a) { for (var b = 0, c = 0, d = void 0; c < a.length; c++) { (d = a[c]) && b < d.length && (b = d.length); } return b; } -;var Ba = /[^\p{L}\p{N}]+/u, Ca = /(\d{3})/g, Da = /(\D)(\d{3})/g, Ea = /(\d{3})(\D)/g, Fa = "".normalize && /[\u0300-\u036f]/g, Ga = !Fa && [["\u00aa", "a"], ["\u00b2", "2"], ["\u00b3", "3"], ["\u00b9", "1"], ["\u00ba", "o"], ["\u00bc", "1\u20444"], ["\u00bd", "1\u20442"], ["\u00be", "3\u20444"], ["\u00e0", "a"], ["\u00e1", "a"], ["\u00e2", "a"], ["\u00e3", "a"], ["\u00e4", "a"], ["\u00e5", "a"], ["\u00e7", "c"], ["\u00e8", "e"], ["\u00e9", "e"], ["\u00ea", "e"], ["\u00eb", "e"], ["\u00ec", "i"], -["\u00ed", "i"], ["\u00ee", "i"], ["\u00ef", "i"], ["\u00f1", "n"], ["\u00f2", "o"], ["\u00f3", "o"], ["\u00f4", "o"], ["\u00f5", "o"], ["\u00f6", "o"], ["\u00f9", "u"], ["\u00fa", "u"], ["\u00fb", "u"], ["\u00fc", "u"], ["\u00fd", "y"], ["\u00ff", "y"], ["\u0101", "a"], ["\u0103", "a"], ["\u0105", "a"], ["\u0107", "c"], ["\u0109", "c"], ["\u010b", "c"], ["\u010d", "c"], ["\u010f", "d"], ["\u0113", "e"], ["\u0115", "e"], ["\u0117", "e"], ["\u0119", "e"], ["\u011b", "e"], ["\u011d", "g"], ["\u011f", -"g"], ["\u0121", "g"], ["\u0123", "g"], ["\u0125", "h"], ["\u0129", "i"], ["\u012b", "i"], ["\u012d", "i"], ["\u012f", "i"], ["\u0133", "ij"], ["\u0135", "j"], ["\u0137", "k"], ["\u013a", "l"], ["\u013c", "l"], ["\u013e", "l"], ["\u0140", "l"], ["\u0144", "n"], ["\u0146", "n"], ["\u0148", "n"], ["\u0149", "n"], ["\u014d", "o"], ["\u014f", "o"], ["\u0151", "o"], ["\u0155", "r"], ["\u0157", "r"], ["\u0159", "r"], ["\u015b", "s"], ["\u015d", "s"], ["\u015f", "s"], ["\u0161", "s"], ["\u0163", "t"], ["\u0165", -"t"], ["\u0169", "u"], ["\u016b", "u"], ["\u016d", "u"], ["\u016f", "u"], ["\u0171", "u"], ["\u0173", "u"], ["\u0175", "w"], ["\u0177", "y"], ["\u017a", "z"], ["\u017c", "z"], ["\u017e", "z"], ["\u017f", "s"], ["\u01a1", "o"], ["\u01b0", "u"], ["\u01c6", "dz"], ["\u01c9", "lj"], ["\u01cc", "nj"], ["\u01ce", "a"], ["\u01d0", "i"], ["\u01d2", "o"], ["\u01d4", "u"], ["\u01d6", "u"], ["\u01d8", "u"], ["\u01da", "u"], ["\u01dc", "u"], ["\u01df", "a"], ["\u01e1", "a"], ["\u01e3", "ae"], ["\u00e6", "ae"], -["\u01fd", "ae"], ["\u01e7", "g"], ["\u01e9", "k"], ["\u01eb", "o"], ["\u01ed", "o"], ["\u01ef", "\u0292"], ["\u01f0", "j"], ["\u01f3", "dz"], ["\u01f5", "g"], ["\u01f9", "n"], ["\u01fb", "a"], ["\u01ff", "\u00f8"], ["\u0201", "a"], ["\u0203", "a"], ["\u0205", "e"], ["\u0207", "e"], ["\u0209", "i"], ["\u020b", "i"], ["\u020d", "o"], ["\u020f", "o"], ["\u0211", "r"], ["\u0213", "r"], ["\u0215", "u"], ["\u0217", "u"], ["\u0219", "s"], ["\u021b", "t"], ["\u021f", "h"], ["\u0227", "a"], ["\u0229", "e"], -["\u022b", "o"], ["\u022d", "o"], ["\u022f", "o"], ["\u0231", "o"], ["\u0233", "y"], ["\u02b0", "h"], ["\u02b1", "h"], ["\u0266", "h"], ["\u02b2", "j"], ["\u02b3", "r"], ["\u02b4", "\u0279"], ["\u02b5", "\u027b"], ["\u02b6", "\u0281"], ["\u02b7", "w"], ["\u02b8", "y"], ["\u02e0", "\u0263"], ["\u02e1", "l"], ["\u02e2", "s"], ["\u02e3", "x"], ["\u02e4", "\u0295"], ["\u0390", "\u03b9"], ["\u03ac", "\u03b1"], ["\u03ad", "\u03b5"], ["\u03ae", "\u03b7"], ["\u03af", "\u03b9"], ["\u03b0", "\u03c5"], ["\u03ca", -"\u03b9"], ["\u03cb", "\u03c5"], ["\u03cc", "\u03bf"], ["\u03cd", "\u03c5"], ["\u03ce", "\u03c9"], ["\u03d0", "\u03b2"], ["\u03d1", "\u03b8"], ["\u03d2", "\u03a5"], ["\u03d3", "\u03a5"], ["\u03d4", "\u03a5"], ["\u03d5", "\u03c6"], ["\u03d6", "\u03c0"], ["\u03f0", "\u03ba"], ["\u03f1", "\u03c1"], ["\u03f2", "\u03c2"], ["\u03f5", "\u03b5"], ["\u0439", "\u0438"], ["\u0450", "\u0435"], ["\u0451", "\u0435"], ["\u0453", "\u0433"], ["\u0457", "\u0456"], ["\u045c", "\u043a"], ["\u045d", "\u0438"], ["\u045e", -"\u0443"], ["\u0477", "\u0475"], ["\u04c2", "\u0436"], ["\u04d1", "\u0430"], ["\u04d3", "\u0430"], ["\u04d7", "\u0435"], ["\u04db", "\u04d9"], ["\u04dd", "\u0436"], ["\u04df", "\u0437"], ["\u04e3", "\u0438"], ["\u04e5", "\u0438"], ["\u04e7", "\u043e"], ["\u04eb", "\u04e9"], ["\u04ed", "\u044d"], ["\u04ef", "\u0443"], ["\u04f1", "\u0443"], ["\u04f3", "\u0443"], ["\u04f5", "\u0447"]]; -function M(a) { - a = void 0 === a ? {} : a; - if (!(this instanceof M)) { +;var Aa = [["\u00aa", "a"], ["\u00b2", "2"], ["\u00b3", "3"], ["\u00b9", "1"], ["\u00ba", "o"], ["\u00bc", "1\u20444"], ["\u00bd", "1\u20442"], ["\u00be", "3\u20444"], ["\u00e0", "a"], ["\u00e1", "a"], ["\u00e2", "a"], ["\u00e3", "a"], ["\u00e4", "a"], ["\u00e5", "a"], ["\u00e7", "c"], ["\u00e8", "e"], ["\u00e9", "e"], ["\u00ea", "e"], ["\u00eb", "e"], ["\u00ec", "i"], ["\u00ed", "i"], ["\u00ee", "i"], ["\u00ef", "i"], ["\u00f1", "n"], ["\u00f2", "o"], ["\u00f3", "o"], ["\u00f4", "o"], ["\u00f5", +"o"], ["\u00f6", "o"], ["\u00f9", "u"], ["\u00fa", "u"], ["\u00fb", "u"], ["\u00fc", "u"], ["\u00fd", "y"], ["\u00ff", "y"], ["\u0101", "a"], ["\u0103", "a"], ["\u0105", "a"], ["\u0107", "c"], ["\u0109", "c"], ["\u010b", "c"], ["\u010d", "c"], ["\u010f", "d"], ["\u0113", "e"], ["\u0115", "e"], ["\u0117", "e"], ["\u0119", "e"], ["\u011b", "e"], ["\u011d", "g"], ["\u011f", "g"], ["\u0121", "g"], ["\u0123", "g"], ["\u0125", "h"], ["\u0129", "i"], ["\u012b", "i"], ["\u012d", "i"], ["\u012f", "i"], ["\u0133", +"ij"], ["\u0135", "j"], ["\u0137", "k"], ["\u013a", "l"], ["\u013c", "l"], ["\u013e", "l"], ["\u0140", "l"], ["\u0144", "n"], ["\u0146", "n"], ["\u0148", "n"], ["\u0149", "n"], ["\u014d", "o"], ["\u014f", "o"], ["\u0151", "o"], ["\u0155", "r"], ["\u0157", "r"], ["\u0159", "r"], ["\u015b", "s"], ["\u015d", "s"], ["\u015f", "s"], ["\u0161", "s"], ["\u0163", "t"], ["\u0165", "t"], ["\u0169", "u"], ["\u016b", "u"], ["\u016d", "u"], ["\u016f", "u"], ["\u0171", "u"], ["\u0173", "u"], ["\u0175", "w"], ["\u0177", +"y"], ["\u017a", "z"], ["\u017c", "z"], ["\u017e", "z"], ["\u017f", "s"], ["\u01a1", "o"], ["\u01b0", "u"], ["\u01c6", "dz"], ["\u01c9", "lj"], ["\u01cc", "nj"], ["\u01ce", "a"], ["\u01d0", "i"], ["\u01d2", "o"], ["\u01d4", "u"], ["\u01d6", "u"], ["\u01d8", "u"], ["\u01da", "u"], ["\u01dc", "u"], ["\u01df", "a"], ["\u01e1", "a"], ["\u01e3", "ae"], ["\u00e6", "ae"], ["\u01fd", "ae"], ["\u01e7", "g"], ["\u01e9", "k"], ["\u01eb", "o"], ["\u01ed", "o"], ["\u01ef", "\u0292"], ["\u01f0", "j"], ["\u01f3", +"dz"], ["\u01f5", "g"], ["\u01f9", "n"], ["\u01fb", "a"], ["\u01ff", "\u00f8"], ["\u0201", "a"], ["\u0203", "a"], ["\u0205", "e"], ["\u0207", "e"], ["\u0209", "i"], ["\u020b", "i"], ["\u020d", "o"], ["\u020f", "o"], ["\u0211", "r"], ["\u0213", "r"], ["\u0215", "u"], ["\u0217", "u"], ["\u0219", "s"], ["\u021b", "t"], ["\u021f", "h"], ["\u0227", "a"], ["\u0229", "e"], ["\u022b", "o"], ["\u022d", "o"], ["\u022f", "o"], ["\u0231", "o"], ["\u0233", "y"], ["\u02b0", "h"], ["\u02b1", "h"], ["\u0266", "h"], +["\u02b2", "j"], ["\u02b3", "r"], ["\u02b4", "\u0279"], ["\u02b5", "\u027b"], ["\u02b6", "\u0281"], ["\u02b7", "w"], ["\u02b8", "y"], ["\u02e0", "\u0263"], ["\u02e1", "l"], ["\u02e2", "s"], ["\u02e3", "x"], ["\u02e4", "\u0295"], ["\u0390", "\u03b9"], ["\u03ac", "\u03b1"], ["\u03ad", "\u03b5"], ["\u03ae", "\u03b7"], ["\u03af", "\u03b9"], ["\u03b0", "\u03c5"], ["\u03ca", "\u03b9"], ["\u03cb", "\u03c5"], ["\u03cc", "\u03bf"], ["\u03cd", "\u03c5"], ["\u03ce", "\u03c9"], ["\u03d0", "\u03b2"], ["\u03d1", +"\u03b8"], ["\u03d2", "\u03a5"], ["\u03d3", "\u03a5"], ["\u03d4", "\u03a5"], ["\u03d5", "\u03c6"], ["\u03d6", "\u03c0"], ["\u03f0", "\u03ba"], ["\u03f1", "\u03c1"], ["\u03f2", "\u03c2"], ["\u03f5", "\u03b5"], ["\u0439", "\u0438"], ["\u0450", "\u0435"], ["\u0451", "\u0435"], ["\u0453", "\u0433"], ["\u0457", "\u0456"], ["\u045c", "\u043a"], ["\u045d", "\u0438"], ["\u045e", "\u0443"], ["\u0477", "\u0475"], ["\u04c2", "\u0436"], ["\u04d1", "\u0430"], ["\u04d3", "\u0430"], ["\u04d7", "\u0435"], ["\u04db", +"\u04d9"], ["\u04dd", "\u0436"], ["\u04df", "\u0437"], ["\u04e3", "\u0438"], ["\u04e5", "\u0438"], ["\u04e7", "\u043e"], ["\u04eb", "\u04e9"], ["\u04ed", "\u044d"], ["\u04ef", "\u0443"], ["\u04f1", "\u0443"], ["\u04f3", "\u0443"], ["\u04f5", "\u0447"]]; +var Ba = /[^\p{L}\p{N}]+/u, Ca = /(\d{3})/g, Da = /(\D)(\d{3})/g, Ea = /(\d{3})(\D)/g, Fa = "".normalize && /[\u0300-\u036f]/g; +function N(a) { + if (!this) { var b = Function.prototype.bind, c = b.apply, d = [null], e = d.concat; if (arguments instanceof Array) { var g = arguments; @@ -920,22 +920,22 @@ function M(a) { } g = h; } - return new (c.call(b, M, e.call(d, g)))(); + return new (c.call(b, N, e.call(d, g)))(); } for (b = 0; b < arguments.length; b++) { this.assign(arguments[b]); } } -M.prototype.assign = function(a) { - this.normalize = H(a.normalize, !0, this.normalize); - var b = a.ra, c = b || a.Ha || a.split; +N.prototype.assign = function(a) { + this.normalize = I(a.normalize, !0, this.normalize); + var b = a.include, c = b || a.exclude || a.split; if ("object" === typeof c) { var d = !b, e = ""; - a.ra || (e += "\\p{Z}"); - c.Ea && (e += "\\p{L}"); - c.Ia && (e += "\\p{N}", d = !!b); - c.Ka && (e += "\\p{S}"); - c.Ja && (e += "\\p{P}"); + a.include || (e += "\\p{Z}"); + c.letter && (e += "\\p{L}"); + c.number && (e += "\\p{N}", d = !!b); + c.symbol && (e += "\\p{S}"); + c.punctuation && (e += "\\p{P}"); c.control && (e += "\\p{C}"); if (c = c.char) { e += "object" === typeof c ? c.join("") : c; @@ -943,145 +943,169 @@ M.prototype.assign = function(a) { this.split = new RegExp("[" + (b ? "^" : "") + e + "]+", "u"); this.numeric = d; } else { - this.split = H(c, Ba, this.split), this.numeric = H(this.numeric, !0); + this.split = I(c, Ba, this.split), this.numeric = I(this.numeric, !0); } - this.la = H(a.la, null, this.la); - this.ca = H(a.ca, null, this.ca); - Ga && (this.K = new Map(Ga)); + this.prepare = I(a.prepare, null, this.prepare); + this.finalize = I(a.finalize, null, this.finalize); + Fa || (this.mapper = new Map(Aa)); this.rtl = a.rtl || !1; - this.H = H(a.H, !0, this.H); - this.filter = H((c = a.filter) && new Set(c), null, this.filter); - this.N = H((c = a.N) && new Map(c), null, this.N); - this.K = H((c = a.K) && new Map(c), null, this.K); - this.U = H((c = a.U) && new Map(c), null, this.U); - this.P = H(a.P, null, this.P); - this.ka = H(a.ka, 1, this.ka); - this.sa = H(a.sa, 0, this.sa); - if (this.cache = c = H(a.cache, !0, this.cache)) { - this.ea = null, this.R = "number" === typeof c ? c : 2e5, this.W = new Map(), this.ba = new Map(), this.A = this.h = 128; + this.dedupe = I(a.dedupe, !0, this.dedupe); + this.filter = I((c = a.filter) && new Set(c), null, this.filter); + this.matcher = I((c = a.matcher) && new Map(c), null, this.matcher); + this.mapper = I((c = a.mapper) && new Map(c), null, this.mapper); + this.stemmer = I((c = a.stemmer) && new Map(c), null, this.stemmer); + this.replacer = I(a.replacer, null, this.replacer); + this.minlength = I(a.minlength, 1, this.minlength); + this.maxlength = I(a.maxlength, 0, this.maxlength); + if (this.cache = c = I(a.cache, !0, this.cache)) { + this.X = null, this.L = "number" === typeof c ? c : 2e5, this.P = new Map(), this.V = new Map(), this.A = this.h = 128; } this.C = ""; this.G = null; this.D = ""; - this.J = null; - if (this.N) { - for (a = y(this.N.keys()), b = a.next(); !b.done; b = a.next()) { + this.H = null; + if (this.matcher) { + for (a = y(this.matcher.keys()), b = a.next(); !b.done; b = a.next()) { this.C += (this.C ? "|" : "") + b.value; } } - if (this.U) { - for (a = y(this.U.keys()), b = a.next(); !b.done; b = a.next()) { + if (this.stemmer) { + for (a = y(this.stemmer.keys()), b = a.next(); !b.done; b = a.next()) { this.D += (this.D ? "|" : "") + b.value; } } return this; }; -M.prototype.encode = function(a) { +N.prototype.encode = function(a) { var b = this; if (this.cache && a.length <= this.h) { - if (this.ea) { - if (this.W.has(a)) { - return this.W.get(a); + if (this.X) { + if (this.P.has(a)) { + return this.P.get(a); } } else { - this.ea = setTimeout(Ha, 0, this); + this.X = setTimeout(Ga, 0, this); } } this.normalize && (a = "function" === typeof this.normalize ? this.normalize(a) : Fa ? a.normalize("NFKD").replace(Fa, "").toLowerCase() : a.toLowerCase()); - this.la && (a = this.la(a)); + this.prepare && (a = this.prepare(a)); this.numeric && 3 < a.length && (a = a.replace(Da, "$1 $2").replace(Ea, "$1 $2").replace(Ca, "$1 ")); - for (var c = !(this.H || this.K || this.filter || this.N || this.U || this.P), d = [], e = this.split || "" === this.split ? a.split(this.split) : a, g = 0, f = void 0, h = void 0; g < e.length; g++) { - if ((f = h = e[g]) && !(f.length < this.ka)) { + for (var c = !(this.dedupe || this.mapper || this.filter || this.matcher || this.stemmer || this.replacer), d = [], e = this.split || "" === this.split ? a.split(this.split) : a, g = 0, f = void 0, h = void 0; g < e.length; g++) { + if ((f = h = e[g]) && !(f.length < this.minlength)) { if (c) { d.push(f); } else { if (!this.filter || !this.filter.has(f)) { if (this.cache && f.length <= this.A) { - if (this.ea) { - var k = this.ba.get(f); + if (this.X) { + var k = this.V.get(f); if (k || "" === k) { k && d.push(k); continue; } } else { - this.ea = setTimeout(Ha, 0, this); + this.X = setTimeout(Ga, 0, this); } } k = void 0; - this.U && 2 < f.length && (this.J || (this.J = new RegExp("(?!^)(" + this.D + ")$")), f = f.replace(this.J, function(q) { - return b.U.get(q); + this.stemmer && 2 < f.length && (this.H || (this.H = new RegExp("(?!^)(" + this.D + ")$")), f = f.replace(this.H, function(q) { + return b.stemmer.get(q); }), k = 1); - this.N && 1 < f.length && (this.G || (this.G = new RegExp("(" + this.C + ")", "g")), f = f.replace(this.G, function(q) { - return b.N.get(q); + this.matcher && 1 < f.length && (this.G || (this.G = new RegExp("(" + this.C + ")", "g")), f = f.replace(this.G, function(q) { + return b.matcher.get(q); }), k = 1); - f && k && (f.length < this.ka || this.filter && this.filter.has(f)) && (f = ""); - if (f && (this.K || this.H && 1 < f.length)) { + f && k && (f.length < this.minlength || this.filter && this.filter.has(f)) && (f = ""); + if (f && (this.mapper || this.dedupe && 1 < f.length)) { k = ""; for (var l = 0, m = "", n = void 0, p = void 0; l < f.length; l++) { - n = f.charAt(l), n === m && this.H || ((p = this.K && this.K.get(n)) || "" === p ? p === m && this.H || !(m = p) || (k += p) : k += m = n); + n = f.charAt(l), n === m && this.dedupe || ((p = this.mapper && this.mapper.get(n)) || "" === p ? p === m && this.dedupe || !(m = p) || (k += p) : k += m = n); } f = k; } - if (f && this.P) { - for (k = 0; f && k < this.P.length; k += 2) { - f = f.replace(this.P[k], this.P[k + 1]); + if (f && this.replacer) { + for (k = 0; f && k < this.replacer.length; k += 2) { + f = f.replace(this.replacer[k], this.replacer[k + 1]); } } - this.cache && h.length <= this.A && (this.ba.set(h, f), this.ba.size > this.R && (this.ba.clear(), this.A = this.A / 1.1 | 0)); + this.cache && h.length <= this.A && (this.V.set(h, f), this.V.size > this.L && (this.V.clear(), this.A = this.A / 1.1 | 0)); f && d.push(f); } } } } - this.ca && (d = this.ca(d) || d); - this.cache && a.length <= this.h && (this.W.set(a, d), this.W.size > this.R && (this.W.clear(), this.h = this.h / 1.1 | 0)); + this.finalize && (d = this.finalize(d) || d); + this.cache && a.length <= this.h && (this.P.set(a, d), this.P.size > this.L && (this.P.clear(), this.h = this.h / 1.1 | 0)); return d; }; -function Ha(a) { - a.ea = null; - a.W.clear(); - a.ba.clear(); +function Ga(a) { + a.X = null; + a.P.clear(); + a.V.clear(); } -;function Ia(a, b, c) { +;function Ha(a, b, c) { a = ("object" === typeof a ? "" + a.query : a).toLowerCase(); var d = this.cache.get(a); if (!d) { d = this.search(a, b, c); - if (d instanceof Promise) { + if (d.then) { var e = this; d.then(function(g) { e.cache.set(a, g); + return g; }); } this.cache.set(a, d); } return d; } -function N(a) { +function Q(a) { this.limit = a && !0 !== a ? a : 1000; this.cache = new Map(); this.h = ""; } -N.prototype.set = function(a, b) { - this.cache.has(a) || (this.cache.set(this.h = a, b), this.limit && this.cache.size > this.limit && this.cache.delete(this.cache.keys().next().value)); +Q.prototype.set = function(a, b) { + this.cache.set(this.h = a, b); + this.cache.size > this.limit && this.cache.delete(this.cache.keys().next().value); }; -N.prototype.get = function(a) { +Q.prototype.get = function(a) { var b = this.cache.get(a); - b && this.limit && this.h !== a && (this.cache.delete(a), this.cache.set(this.h = a, b)); + b && this.h !== a && (this.cache.delete(a), this.cache.set(this.h = a, b)); return b; }; -N.prototype.remove = function(a) { +Q.prototype.remove = function(a) { for (var b = y(this.cache), c = b.next(); !c.done; c = b.next()) { c = c.value; var d = c[0]; c[1].includes(a) && this.cache.delete(d); } }; -N.prototype.clear = function() { +Q.prototype.clear = function() { this.cache.clear(); this.h = ""; }; -function Ja(a, b, c, d) { +var Ia = {normalize:function(a) { + return a.toLowerCase(); +}, dedupe:!1}; +var Ja = new Map([["b", "p"], ["v", "f"], ["w", "f"], ["z", "s"], ["x", "s"], ["d", "t"], ["n", "m"], ["c", "k"], ["g", "k"], ["j", "k"], ["q", "k"], ["i", "e"], ["y", "e"], ["u", "o"]]); +var Ka = new Map([["ai", "ei"], ["ae", "a"], ["oe", "o"], ["ue", "u"], ["sh", "s"], ["ch", "c"], ["th", "t"], ["ph", "f"], ["pf", "f"]]), La = [/([^aeo])h([aeo$])/g, "$1$2", /([aeo])h([^aeo]|$)/g, "$1$2"]; +var Ma = {a:"", e:"", i:"", o:"", u:"", y:"", b:1, f:1, p:1, v:1, c:2, g:2, j:2, k:2, q:2, s:2, x:2, z:2, "\u00df":2, d:3, t:3, l:4, m:5, n:5, r:6}; +var Na = /[\x00-\x7F]+/g; +var Oa = /[\x00-\x7F]+/g; +var Pa = /[\x00-\x7F]+/g; +var Qa = {LatinExact:{normalize:!1, dedupe:!1}, LatinDefault:Ia, LatinSimple:{normalize:!0, dedupe:!0}, LatinBalance:{normalize:!0, dedupe:!0, mapper:Ja}, LatinAdvanced:{normalize:!0, dedupe:!0, mapper:Ja, replacer:La, matcher:Ka}, LatinExtra:{normalize:!0, dedupe:!0, mapper:Ja, replacer:La.concat([/(?!^)[aeoy]/g, ""]), matcher:Ka}, LatinSoundex:{normalize:!0, dedupe:!1, include:{letter:!0}, finalize:function(a) { + for (var b = 0; b < a.length; b++) { + for (var c = a[b], d = c.charAt(0), e = Ma[d], g = 1, f; g < c.length && (f = c.charAt(g), "h" === f || "w" === f || !(f = Ma[f]) || f === e || (d += f, e = f, 4 !== d.length)); g++) { + } + a[b] = d; + } +}}, ArabicDefault:{rtl:!0, normalize:!1, dedupe:!0, prepare:function(a) { + return ("" + a).replace(Na, " "); +}}, CjkDefault:{normalize:!1, dedupe:!0, split:"", prepare:function(a) { + return ("" + a).replace(Oa, ""); +}}, CyrillicDefault:{normalize:!1, dedupe:!0, prepare:function(a) { + return ("" + a).replace(Pa, " "); +}}}; +function Ra(a, b, c, d) { for (var e = [], g = 0, f; g < a.index.length; g++) { if (f = a.index[g], b >= f.length) { b -= f.length; @@ -1097,9 +1121,9 @@ function Ja(a, b, c, d) { } return e; } -function Q(a) { - if (!(this instanceof Q)) { - return new Q(a); +function R(a) { + if (!this) { + return new R(a); } this.index = a ? [a] : []; this.length = a ? a.length : 0; @@ -1146,12 +1170,12 @@ function Q(a) { } if ("slice" === d) { return function(e, g) { - return Ja(b, e || 0, g || b.length, !1); + return Ra(b, e || 0, g || b.length, !1); }; } if ("splice" === d) { return function(e, g) { - return Ja(b, e || 0, g || b.length, !0); + return Ra(b, e || 0, g || b.length, !0); }; } if ("constructor" === d) { @@ -1167,59 +1191,59 @@ function Q(a) { return !0; }}); } -Q.prototype.clear = function() { +R.prototype.clear = function() { this.index.length = 0; }; -Q.prototype.push = function() { -}; -function R(a) { - a = void 0 === a ? 8 : a; - if (!(this instanceof R)) { - return new R(a); - } - this.index = I(); - this.C = []; - this.size = 0; - 32 < a ? (this.h = Ka, this.A = BigInt(a)) : (this.h = La, this.A = a); -} -R.prototype.get = function(a) { - var b = this.h(a); - return (b = this.index[b]) && b.get(a); -}; -R.prototype.set = function(a, b) { - var c = this.h(a), d = this.index[c]; - d ? (c = d.size, d.set(a, b), (c -= d.size) && this.size++) : (this.index[c] = d = new Map([[a, b]]), this.C.push(d)); +R.prototype.push = function() { }; function S(a) { a = void 0 === a ? 8 : a; - if (!(this instanceof S)) { + if (!this) { return new S(a); } - this.index = I(); - this.h = []; - 32 < a ? (this.C = Ka, this.A = BigInt(a)) : (this.C = La, this.A = a); + this.index = K(); + this.C = []; + this.size = 0; + 32 < a ? (this.h = Sa, this.A = BigInt(a)) : (this.h = Ta, this.A = a); } -S.prototype.add = function(a) { +S.prototype.get = function(a) { + var b = this.h(a); + return (b = this.index[b]) && b.get(a); +}; +S.prototype.set = function(a, b) { + var c = this.h(a), d = this.index[c]; + d ? (c = d.size, d.set(a, b), (c -= d.size) && this.size++) : (this.index[c] = d = new Map([[a, b]]), this.C.push(d)); +}; +function T(a) { + a = void 0 === a ? 8 : a; + if (!this) { + return new T(a); + } + this.index = K(); + this.h = []; + 32 < a ? (this.C = Sa, this.A = BigInt(a)) : (this.C = Ta, this.A = a); +} +T.prototype.add = function(a) { var b = this.C(a), c = this.index[b]; c ? (b = c.size, c.add(a), (b -= c.size) && this.size++) : (this.index[b] = c = new Set([a]), this.h.push(c)); }; -w = R.prototype; -w.has = S.prototype.has = function(a) { +w = S.prototype; +w.has = T.prototype.has = function(a) { var b = this.C(a); return (b = this.index[b]) && b.has(a); }; -w.delete = S.prototype.delete = function(a) { +w.delete = T.prototype.delete = function(a) { var b = this.C(a); (b = this.index[b]) && b.delete(a) && this.size--; }; -w.clear = S.prototype.clear = function() { - this.index = I(); +w.clear = T.prototype.clear = function() { + this.index = K(); this.h = []; this.size = 0; }; -w.values = S.prototype.values = function Ma() { +w.values = T.prototype.values = function Ua() { var b, c = this, d, e, g; - return ra(Ma, function(f) { + return qa(Ua, function(f) { switch(f.h) { case 1: b = 0; @@ -1243,9 +1267,9 @@ w.values = S.prototype.values = function Ma() { } }); }; -w.keys = S.prototype.keys = function Na() { +w.keys = T.prototype.keys = function Va() { var b, c = this, d, e, g; - return ra(Na, function(f) { + return qa(Va, function(f) { switch(f.h) { case 1: b = 0; @@ -1269,9 +1293,9 @@ w.keys = S.prototype.keys = function Na() { } }); }; -w.entries = S.prototype.entries = function Oa() { +w.entries = T.prototype.entries = function Wa() { var b, c = this, d, e, g; - return ra(Oa, function(f) { + return qa(Wa, function(f) { switch(f.h) { case 1: b = 0; @@ -1295,7 +1319,7 @@ w.entries = S.prototype.entries = function Oa() { } }); }; -function La(a) { +function Ta(a) { var b = Math.pow(2, this.A) - 1; if ("number" == typeof a) { return a & b; @@ -1305,26 +1329,23 @@ function La(a) { } return 32 === this.A ? c + Math.pow(2, 31) : c; } -function Ka() { +function Sa() { throw Error("The keystore is limited to 32 for EcmaScript5"); } -;function Pa(a, b, c, d, e, g, f, h) { +;function Xa(a, b, c, d, e, g, f, h) { (d = a(c ? c + "." + d : d, JSON.stringify(f))) && d.then ? d.then(function() { b.export(a, b, c, e, g + 1, h); }) : b.export(a, b, c, e, g + 1, h); } -;var Qa = {normalize:function(a) { - return a.toLowerCase(); -}, H:!1}; -var Ra = {memory:{resolution:1}, performance:{resolution:6, fastupdate:!0, context:{depth:1, resolution:3}}, match:{tokenize:"forward"}, score:{resolution:9, context:{depth:2, resolution:9}}}; -function Sa(a) { - Ta.call(a, "add"); - Ta.call(a, "append"); - Ta.call(a, "search"); - Ta.call(a, "update"); - Ta.call(a, "remove"); +;var Ya = {memory:{resolution:1}, performance:{resolution:6, fastupdate:!0, context:{depth:1, resolution:3}}, match:{tokenize:"forward"}, score:{resolution:9, context:{depth:2, resolution:9}}}; +function Za(a) { + U.call(a, "add"); + U.call(a, "append"); + U.call(a, "search"); + U.call(a, "update"); + U.call(a, "remove"); } -function Ta(a) { +function U(a) { this[a + "Async"] = function() { var b = arguments, c = b[b.length - 1]; if ("function" === typeof c) { @@ -1338,26 +1359,26 @@ function Ta(a) { return b; }; } -;I(); -T.prototype.add = function(a, b, c, d) { +;K(); +V.prototype.add = function(a, b, c, d) { if (b && (a || 0 === a)) { if (!d && !c && this.B.has(a)) { return this.update(a, b); } b = this.encoder.encode(b); if (d = b.length) { - for (var e = I(), g = I(), f = this.depth, h = this.resolution, k = 0; k < d; k++) { + for (var e = K(), g = K(), f = this.depth, h = this.resolution, k = 0; k < d; k++) { var l = b[this.rtl ? d - 1 - k : k], m = l.length; if (m && (f || !g[l])) { - var n = this.score ? this.score(b, l, k, null, 0) : Ua(h, d, k), p = ""; + var n = this.score ? this.score(b, l, k, null, 0) : $a(h, d, k), p = ""; switch(this.tokenize) { case "full": if (2 < m) { for (n = 0; n < m; n++) { for (var q = m; q > n; q--) { p = l.substring(n, q); - var r = this.score ? this.score(b, l, k, p, n) : Ua(h, d, k, m, n); - Va(this, g, p, r, a, c); + var r = this.score ? this.score(b, l, k, p, n) : $a(h, d, k, m, n); + ab(this, g, p, r, a, c); } } break; @@ -1365,24 +1386,24 @@ T.prototype.add = function(a, b, c, d) { case "reverse": if (1 < m) { for (q = m - 1; 0 < q; q--) { - p = l[q] + p, r = this.score ? this.score(b, l, k, p, q) : Ua(h, d, k, m, q), Va(this, g, p, r, a, c); + p = l[q] + p, r = this.score ? this.score(b, l, k, p, q) : $a(h, d, k, m, q), ab(this, g, p, r, a, c); } p = ""; } case "forward": if (1 < m) { for (q = 0; q < m; q++) { - p += l[q], Va(this, g, p, n, a, c); + p += l[q], ab(this, g, p, n, a, c); } break; } default: - if (Va(this, g, l, n, a, c), f && 1 < d && k < d - 1) { - for (m = I(), p = this.na, n = l, q = Math.min(f + 1, d - k), r = m[n] = 1; r < q; r++) { + if (ab(this, g, l, n, a, c), f && 1 < d && k < d - 1) { + for (m = K(), p = this.da, n = l, q = Math.min(f + 1, d - k), r = m[n] = 1; r < q; r++) { if ((l = b[this.rtl ? d - 1 - k - r : k + r]) && !m[l]) { m[l] = 1; - var x = this.score ? this.score(b, n, k, l, r) : Ua(p + (d / 2 > p ? 0 : 1), d, k, q - 1, r - 1), u = this.bidirectional && l > n; - Va(this, e, u ? n : l, x, a, c, u ? l : n); + var x = this.score ? this.score(b, n, k, l, r) : $a(p + (d / 2 > p ? 0 : 1), d, k, q - 1, r - 1), u = this.bidirectional && l > n; + ab(this, e, u ? n : l, x, a, c, u ? l : n); } } } @@ -1394,15 +1415,15 @@ T.prototype.add = function(a, b, c, d) { b = ""; } } - this.db && (b || this.X.push({del:a}), this.qa && Wa(this)); + this.db && (b || this.R.push({del:a}), this.ga && bb(this)); return this; }; -function Va(a, b, c, d, e, g, f) { - var h = f ? a.M : a.map, k; +function ab(a, b, c, d, e, g, f) { + var h = f ? a.J : a.map, k; if (!b[c] || !f || !(k = b[c])[f]) { - if (f ? (b = k || (b[c] = I()), b[f] = 1, (k = h.get(f)) ? h = k : h.set(f, h = new Map())) : b[c] = 1, (k = h.get(c)) ? h = k : h.set(c, h = k = []), h = h[d] || (h[d] = []), !g || !h.includes(e)) { + if (f ? (b = k || (b[c] = K()), b[f] = 1, (k = h.get(f)) ? h = k : h.set(f, h = new Map())) : b[c] = 1, (k = h.get(c)) ? h = k : h.set(c, h = k = []), h = h[d] || (h[d] = []), !g || !h.includes(e)) { if (h.length === Math.pow(2, 31) - 1) { - b = new Q(h); + b = new R(h); if (a.fastupdate) { for (c = y(a.B.values()), g = c.next(); !g.done; g = c.next()) { g = g.value, g.includes(h) && (g[g.indexOf(h)] = b); @@ -1415,12 +1436,12 @@ function Va(a, b, c, d, e, g, f) { } } } -function Ua(a, b, c, d, e) { +function $a(a, b, c, d, e) { return c && 1 < a ? b + (d || 0) <= a ? c + (e || 0) : (a - 1) / (b + (d || 0)) * (c + (e || 0)) + 1 | 0 : 0; } -;function U(a, b, c, d) { +;function W(a, b, c, d) { if (1 === a.length) { - return a = a[0], a = c || a.length > b ? b ? a.slice(c, c + b) : a.slice(c) : a, d ? Xa(a) : a; + return a = a[0], a = c || a.length > b ? b ? a.slice(c, c + b) : a.slice(c) : a, d ? cb(a) : a; } for (var e = [], g = 0, f = void 0, h = void 0; g < a.length; g++) { if ((f = a[g]) && (h = f.length)) { @@ -1435,7 +1456,7 @@ function Ua(a, b, c, d, e) { h > b && (f = f.slice(0, b), h = f.length), e.push(f); } else { if (h >= b) { - return h > b && (f = f.slice(0, b)), d ? Xa(f) : f; + return h > b && (f = f.slice(0, b)), d ? cb(f) : f; } e = [f]; } @@ -1449,17 +1470,17 @@ function Ua(a, b, c, d, e) { return e; } e = 1 < e.length ? [].concat.apply([], e) : e[0]; - return d ? Xa(e) : e; + return d ? cb(e) : e; } -function Xa(a) { +function cb(a) { for (var b = 0; b < a.length; b++) { a[b] = {score:b, id:a[b]}; } return a; } -;V.prototype.or = function() { +;X.prototype.or = function() { var a = this, b = arguments, c = b[0]; - if (c instanceof Promise) { + if (c.then) { return c.then(function() { return a.or.apply(a, b); }); @@ -1472,7 +1493,7 @@ function Xa(a) { for (var e = 0, g = 0, f, h, k = 0, l = void 0; k < b.length; k++) { if (l = b[k]) { var m = void 0; - if (l instanceof V) { + if (l.constructor === X) { m = l.result; } else if (l.constructor === Array) { m = l; @@ -1482,8 +1503,8 @@ function Xa(a) { m = this.and(l.and); } else if (l.xor) { m = this.xor(l.xor); - } else if (l.I) { - m = this.I(l.I); + } else if (l.not) { + m = this.not(l.not); } else { e = l.limit || 0; g = l.offset || 0; @@ -1492,30 +1513,30 @@ function Xa(a) { continue; } d[k] = m; - m instanceof Promise && c.push(m); + m.then && c.push(m); } } if (c.length) { return Promise.all(c).then(function() { a.result.length && (d = [a.result].concat(d)); - a.result = Ya(d, e, g, f, h, a.V); + a.result = db(d, e, g, f, h, a.M); return h ? a.result : a; }); } this.result.length && (d = [this.result].concat(d)); - this.result = Ya(d, e, g, f, h, a.V); + this.result = db(d, e, g, f, h, a.M); return h ? this.result : this; }; -function Ya(a, b, c, d, e, g) { +function db(a, b, c, d, e, g) { if (!a.length) { return a; } "object" === typeof b && (c = b.offset || 0, d = b.enrich || !1, b = b.limit || 0); if (2 > a.length) { - return e ? U(a[0], b, c, d) : a[0]; + return e ? W(a[0], b, c, d) : a[0]; } d = []; - for (var f = 0, h = I(), k = Aa(a), l = 0, m; l < k; l++) { + for (var f = 0, h = K(), k = za(a), l = 0, m; l < k; l++) { for (var n = 0; n < a.length; n++) { if (m = a[n]) { if (m = m[l]) { @@ -1543,10 +1564,10 @@ function Ya(a, b, c, d, e, g) { } return d; } -;V.prototype.and = function() { +;X.prototype.and = function() { if (this.result.length) { var a = this, b = arguments, c = b[0]; - if (c instanceof Promise) { + if (c.then) { return c.then(function() { return a.and.apply(a, b); }); @@ -1559,7 +1580,7 @@ function Ya(a, b, c, d, e, g) { for (var e = 0, g = 0, f, h = 0, k = void 0; h < b.length; h++) { if (k = b[h]) { var l = void 0; - if (k instanceof V) { + if (k.constructor === X) { l = k.result; } else if (k.constructor === Array) { l = k; @@ -1569,8 +1590,8 @@ function Ya(a, b, c, d, e, g) { l = this.or(k.or); } else if (k.xor) { l = this.xor(k.xor); - } else if (k.I) { - l = this.I(k.I); + } else if (k.not) { + l = this.not(k.not); } else { e = k.limit || 0; g = k.offset || 0; @@ -1578,27 +1599,27 @@ function Ya(a, b, c, d, e, g) { continue; } d[h] = l; - l instanceof Promise && c.push(l); + l.then && c.push(l); } } if (c.length) { return Promise.all(c).then(function() { d = [a.result].concat(d); - a.result = Za(d, e, g, f, a.V); + a.result = eb(d, e, g, f, a.M); return f ? a.result : a; }); } d = [this.result].concat(d); - this.result = Za(d, e, g, f, a.V); + this.result = eb(d, e, g, f, a.M); return f ? this.result : this; } return this; }; -function Za(a, b, c, d, e) { +function eb(a, b, c, d, e) { if (2 > a.length) { return []; } - var g = [], f = 0, h = I(), k = Aa(a); + var g = [], f = 0, h = K(), k = za(a); if (!k) { return g; } @@ -1607,7 +1628,7 @@ function Za(a, b, c, d, e) { if (!m || !m.length) { return []; } - for (var n = I(), p = 0, q = l === a.length - 1, r = 0, x; r < k; r++) { + for (var n = K(), p = 0, q = l === a.length - 1, r = 0, x; r < k; r++) { if (x = m[r]) { for (var u = 0, t, v; u < x.length; u++) { if (t = x[u], !l) { @@ -1635,9 +1656,9 @@ function Za(a, b, c, d, e) { } return g; } -;V.prototype.xor = function() { +;X.prototype.xor = function() { var a = this, b = arguments, c = b[0]; - if (c instanceof Promise) { + if (c.then) { return c.then(function() { return a.xor.apply(a, b); }); @@ -1650,7 +1671,7 @@ function Za(a, b, c, d, e) { for (var e = 0, g = 0, f, h, k = 0, l = void 0; k < b.length; k++) { if (l = b[k]) { var m = void 0; - if (l instanceof V) { + if (l.constructor === X) { m = l.result; } else if (l.constructor === Array) { m = l; @@ -1660,8 +1681,8 @@ function Za(a, b, c, d, e) { m = this.or(l.or); } else if (l.and) { m = this.and(l.and); - } else if (l.I) { - m = this.I(l.I); + } else if (l.not) { + m = this.not(l.not); } else { e = l.limit || 0; g = l.offset || 0; @@ -1670,29 +1691,29 @@ function Za(a, b, c, d, e) { continue; } d[k] = m; - m instanceof Promise && c.push(m); + m.then && c.push(m); } } if (c.length) { return Promise.all(c).then(function() { a.result.length && (d = [a.result].concat(d)); - a.result = $a(d, e, g, f, !h, a.V); + a.result = fb(d, e, g, f, !h, a.M); return h ? a.result : a; }); } this.result.length && (d = [this.result].concat(d)); - this.result = $a(d, e, g, f, !h, a.V); + this.result = fb(d, e, g, f, !h, a.M); return h ? this.result : this; }; -function $a(a, b, c, d, e, g) { +function fb(a, b, c, d, e, g) { if (!a.length) { return a; } if (2 > a.length) { - return e ? U(a[0], b, c, d) : a[0]; + return e ? W(a[0], b, c, d) : a[0]; } b = []; - c = I(); + c = K(); d = 0; for (var f; d < a.length; d++) { if (f = a[d]) { @@ -1726,22 +1747,22 @@ function $a(a, b, c, d, e, g) { } return b; } -;V.prototype.I = function() { +;X.prototype.not = function() { var a = this, b = arguments, c = b[0]; - if (c instanceof Promise) { + if (c.then) { return c.then(function() { - return a.I.apply(a, b); + return a.not.apply(a, b); }); } if (c[0] && c[0].index) { - return this.I.apply(this, c); + return this.not.apply(this, c); } var d = []; c = []; for (var e, g = 0, f = void 0; g < b.length; g++) { if (f = b[g]) { var h = void 0; - if (f instanceof V) { + if (f.constructor === X) { h = f.result; } else if (f.constructor === Array) { h = f; @@ -1758,19 +1779,19 @@ function $a(a, b, c, d, e, g) { continue; } d[g] = h; - h instanceof Promise && c.push(h); + h.then && c.push(h); } } if (c.length) { return Promise.all(c).then(function() { - a.result = ab.call(a, d, e); + a.result = gb.call(a, d, e); return e ? a.result : a; }); } - this.result = ab.call(this, d, e); + this.result = gb.call(this, d, e); return e ? this.result : this; }; -function ab(a, b) { +function gb(a, b) { if (!a.length) { return this.result; } @@ -1785,21 +1806,21 @@ function ab(a, b) { } return c; } -;function V(a) { +;function X(a) { + if (!this) { + return new X(a); + } if (a && a.index) { return a.resolve = !1, this.index = a.index, a.index.search(a); } - if (!(this instanceof V)) { - return new V(a); - } - if (a instanceof V) { + if (a.constructor === X) { return a; } this.index = null; this.result = a || []; - this.V = 0; + this.M = 0; } -V.prototype.limit = function(a) { +X.prototype.limit = function(a) { if (this.result.length) { for (var b = [], c = 0, d = 0, e; d < this.result.length; d++) { if (e = this.result[d], e.length + c < a) { @@ -1813,7 +1834,7 @@ V.prototype.limit = function(a) { } return this; }; -V.prototype.offset = function(a) { +X.prototype.offset = function(a) { if (this.result.length) { for (var b = [], c = 0, d = 0, e; d < this.result.length; d++) { e = this.result[d], e.length + c < a ? c += e.length : (b[d] = e.slice(a - c), c = a); @@ -1822,18 +1843,22 @@ V.prototype.offset = function(a) { } return this; }; -V.prototype.resolve = function(a, b, c) { - bb = 1; +X.prototype.boost = function(a) { + this.M += a; + return this; +}; +X.prototype.resolve = function(a, b, c) { + hb = 1; var d = this.result; this.result = this.index = null; - return d.length ? ("object" === typeof a && (c = a.enrich, b = a.offset, a = a.limit), U(d, a || 100, b, c)) : d; + return d.length ? ("object" === typeof a && (c = a.enrich, b = a.offset, a = a.limit), W(d, a || 100, b, c)) : d; }; -function cb(a, b, c, d) { +function ib(a, b, c, d) { var e = a.length, g = [], f = 0, h; d && (d = []); for (var k = e - 1, l; 0 <= k; k--) { var m = a[k]; - e = I(); + e = K(); l = !x; for (var n = 0, p; n < m.length; n++) { if ((p = m[n]) && p.length) { @@ -1886,8 +1911,8 @@ function cb(a, b, c, d) { } return g; } -function db(a, b) { - for (var c = I(), d = I(), e = [], g = 0; g < a.length; g++) { +function jb(a, b) { + for (var c = K(), d = K(), e = [], g = 0; g < a.length; g++) { c[a[g]] = 1; } for (a = 0; a < b.length; a++) { @@ -1898,9 +1923,9 @@ function db(a, b) { } return e; } -;var bb = 1; -T.prototype.search = function(a, b, c) { - c || (!b && K(a) ? (c = a, a = "") : K(b) && (c = b, b = 0)); +;var hb = 1; +V.prototype.search = function(a, b, c) { + c || (!b && M(a) ? (c = a, a = "") : M(b) && (c = b, b = 0)); var d = [], e = 0, g; if (c) { a = c.query || a; @@ -1908,30 +1933,30 @@ T.prototype.search = function(a, b, c) { e = c.offset || 0; var f = c.context; var h = c.suggest; - (g = bb && !1 !== c.resolve) || (bb = 0); + (g = hb && !1 !== c.resolve) || (hb = 0); var k = g && c.enrich; var l = this.db && c.tag; } else { - g = this.resolve || bb; + g = this.resolve || hb; } a = this.encoder.encode(a); var m = a.length; b || !g || (b = 100); if (1 === m) { - return eb.call(this, a[0], "", b, e, g, k, l); + return kb.call(this, a[0], "", b, e, g, k, l); } f = this.depth && !1 !== f; if (2 === m && f && !h) { - return eb.call(this, a[0], a[1], b, e, g, k, l); + return kb.call(this, a[0], a[1], b, e, g, k, l); } var n = c = 0; if (1 < m) { - for (var p = I(), q = [], r = 0, x = void 0; r < m; r++) { + for (var p = K(), q = [], r = 0, x = void 0; r < m; r++) { if ((x = a[r]) && !p[x]) { - if (h || this.db || W(this, x)) { + if (h || this.db || Y(this, x)) { q.push(x), p[x] = 1; } else { - return g ? d : new V(d); + return g ? d : new X(d); } x = x.length; c = Math.max(c, x); @@ -1942,21 +1967,21 @@ T.prototype.search = function(a, b, c) { m = a.length; } if (!m) { - return g ? d : new V(d); + return g ? d : new X(d); } var u = 0; if (1 === m) { - return eb.call(this, a[0], "", b, e, g, k, l); + return kb.call(this, a[0], "", b, e, g, k, l); } if (2 === m && f && !h) { - return eb.call(this, a[0], a[1], b, e, g, k, l); + return kb.call(this, a[0], a[1], b, e, g, k, l); } if (1 < m) { if (f) { var t = a[0]; u = 1; } else { - 9 < c && 3 < c / n && a.sort(xa); + 9 < c && 3 < c / n && a.sort(wa); } } if (this.db) { @@ -1965,8 +1990,8 @@ T.prototype.search = function(a, b, c) { } var v = this; return function() { - var A, D, C; - return ta(function(E) { + var A, D, B; + return sa(function(E) { switch(E.h) { case 1: D = A = void 0; @@ -1976,21 +2001,21 @@ T.prototype.search = function(a, b, c) { break; } D = a[u]; - return t ? F(E, W(v, D, t), 8) : F(E, W(v, D), 7); + return t ? F(E, Y(v, D, t), 8) : F(E, Y(v, D), 7); case 7: A = E.G; - A = fb(A, d, h, v.resolution, b, e, 1 === m); + A = lb(A, d, h, v.resolution, b, e, 1 === m); E.h = 6; break; case 8: - A = E.G, A = fb(A, d, h, v.na, b, e, 2 === m), h && !1 === A && d.length || (t = D); + A = E.G, A = lb(A, d, h, v.da, b, e, 2 === m), h && !1 === A && d.length || (t = D); case 6: if (A) { return E.return(A); } if (h && u === m - 1) { - C = d.length; - if (!C) { + B = d.length; + if (!B) { if (t) { t = ""; u = -1; @@ -1999,8 +2024,8 @@ T.prototype.search = function(a, b, c) { } return E.return(d); } - if (1 === C) { - return E.return(g ? U(d[0], b, e) : new V(d[0])); + if (1 === B) { + return E.return(g ? W(d[0], b, e) : new X(d[0])); } } case 3: @@ -2008,14 +2033,14 @@ T.prototype.search = function(a, b, c) { E.h = 2; break; case 4: - return E.return(g ? cb(d, b, e, h) : new V(d[0])); + return E.return(g ? ib(d, b, e, h) : new X(d[0])); } }); }(); } for (k = f = void 0; u < m; u++) { k = a[u]; - t ? (f = W(this, k, t), f = fb(f, d, h, this.na, b, e, 2 === m), h && !1 === f && d.length || (t = k)) : (f = W(this, k), f = fb(f, d, h, this.resolution, b, e, 1 === m)); + t ? (f = Y(this, k, t), f = lb(f, d, h, this.da, b, e, 2 === m), h && !1 === f && d.length || (t = k)) : (f = Y(this, k), f = lb(f, d, h, this.resolution, b, e, 1 === m)); if (f) { return f; } @@ -2030,19 +2055,19 @@ T.prototype.search = function(a, b, c) { return d; } if (1 === f) { - return g ? U(d[0], b, e) : new V(d[0]); + return g ? W(d[0], b, e) : new X(d[0]); } } } - return g ? cb(d, b, e, h) : new V(d[0]); + return g ? ib(d, b, e, h) : new X(d[0]); }; -function eb(a, b, c, d, e, g, f) { - a = W(this, a, b, c, d, e, g, f); +function kb(a, b, c, d, e, g, f) { + a = Y(this, a, b, c, d, e, g, f); return this.db ? a.then(function(h) { - return e ? h : h && h.length ? e ? U(h, c, d) : new V(h) : e ? [] : new V([]); - }) : a && a.length ? e ? U(a, c, d) : new V(a) : e ? [] : new V([]); + return e ? h : h && h.length ? e ? W(h, c, d) : new X(h) : e ? [] : new X([]); + }) : a && a.length ? e ? W(a, c, d) : new X(a) : e ? [] : new X([]); } -function fb(a, b, c, d, e, g, f) { +function lb(a, b, c, d, e, g, f) { var h = []; if (a) { d = Math.min(a.length, d); @@ -2055,7 +2080,7 @@ function fb(a, b, c, d, e, g, f) { } if (h.length) { if (f) { - return U(h, e, 0); + return W(h, e, 0); } b.push(h); return; @@ -2063,16 +2088,16 @@ function fb(a, b, c, d, e, g, f) { } return !c && h; } -function W(a, b, c, d, e, g, f, h) { +function Y(a, b, c, d, e, g, f, h) { var k; c && (k = a.bidirectional && b > c); if (a.db) { return c ? a.db.get(k ? c : b, k ? b : c, d, e, g, f, h) : a.db.get(b, "", d, e, g, f, h); } - a = c ? (a = a.M.get(k ? b : c)) && a.get(k ? c : b) : a.map.get(b); + a = c ? (a = a.J.get(k ? b : c)) && a.get(k ? c : b) : a.map.get(b); return a; } -;T.prototype.remove = function(a, b) { +;V.prototype.remove = function(a, b) { var c = this.B.size && (this.fastupdate ? this.B.get(a) : this.B.has(a)); if (c) { if (this.fastupdate) { @@ -2087,15 +2112,15 @@ function W(a, b, c, d, e, g, f, h) { } } } else { - gb(this.map, a), this.depth && gb(this.M, a); + mb(this.map, a), this.depth && mb(this.J, a); } b || this.B.delete(a); } - this.db && (this.X.push({del:a}), this.qa && Wa(this)); + this.db && (this.R.push({del:a}), this.ga && bb(this)); this.cache && this.cache.remove(a); return this; }; -function gb(a, b) { +function mb(a, b) { var c = 0; if (a.constructor === Array) { for (var d = 0, e = void 0, g; d < a.length; d++) { @@ -2110,24 +2135,24 @@ function gb(a, b) { } } else { for (d = y(a), e = d.next(); !e.done; e = d.next()) { - g = e.value, e = g[0], (g = gb(g[1], b)) ? c += g : a.delete(e); + g = e.value, e = g[0], (g = mb(g[1], b)) ? c += g : a.delete(e); } } return c; } -;function T(a, b) { - if (!(this instanceof T)) { - return new T(a); +;function V(a, b) { + if (!this) { + return new V(a); } if (a) { - var c = J(a) ? a : a.preset; - c && (Ra[c] || console.warn("Preset not found: " + c), a = Object.assign({}, Ra[c], a)); + var c = L(a) ? a : a.preset; + c && (Ya[c] || console.warn("Preset not found: " + c), a = Object.assign({}, Ya[c], a)); } else { a = {}; } c = a.context || {}; - var d = a.encode || a.encoder || Qa; - this.encoder = d.encode ? d : "object" === typeof d ? new M(d) : {encode:d}; + var d = a.encode || a.encoder || Ia; + this.encoder = d.encode ? d : "object" === typeof d ? new N(d) : {encode:d}; var e; this.resolution = a.resolution || 9; this.tokenize = e = a.tokenize || "strict"; @@ -2136,21 +2161,21 @@ function gb(a, b) { this.fastupdate = !!a.fastupdate; this.score = a.score || null; (e = a.keystore || 0) && (this.keystore = e); - this.map = e ? new R(e) : new Map(); - this.M = e ? new R(e) : new Map(); - this.B = b || (this.fastupdate ? e ? new R(e) : new Map() : e ? new S(e) : new Set()); - this.na = c.resolution || 1; + this.map = e ? new S(e) : new Map(); + this.J = e ? new S(e) : new Map(); + this.B = b || (this.fastupdate ? e ? new S(e) : new Map() : e ? new T(e) : new Set()); + this.da = c.resolution || 1; this.rtl = d.rtl || a.rtl || !1; - this.cache = (e = a.cache || null) && new N(e); + this.cache = (e = a.cache || null) && new Q(e); this.resolve = !1 !== a.resolve; if (e = a.db) { this.db = e.mount(this); } - this.qa = !1 !== a.commit; - this.X = []; + this.ga = !1 !== a.commit; + this.R = []; this.h = null; } -w = T.prototype; +w = V.prototype; w.mount = function(a) { this.h && (clearTimeout(this.h), this.h = null); return a.mount(this); @@ -2159,7 +2184,7 @@ w.commit = function(a, b) { this.h && (clearTimeout(this.h), this.h = null); return this.db.commit(this, a, b); }; -function Wa(a) { +function bb(a) { a.h || (a.h = setTimeout(function() { a.h = null; a.db.commit(a, void 0, void 0); @@ -2167,10 +2192,10 @@ function Wa(a) { } w.clear = function() { this.map.clear(); - this.M.clear(); + this.J.clear(); this.B.clear(); this.cache && this.cache.clear(); - this.db && (this.h && clearTimeout(this.h), this.h = null, this.X = [{clear:!0}]); + this.db && (this.h && clearTimeout(this.h), this.h = null, this.R = [{clear:!0}]); return this; }; w.append = function(a, b) { @@ -2188,7 +2213,7 @@ w.update = function(a, b) { } return this.remove(a).add(a, b); }; -function hb(a) { +function nb(a) { var b = 0; if (a.constructor === Array) { for (var c = 0, d = void 0; c < a.length; c++) { @@ -2198,7 +2223,7 @@ function hb(a) { for (c = y(a), d = c.next(); !d.done; d = c.next()) { var e = d.value; d = e[0]; - (e = hb(e[1])) ? b += e : a.delete(d); + (e = nb(e[1])) ? b += e : a.delete(d); } } return b; @@ -2207,11 +2232,11 @@ w.cleanup = function() { if (!this.fastupdate) { return console.info('Cleanup the index isn\'t required when not using "fastupdate".'), this; } - hb(this.map); - this.depth && hb(this.M); + nb(this.map); + this.depth && nb(this.J); return this; }; -w.searchCache = Ia; +w.searchCache = Ha; w.export = function(a, b, c, d, e, g) { var f = !0; "undefined" === typeof g && (f = new Promise(function(n) { @@ -2221,7 +2246,7 @@ w.export = function(a, b, c, d, e, g) { case 0: var h = "reg"; if (this.fastupdate) { - var k = I(); + var k = K(); for (var l = y(this.B.keys()), m = l.next(); !m.done; m = l.next()) { k[m.value] = 1; } @@ -2239,18 +2264,18 @@ w.export = function(a, b, c, d, e, g) { break; case 3: h = "ctx"; - k = this.M; + k = this.J; break; default: "undefined" === typeof c && g && g(); return; } - Pa(a, b || this, c, h, d, e, k, g); + Xa(a, b || this, c, h, d, e, k, g); return f; }; w.import = function(a, b) { if (b) { - switch(J(b) && (b = JSON.parse(b)), a) { + switch(L(b) && (b = JSON.parse(b)), a) { case "cfg": this.A = !!b.opt; break; @@ -2262,14 +2287,14 @@ w.import = function(a, b) { this.map = b; break; case "ctx": - this.M = b; + this.J = b; } } }; -Sa(T.prototype); -function ib(a) { +Za(V.prototype); +function ob(a) { var b, c, d, e, g, f, h, k; - return ta(function(l) { + return sa(function(l) { a = a.data; b = self._index; c = a.args; @@ -2278,7 +2303,7 @@ function ib(a) { case "init": e = a.options || {}; (g = e.config) && (e = g); - (f = a.factory) ? (Function("return " + f)()(self), self._index = new self.FlexSearch.Index(e), delete self.FlexSearch) : self._index = new T(e); + (f = a.factory) ? (Function("return " + f)()(self), self._index = new self.FlexSearch.Index(e), delete self.FlexSearch) : self._index = new V(e); postMessage({id:a.id}); break; default: @@ -2287,95 +2312,95 @@ function ib(a) { l.h = 0; }); } -;var jb = 0; -function X(a) { +;var pb = 0; +function qb(a) { function b(g) { g = g.data || g; var f = g.id, h = f && e.h[f]; h && (h(g.msg), delete e.h[f]); } - if (!(this instanceof X)) { - return new X(a); + if (!this) { + return new qb(a); } a || (a = {}); var c = (self || window)._factory; c && (c = c.toString()); var d = "undefined" === typeof window && self.exports, e = this; - this.worker = kb(c, d, a.worker); - this.h = I(); + this.worker = rb(c, d, a.worker); + this.h = K(); if (this.worker) { d ? this.worker.on("message", b) : this.worker.onmessage = b; if (a.config) { return new Promise(function(g) { - e.h[++jb] = function() { + e.h[++pb] = function() { g(e); }; - e.worker.postMessage({id:jb, task:"init", factory:c, options:a}); + e.worker.postMessage({id:pb, task:"init", factory:c, options:a}); }); } this.worker.postMessage({task:"init", factory:c, options:a}); } } -lb("add"); -lb("append"); -lb("search"); -lb("update"); -lb("remove"); -function lb(a) { - X.prototype[a] = X.prototype[a + "Async"] = function() { +sb("add"); +sb("append"); +sb("search"); +sb("update"); +sb("remove"); +function sb(a) { + qb.prototype[a] = qb.prototype[a + "Async"] = function() { var b = this, c = [].slice.call(arguments), d = c[c.length - 1]; if ("function" === typeof d) { var e = d; c.splice(c.length - 1, 1); } d = new Promise(function(g) { - b.h[++jb] = g; - b.worker.postMessage({task:a, id:jb, args:c}); + b.h[++pb] = g; + b.worker.postMessage({task:a, id:pb, args:c}); }); return e ? (d.then(e), this) : d; }; } -function kb(a, b, c) { - return b ? new (require("worker_threads")["Worker"])(__dirname + "/node/node.js") : a ? new window.Worker(URL.createObjectURL(new Blob(["onmessage=" + ib.toString()], {type:"text/javascript"}))) : new window.Worker(J(c) ? c : "worker/worker.js", {type:"module"}); +function rb(a, b, c) { + return b ? new (require("worker_threads")["Worker"])(__dirname + "/node/node.js") : a ? new window.Worker(URL.createObjectURL(new Blob(["onmessage=" + ob.toString()], {type:"text/javascript"}))) : new window.Worker(L(c) ? c : "worker/worker.js", {type:"module"}); } -;Y.prototype.add = function(a, b, c) { - K(a) && (b = a, a = za(b, this.key)); +;Z.prototype.add = function(a, b, c) { + M(a) && (b = a, a = ya(b, this.key)); if (b && (a || 0 === a)) { if (!c && this.B.has(a)) { return this.update(a, b); } for (var d = 0, e; d < this.field.length; d++) { - e = this.T[d]; + e = this.O[d]; var g = this.index.get(this.field[d]); if ("function" === typeof e) { (e = e(b)) && g.add(a, e, !1, !0); } else { - var f = e.aa; + var f = e.U; if (!f || f(b)) { - e instanceof String ? e = ["" + e] : J(e) && (e = [e]), mb(b, e, this.da, 0, g, a, e[0], c); + e.constructor === String ? e = ["" + e] : L(e) && (e = [e]), tb(b, e, this.W, 0, g, a, e[0], c); } } } if (this.tag) { - for (d = 0; d < this.S.length; d++) { - f = this.S[d]; - var h = this.ma[d]; + for (d = 0; d < this.N.length; d++) { + f = this.N[d]; + var h = this.ca[d]; g = this.tag.get(h); - e = I(); + e = K(); if ("function" === typeof f) { if (f = f(b), !f) { continue; } } else { - var k = f.aa; + var k = f.U; if (k && !k(b)) { continue; } - f instanceof String && (f = "" + f); - f = za(b, f); + f.constructor === String && (f = "" + f); + f = ya(b, f); } if (g && f) { - for (J(f) && (f = [f]), h = 0, k = void 0; h < f.length; h++) { + for (L(f) && (f = [f]), h = 0, k = void 0; h < f.length; h++) { var l = f[h]; if (!e[l]) { e[l] = 1; @@ -2383,7 +2408,7 @@ function kb(a, b, c) { (m = g.get(l)) ? k = m : g.set(l, k = []); if (!c || !k.includes(a)) { if (k.length === Math.pow(2, 31) - 1) { - m = new Q(k); + m = new R(k); if (this.fastupdate) { for (var n = y(this.B.values()), p = n.next(); !p.done; p = n.next()) { p = p.value, p.includes(k) && (p[p.indexOf(k)] = m); @@ -2402,22 +2427,22 @@ function kb(a, b, c) { } } if (this.store && (!c || !this.store.has(a))) { - if (this.L) { - var q = I(); - for (c = 0; c < this.L.length; c++) { - if (d = this.L[c], g = d.aa, !g || g(b)) { + if (this.I) { + var q = K(); + for (c = 0; c < this.I.length; c++) { + if (d = this.I[c], g = d.U, !g || g(b)) { g = void 0; if ("function" === typeof d) { g = d(b); if (!g) { continue; } - d = [d.Aa]; - } else if (J(d) || d instanceof String) { + d = [d.oa]; + } else if (L(d) || d.constructor === String) { q[d] = b[d]; continue; } - nb(b, q, d, 0, d[0], g); + ub(b, q, d, 0, d[0], g); } } } @@ -2426,21 +2451,21 @@ function kb(a, b, c) { } return this; }; -function nb(a, b, c, d, e, g) { +function ub(a, b, c, d, e, g) { a = a[e]; if (d === c.length - 1) { b[e] = g || a; } else if (a) { if (a.constructor === Array) { for (b = b[e] = Array(a.length), e = 0; e < a.length; e++) { - nb(a, b, c, d, e); + ub(a, b, c, d, e); } } else { - b = b[e] || (b[e] = I()), e = c[++d], nb(a, b, c, d, e); + b = b[e] || (b[e] = K()), e = c[++d], ub(a, b, c, d, e); } } } -function mb(a, b, c, d, e, g, f, h) { +function tb(a, b, c, d, e, g, f, h) { if (a = a[f]) { if (d === b.length - 1) { if (a.constructor === Array) { @@ -2456,18 +2481,18 @@ function mb(a, b, c, d, e, g, f, h) { } else { if (a.constructor === Array) { for (f = 0; f < a.length; f++) { - mb(a, b, c, d, e, g, f, h); + tb(a, b, c, d, e, g, f, h); } } else { - f = b[++d], mb(a, b, c, d, e, g, f, h); + f = b[++d], tb(a, b, c, d, e, g, f, h); } } } else { e.db && e.remove(g); } } -;Y.prototype.search = function(a, b, c, d) { - c || (!b && K(a) ? (c = a, a = "") : K(b) && (c = b, b = 0)); +;Z.prototype.search = function(a, b, c, d) { + c || (!b && M(a) ? (c = a, a = "") : M(b) && (c = b, b = 0)); var e = [], g = [], f = 0; if (c) { if (c.constructor === Array) { @@ -2488,7 +2513,7 @@ function mb(a, b, c, d, e, g, f, h) { m.constructor !== Array && (m = [m]); for (var r = [], x = 0, u = void 0; x < m.length; x++) { u = m[x]; - if (J(u)) { + if (L(u)) { throw Error("A tag option can't be a string, instead it needs a { field: tag } format."); } if (u.field && u.tag) { @@ -2505,8 +2530,8 @@ function mb(a, b, c, d, e, g, f, h) { v = 0; for (var A = void 0, D = void 0; v < t.length; v++) { if (A = t[v], D = u[A], D.constructor === Array) { - for (var C = 0; C < D.length; C++) { - r.push(A, D[C]); + for (var B = 0; B < D.length; B++) { + r.push(A, D[B]); } } else { r.push(A, D); @@ -2531,7 +2556,7 @@ function mb(a, b, c, d, e, g, f, h) { } g.push(p = p.db.tag(r[k + 1], b, q, n)); } else { - p = ob.call(this, r[k], r[k + 1], b, q, n); + p = vb.call(this, r[k], r[k + 1], b, q, n); } e.push({field:r[k], tag:r[k + 1], result:p}); } @@ -2544,16 +2569,16 @@ function mb(a, b, c, d, e, g, f, h) { }) : e; } } - J(h) && (h = [h]); + L(h) && (h = [h]); } } h || (h = this.field); r = !d && (this.worker || this.async) && []; x = 0; for (v = u = t = void 0; x < h.length; x++) { - if (u = h[x], !this.db || !this.tag || this.T[x]) { + if (u = h[x], !this.db || !this.tag || this.O[x]) { t = void 0; - J(u) || (t = u, u = t.field, a = t.query || a, b = t.limit || b, p = t.suggest || p); + L(u) || (t = u, u = t.field, a = t.query || a, b = t.limit || b, p = t.suggest || p); if (d) { t = d[x]; } else { @@ -2562,7 +2587,7 @@ function mb(a, b, c, d, e, g, f, h) { if (m) { if (this.db) { v.tag = m; - var E = t.db.Fa; + var E = t.db.sa; v.field = h; } E || (v.enrich = !1); @@ -2581,35 +2606,35 @@ function mb(a, b, c, d, e, g, f, h) { D = 0; if (this.db && d) { if (!E) { - for (C = h.length; C < d.length; C++) { - var L = d[C]; - if (L && L.length) { - D++, A.push(L); + for (B = h.length; B < d.length; B++) { + var J = d[B]; + if (J && J.length) { + D++, A.push(J); } else if (!p) { return e; } } } } else { - C = 0; - for (var Db = L = void 0; C < m.length; C += 2) { - L = this.tag.get(m[C]); - if (!L) { - if (console.warn("Tag '" + m[C] + ":" + m[C + 1] + "' will be skipped because there is no field '" + m[C] + "'."), p) { + B = 0; + for (var Gb = J = void 0; B < m.length; B += 2) { + J = this.tag.get(m[B]); + if (!J) { + if (console.warn("Tag '" + m[B] + ":" + m[B + 1] + "' will be skipped because there is no field '" + m[B] + "'."), p) { continue; } else { return e; } } - if (Db = (L = L && L.get(m[C + 1])) && L.length) { - D++, A.push(L); + if (Gb = (J = J && J.get(m[B + 1])) && J.length) { + D++, A.push(J); } else if (!p) { return e; } } } if (D) { - t = db(t, A); + t = jb(t, A); v = t.length; if (!v && !p) { return e; @@ -2638,9 +2663,9 @@ function mb(a, b, c, d, e, g, f, h) { r.push(g.db.tag(m[n + 1], b, q, !1)); } } - var Eb = this; + var Hb = this; return Promise.all(r).then(function(O) { - return O.length ? Eb.search(a, b, c, O) : O; + return O.length ? Hb.search(a, b, c, O) : O; }); } if (!f) { @@ -2653,7 +2678,7 @@ function mb(a, b, c, d, e, g, f, h) { q = 0; for (p = void 0; q < g.length; q++) { p = e[q]; - n && p.length && !p[0].doc && (this.db ? r.push(p = this.index.get(this.field[0]).db.enrich(p)) : p.length && (p = pb.call(this, p))); + n && p.length && !p[0].doc && (this.db ? r.push(p = this.index.get(this.field[0]).db.enrich(p)) : p.length && (p = wb.call(this, p))); if (k) { return p; } @@ -2663,11 +2688,11 @@ function mb(a, b, c, d, e, g, f, h) { for (var P = 0; P < O.length; P++) { e[P].result = O[P]; } - return l ? qb(e, b) : e; - }) : l ? qb(e, b) : e; + return l ? xb(e, b) : e; + }) : l ? xb(e, b) : e; }; -function qb(a, b) { - for (var c = [], d = I(), e = 0, g, f; e < a.length; e++) { +function xb(a, b) { + for (var c = [], d = K(), e = 0, g, f; e < a.length; e++) { g = a[e]; f = g.result; for (var h = 0, k, l, m; h < f.length; h++) { @@ -2684,7 +2709,7 @@ function qb(a, b) { } return c; } -function ob(a, b, c, d, e) { +function vb(a, b, c, d, e) { var g = this.tag.get(a); if (!g) { return console.warn("Tag '" + a + "' was not found"), []; @@ -2693,80 +2718,80 @@ function ob(a, b, c, d, e) { if (a > c || d) { g = g.slice(d, d + c); } - e && (g = pb.call(this, g)); + e && (g = wb.call(this, g)); return g; } } -function pb(a) { +function wb(a) { for (var b = Array(a.length), c = 0, d; c < a.length; c++) { d = a[c], b[c] = {id:d, doc:this.store.get(d)}; } return b; } -;function Y(a) { - if (!(this instanceof Y)) { - return new Y(a); +;function Z(a) { + if (!this) { + return new Z(a); } var b = a.document || a.doc || a, c, d; - this.T = []; + this.O = []; this.field = []; - this.da = []; - this.key = (c = b.key || b.id) && rb(c, this.da) || "id"; + this.W = []; + this.key = (c = b.key || b.id) && yb(c, this.W) || "id"; (d = a.keystore || 0) && (this.keystore = d); - this.B = (this.fastupdate = !!a.fastupdate) ? d ? new R(d) : new Map() : d ? new S(d) : new Set(); - this.L = (c = b.store || null) && !0 !== c && []; - this.store = c && (d ? new R(d) : new Map()); - this.cache = (c = a.cache || null) && new N(c); + this.B = (this.fastupdate = !!a.fastupdate) ? d ? new S(d) : new Map() : d ? new T(d) : new Set(); + this.I = (c = b.store || null) && !0 !== c && []; + this.store = c && (d ? new S(d) : new Map()); + this.cache = (c = a.cache || null) && new Q(c); a.cache = !1; this.worker = a.worker; this.async = !1; c = new Map(); d = b.index || b.field || b; - J(d) && (d = [d]); + L(d) && (d = [d]); for (var e = 0, g, f = void 0; e < d.length; e++) { g = d[e]; - J(g) || (f = g, g = g.field); - f = K(f) ? Object.assign({}, a, f) : a; + L(g) || (f = g, g = g.field); + f = M(f) ? Object.assign({}, a, f) : a; if (this.worker) { - var h = new X(f); + var h = new qb(f); c.set(g, h); h.worker || (this.worker = !1); } - this.worker || c.set(g, new T(f, this.B)); - f.Y ? this.T[e] = f.Y : (this.T[e] = rb(g, this.da), f.filter && ("string" === typeof this.T[e] && (this.T[e] = new String(this.T[e])), this.T[e].aa = f.filter)); + this.worker || c.set(g, new V(f, this.B)); + f.custom ? this.O[e] = f.custom : (this.O[e] = yb(g, this.W), f.filter && ("string" === typeof this.O[e] && (this.O[e] = new String(this.O[e])), this.O[e].U = f.filter)); this.field[e] = g; } - if (this.L) { - for (d = b.store, J(d) && (d = [d]), e = 0; e < d.length; e++) { - g = d[e], f = g.field || g, g.Y ? (this.L[e] = g.Y, g.Y.Aa = f) : (this.L[e] = rb(f, this.da), g.filter && ("string" === typeof this.L[e] && (this.L[e] = new String(this.L[e])), this.L[e].aa = g.filter)); + if (this.I) { + for (d = b.store, L(d) && (d = [d]), e = 0; e < d.length; e++) { + g = d[e], f = g.field || g, g.custom ? (this.I[e] = g.custom, g.custom.oa = f) : (this.I[e] = yb(f, this.W), g.filter && ("string" === typeof this.I[e] && (this.I[e] = new String(this.I[e])), this.I[e].U = g.filter)); } } this.index = c; this.tag = null; if (c = b.tag) { if ("string" === typeof c && (c = [c]), c.length) { - for (this.tag = new Map(), this.S = [], this.ma = [], b = 0; b < c.length; b++) { + for (this.tag = new Map(), this.N = [], this.ca = [], b = 0; b < c.length; b++) { d = c[b]; e = d.field || d; if (!e) { throw Error("The tag field from the document descriptor is undefined."); } - d.Y ? this.S[b] = d.Y : (this.S[b] = rb(e, this.da), d.filter && ("string" === typeof this.S[b] && (this.S[b] = new String(this.S[b])), this.S[b].aa = d.filter)); - this.ma[b] = e; + d.custom ? this.N[b] = d.custom : (this.N[b] = yb(e, this.W), d.filter && ("string" === typeof this.N[b] && (this.N[b] = new String(this.N[b])), this.N[b].U = d.filter)); + this.ca[b] = e; this.tag.set(e, new Map()); } } } a.db && this.mount(a.db); } -w = Y.prototype; +w = Z.prototype; w.mount = function(a) { var b = this.field; if (this.tag) { - for (var c = 0, d; c < this.ma.length; c++) { - d = this.ma[c]; + for (var c = 0, d; c < this.ca.length; c++) { + d = this.ca[c]; var e = this.index.get(d); - e || (this.index.set(d, e = new T({}, this.B)), b === this.field && (b = b.slice(0)), b.push(d)); + e || (this.index.set(d, e = new V({}, this.B)), b === this.field && (b = b.slice(0)), b.push(d)); e.tag = this.tag.get(d); } } @@ -2780,14 +2805,14 @@ w.mount = function(a) { f.id = a.id; c[e] = f.mount(g); g.document = !0; - e ? g.Ba = !0 : g.store = this.store; + e ? g.pa = !0 : g.store = this.store; } this.db = this.async = !0; return Promise.all(c); }; w.commit = function(a, b) { var c = this, d, e, g, f; - return ta(function(h) { + return sa(function(h) { if (1 == h.h) { d = []; e = y(c.index.values()); @@ -2800,7 +2825,7 @@ w.commit = function(a, b) { h.h = 0; }); }; -function rb(a, b) { +function yb(a, b) { for (var c = a.split(":"), d = 0, e = 0; e < c.length; e++) { a = c[e], "]" === a[a.length - 1] && (a = a.substring(0, a.length - 2)) && (b[d] = !0), a && (c[d++] = a); } @@ -2814,7 +2839,7 @@ w.update = function(a, b) { return this.remove(a).add(a, b); }; w.remove = function(a) { - K(a) && (a = za(a, this.key)); + M(a) && (a = ya(a, this.key)); for (var b = y(this.index.values()), c = b.next(); !c.done; c = b.next()) { c.value.remove(a, !0); } @@ -2867,7 +2892,7 @@ w.set = function(a, b) { this.store.set(a, b); return this; }; -w.searchCache = Ia; +w.searchCache = Ha; w.export = function(a, b, c, d, e, g) { var f; "undefined" === typeof g && (f = new Promise(function(k) { @@ -2896,13 +2921,13 @@ w.export = function(a, b, c, d, e, g) { g(); return; } - Pa(a, this, c, b, d, e, h, g); + Xa(a, this, c, b, d, e, h, g); } return f; }; w.import = function(a, b) { if (b) { - switch(J(b) && (b = JSON.parse(b)), a) { + switch(L(b) && (b = JSON.parse(b)), a) { case "tag": this.h = b; break; @@ -2922,36 +2947,36 @@ w.import = function(a, b) { } } }; -Sa(Y.prototype); -var sb = "undefined" !== typeof window && (window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB), tb = ["map", "ctx", "tag", "reg", "cfg"]; -function ub(a, b) { +Za(Z.prototype); +var zb = "undefined" !== typeof window && (window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB), Ab = ["map", "ctx", "tag", "reg", "cfg"]; +function Bb(a, b) { b = void 0 === b ? {} : b; - if (!(this instanceof ub)) { - return new ub(a, b); + if (!this) { + return new Bb(a, b); } "object" === typeof a && (b = a = a.name); a || console.info("Default storage space was used, because a name was not passed."); this.id = "flexsearch" + (a ? ":" + a.toLowerCase().replace(/[^a-z0-9_\-]/g, "") : ""); this.field = b.field ? b.field.toLowerCase().replace(/[^a-z0-9_\-]/g, "") : ""; - this.Fa = !1; + this.sa = !1; this.db = null; this.h = {}; } -w = ub.prototype; +w = Bb.prototype; w.mount = function(a) { - if (a instanceof Y) { + if (a.constructor === Z) { return a.mount(this); } a.db = this; - return vb(this); + return Cb(this); }; -function vb(a) { +function Cb(a) { navigator.storage && navigator.storage.persist(); return a.db || new Promise(function(b, c) { - var d = sb.open(a.id + (a.field ? ":" + a.field : ""), 1); + var d = zb.open(a.id + (a.field ? ":" + a.field : ""), 1); d.onupgradeneeded = function() { var e = a.db = this.result; - tb.forEach(function(g) { + Ab.forEach(function(g) { e.objectStoreNames.contains(g) || e.createObjectStore(g); }); }; @@ -2977,10 +3002,10 @@ w.close = function() { this.db = null; }; w.clear = function() { - for (var a = this.db.transaction(tb, "readwrite"), b = 0; b < tb.length; b++) { - a.objectStore(tb[b]).clear(); + for (var a = this.db.transaction(Ab, "readwrite"), b = 0; b < Ab.length; b++) { + a.objectStore(Ab[b]).clear(); } - return wb(a); + return Db(a); }; w.get = function(a, b, c, d, e, g) { c = void 0 === c ? 0 : c; @@ -2989,7 +3014,7 @@ w.get = function(a, b, c, d, e, g) { g = void 0 === g ? !1 : g; a = this.db.transaction(b ? "ctx" : "map", "readonly").objectStore(b ? "ctx" : "map").get(b ? b + ":" + a : a); var f = this; - return wb(a).then(function(h) { + return Db(a).then(function(h) { var k = []; if (!h || !h.length) { return k; @@ -3024,7 +3049,7 @@ w.tag = function(a, b, c, d) { d = void 0 === d ? !1 : d; a = this.db.transaction("tag", "readonly").objectStore("tag").get(a); var e = this; - return wb(a).then(function(g) { + return Db(a).then(function(g) { if (!g || !g.length || c >= g.length) { return []; } @@ -3038,7 +3063,7 @@ w.tag = function(a, b, c, d) { w.enrich = function(a) { "object" !== typeof a && (a = [a]); for (var b = this.db.transaction("reg", "readonly").objectStore("reg"), c = [], d = 0; d < a.length; d++) { - c[d] = wb(b.get(a[d])); + c[d] = Db(b.get(a[d])); } return Promise.all(c).then(function(e) { for (var g = 0; g < e.length; g++) { @@ -3049,7 +3074,7 @@ w.enrich = function(a) { }; w.has = function(a) { a = this.db.transaction("reg", "readonly").objectStore("reg").getKey(a); - return wb(a); + return Db(a); }; w.search = null; w.info = function() { @@ -3077,14 +3102,14 @@ w.transaction = function(a, b, c) { }; w.commit = function(a, b, c) { var d = this, e, g, f; - return ta(function(h) { + return sa(function(h) { switch(h.h) { case 1: if (b) { return F(h, d.clear(), 12); } - e = a.X; - a.X = []; + e = a.R; + a.R = []; g = 0; f = void 0; case 4: @@ -3094,7 +3119,7 @@ w.commit = function(a, b, c) { } f = e[g]; if (!f.clear) { - e[g] = f.Ga; + e[g] = f.ta; h.h = 5; break; } @@ -3112,7 +3137,7 @@ w.commit = function(a, b, c) { h.h = 3; break; } - c || (e = e.concat(ya(a.B))); + c || (e = e.concat(xa(a.B))); if (!e.length) { h.h = 10; break; @@ -3123,16 +3148,16 @@ w.commit = function(a, b, c) { h.h = 3; break; case 12: - a.X = []; + a.R = []; case 3: return a.B.size ? F(h, d.transaction("map", "readwrite", function(k) { - for (var l = y(a.map), m = l.next(), n = {}; !m.done; n = {Z:void 0, ia:void 0}, m = l.next()) { - m = m.value, n.ia = m[0], n.Z = m[1], n.Z.length && (b ? k.put(n.Z, n.ia) : k.get(n.ia).onsuccess = function(p) { + for (var l = y(a.map), m = l.next(), n = {}; !m.done; n = {S:void 0, aa:void 0}, m = l.next()) { + m = m.value, n.aa = m[0], n.S = m[1], n.S.length && (b ? k.put(n.S, n.aa) : k.get(n.aa).onsuccess = function(p) { return function() { var q = this.result, r; if (q && q.length) { - for (var x = Math.max(q.length, p.Z.length), u = 0, t; u < x; u++) { - if ((t = p.Z[u]) && t.length) { + for (var x = Math.max(q.length, p.S.length), u = 0, t; u < x; u++) { + if ((t = p.S[u]) && t.length) { if ((r = q[u]) && r.length) { for (var v = 0; v < t.length; v++) { r.push(t[v]); @@ -3144,29 +3169,29 @@ w.commit = function(a, b, c) { } } } else { - q = p.Z, r = 1; + q = p.S, r = 1; } - r && k.put(q, p.ia); + r && k.put(q, p.aa); }; }(n)); } }), 13) : h.return(); case 13: return F(h, d.transaction("ctx", "readwrite", function(k) { - for (var l = y(a.M), m = l.next(), n = {}; !m.done; n = {ga:void 0}, m = l.next()) { + for (var l = y(a.J), m = l.next(), n = {}; !m.done; n = {Z:void 0}, m = l.next()) { m = m.value; - n.ga = m[0]; + n.Z = m[0]; m = y(m[1]); - for (var p = m.next(), q = {}; !p.done; q = {$:void 0, ja:void 0}, p = m.next()) { - p = p.value, q.ja = p[0], q.$ = p[1], q.$.length && (b ? k.put(q.$, n.ga + ":" + q.ja) : k.get(n.ga + ":" + q.ja).onsuccess = function(r, x) { + for (var p = m.next(), q = {}; !p.done; q = {T:void 0, ba:void 0}, p = m.next()) { + p = p.value, q.ba = p[0], q.T = p[1], q.T.length && (b ? k.put(q.T, n.Z + ":" + q.ba) : k.get(n.Z + ":" + q.ba).onsuccess = function(r, x) { return function() { var u = this.result, t; if (u && u.length) { - for (var v = Math.max(u.length, r.$.length), A = 0, D; A < v; A++) { - if ((D = r.$[A]) && D.length) { + for (var v = Math.max(u.length, r.T.length), A = 0, D; A < v; A++) { + if ((D = r.T[A]) && D.length) { if ((t = u[A]) && t.length) { - for (var C = 0; C < D.length; C++) { - t.push(D[C]); + for (var B = 0; B < D.length; B++) { + t.push(D[B]); } } else { u[A] = D; @@ -3175,9 +3200,9 @@ w.commit = function(a, b, c) { } } } else { - u = r.$, t = 1; + u = r.T, t = 1; } - t && k.put(u, x.ga + ":" + r.ja); + t && k.put(u, x.Z + ":" + r.ba); }; }(q, n)); } @@ -3194,7 +3219,7 @@ w.commit = function(a, b, c) { } }), 16); } - if (a.Ba) { + if (a.pa) { h.h = 16; break; } @@ -3209,22 +3234,22 @@ w.commit = function(a, b, c) { break; } return F(h, d.transaction("tag", "readwrite", function(k) { - for (var l = y(a.tag), m = l.next(), n = {}; !m.done; n = {ha:void 0, oa:void 0}, m = l.next()) { - m = m.value, n.oa = m[0], n.ha = m[1], n.ha.length && (k.get(n.oa).onsuccess = function(p) { + for (var l = y(a.tag), m = l.next(), n = {}; !m.done; n = {$:void 0, ea:void 0}, m = l.next()) { + m = m.value, n.ea = m[0], n.$ = m[1], n.$.length && (k.get(n.ea).onsuccess = function(p) { return function() { var q = this.result; - q = q && q.length ? q.concat(p.ha) : p.ha; - k.put(q, p.oa); + q = q && q.length ? q.concat(p.$) : p.$; + k.put(q, p.ea); }; }(n)); } }), 20); case 20: - a.map.clear(), a.M.clear(), a.tag && a.tag.clear(), a.store && a.store.clear(), a.document || a.B.clear(), h.h = 0; + a.map.clear(), a.J.clear(), a.tag && a.tag.clear(), a.store && a.store.clear(), a.document || a.B.clear(), h.h = 0; } }); }; -function xb(a, b, c) { +function Eb(a, b, c) { for (var d = a.value, e, g, f = 0, h = 0, k; h < d.length; h++) { if (k = c ? d : d[h]) { for (var l = 0, m, n; l < b.length; l++) { @@ -3251,17 +3276,17 @@ w.remove = function(a) { return Promise.all([this.transaction("map", "readwrite", function(b) { b.openCursor().onsuccess = function() { var c = this.result; - c && xb(c, a); + c && Eb(c, a); }; }), this.transaction("ctx", "readwrite", function(b) { b.openCursor().onsuccess = function() { var c = this.result; - c && xb(c, a); + c && Eb(c, a); }; }), this.transaction("tag", "readwrite", function(b) { b.openCursor().onsuccess = function() { var c = this.result; - c && xb(c, a, !0); + c && Eb(c, a, !0); }; }), this.transaction("reg", "readwrite", function(b) { for (var c = 0; c < a.length; c++) { @@ -3269,7 +3294,7 @@ w.remove = function(a) { } })]); }; -function wb(a) { +function Db(a) { return new Promise(function(b, c) { a.onsuccess = function() { b(this.result); @@ -3281,26 +3306,8 @@ function wb(a) { a = null; }); } -;var Z = I(); -var yb = new Map([["b", "p"], ["v", "f"], ["w", "f"], ["z", "s"], ["x", "s"], ["d", "t"], ["n", "m"], ["c", "k"], ["g", "k"], ["j", "k"], ["q", "k"], ["i", "e"], ["y", "e"], ["u", "o"]]), zb = {normalize:!0, H:!0, K:yb}; -var Ab = new Map([["ai", "ei"], ["ae", "a"], ["oe", "o"], ["ue", "u"], ["sh", "s"], ["ch", "c"], ["th", "t"], ["ph", "f"], ["pf", "f"]]), Bb = [/([^aeo])h([aeo$])/g, "$1$2", /([aeo])h([^aeo]|$)/g, "$1$2"], Cb = {normalize:!0, H:!0, K:yb, P:Bb, N:Ab}; -var Fb = {normalize:!0, H:!0, K:yb, P:Bb.concat([/(?!^)[aeoy]/g, ""]), N:Ab}; -var Gb = {a:"", e:"", i:"", o:"", u:"", y:"", b:1, f:1, p:1, v:1, c:2, g:2, j:2, k:2, q:2, s:2, x:2, z:2, "\u00df":2, d:3, t:3, l:4, m:5, n:5, r:6}; -Z["latin:exact"] = {normalize:!1, H:!1}; -Z["latin:default"] = Qa; -Z["latin:simple"] = {normalize:!0, H:!0}; -Z["latin:balance"] = zb; -Z["latin:advanced"] = Cb; -Z["latin:extra"] = Fb; -Z["latin:soundex"] = {normalize:!0, H:!1, ra:{Ea:!0}, ca:function(a) { - for (var b = 0; b < a.length; b++) { - for (var c = a[b], d = c.charAt(0), e = Gb[d], g = 1, f; g < c.length && (f = c.charAt(g), "h" === f || "w" === f || !(f = Gb[f]) || f === e || (d += f, e = f, 4 !== d.length)); g++) { - } - a[b] = d; - } -}}; -var Hb = {Index:T, Charset:Z, Encoder:M, Document:Y, Worker:X, Resolver:V, IndexedDB:ub}, Ib = self, Jb; +;var Fb = {Index:V, Charset:Qa, Encoder:N, Document:Z, Worker:qb, Resolver:X, IndexedDB:Bb, Language:{}}, Ib = self, Jb; (Jb = Ib.define) && Jb.amd ? Jb([], function() { - return Hb; -}) : "object" === typeof Ib.exports ? Ib.exports = Hb : Ib.FlexSearch = Hb; + return Fb; +}) : "object" === typeof Ib.exports ? Ib.exports = Fb : Ib.FlexSearch = Fb; }(this)); diff --git a/dist/flexsearch.es5.min.js b/dist/flexsearch.es5.min.js index c2a9f69..f2289c0 100644 --- a/dist/flexsearch.es5.min.js +++ b/dist/flexsearch.es5.min.js @@ -7,116 +7,115 @@ */ (function(self){'use strict';var w;function aa(a){var b=0;return function(){return b>>0)+"_",e=0;return b}); -B("Symbol.iterator",function(a){if(a)return a;a=Symbol("Symbol.iterator");for(var b="Array Int8Array Uint8Array Uint8ClampedArray Int16Array Uint16Array Int32Array Uint32Array Float32Array Float64Array".split(" "),c=0;cc&&(c=Math.max(c+e,0));cthis.R&&(this.ba.clear(),this.A=this.A/1.1| -0));f&&d.push(f)}this.ca&&(d=this.ca(d)||d);this.cache&&a.length<=this.h&&(this.W.set(a,d),this.W.size>this.R&&(this.W.clear(),this.h=this.h/1.1|0));return d};function Ha(a){a.ea=null;a.W.clear();a.ba.clear()};function Ia(a,b,c){a=("object"===typeof a?""+a.query:a).toLowerCase();var d=this.cache.get(a);if(!d){d=this.search(a,b,c);if(d instanceof Promise){var e=this;d.then(function(g){e.cache.set(a,g)})}this.cache.set(a,d)}return d}function N(a){this.limit=a&&!0!==a?a:1E3;this.cache=new Map;this.h=""}N.prototype.set=function(a,b){this.cache.has(a)||(this.cache.set(this.h=a,b),this.limit&&this.cache.size>this.limit&&this.cache.delete(this.cache.keys().next().value))}; -N.prototype.get=function(a){var b=this.cache.get(a);b&&this.limit&&this.h!==a&&(this.cache.delete(a),this.cache.set(this.h=a,b));return b};N.prototype.remove=function(a){for(var b=y(this.cache),c=b.next();!c.done;c=b.next()){c=c.value;var d=c[0];c[1].includes(a)&&this.cache.delete(d)}};N.prototype.clear=function(){this.cache.clear();this.h=""};function Ja(a,b,c,d){for(var e=[],g=0,f;g=f.length)b-=f.length;else{b=f[d?"splice":"slice"](b,c);if(f=b.length)if(e=e.length?e.concat(b):b,c-=f,d&&(a.length-=f),!c)break;b=0}return e} -function Q(a){if(!(this instanceof Q))return new Q(a);this.index=a?[a]:[];this.length=a?a.length:0;var b=this;return new Proxy([],{get:function(c,d){if("length"===d)return b.length;if("push"===d)return function(e){b.index[b.index.length-1].push(e);b.length++};if("pop"===d)return function(){if(b.length)return b.length--,b.index[b.index.length-1].pop()};if("indexOf"===d)return function(e){for(var g=0,f=0,h,k;fn;q--){p=l.substring(n,q);var r=this.score?this.score(b,l,k,p,n):Ua(h,d,k,m,n);Va(this,g,p,r,a,c)}break}case "reverse":if(1< -m){for(q=m-1;0p?0:1),d,k,q-1,r-1),u=this.bidirectional&&l>n;Va(this,e,u?n:l,x,a,c,u?l:n)}}}}this.fastupdate||this.B.add(a)}else b=""}this.db&& -(b||this.X.push({del:a}),this.qa&&Wa(this));return this}; -function Va(a,b,c,d,e,g,f){var h=f?a.M:a.map,k;if(!b[c]||!f||!(k=b[c])[f])if(f?(b=k||(b[c]=I()),b[f]=1,(k=h.get(f))?h=k:h.set(f,h=new Map)):b[c]=1,(k=h.get(c))?h=k:h.set(c,h=k=[]),h=h[d]||(h[d]=[]),!g||!h.includes(e)){if(h.length===Math.pow(2,31)-1){b=new Q(h);if(a.fastupdate)for(c=y(a.B.values()),g=c.next();!g.done;g=c.next())g=g.value,g.includes(h)&&(g[g.indexOf(h)]=b);k[d]=h=b}h.push(e);a.fastupdate&&((d=a.B.get(e))?d.push(h):a.B.set(e,[h]))}} -function Ua(a,b,c,d,e){return c&&1b?b?a.slice(c,c+b):a.slice(c):a,d?Xa(a):a;for(var e=[],g=0,f=void 0,h=void 0;g=h){c-=h;continue}cb&&(f=f.slice(0,b),h=f.length),e.push(f);else{if(h>=b)return h>b&&(f=f.slice(0,b)),d?Xa(f):f;e=[f]}b-=h;if(!b)break}if(!e.length)return e;e=1a.length)return e?U(a[0],b,c,d):a[0];d=[];for(var f=0,h=I(),k=Aa(a),l=0,m;la.length)return[];var g=[],f=0,h=I(),k=Aa(a);if(!k)return g;for(var l=0,m;la.length)return e?U(a[0],b,c,d):a[0];b=[];c=I();d=0;for(var f;d=e)))break;if(h.length){if(f)return U(h,e,0);b.push(h);return}}return!c&&h}function W(a,b,c,d,e,g,f,h){var k;c&&(k=a.bidirectional&&b>c);if(a.db)return c?a.db.get(k?c:b,k?b:c,d,e,g,f,h):a.db.get(b,"",d,e,g,f,h);a=c?(a=a.M.get(k?b:c))&&a.get(k?c:b):a.map.get(b);return a};T.prototype.remove=function(a,b){var c=this.B.size&&(this.fastupdate?this.B.get(a):this.B.has(a));if(c){if(this.fastupdate)for(var d=0,e;de.length)e.pop();else{var g=e.indexOf(a);g===c.length-1?e.pop():e.splice(g,1)}}else gb(this.map,a),this.depth&&gb(this.M,a);b||this.B.delete(a)}this.db&&(this.X.push({del:a}),this.qa&&Wa(this));this.cache&&this.cache.remove(a);return this}; -function gb(a,b){var c=0;if(a.constructor===Array)for(var d=0,e=void 0,g;dc||d)a=a.slice(d,d+c);e&&(a=pb.call(this,a));return a}}function pb(a){for(var b=Array(a.length),c=0,d;cthis.L&&(this.V.clear(),this.A=this.A/1.1|0));f&&d.push(f)}this.finalize&&(d=this.finalize(d)||d);this.cache&&a.length<=this.h&&(this.P.set(a,d),this.P.size>this.L&&(this.P.clear(),this.h=this.h/1.1|0));return d};function Ga(a){a.X=null;a.P.clear();a.V.clear()};function Ha(a,b,c){a=("object"===typeof a?""+a.query:a).toLowerCase();var d=this.cache.get(a);if(!d){d=this.search(a,b,c);if(d.then){var e=this;d.then(function(g){e.cache.set(a,g);return g})}this.cache.set(a,d)}return d}function Q(a){this.limit=a&&!0!==a?a:1E3;this.cache=new Map;this.h=""}Q.prototype.set=function(a,b){this.cache.set(this.h=a,b);this.cache.size>this.limit&&this.cache.delete(this.cache.keys().next().value)}; +Q.prototype.get=function(a){var b=this.cache.get(a);b&&this.h!==a&&(this.cache.delete(a),this.cache.set(this.h=a,b));return b};Q.prototype.remove=function(a){for(var b=y(this.cache),c=b.next();!c.done;c=b.next()){c=c.value;var d=c[0];c[1].includes(a)&&this.cache.delete(d)}};Q.prototype.clear=function(){this.cache.clear();this.h=""};var Ia={normalize:function(a){return a.toLowerCase()},dedupe:!1};var Ja=new Map([["b","p"],["v","f"],["w","f"],["z","s"],["x","s"],["d","t"],["n","m"],["c","k"],["g","k"],["j","k"],["q","k"],["i","e"],["y","e"],["u","o"]]);var Ka=new Map([["ai","ei"],["ae","a"],["oe","o"],["ue","u"],["sh","s"],["ch","c"],["th","t"],["ph","f"],["pf","f"]]),La=[/([^aeo])h([aeo$])/g,"$1$2",/([aeo])h([^aeo]|$)/g,"$1$2"];var Ma={a:"",e:"",i:"",o:"",u:"",y:"",b:1,f:1,p:1,v:1,c:2,g:2,j:2,k:2,q:2,s:2,x:2,z:2,"\u00df":2,d:3,t:3,l:4,m:5,n:5,r:6};var Na=/[\x00-\x7F]+/g;var Oa=/[\x00-\x7F]+/g;var Pa=/[\x00-\x7F]+/g;var Qa={LatinExact:{normalize:!1,dedupe:!1},LatinDefault:Ia,LatinSimple:{normalize:!0,dedupe:!0},LatinBalance:{normalize:!0,dedupe:!0,mapper:Ja},LatinAdvanced:{normalize:!0,dedupe:!0,mapper:Ja,replacer:La,matcher:Ka},LatinExtra:{normalize:!0,dedupe:!0,mapper:Ja,replacer:La.concat([/(?!^)[aeoy]/g,""]),matcher:Ka},LatinSoundex:{normalize:!0,dedupe:!1,include:{letter:!0},finalize:function(a){for(var b=0;b=f.length)b-=f.length;else{b=f[d?"splice":"slice"](b,c);if(f=b.length)if(e=e.length?e.concat(b):b,c-=f,d&&(a.length-=f),!c)break;b=0}return e} +function R(a){if(!this)return new R(a);this.index=a?[a]:[];this.length=a?a.length:0;var b=this;return new Proxy([],{get:function(c,d){if("length"===d)return b.length;if("push"===d)return function(e){b.index[b.index.length-1].push(e);b.length++};if("pop"===d)return function(){if(b.length)return b.length--,b.index[b.index.length-1].pop()};if("indexOf"===d)return function(e){for(var g=0,f=0,h,k;fn;q--){p=l.substring(n,q);var r=this.score?this.score(b,l,k,p,n):$a(h,d,k,m,n);ab(this,g,p,r,a,c)}break}case "reverse":if(1< +m){for(q=m-1;0p?0:1),d,k,q-1,r-1),u=this.bidirectional&&l>n;ab(this,e,u?n:l,x,a,c,u?l:n)}}}}this.fastupdate||this.B.add(a)}else b=""}this.db&& +(b||this.R.push({del:a}),this.ga&&bb(this));return this}; +function ab(a,b,c,d,e,g,f){var h=f?a.J:a.map,k;if(!b[c]||!f||!(k=b[c])[f])if(f?(b=k||(b[c]=K()),b[f]=1,(k=h.get(f))?h=k:h.set(f,h=new Map)):b[c]=1,(k=h.get(c))?h=k:h.set(c,h=k=[]),h=h[d]||(h[d]=[]),!g||!h.includes(e)){if(h.length===Math.pow(2,31)-1){b=new R(h);if(a.fastupdate)for(c=y(a.B.values()),g=c.next();!g.done;g=c.next())g=g.value,g.includes(h)&&(g[g.indexOf(h)]=b);k[d]=h=b}h.push(e);a.fastupdate&&((d=a.B.get(e))?d.push(h):a.B.set(e,[h]))}} +function $a(a,b,c,d,e){return c&&1b?b?a.slice(c,c+b):a.slice(c):a,d?cb(a):a;for(var e=[],g=0,f=void 0,h=void 0;g=h){c-=h;continue}cb&&(f=f.slice(0,b),h=f.length),e.push(f);else{if(h>=b)return h>b&&(f=f.slice(0,b)),d?cb(f):f;e=[f]}b-=h;if(!b)break}if(!e.length)return e;e=1a.length)return e?W(a[0],b,c,d):a[0];d=[];for(var f=0,h=K(),k=za(a),l=0,m;la.length)return[];var g=[],f=0,h=K(),k=za(a);if(!k)return g;for(var l=0,m;la.length)return e?W(a[0],b,c,d):a[0];b=[];c=K();d=0;for(var f;d=e)))break;if(h.length){if(f)return W(h,e,0);b.push(h);return}}return!c&&h}function Y(a,b,c,d,e,g,f,h){var k;c&&(k=a.bidirectional&&b>c);if(a.db)return c?a.db.get(k?c:b,k?b:c,d,e,g,f,h):a.db.get(b,"",d,e,g,f,h);a=c?(a=a.J.get(k?b:c))&&a.get(k?c:b):a.map.get(b);return a};V.prototype.remove=function(a,b){var c=this.B.size&&(this.fastupdate?this.B.get(a):this.B.has(a));if(c){if(this.fastupdate)for(var d=0,e;de.length)e.pop();else{var g=e.indexOf(a);g===c.length-1?e.pop():e.splice(g,1)}}else mb(this.map,a),this.depth&&mb(this.J,a);b||this.B.delete(a)}this.db&&(this.R.push({del:a}),this.ga&&bb(this));this.cache&&this.cache.remove(a);return this}; +function mb(a,b){var c=0;if(a.constructor===Array)for(var d=0,e=void 0,g;dc||d)a=a.slice(d,d+c);e&&(a=wb.call(this,a));return a}}function wb(a){for(var b=Array(a.length),c=0,d;c=m.length)d-=m.length;else{for(var n=c?d+Math.min(m.length-d,c):m.length,p=d;p=g.length)return[];if(!b&&!c)return g;g=g.slice(c,c+b);return d?e.enrich(g):g})}; -w.enrich=function(a){"object"!==typeof a&&(a=[a]);for(var b=this.db.transaction("reg","readonly").objectStore("reg"),c=[],d=0;d=m.length)d-=m.length;else{for(var n=c?d+Math.min(m.length-d,c):m.length,p=d;p=g.length)return[];if(!b&&!c)return g;g=g.slice(c,c+b);return d?e.enrich(g):g})}; +w.enrich=function(a){"object"!==typeof a&&(a=[a]);for(var b=this.db.transaction("reg","readonly").objectStore("reg"),c=[],d=0;dm&&!g&&"string"===typeof n&&!isNaN(n)&&(m=k.indexOf(parseInt(n,10)))&&(g=1),0<=m)if(e=1,1m&&!g&&"string"===typeof n&&!isNaN(n)&&(m=k.indexOf(parseInt(n,10)))&&(g=1),0<=m)if(e=1,1 this.m.get(r)), l = 1); - this.j && 1 < f.length && (this.I || (this.I = new RegExp("(" + this.D + ")", "g")), f = f.replace(this.I, r => this.j.get(r)), l = 1); - f && l && (f.length < this.G || this.filter && this.filter.has(f)) && (f = ""); - if (f && (this.s || this.i && 1 < f.length)) { + this.stemmer && 2 < f.length && (this.A || (this.A = new RegExp("(?!^)(" + this.u + ")$")), f = f.replace(this.A, r => this.stemmer.get(r)), l = 1); + this.matcher && 1 < f.length && (this.v || (this.v = new RegExp("(" + this.s + ")", "g")), f = f.replace(this.v, r => this.matcher.get(r)), l = 1); + f && l && (f.length < this.minlength || this.filter && this.filter.has(f)) && (f = ""); + if (f && (this.mapper || this.dedupe && 1 < f.length)) { e = ""; for (let r = 0, k = "", p, q; r < f.length; r++) { - p = f.charAt(r), p === k && this.i || ((q = this.s && this.s.get(p)) || "" === q ? q === k && this.i || !(k = q) || (e += q) : e += k = p); + p = f.charAt(r), p === k && this.dedupe || ((q = this.mapper && this.mapper.get(p)) || "" === q ? q === k && this.dedupe || !(k = q) || (e += q) : e += k = p); } f = e; } - if (f && this.l) { - for (e = 0; f && e < this.l.length; e += 2) { - f = f.replace(this.l[e], this.l[e + 1]); + if (f && this.replacer) { + for (e = 0; f && e < this.replacer.length; e += 2) { + f = f.replace(this.replacer[e], this.replacer[e + 1]); } } - this.cache && h.length <= this.C && (this.u.set(h, f), this.u.size > this.L && (this.u.clear(), this.C = this.C / 1.1 | 0)); + this.cache && h.length <= this.o && (this.j.set(h, f), this.j.size > this.B && (this.j.clear(), this.o = this.o / 1.1 | 0)); f && b.push(f); } - this.B && (b = this.B(b) || b); - this.cache && a.length <= this.h && (this.o.set(a, b), this.o.size > this.L && (this.o.clear(), this.h = this.h / 1.1 | 0)); + this.finalize && (b = this.finalize(b) || b); + this.cache && a.length <= this.h && (this.i.set(a, b), this.i.size > this.B && (this.i.clear(), this.h = this.h / 1.1 | 0)); return b; }; -function D(a) { - a.A = null; - a.o.clear(); - a.u.clear(); +function C(a) { + a.m = null; + a.i.clear(); + a.j.clear(); } -;function E(a) { +;function D(a) { this.limit = a && !0 !== a ? a : 1000; this.cache = new Map(); this.h = ""; } -E.prototype.set = function(a, c) { - this.cache.has(a) || (this.cache.set(this.h = a, c), this.limit && this.cache.size > this.limit && this.cache.delete(this.cache.keys().next().value)); +D.prototype.set = function(a, c) { + this.cache.set(this.h = a, c); + this.cache.size > this.limit && this.cache.delete(this.cache.keys().next().value); }; -E.prototype.get = function(a) { +D.prototype.get = function(a) { const c = this.cache.get(a); - c && this.limit && this.h !== a && (this.cache.delete(a), this.cache.set(this.h = a, c)); + c && this.h !== a && (this.cache.delete(a), this.cache.set(this.h = a, c)); return c; }; -E.prototype.remove = function(a) { +D.prototype.remove = function(a) { for (const c of this.cache) { const b = c[0]; c[1].includes(a) && this.cache.delete(b); } }; -E.prototype.clear = function() { +D.prototype.clear = function() { this.cache.clear(); this.h = ""; }; -var F = {normalize:function(a) { +const E = {normalize:function(a) { return a.toLowerCase(); -}, i:!1}; -const G = {memory:{resolution:1}, performance:{resolution:6, fastupdate:!0, context:{depth:1, resolution:3}}, match:{tokenize:"forward"}, score:{resolution:9, context:{depth:2, resolution:9}}}; -u(); -H.prototype.add = function(a, c, b, d) { +}, dedupe:!1}; +const F = {memory:{resolution:1}, performance:{resolution:6, fastupdate:!0, context:{depth:1, resolution:3}}, match:{tokenize:"forward"}, score:{resolution:9, context:{depth:2, resolution:9}}}; +Object.create(null); +G.prototype.add = function(a, c, b, d) { if (c && (a || 0 === a)) { if (!d && !b && this.g.has(a)) { return this.update(a, c); } c = this.encoder.encode(c); if (d = c.length) { - const r = u(), k = u(), p = this.depth, q = this.resolution; + const r = Object.create(null), k = Object.create(null), p = this.depth, q = this.resolution; for (let m = 0; m < d; m++) { let n = c[this.rtl ? d - 1 - m : m]; var e = n.length; if (e && (p || !k[n])) { - var g = this.score ? this.score(c, n, m, null, 0) : I(q, d, m), f = ""; + var g = this.score ? this.score(c, n, m, null, 0) : H(q, d, m), f = ""; switch(this.tokenize) { case "full": if (2 < e) { for (g = 0; g < e; g++) { for (var h = e; h > g; h--) { f = n.substring(g, h); - var l = this.score ? this.score(c, n, m, f, g) : I(q, d, m, e, g); - J(this, k, f, l, a, b); + var l = this.score ? this.score(c, n, m, f, g) : H(q, d, m, e, g); + I(this, k, f, l, a, b); } } break; @@ -231,24 +229,24 @@ H.prototype.add = function(a, c, b, d) { case "reverse": if (1 < e) { for (h = e - 1; 0 < h; h--) { - f = n[h] + f, l = this.score ? this.score(c, n, m, f, h) : I(q, d, m, e, h), J(this, k, f, l, a, b); + f = n[h] + f, l = this.score ? this.score(c, n, m, f, h) : H(q, d, m, e, h), I(this, k, f, l, a, b); } f = ""; } case "forward": if (1 < e) { for (h = 0; h < e; h++) { - f += n[h], J(this, k, f, g, a, b); + f += n[h], I(this, k, f, g, a, b); } break; } default: - if (J(this, k, n, g, a, b), p && 1 < d && m < d - 1) { - for (e = u(), f = this.M, g = n, h = Math.min(p + 1, d - m), e[g] = 1, l = 1; l < h; l++) { + if (I(this, k, n, g, a, b), p && 1 < d && m < d - 1) { + for (e = Object.create(null), f = this.C, g = n, h = Math.min(p + 1, d - m), e[g] = 1, l = 1; l < h; l++) { if ((n = c[this.rtl ? d - 1 - m - l : m + l]) && !e[n]) { e[n] = 1; - const v = this.score ? this.score(c, g, m, n, l) : I(f + (d / 2 > f ? 0 : 1), d, m, h - 1, l - 1), M = this.bidirectional && n > g; - J(this, r, M ? g : n, v, a, b, M ? n : g); + const v = this.score ? this.score(c, g, m, n, l) : H(f + (d / 2 > f ? 0 : 1), d, m, h - 1, l - 1), L = this.bidirectional && n > g; + I(this, r, L ? g : n, v, a, b, L ? n : g); } } } @@ -260,14 +258,14 @@ H.prototype.add = function(a, c, b, d) { } return this; }; -function J(a, c, b, d, e, g, f) { - let h = f ? a.v : a.map, l; - c[b] && f && (l = c[b])[f] || (f ? (c = l || (c[b] = u()), c[f] = 1, (l = h.get(f)) ? h = l : h.set(f, h = new Map())) : c[b] = 1, (l = h.get(b)) ? h = l : h.set(b, h = []), h = h[d] || (h[d] = []), g && h.includes(e) || (h.push(e), a.fastupdate && ((c = a.g.get(e)) ? c.push(h) : a.g.set(e, [h])))); +function I(a, c, b, d, e, g, f) { + let h = f ? a.l : a.map, l; + c[b] && f && (l = c[b])[f] || (f ? (c = l || (c[b] = Object.create(null)), c[f] = 1, (l = h.get(f)) ? h = l : h.set(f, h = new Map())) : c[b] = 1, (l = h.get(b)) ? h = l : h.set(b, h = []), h = h[d] || (h[d] = []), g && h.includes(e) || (h.push(e), a.fastupdate && ((c = a.g.get(e)) ? c.push(h) : a.g.set(e, [h])))); } -function I(a, c, b, d, e) { +function H(a, c, b, d, e) { return b && 1 < a ? c + (d || 0) <= a ? b + (e || 0) : (a - 1) / (c + (d || 0)) * (b + (e || 0)) + 1 | 0 : 0; } -;function K(a, c, b) { +;function J(a, c, b) { if (1 === a.length) { return a = a[0], a = b || a.length > c ? c ? a.slice(b, b + c) : a.slice(b) : a; } @@ -297,13 +295,13 @@ function I(a, c, b, d, e) { } return d.length ? d = 1 < d.length ? [].concat.apply([], d) : d[0] : d; } -;function L(a, c, b, d) { +;function K(a, c, b, d) { var e = a.length; let g = [], f = 0, h, l, r; d && (d = []); for (let k = e - 1, p; 0 <= k; k--) { r = a[k]; - e = u(); + e = Object.create(null); p = !h; for (let q = 0, m; q < r.length; q++) { if ((m = r[q]) && m.length) { @@ -358,7 +356,7 @@ function I(a, c, b, d, e) { } return g; } -;H.prototype.search = function(a, c, b) { +;G.prototype.search = function(a, c, b) { b || (c || "object" !== typeof a ? "object" === typeof c && (b = c, c = 0) : (b = a, a = "")); let d = []; let e, g = 0; @@ -373,19 +371,19 @@ function I(a, c, b, d, e) { b = a.length; c || (c = 100); if (1 === b) { - return N.call(this, a[0], "", c, g); + return M.call(this, a[0], "", c, g); } f = this.depth && !1 !== f; if (2 === b && f && !e) { - return N.call(this, a[0], a[1], c, g); + return M.call(this, a[0], a[1], c, g); } let h = 0, l = 0; if (1 < b) { - var r = u(); + var r = Object.create(null); const p = []; for (let q = 0, m; q < b; q++) { if ((m = a[q]) && !r[m]) { - if (e || O(this, m)) { + if (e || N(this, m)) { p.push(m), r[m] = 1; } else { return d; @@ -404,15 +402,15 @@ function I(a, c, b, d, e) { r = 0; let k; if (1 === b) { - return N.call(this, a[0], "", c, g); + return M.call(this, a[0], "", c, g); } if (2 === b && f && !e) { - return N.call(this, a[0], a[1], c, g); + return M.call(this, a[0], a[1], c, g); } - 1 < b && (f ? (k = a[0], r = 1) : 9 < h && 3 < h / l && a.sort(w)); + 1 < b && (f ? (k = a[0], r = 1) : 9 < h && 3 < h / l && a.sort(u)); for (let p, q; r < b; r++) { q = a[r]; - k ? (p = O(this, q, k), p = P(p, d, e, this.M, c, g, 2 === b), e && !1 === p && d.length || (k = q)) : (p = O(this, q), p = P(p, d, e, this.resolution, c, g, 1 === b)); + k ? (p = N(this, q, k), p = O(p, d, e, this.C, c, g, 2 === b), e && !1 === p && d.length || (k = q)) : (p = N(this, q), p = O(p, d, e, this.resolution, c, g, 1 === b)); if (p) { return p; } @@ -427,16 +425,16 @@ function I(a, c, b, d, e) { return d; } if (1 === f) { - return K(d[0], c, g); + return J(d[0], c, g); } } } - return L(d, c, g, e); + return K(d, c, g, e); }; -function N(a, c, b, d) { - return (a = O(this, a, c)) && a.length ? K(a, b, d) : []; +function M(a, c, b, d) { + return (a = N(this, a, c)) && a.length ? J(a, b, d) : []; } -function P(a, c, b, d, e, g, f) { +function O(a, c, b, d, e, g, f) { let h = []; if (a) { d = Math.min(a.length, d); @@ -449,7 +447,7 @@ function P(a, c, b, d, e, g, f) { } if (h.length) { if (f) { - return K(h, e, 0); + return J(h, e, 0); } c.push(h); return; @@ -457,13 +455,13 @@ function P(a, c, b, d, e, g, f) { } return !b && h; } -function O(a, c, b) { +function N(a, c, b) { let d; b && (d = a.bidirectional && c > b); - a = b ? (a = a.v.get(d ? c : b)) && a.get(d ? b : c) : a.map.get(c); + a = b ? (a = a.l.get(d ? c : b)) && a.get(d ? b : c) : a.map.get(c); return a; } -;H.prototype.remove = function(a, c) { +;G.prototype.remove = function(a, c) { const b = this.g.size && (this.fastupdate ? this.g.get(a) : this.g.has(a)); if (b) { if (this.fastupdate) { @@ -478,14 +476,14 @@ function O(a, c, b) { } } } else { - Q(this.map, a), this.depth && Q(this.v, a); + P(this.map, a), this.depth && P(this.l, a); } c || this.g.delete(a); } this.cache && this.cache.remove(a); return this; }; -function Q(a, c) { +function P(a, c) { let b = 0; if (a.constructor === Array) { for (let d = 0, e, g; d < a.length; d++) { @@ -500,25 +498,25 @@ function Q(a, c) { } } else { for (let d of a) { - const e = d[0], g = Q(d[1], c); + const e = d[0], g = P(d[1], c); g ? b += g : a.delete(e); } } return b; } -;function H(a, c) { - if (!(this instanceof H)) { - return new H(a); +;function G(a, c) { + if (!this) { + return new G(a); } if (a) { var b = "string" === typeof a ? a : a.preset; - b && (G[b] || console.warn("Preset not found: " + b), a = Object.assign({}, G[b], a)); + b && (F[b] || console.warn("Preset not found: " + b), a = Object.assign({}, F[b], a)); } else { a = {}; } b = a.context || {}; - const d = a.encode || a.encoder || F; - this.encoder = d.encode ? d : "object" === typeof d ? new C(d) : {encode:d}; + const d = a.encode || a.encoder || E; + this.encoder = d.encode ? d : "object" === typeof d ? new B(d) : {encode:d}; let e; this.resolution = a.resolution || 9; this.tokenize = e = a.tokenize || "strict"; @@ -528,33 +526,33 @@ function Q(a, c) { this.score = a.score || null; e = !1; this.map = new Map(); - this.v = new Map(); + this.l = new Map(); this.g = c || (this.fastupdate ? new Map() : new Set()); - this.M = b.resolution || 1; + this.C = b.resolution || 1; this.rtl = d.rtl || a.rtl || !1; - this.cache = (e = a.cache || null) && new E(e); + this.cache = (e = a.cache || null) && new D(e); } -H.prototype.clear = function() { +G.prototype.clear = function() { this.map.clear(); - this.v.clear(); + this.l.clear(); this.g.clear(); this.cache && this.cache.clear(); return this; }; -H.prototype.append = function(a, c) { +G.prototype.append = function(a, c) { return this.add(a, c, !0); }; -H.prototype.contain = function(a) { +G.prototype.contain = function(a) { return this.g.has(a); }; -H.prototype.update = function(a, c) { +G.prototype.update = function(a, c) { if (this.async) { const b = this, d = this.remove(a); return d.then ? d.then(() => b.add(a, c)) : this.add(a, c); } return this.remove(a).add(a, c); }; -function R(a) { +function Q(a) { let c = 0; if (a.constructor === Array) { for (let b = 0, d; b < a.length; b++) { @@ -562,39 +560,39 @@ function R(a) { } } else { for (const b of a) { - const d = b[0], e = R(b[1]); + const d = b[0], e = Q(b[1]); e ? c += e : a.delete(d); } } return c; } -H.prototype.cleanup = function() { +G.prototype.cleanup = function() { if (!this.fastupdate) { return console.info('Cleanup the index isn\'t required when not using "fastupdate".'), this; } - R(this.map); - this.depth && R(this.v); + Q(this.map); + this.depth && Q(this.l); return this; }; -H.prototype.searchCache = function(a, c, b) { +G.prototype.searchCache = function(a, c, b) { a = ("object" === typeof a ? "" + a.query : a).toLowerCase(); let d = this.cache.get(a); if (!d) { d = this.search(a, c, b); - if (d instanceof Promise) { + if (d.then) { const e = this; d.then(function(g) { e.cache.set(a, g); + return g; }); } this.cache.set(a, d); } return d; }; -const S = u(); -const T = {Index:H, Charset:S, Encoder:C, Document:null, Worker:null, Resolver:null, IndexedDB:null}, U = self; -let V; -(V = U.define) && V.amd ? V([], function() { - return T; -}) : "object" === typeof U.exports ? U.exports = T : U.FlexSearch = T; +const R = {Index:G, Charset:null, Encoder:B, Document:null, Worker:null, Resolver:null, IndexedDB:null, Language:{}}, S = self; +let T; +(T = S.define) && T.amd ? T([], function() { + return R; +}) : "object" === typeof S.exports ? S.exports = R : S.FlexSearch = R; }(this)); diff --git a/dist/flexsearch.light.min.js b/dist/flexsearch.light.min.js index e781097..5bf4c29 100644 --- a/dist/flexsearch.light.min.js +++ b/dist/flexsearch.light.min.js @@ -5,21 +5,21 @@ * Hosted by Nextapps GmbH * https://github.com/nextapps-de/flexsearch */ -(function(self){'use strict';function t(a,c,b){const d=typeof b,e=typeof a;if("undefined"!==d){if("undefined"!==e){if(b){if("function"===e&&d===e)return function(h){return a(b(h))};c=a.constructor;if(c===b.constructor){if(c===Array)return b.concat(a);if(c===Map){var g=new Map(b);for(var f of a)g.set(f[0],f[1]);return g}if(c===Set){f=new Set(b);for(g of a.values())f.add(g);return f}}}return a}return b}return"undefined"===e?c:a}function u(){return Object.create(null)}function w(a,c){return c.length-a.length};const x=/[^\p{L}\p{N}]+/u,y=/(\d{3})/g,z=/(\D)(\d{3})/g,A=/(\d{3})(\D)/g,B="".normalize&&/[\u0300-\u036f]/g;function C(a={}){if(!(this instanceof C))return new C(...arguments);for(a=0;athis.m.get(r)),l=1);this.j&&1this.j.get(r)), -l=1);f&&l&&(f.lengththis.L&&(this.u.clear(),this.C=this.C/1.1|0));f&&b.push(f)}this.B&&(b=this.B(b)||b);this.cache&&a.length<=this.h&& -(this.o.set(a,b),this.o.size>this.L&&(this.o.clear(),this.h=this.h/1.1|0));return b};function D(a){a.A=null;a.o.clear();a.u.clear()};function E(a){this.limit=a&&!0!==a?a:1E3;this.cache=new Map;this.h=""}E.prototype.set=function(a,c){this.cache.has(a)||(this.cache.set(this.h=a,c),this.limit&&this.cache.size>this.limit&&this.cache.delete(this.cache.keys().next().value))};E.prototype.get=function(a){const c=this.cache.get(a);c&&this.limit&&this.h!==a&&(this.cache.delete(a),this.cache.set(this.h=a,c));return c};E.prototype.remove=function(a){for(const c of this.cache){const b=c[0];c[1].includes(a)&&this.cache.delete(b)}}; -E.prototype.clear=function(){this.cache.clear();this.h=""};var F={normalize:function(a){return a.toLowerCase()},i:!1};const G={memory:{resolution:1},performance:{resolution:6,fastupdate:!0,context:{depth:1,resolution:3}},match:{tokenize:"forward"},score:{resolution:9,context:{depth:2,resolution:9}}};u();H.prototype.add=function(a,c,b,d){if(c&&(a||0===a)){if(!d&&!b&&this.g.has(a))return this.update(a,c);c=this.encoder.encode(c);if(d=c.length){const r=u(),k=u(),p=this.depth,q=this.resolution;for(let m=0;mg;h--){f=n.substring(g,h);var l=this.score?this.score(c,n,m,f,g):I(q,d,m,e,g);J(this,k,f,l,a,b)}break}case "reverse":if(1< -e){for(h=e-1;0f?0:1),d,m,h-1,l-1),L=this.bidirectional&&n>g;J(this,r,L?g:n,v,a,b,L?n:g)}}}}this.fastupdate||this.g.add(a)}}return this}; -function J(a,c,b,d,e,g,f){let h=f?a.v:a.map,l;c[b]&&f&&(l=c[b])[f]||(f?(c=l||(c[b]=u()),c[f]=1,(l=h.get(f))?h=l:h.set(f,h=new Map)):c[b]=1,(l=h.get(b))?h=l:h.set(b,h=[]),h=h[d]||(h[d]=[]),g&&h.includes(e)||(h.push(e),a.fastupdate&&((c=a.g.get(e))?c.push(h):a.g.set(e,[h]))))}function I(a,c,b,d,e){return b&&1c?c?a.slice(b,b+c):a.slice(b):a;let d=[];for(let e=0,g,f;e=f){b-=f;continue}bc&&(g=g.slice(0,c),f=g.length),d.push(g);else{if(f>=c)return f>c&&(g=g.slice(0,c)),g;d=[g]}c-=f;if(!c)break}return d.length?d=1=e)))break;if(h.length){if(f)return K(h,e,0);c.push(h);return}}return!b&&h}function O(a,c,b){let d;b&&(d=a.bidirectional&&c>b);a=b?(a=a.v.get(d?c:b))&&a.get(d?b:c):a.map.get(c);return a};H.prototype.remove=function(a,c){const b=this.g.size&&(this.fastupdate?this.g.get(a):this.g.has(a));if(b){if(this.fastupdate)for(let d=0,e;de.length)e.pop();else{const g=e.indexOf(a);g===b.length-1?e.pop():e.splice(g,1)}}else Q(this.map,a),this.depth&&Q(this.v,a);c||this.g.delete(a)}this.cache&&this.cache.remove(a);return this}; -function Q(a,c){let b=0;if(a.constructor===Array)for(let d=0,e,g;db.add(a,c)):this.add(a,c)}return this.remove(a).add(a,c)};function R(a){let c=0;if(a.constructor===Array)for(let b=0,d;bthis.stemmer.get(r)),l=1);this.matcher&&1this.matcher.get(r)),l=1);f&&l&&(f.lengththis.B&&(this.j.clear(),this.o=this.o/1.1|0));f&&b.push(f)}this.finalize&&(b=this.finalize(b)||b);this.cache&&a.length<=this.h&&(this.i.set(a,b),this.i.size>this.B&&(this.i.clear(),this.h=this.h/1.1|0));return b};function C(a){a.m=null;a.i.clear();a.j.clear()};function D(a){this.limit=a&&!0!==a?a:1E3;this.cache=new Map;this.h=""}D.prototype.set=function(a,c){this.cache.set(this.h=a,c);this.cache.size>this.limit&&this.cache.delete(this.cache.keys().next().value)};D.prototype.get=function(a){const c=this.cache.get(a);c&&this.h!==a&&(this.cache.delete(a),this.cache.set(this.h=a,c));return c};D.prototype.remove=function(a){for(const c of this.cache){const b=c[0];c[1].includes(a)&&this.cache.delete(b)}}; +D.prototype.clear=function(){this.cache.clear();this.h=""};const E={normalize:function(a){return a.toLowerCase()},dedupe:!1};const F={memory:{resolution:1},performance:{resolution:6,fastupdate:!0,context:{depth:1,resolution:3}},match:{tokenize:"forward"},score:{resolution:9,context:{depth:2,resolution:9}}};Object.create(null);G.prototype.add=function(a,c,b,d){if(c&&(a||0===a)){if(!d&&!b&&this.g.has(a))return this.update(a,c);c=this.encoder.encode(c);if(d=c.length){const r=Object.create(null),k=Object.create(null),p=this.depth,q=this.resolution;for(let m=0;mg;h--){f=n.substring(g,h);var l=this.score?this.score(c,n,m,f,g):H(q,d, +m,e,g);I(this,k,f,l,a,b)}break}case "reverse":if(1f?0:1),d,m,h-1,l-1),K=this.bidirectional&&n>g;I(this,r,K?g:n, +v,a,b,K?n:g)}}}}this.fastupdate||this.g.add(a)}}return this};function I(a,c,b,d,e,g,f){let h=f?a.l:a.map,l;c[b]&&f&&(l=c[b])[f]||(f?(c=l||(c[b]=Object.create(null)),c[f]=1,(l=h.get(f))?h=l:h.set(f,h=new Map)):c[b]=1,(l=h.get(b))?h=l:h.set(b,h=[]),h=h[d]||(h[d]=[]),g&&h.includes(e)||(h.push(e),a.fastupdate&&((c=a.g.get(e))?c.push(h):a.g.set(e,[h]))))}function H(a,c,b,d,e){return b&&1c?c?a.slice(b,b+c):a.slice(b):a;let d=[];for(let e=0,g,f;e=f){b-=f;continue}bc&&(g=g.slice(0,c),f=g.length),d.push(g);else{if(f>=c)return f>c&&(g=g.slice(0,c)),g;d=[g]}c-=f;if(!c)break}return d.length?d=1=e)))break;if(h.length){if(f)return J(h,e,0);c.push(h);return}}return!b&&h}function N(a,c,b){let d;b&&(d=a.bidirectional&&c>b);a=b?(a=a.l.get(d?c:b))&&a.get(d?b:c):a.map.get(c);return a};G.prototype.remove=function(a,c){const b=this.g.size&&(this.fastupdate?this.g.get(a):this.g.has(a));if(b){if(this.fastupdate)for(let d=0,e;de.length)e.pop();else{const g=e.indexOf(a);g===b.length-1?e.pop():e.splice(g,1)}}else P(this.map,a),this.depth&&P(this.l,a);c||this.g.delete(a)}this.cache&&this.cache.remove(a);return this}; +function P(a,c){let b=0;if(a.constructor===Array)for(let d=0,e,g;db.add(a,c)):this.add(a,c)}return this.remove(a).add(a,c)}; +function Q(a){let c=0;if(a.constructor===Array)for(let b=0,d;b this.limit && this.cache.delete(this.cache.keys().next().value)); +v.prototype.set = function(a, b) { + this.cache.set(this.B = a, b); + this.cache.size > this.limit && this.cache.delete(this.cache.keys().next().value); }; -w.prototype.get = function(a) { +v.prototype.get = function(a) { const b = this.cache.get(a); - b && this.limit && this.C !== a && (this.cache.delete(a), this.cache.set(this.C = a, b)); + b && this.B !== a && (this.cache.delete(a), this.cache.set(this.B = a, b)); return b; }; -w.prototype.remove = function(a) { +v.prototype.remove = function(a) { for (const b of this.cache) { const c = b[0]; b[1].includes(a) && this.cache.delete(c); } }; -w.prototype.clear = function() { +v.prototype.clear = function() { this.cache.clear(); - this.C = ""; + this.B = ""; }; -const x = {memory:{resolution:1}, performance:{resolution:6, fastupdate:!0, context:{depth:1, resolution:3}}, match:{tokenize:"forward"}, score:{resolution:9, context:{depth:2, resolution:9}}}; -function y(a) { +const w = new Map([["b", "p"], ["v", "f"], ["w", "f"], ["z", "s"], ["x", "s"], ["d", "t"], ["n", "m"], ["c", "k"], ["g", "k"], ["j", "k"], ["q", "k"], ["i", "e"], ["y", "e"], ["u", "o"]]); +const x = new Map([["ai", "ei"], ["ae", "a"], ["oe", "o"], ["ue", "u"], ["sh", "s"], ["ch", "c"], ["th", "t"], ["ph", "f"], ["pf", "f"]]), y = [/([^aeo])h([aeo$])/g, "$1$2", /([aeo])h([^aeo]|$)/g, "$1$2"]; +const z = {a:"", e:"", i:"", o:"", u:"", y:"", b:1, f:1, p:1, v:1, c:2, g:2, j:2, k:2, q:2, s:2, x:2, z:2, "\u00df":2, d:3, t:3, l:4, m:5, n:5, r:6}; +const A = /[\x00-\x7F]+/g; +const B = /[\x00-\x7F]+/g; +const C = /[\x00-\x7F]+/g; +var D = {LatinExact:{normalize:!1, dedupe:!1}, LatinDefault:{normalize:function(a) { + return a.toLowerCase(); +}, dedupe:!1}, LatinSimple:{normalize:!0, dedupe:!0}, LatinBalance:{normalize:!0, dedupe:!0, mapper:w}, LatinAdvanced:{normalize:!0, dedupe:!0, mapper:w, replacer:y, matcher:x}, LatinExtra:{normalize:!0, dedupe:!0, mapper:w, replacer:y.concat([/(?!^)[aeoy]/g, ""]), matcher:x}, LatinSoundex:{normalize:!0, dedupe:!1, include:{letter:!0}, finalize:function(a) { + for (let c = 0; c < a.length; c++) { + var b = a[c]; + let d = b.charAt(0), e = z[d]; + for (let f = 1, g; f < b.length && (g = b.charAt(f), "h" === g || "w" === g || !(g = z[g]) || g === e || (d += g, e = g, 4 !== d.length)); f++) { + } + a[c] = d; + } +}}, ArabicDefault:{rtl:!0, normalize:!1, dedupe:!0, prepare:function(a) { + return ("" + a).replace(A, " "); +}}, CjkDefault:{normalize:!1, dedupe:!0, split:"", prepare:function(a) { + return ("" + a).replace(B, ""); +}}, CyrillicDefault:{normalize:!1, dedupe:!0, prepare:function(a) { + return ("" + a).replace(C, " "); +}}}; +const E = {memory:{resolution:1}, performance:{resolution:6, fastupdate:!0, context:{depth:1, resolution:3}}, match:{tokenize:"forward"}, score:{resolution:9, context:{depth:2, resolution:9}}}; +function F(a) { const b = "string" === typeof a ? a : a.preset; - b && (x[b] || console.warn("Preset not found: " + b), a = Object.assign({}, x[b], a)); + b && (E[b] || console.warn("Preset not found: " + b), a = Object.assign({}, E[b], a)); return a; } -;t(); -z.prototype.add = function(a, b, c, d) { +;Object.create(null); +G.prototype.add = function(a, b, c, d) { if (b && (a || 0 === a)) { if (!d && !c && this.h.has(a)) { return this.update(a, b); } b = this.encoder.encode(b); if (d = b.length) { - const r = t(), k = t(), p = this.depth, q = this.resolution; + const r = Object.create(null), k = Object.create(null), p = this.depth, q = this.resolution; for (let l = 0; l < d; l++) { let n = b[this.rtl ? d - 1 - l : l]; var e = n.length; if (e && (p || !k[n])) { - var f = this.score ? this.score(b, n, l, null, 0) : A(q, d, l), g = ""; + var f = this.score ? this.score(b, n, l, null, 0) : H(q, d, l), g = ""; switch(this.tokenize) { case "full": if (2 < e) { for (f = 0; f < e; f++) { for (var h = e; h > f; h--) { g = n.substring(f, h); - var m = this.score ? this.score(b, n, l, g, f) : A(q, d, l, e, f); - B(this, k, g, m, a, c); + var m = this.score ? this.score(b, n, l, g, f) : H(q, d, l, e, f); + J(this, k, g, m, a, c); } } break; @@ -69,24 +90,24 @@ z.prototype.add = function(a, b, c, d) { case "reverse": if (1 < e) { for (h = e - 1; 0 < h; h--) { - g = n[h] + g, m = this.score ? this.score(b, n, l, g, h) : A(q, d, l, e, h), B(this, k, g, m, a, c); + g = n[h] + g, m = this.score ? this.score(b, n, l, g, h) : H(q, d, l, e, h), J(this, k, g, m, a, c); } g = ""; } case "forward": if (1 < e) { for (h = 0; h < e; h++) { - g += n[h], B(this, k, g, f, a, c); + g += n[h], J(this, k, g, f, a, c); } break; } default: - if (B(this, k, n, f, a, c), p && 1 < d && l < d - 1) { - for (e = t(), g = this.F, f = n, h = Math.min(p + 1, d - l), e[f] = 1, m = 1; m < h; m++) { + if (J(this, k, n, f, a, c), p && 1 < d && l < d - 1) { + for (e = Object.create(null), g = this.C, f = n, h = Math.min(p + 1, d - l), e[f] = 1, m = 1; m < h; m++) { if ((n = b[this.rtl ? d - 1 - l - m : l + m]) && !e[n]) { e[n] = 1; - const u = this.score ? this.score(b, f, l, n, m) : A(g + (d / 2 > g ? 0 : 1), d, l, h - 1, m - 1), H = this.bidirectional && n > f; - B(this, r, H ? f : n, u, a, c, H ? n : f); + const u = this.score ? this.score(b, f, l, n, m) : H(g + (d / 2 > g ? 0 : 1), d, l, h - 1, m - 1), I = this.bidirectional && n > f; + J(this, r, I ? f : n, u, a, c, I ? n : f); } } } @@ -98,14 +119,14 @@ z.prototype.add = function(a, b, c, d) { } return this; }; -function B(a, b, c, d, e, f, g) { - let h = g ? a.B : a.map, m; - b[c] && g && (m = b[c])[g] || (g ? (b = m || (b[c] = t()), b[g] = 1, (m = h.get(g)) ? h = m : h.set(g, h = new Map())) : b[c] = 1, (m = h.get(c)) ? h = m : h.set(c, h = []), h = h[d] || (h[d] = []), f && h.includes(e) || (h.push(e), a.fastupdate && ((b = a.h.get(e)) ? b.push(h) : a.h.set(e, [h])))); +function J(a, b, c, d, e, f, g) { + let h = g ? a.A : a.map, m; + b[c] && g && (m = b[c])[g] || (g ? (b = m || (b[c] = Object.create(null)), b[g] = 1, (m = h.get(g)) ? h = m : h.set(g, h = new Map())) : b[c] = 1, (m = h.get(c)) ? h = m : h.set(c, h = []), h = h[d] || (h[d] = []), f && h.includes(e) || (h.push(e), a.fastupdate && ((b = a.h.get(e)) ? b.push(h) : a.h.set(e, [h])))); } -function A(a, b, c, d, e) { +function H(a, b, c, d, e) { return c && 1 < a ? b + (d || 0) <= a ? c + (e || 0) : (a - 1) / (b + (d || 0)) * (c + (e || 0)) + 1 | 0 : 0; } -;function C(a, b, c) { +;function K(a, b, c) { if (1 === a.length) { return a = a[0], a = c || a.length > b ? b ? a.slice(c, c + b) : a.slice(c) : a; } @@ -135,13 +156,13 @@ function A(a, b, c, d, e) { } return d.length ? d = 1 < d.length ? [].concat.apply([], d) : d[0] : d; } -;function D(a, b, c, d) { +;function L(a, b, c, d) { var e = a.length; let f = [], g = 0, h, m, r; d && (d = []); for (let k = e - 1, p; 0 <= k; k--) { r = a[k]; - e = t(); + e = Object.create(null); p = !h; for (let q = 0, l; q < r.length; q++) { if ((l = r[q]) && l.length) { @@ -196,7 +217,7 @@ function A(a, b, c, d, e) { } return f; } -;z.prototype.search = function(a, b, c) { +;G.prototype.search = function(a, b, c) { c || (b || "object" !== typeof a ? "object" === typeof b && (c = b, b = 0) : (c = a, a = "")); let d = []; let e, f = 0; @@ -211,19 +232,19 @@ function A(a, b, c, d, e) { c = a.length; b || (b = 100); if (1 === c) { - return E.call(this, a[0], "", b, f); + return M.call(this, a[0], "", b, f); } g = this.depth && !1 !== g; if (2 === c && g && !e) { - return E.call(this, a[0], a[1], b, f); + return M.call(this, a[0], a[1], b, f); } let h = 0, m = 0; if (1 < c) { - var r = t(); + var r = Object.create(null); const p = []; for (let q = 0, l; q < c; q++) { if ((l = a[q]) && !r[l]) { - if (e || F(this, l)) { + if (e || N(this, l)) { p.push(l), r[l] = 1; } else { return d; @@ -242,15 +263,15 @@ function A(a, b, c, d, e) { r = 0; let k; if (1 === c) { - return E.call(this, a[0], "", b, f); + return M.call(this, a[0], "", b, f); } if (2 === c && g && !e) { - return E.call(this, a[0], a[1], b, f); + return M.call(this, a[0], a[1], b, f); } - 1 < c && (g ? (k = a[0], r = 1) : 9 < h && 3 < h / m && a.sort(v)); + 1 < c && (g ? (k = a[0], r = 1) : 9 < h && 3 < h / m && a.sort(t)); for (let p, q; r < c; r++) { q = a[r]; - k ? (p = F(this, q, k), p = G(p, d, e, this.F, b, f, 2 === c), e && !1 === p && d.length || (k = q)) : (p = F(this, q), p = G(p, d, e, this.resolution, b, f, 1 === c)); + k ? (p = N(this, q, k), p = O(p, d, e, this.C, b, f, 2 === c), e && !1 === p && d.length || (k = q)) : (p = N(this, q), p = O(p, d, e, this.resolution, b, f, 1 === c)); if (p) { return p; } @@ -265,16 +286,16 @@ function A(a, b, c, d, e) { return d; } if (1 === g) { - return C(d[0], b, f); + return K(d[0], b, f); } } } - return D(d, b, f, e); + return L(d, b, f, e); }; -function E(a, b, c, d) { - return (a = F(this, a, b)) && a.length ? C(a, c, d) : []; +function M(a, b, c, d) { + return (a = N(this, a, b)) && a.length ? K(a, c, d) : []; } -function G(a, b, c, d, e, f, g) { +function O(a, b, c, d, e, f, g) { let h = []; if (a) { d = Math.min(a.length, d); @@ -287,7 +308,7 @@ function G(a, b, c, d, e, f, g) { } if (h.length) { if (g) { - return C(h, e, 0); + return K(h, e, 0); } b.push(h); return; @@ -295,13 +316,13 @@ function G(a, b, c, d, e, f, g) { } return !c && h; } -function F(a, b, c) { +function N(a, b, c) { let d; c && (d = a.bidirectional && b > c); - a = c ? (a = a.B.get(d ? b : c)) && a.get(d ? c : b) : a.map.get(b); + a = c ? (a = a.A.get(d ? b : c)) && a.get(d ? c : b) : a.map.get(b); return a; } -;z.prototype.remove = function(a, b) { +;G.prototype.remove = function(a, b) { const c = this.h.size && (this.fastupdate ? this.h.get(a) : this.h.has(a)); if (c) { if (this.fastupdate) { @@ -316,14 +337,14 @@ function F(a, b, c) { } } } else { - I(this.map, a), this.depth && I(this.B, a); + P(this.map, a), this.depth && P(this.A, a); } b || this.h.delete(a); } this.cache && this.cache.remove(a); return this; }; -function I(a, b) { +function P(a, b) { let c = 0; if (a.constructor === Array) { for (let d = 0, e, f; d < a.length; d++) { @@ -338,17 +359,17 @@ function I(a, b) { } } else { for (let d of a) { - const e = d[0], f = I(d[1], b); + const e = d[0], f = P(d[1], b); f ? c += f : a.delete(e); } } return c; } -;function z(a, b) { - if (!(this instanceof z)) { - return new z(a); +;function G(a, b) { + if (!this) { + return new G(a); } - a = a ? y(a) : {}; + a = a ? F(a) : {}; const c = a.context || {}, d = a.encode || a.encoder || function(f) { return f.toLowerCase().trim().split(/\s+/); }; @@ -362,33 +383,33 @@ function I(a, b) { this.score = a.score || null; e = !1; this.map = new Map(); - this.B = new Map(); + this.A = new Map(); this.h = b || (this.fastupdate ? new Map() : new Set()); - this.F = c.resolution || 1; + this.C = c.resolution || 1; this.rtl = d.rtl || a.rtl || !1; - this.cache = (e = a.cache || null) && new w(e); + this.cache = (e = a.cache || null) && new v(e); } -z.prototype.clear = function() { +G.prototype.clear = function() { this.map.clear(); - this.B.clear(); + this.A.clear(); this.h.clear(); this.cache && this.cache.clear(); return this; }; -z.prototype.append = function(a, b) { +G.prototype.append = function(a, b) { return this.add(a, b, !0); }; -z.prototype.contain = function(a) { +G.prototype.contain = function(a) { return this.h.has(a); }; -z.prototype.update = function(a, b) { +G.prototype.update = function(a, b) { if (this.async) { const c = this, d = this.remove(a); return d.then ? d.then(() => c.add(a, b)) : this.add(a, b); } return this.remove(a).add(a, b); }; -function J(a) { +function Q(a) { let b = 0; if (a.constructor === Array) { for (let c = 0, d; c < a.length; c++) { @@ -396,59 +417,36 @@ function J(a) { } } else { for (const c of a) { - const d = c[0], e = J(c[1]); + const d = c[0], e = Q(c[1]); e ? b += e : a.delete(d); } } return b; } -z.prototype.cleanup = function() { +G.prototype.cleanup = function() { if (!this.fastupdate) { return console.info('Cleanup the index isn\'t required when not using "fastupdate".'), this; } - J(this.map); - this.depth && J(this.B); + Q(this.map); + this.depth && Q(this.A); return this; }; -z.prototype.searchCache = function(a, b, c) { +G.prototype.searchCache = function(a, b, c) { a = ("object" === typeof a ? "" + a.query : a).toLowerCase(); let d = this.cache.get(a); if (!d) { d = this.search(a, b, c); - if (d instanceof Promise) { + if (d.then) { const e = this; d.then(function(f) { e.cache.set(a, f); + return f; }); } this.cache.set(a, d); } return d; }; -const K = t(); -const L = new Map([["b", "p"], ["v", "f"], ["w", "f"], ["z", "s"], ["x", "s"], ["d", "t"], ["n", "m"], ["c", "k"], ["g", "k"], ["j", "k"], ["q", "k"], ["i", "e"], ["y", "e"], ["u", "o"]]); -var M = {normalize:!0, A:!0, D:L}; -const N = new Map([["ai", "ei"], ["ae", "a"], ["oe", "o"], ["ue", "u"], ["sh", "s"], ["ch", "c"], ["th", "t"], ["ph", "f"], ["pf", "f"]]), O = [/([^aeo])h([aeo$])/g, "$1$2", /([aeo])h([^aeo]|$)/g, "$1$2"]; -var P = {normalize:!0, A:!0, D:L, H:O, G:N}; -var Q = {normalize:!0, A:!0, D:L, H:O.concat([/(?!^)[aeoy]/g, ""]), G:N}; -const R = {a:"", e:"", i:"", o:"", u:"", y:"", b:1, f:1, p:1, v:1, c:2, g:2, j:2, k:2, q:2, s:2, x:2, z:2, "\u00df":2, d:3, t:3, l:4, m:5, n:5, r:6}; -K["latin:exact"] = {normalize:!1, A:!1}; -K["latin:default"] = {normalize:function(a) { - return a.toLowerCase(); -}, A:!1}; -K["latin:simple"] = {normalize:!0, A:!0}; -K["latin:balance"] = M; -K["latin:advanced"] = P; -K["latin:extra"] = Q; -K["latin:soundex"] = {normalize:!0, A:!1, J:{K:!0}, I:function(a) { - for (let c = 0; c < a.length; c++) { - var b = a[c]; - let d = b.charAt(0), e = R[d]; - for (let f = 1, g; f < b.length && (g = b.charAt(f), "h" === g || "w" === g || !(g = R[g]) || g === e || (d += g, e = g, 4 !== d.length)); f++) { - } - a[c] = d; - } -}}; -export default {Index:z, Charset:K, Encoder:null, Document:null, Worker:null, Resolver:null, IndexedDB:null}; +export default {Index:G, Charset:D, Encoder:null, Document:null, Worker:null, Resolver:null, IndexedDB:null, Language:{}}; -export const Index=z;export const Charset=K;export const Encoder=null;export const Document=null;export const Worker=null;export const Resolver=null;export const IndexedDB=null; \ No newline at end of file +export const Index=G;export const Charset=D;export const Encoder=null;export const Document=null;export const Worker=null;export const Resolver=null;export const IndexedDB=null;export const Language={}; \ No newline at end of file diff --git a/dist/flexsearch.light.module.min.js b/dist/flexsearch.light.module.min.js index c0d100d..f4f53d4 100644 --- a/dist/flexsearch.light.module.min.js +++ b/dist/flexsearch.light.module.min.js @@ -5,16 +5,16 @@ * Hosted by Nextapps GmbH * https://github.com/nextapps-de/flexsearch */ -function t(){return Object.create(null)}function v(a,b){return b.length-a.length};function w(a){this.limit=a&&!0!==a?a:1E3;this.cache=new Map;this.C=""}w.prototype.set=function(a,b){this.cache.has(a)||(this.cache.set(this.C=a,b),this.limit&&this.cache.size>this.limit&&this.cache.delete(this.cache.keys().next().value))};w.prototype.get=function(a){const b=this.cache.get(a);b&&this.limit&&this.C!==a&&(this.cache.delete(a),this.cache.set(this.C=a,b));return b};w.prototype.remove=function(a){for(const b of this.cache){const c=b[0];b[1].includes(a)&&this.cache.delete(c)}}; -w.prototype.clear=function(){this.cache.clear();this.C=""};const x={memory:{resolution:1},performance:{resolution:6,fastupdate:!0,context:{depth:1,resolution:3}},match:{tokenize:"forward"},score:{resolution:9,context:{depth:2,resolution:9}}};function y(a){const b="string"===typeof a?a:a.preset;b&&(a=Object.assign({},x[b],a));return a};t();z.prototype.add=function(a,b,c,d){if(b&&(a||0===a)){if(!d&&!c&&this.h.has(a))return this.update(a,b);b=this.encoder.encode(b);if(d=b.length){const r=t(),k=t(),p=this.depth,q=this.resolution;for(let l=0;lf;h--){g=n.substring(f,h);var m=this.score?this.score(b,n,l,g,f):A(q,d,l,e,f);B(this,k,g,m,a,c)}break}case "reverse":if(1< -e){for(h=e-1;0g?0:1),d,l,h-1,m-1),G=this.bidirectional&&n>f;B(this,r,G?f:n,u,a,c,G?n:f)}}}}this.fastupdate||this.h.add(a)}}return this}; -function B(a,b,c,d,e,f,g){let h=g?a.B:a.map,m;b[c]&&g&&(m=b[c])[g]||(g?(b=m||(b[c]=t()),b[g]=1,(m=h.get(g))?h=m:h.set(g,h=new Map)):b[c]=1,(m=h.get(c))?h=m:h.set(c,h=[]),h=h[d]||(h[d]=[]),f&&h.includes(e)||(h.push(e),a.fastupdate&&((b=a.h.get(e))?b.push(h):a.h.set(e,[h]))))}function A(a,b,c,d,e){return c&&1b?b?a.slice(c,c+b):a.slice(c):a;let d=[];for(let e=0,f,g;e=g){c-=g;continue}cb&&(f=f.slice(0,b),g=f.length),d.push(f);else{if(g>=b)return g>b&&(f=f.slice(0,b)),f;d=[f]}b-=g;if(!b)break}return d.length?d=1=e)))break;if(h.length){if(g)return C(h,e,0);b.push(h);return}}return!c&&h}function F(a,b,c){let d;c&&(d=a.bidirectional&&b>c);a=c?(a=a.B.get(d?b:c))&&a.get(d?c:b):a.map.get(b);return a};z.prototype.remove=function(a,b){const c=this.h.size&&(this.fastupdate?this.h.get(a):this.h.has(a));if(c){if(this.fastupdate)for(let d=0,e;de.length)e.pop();else{const f=e.indexOf(a);f===c.length-1?e.pop():e.splice(f,1)}}else I(this.map,a),this.depth&&I(this.B,a);b||this.h.delete(a)}this.cache&&this.cache.remove(a);return this}; -function I(a,b){let c=0;if(a.constructor===Array)for(let d=0,e,f;dc.add(a,b)):this.add(a,b)}return this.remove(a).add(a,b)}; -function J(a){let b=0;if(a.constructor===Array)for(let c=0,d;cthis.limit&&this.cache.delete(this.cache.keys().next().value)};v.prototype.get=function(a){const b=this.cache.get(a);b&&this.B!==a&&(this.cache.delete(a),this.cache.set(this.B=a,b));return b};v.prototype.remove=function(a){for(const b of this.cache){const c=b[0];b[1].includes(a)&&this.cache.delete(c)}}; +v.prototype.clear=function(){this.cache.clear();this.B=""};const w=new Map([["b","p"],["v","f"],["w","f"],["z","s"],["x","s"],["d","t"],["n","m"],["c","k"],["g","k"],["j","k"],["q","k"],["i","e"],["y","e"],["u","o"]]);const x=new Map([["ai","ei"],["ae","a"],["oe","o"],["ue","u"],["sh","s"],["ch","c"],["th","t"],["ph","f"],["pf","f"]]),y=[/([^aeo])h([aeo$])/g,"$1$2",/([aeo])h([^aeo]|$)/g,"$1$2"];const z={a:"",e:"",i:"",o:"",u:"",y:"",b:1,f:1,p:1,v:1,c:2,g:2,j:2,k:2,q:2,s:2,x:2,z:2,"\u00df":2,d:3,t:3,l:4,m:5,n:5,r:6};const A=/[\x00-\x7F]+/g;const B=/[\x00-\x7F]+/g;const C=/[\x00-\x7F]+/g;var D={LatinExact:{normalize:!1,dedupe:!1},LatinDefault:{normalize:function(a){return a.toLowerCase()},dedupe:!1},LatinSimple:{normalize:!0,dedupe:!0},LatinBalance:{normalize:!0,dedupe:!0,mapper:w},LatinAdvanced:{normalize:!0,dedupe:!0,mapper:w,replacer:y,matcher:x},LatinExtra:{normalize:!0,dedupe:!0,mapper:w,replacer:y.concat([/(?!^)[aeoy]/g,""]),matcher:x},LatinSoundex:{normalize:!0,dedupe:!1,include:{letter:!0},finalize:function(a){for(let c=0;cf;h--){g=n.substring(f,h);var m=this.score?this.score(b,n,l,g,f):I(q,d, +l,e,f);J(this,k,g,m,a,c)}break}case "reverse":if(1g?0:1),d,l,h-1,m-1),H=this.bidirectional&&n>f;J(this,r,H?f:n, +u,a,c,H?n:f)}}}}this.fastupdate||this.h.add(a)}}return this};function J(a,b,c,d,e,f,g){let h=g?a.A:a.map,m;b[c]&&g&&(m=b[c])[g]||(g?(b=m||(b[c]=Object.create(null)),b[g]=1,(m=h.get(g))?h=m:h.set(g,h=new Map)):b[c]=1,(m=h.get(c))?h=m:h.set(c,h=[]),h=h[d]||(h[d]=[]),f&&h.includes(e)||(h.push(e),a.fastupdate&&((b=a.h.get(e))?b.push(h):a.h.set(e,[h]))))}function I(a,b,c,d,e){return c&&1b?b?a.slice(c,c+b):a.slice(c):a;let d=[];for(let e=0,f,g;e=g){c-=g;continue}cb&&(f=f.slice(0,b),g=f.length),d.push(f);else{if(g>=b)return g>b&&(f=f.slice(0,b)),f;d=[f]}b-=g;if(!b)break}return d.length?d=1=e)))break;if(h.length){if(g)return K(h,e,0);b.push(h);return}}return!c&&h}function N(a,b,c){let d;c&&(d=a.bidirectional&&b>c);a=c?(a=a.A.get(d?b:c))&&a.get(d?c:b):a.map.get(b);return a};G.prototype.remove=function(a,b){const c=this.h.size&&(this.fastupdate?this.h.get(a):this.h.has(a));if(c){if(this.fastupdate)for(let d=0,e;de.length)e.pop();else{const f=e.indexOf(a);f===c.length-1?e.pop():e.splice(f,1)}}else P(this.map,a),this.depth&&P(this.A,a);b||this.h.delete(a)}this.cache&&this.cache.remove(a);return this}; +function P(a,b){let c=0;if(a.constructor===Array)for(let d=0,e,f;dc.add(a,b)):this.add(a,b)}return this.remove(a).add(a,b)}; +function Q(a){let b=0;if(a.constructor===Array)for(let c=0,d;cb.get(d)));return a.replace(/str\b/g,"strasse").replace(/(?!\b)strasse\b/g," strasse")},filter:new Set("aber als am an auch auf aus bei bin bis bist da dadurch daher darum das dass dass dein deine dem den der des dessen deshalb die dies dieser dieses doch dort du durch ein eine einem einen einer eines er es euer eure fuer hatte hatten hattest hattet hier hinter ich ihr ihre im in ist ja jede jedem jeden jeder jedes jener jenes jetzt ggf kann kannst koennen koennt machen mein meine mit muss musst musst muessen muesst nach nachdem nein nicht noch nun oder seid sein seine sich sie sind soll sollen sollst sollt sonst soweit sowie und unser unsere unter usw uvm vom von vor wann warum was weiter weitere wenn wer werde werden werdet weshalb wie wieder wieso wir wird wirst wo woher wohin zu zum zur ueber".split(" ")), +stemmer:new Map([["niss",""],["isch",""],["lich",""],["heit",""],["keit",""],["ell",""],["bar",""],["end",""],["ung",""],["est",""],["ern",""],["em",""],["er",""],["en",""],["es",""],["st",""],["ig",""],["ik",""],["e",""],["s",""]])};"undefined"!==typeof module&&module.exports?module.exports=c:self.FlexSearch&&(self.FlexSearch.Language.de=c);}(this)); diff --git a/dist/lang/en.min.js b/dist/lang/en.min.js index 4892f33..114e44f 100644 --- a/dist/lang/en.min.js +++ b/dist/lang/en.min.js @@ -1,2 +1,3 @@ -(function(self){'use strict';self.FlexSearch.registerLanguage("en",{filter:"a about above after again against all also am an and any are aren't as at be because been before being below both but by can cannot can't come could couldn't did didn't do does doesn't doing dont down during each even few first for from further get go had hadn't has hasn't have haven't having he hed her here here's hers herself hes him himself his how how's i id if ill im in into is isn't it it's itself i've just know let's like make me more most mustn't my myself new no nor not now of off on once only or other ought our our's ourselves out over own same say see shan't she she'd shell shes should shouldn't so some such than that that's the their theirs them themselves then there there's these they they'd they'll they're they've this those through time to too until up us very want was wasn't way we wed well were weren't we've what what's when when's where where's which while who whom who's why why's will with won't would wouldn't you you'd you'll your you're your's yourself yourselves you've".split(" "), -h:{ational:"ate",iveness:"ive",fulness:"ful",ousness:"ous",ization:"ize",tional:"tion",biliti:"ble",icate:"ic",ative:"",alize:"al",iciti:"ic",entli:"ent",ousli:"ous",alism:"al",ation:"ate",aliti:"al",iviti:"ive",ement:"",enci:"ence",anci:"ance",izer:"ize",alli:"al",ator:"ate",logi:"log",ical:"ic",ance:"",ence:"",ness:"",able:"",ible:"",ment:"",eli:"e",bli:"ble",ful:"",ant:"",ent:"",ism:"",ate:"",iti:"",ous:"",ive:"",ize:"",al:"",ou:"",er:"",ic:""},g:{}});}(this)); +(function(self){'use strict';const a={prepare:function(b){return b.replace(/\u00b4`\u2019\u02bc/g,"'").replace(/_+/g," ").replace(/&/g," and ").replace(/\$/g," USD ").replace(/\u00a3/g," GBP ").replace(/([a-z])'s\b/g,"$1 is").replace(/\bi'm\b/g,"i am").replace(/\b(can't|cannot)\b/g,"can not").replace(/\bwon't\b/g,"will not").replace(/([a-z])n't\b/g,"$1 not").replace(/([a-z])'ll\b/g,"$1 will").replace(/([a-z])'re\b/g,"$1 are").replace(/([a-z])'ve\b/g,"$1 have").replace(/([a-z])'d\b/g,"$1 would")},filter:new Set("a about above after again against all also am an and any are arent as at back be because been before being below between both but by can cannot cant come could couldnt did didnt do does doesnt doing dont down during each even few for from further get go good had hadnt has hasnt have havent having he hed her here heres hers herself hes him himself his how hows i id if ill im in into is isnt it its itself ive just know lets like lot make made me more most mustnt my myself new no nor not now of off on once one only or other ought our ours ourselves out over own people same say see shant she shed shell shes should shouldnt so some such take than that thats the their theirs them themselves then there theres these they theyd theyll theyre theyve think this those through time times to too under until up us use very want was wasnt way we wed well were werent weve what whats when whens where wheres which while who whom whos why whys will with wont work would wouldnt ya you youd youll your youre yours yourself yourselves youve".split(" ")), +stemmer:new Map([["ational",""],["iveness",""],["fulness",""],["ousness",""],["ization",""],["tional",""],["biliti",""],["icate",""],["ative",""],["alize",""],["iciti",""],["entli",""],["ousli",""],["alism",""],["ation",""],["aliti",""],["iviti",""],["ement",""],["izer",""],["able",""],["alli",""],["ator",""],["logi",""],["ical",""],["ance",""],["ence",""],["ness",""],["ble",""],["ment",""],["eli",""],["bli",""],["ful",""],["ant",""],["ent",""],["ism",""],["ate",""],["iti",""],["ous",""],["ive",""], +["ize",""],["ing",""],["ion",""],["al",""],["ou",""],["er",""],["ic",""],["ly",""]])};"undefined"!==typeof module&&module.exports?module.exports=a:self.FlexSearch&&(self.FlexSearch.Language.en=a);}(this)); diff --git a/dist/lang/fr.min.js b/dist/lang/fr.min.js new file mode 100644 index 0000000..6aa14a1 --- /dev/null +++ b/dist/lang/fr.min.js @@ -0,0 +1,3 @@ +(function(self){'use strict';const a={prepare:function(b){return b.replace(/\u00b4`\u2019\u02bc/g,"'").replace(/_+/g," ").replace(/&/g," et ").replace(/\u20ac/g," EUR ").replace(/\bl'([^\b])/g,"la le $1").replace(/\bt'([^\b])/g,"ta te $1").replace(/\bc'([^\b])/g,"ca ce $1").replace(/\bd'([^\b])/g,"da de $1").replace(/\bj'([^\b])/g,"ja je $1").replace(/\bn'([^\b])/g,"na ne $1").replace(/\bm'([^\b])/g,"ma me $1").replace(/\bs'([^\b])/g,"sa se $1").replace(/\bau\b/g,"a le").replace(/\baux\b/g,"a les").replace(/\bdu\b/g,"de le").replace(/\bdes\b/g, +"de les")},filter:new Set("au aux avec ce ces dans de des du elle en et eux il je la le leur lui ma mais me meme mes moi mon ne nos notre nous on ou par pas pour qu que qui sa se ses son sur ta te tes toi ton tu un une vos votre vous c d j l m n s t a y ete etee etees etes etant suis es est sommes etes sont serai seras sera serons serez seront serais serait serions seriez seraient etais etait etions etiez etaient fus fut fumes futes furent sois soit soyons soyez soient fusse fusses fut fussions fussiez fussent ayant eu eue eues eus ai as avons avez ont aurai auras aura aurons aurez auront aurais aurait aurions auriez auraient avais avait avions aviez avaient eut eumes eutes eurent aie aies ait ayons ayez aient eusse eusses eut eussions eussiez eussent ceci cela cela cet cette ici ils les leurs quel quels quelle quelles sans soi".split(" ")), +stemmer:new Map([["lement",""],["ient",""],["nera",""],["ment",""],["ais",""],["ait",""],["ant",""],["ent",""],["iez",""],["ion",""],["nez",""],["ai",""],["es",""],["er",""],["ez",""],["le",""],["na",""],["ne",""],["a",""],["e",""]])};"undefined"!==typeof module&&module.exports?module.exports=a:self.FlexSearch&&(self.FlexSearch.Language.fr=a);}(this)); diff --git a/dist/lang/latin/advanced.min.js b/dist/lang/latin/advanced.min.js deleted file mode 100644 index f0202a8..0000000 --- a/dist/lang/latin/advanced.min.js +++ /dev/null @@ -1,2 +0,0 @@ -(function(self){'use strict';const b=/[\u0300-\u036f]/g;function l(a){return new RegExp(a,"g")};const p=/[\W_]+/,r=[l("[\u00e0\u00e1\u00e2\u00e3\u00e4\u00e5]"),"a",l("[\u00e8\u00e9\u00ea\u00eb]"),"e",l("[\u00ec\u00ed\u00ee\u00ef]"),"i",l("[\u00f2\u00f3\u00f4\u00f5\u00f6\u0151]"),"o",l("[\u00f9\u00fa\u00fb\u00fc\u0171]"),"u",l("[\u00fd\u0177\u00ff]"),"y",l("\u00f1"),"n",l("[\u00e7c]"),"k",l("\u00df"),"s",l(" & ")," and "];const t=/[^a-z0-9]+/,u={b:"p",v:"f",w:"f",z:"s",x:"s","\u00df":"s",d:"t",n:"m",c:"k",g:"k",j:"k",q:"k",i:"e",y:"e",u:"o"};const v=[l("ae"),"a",l("oe"),"o",l("sh"),"s",l("th"),"t",l("ph"),"f",l("pf"),"f"];self.FlexSearch.registerCharset("latin:advanced",{encode:function(a,h){if(a){var e=this.h,f=a;f.normalize&&(f=f.normalize("NFD").replace(b,""));a=e.call(this,f.toLowerCase(),!a.normalize&&r,p,!1).join(" ");e=[];if(a){f=a.split(t);const c=f.length;for(let g=0,d,w=0;g this.limit) { - this.cache.delete(this.cache.keys().next().value); - } + //if(!this.cache.has(key)){ + this.cache.set(this.last = key, value); + if (this.cache.size > this.limit) { + this.cache.delete(this.cache.keys().next().value); } + //} }; CacheClass.prototype.get = function (key) { const cache = this.cache.get(key); - if (cache && this.limit && this.last !== key) { + if (cache && this.last !== key) { this.cache.delete(key); this.cache.set(this.last = key, cache); } diff --git a/dist/module-debug/charset.js b/dist/module-debug/charset.js index 9a6e07f..ae8cf2a 100644 --- a/dist/module-debug/charset.js +++ b/dist/module-debug/charset.js @@ -1,16 +1,58 @@ -import { create_object } from "./common.js"; +import charset_latin_exact from "./charset/latin/exact.js"; +import charset_latin_default from "./charset/latin/default.js"; +import charset_latin_simple from "./charset/latin/simple.js"; +import charset_latin_balance from "./charset/latin/balance.js"; +import charset_latin_advanced from "./charset/latin/advanced.js"; +import charset_latin_extra from "./charset/latin/extra.js"; +import charset_latin_soundex from "./charset/latin/soundex.js"; +import charset_arabic_default from "./charset/arabic/default.js"; +import charset_cjk_default from "./charset/cjk/default.js"; +import charset_cyrillic_default from "./charset/cyrillic/default.js"; + +export const LatinExact = charset_latin_exact; +export const LatinDefault = charset_latin_default; +export const LatinSimple = charset_latin_simple; +export const LatinBalance = charset_latin_balance; +export const LatinAdvanced = charset_latin_advanced; +export const LatinExtra = charset_latin_extra; +export const LatinSoundex = charset_latin_soundex; +export const ArabicDefault = charset_arabic_default; +export const CjkDefault = charset_cjk_default; +export const CyrillicDefault = charset_cyrillic_default; // export const global_lang = create_object(); -export const global_charset = create_object(); +//export const global_charset = create_object(); + +export default { + LatinExact: charset_latin_exact, + LatinDefault: charset_latin_default, + LatinSimple: charset_latin_simple, + LatinBalance: charset_latin_balance, + LatinAdvanced: charset_latin_advanced, + LatinExtra: charset_latin_extra, + LatinSoundex: charset_latin_soundex, + ArabicDefault: charset_arabic_default, + CjkDefault: charset_cjk_default, + CyrillicDefault: charset_cyrillic_default +}; + +// global_charset["latin:exact"] = charset_exact; +// global_charset["latin:default"] = charset_default; +// global_charset["latin:simple"] = charset_simple; +// global_charset["latin:balance"] = charset_balance; +// global_charset["latin:advanced"] = charset_advanced; +// global_charset["latin:extra"] = charset_extra; +// global_charset["latin:soundex"] = charset_soundex; + /** * @param {!string} name * @param {Object} charset */ -export function registerCharset(name, charset) { - global_charset[name] = charset; -} +// export function registerCharset(name, charset){ +// global_charset[name] = charset; +// } /** * @param {!string} name diff --git a/dist/module-debug/charset/arabic/default.js b/dist/module-debug/charset/arabic/default.js new file mode 100644 index 0000000..028abbd --- /dev/null +++ b/dist/module-debug/charset/arabic/default.js @@ -0,0 +1,42 @@ +import { EncoderOptions } from "../../type.js"; +const regex = /[\x00-\x7F]+/g, + split = /\s+/, + options = { + rtl: !0, + normalize: !1, + dedupe: !0, + prepare: function (str) { + return ("" + str).replace(regex, " "); + } +}; + +/** @type EncoderOptions */ + +export default options; + +// import { pipeline } from "../../lang.js"; +// +// export const rtl = true; +// export const tokenize = ""; +// export default { +// encode: encode, +// rtl: rtl +// } +// +// +// +// /** +// * @param {string|number} str +// */ +// +// export function encode(str){ +// +// return pipeline.call( +// +// this, +// /* string: */ ("" + str).replace(regex, " "), +// /* normalize: */ false, +// /* split: */ split, +// /* collapse: */ false +// ); +// } \ No newline at end of file diff --git a/dist/module-debug/charset/cjk/default.js b/dist/module-debug/charset/cjk/default.js new file mode 100644 index 0000000..c4caf70 --- /dev/null +++ b/dist/module-debug/charset/cjk/default.js @@ -0,0 +1,42 @@ +import { EncoderOptions } from "../../type.js"; +const regex = /[\x00-\x7F]+/g, + options = { + normalize: !1, + dedupe: !0, + split: "", + prepare: function (str) { + return ("" + str).replace(regex, ""); + } +}; + +/** @type EncoderOptions */ + +export default options; + +// import { pipeline } from "../../lang.js"; +// +// export const rtl = false; +// export const tokenize = "strict"; +// export default { +// encode: encode, +// rtl: rtl, +// tokenize: tokenize +// } +// +// const regex = /[\x00-\x7F]+/g; +// +// /** +// * @param {string|number} str +// */ +// +// export function encode(str){ +// +// return pipeline.call( +// +// this, +// /* string: */ ("" + str).replace(regex, ""), +// /* normalize: */ false, +// /* split: */ "", +// /* collapse: */ false +// ); +// } \ No newline at end of file diff --git a/dist/module-debug/charset/cyrillic/default.js b/dist/module-debug/charset/cyrillic/default.js new file mode 100644 index 0000000..6de8b9a --- /dev/null +++ b/dist/module-debug/charset/cyrillic/default.js @@ -0,0 +1,41 @@ +import { EncoderOptions } from "../../type.js"; +const regex = /[\x00-\x7F]+/g, + options = { + normalize: !1, + dedupe: !0, + prepare: function (str) { + return ("" + str).replace(regex, " "); + } +}; + +/** @type EncoderOptions */ + +export default options; + +// import { pipeline } from "../../lang.js"; +// +// export const rtl = false; +// export const tokenize = ""; +// export default { +// encode: encode, +// rtl: rtl +// } +// +// const regex = /[\x00-\x7F]+/g; +// const split = /\s+/; +// +// /** +// * @param {string|number} str +// */ +// +// export function encode(str){ +// +// return pipeline.call( +// +// this, +// /* string: */ ("" + str).replace(regex, " "), +// /* normalize: */ false, +// /* split: */ split, +// /* collapse: */ false +// ); +// } \ No newline at end of file diff --git a/dist/module-debug/lang/latin/advanced.js b/dist/module-debug/charset/latin/advanced.js similarity index 91% rename from dist/module-debug/lang/latin/advanced.js rename to dist/module-debug/charset/latin/advanced.js index fa5ea16..9d30352 100644 --- a/dist/module-debug/lang/latin/advanced.js +++ b/dist/module-debug/charset/latin/advanced.js @@ -1,4 +1,4 @@ -import Encoder from "../../encoder.js"; +import { EncoderOptions } from "../../type.js"; import { soundex } from "./balance.js"; // const soundex = new Map([ @@ -43,14 +43,15 @@ export const matcher = new Map([["ai", "ei"], ["ae", "a"], ["oe", "o"], ["ue", " export const replacer = [/([^aeo])h([aeo$])/g, "$1$2", /([aeo])h([^aeo]|$)/g, "$1$2"]; -export default { - normalize: /* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */ /* skip deletion */ // splice: - !0 /*await rows.hasNext()*/ /*await rows.hasNext()*/ /*await rows.hasNext()*/, +/** @type EncoderOptions */ +const options = { + normalize: !0, dedupe: !0, mapper: soundex, replacer: replacer, matcher: matcher }; +export default options; // import { regex, replace, collapse } from "../../lang.js"; // import { encode as encode_balance } from "./balance.js"; diff --git a/dist/module-debug/lang/latin/balance.js b/dist/module-debug/charset/latin/balance.js similarity index 94% rename from dist/module-debug/lang/latin/balance.js rename to dist/module-debug/charset/latin/balance.js index bd8fd20..f6791e8 100644 --- a/dist/module-debug/lang/latin/balance.js +++ b/dist/module-debug/charset/latin/balance.js @@ -1,4 +1,4 @@ -import Encoder from "../../encoder.js"; +import { EncoderOptions } from "../../type.js"; export const soundex = new Map([["b", "p"], //["p", "p"], @@ -28,12 +28,13 @@ export const soundex = new Map([["b", "p"], //["o", "o"], ["u", "o"]]); -export default { - normalize: /* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */ /* skip deletion */ // splice: - !0 /*await rows.hasNext()*/ /*await rows.hasNext()*/ /*await rows.hasNext()*/, +/** @type EncoderOptions */ +const options = { + normalize: !0, dedupe: !0, mapper: soundex }; +export default options; // //import { encode as encode_simple } from "./simple.js"; // import { pipeline } from "../../lang.js"; diff --git a/dist/module-debug/lang/latin/default.js b/dist/module-debug/charset/latin/default.js similarity index 82% rename from dist/module-debug/lang/latin/default.js rename to dist/module-debug/charset/latin/default.js index a7e126e..5501ee9 100644 --- a/dist/module-debug/lang/latin/default.js +++ b/dist/module-debug/charset/latin/default.js @@ -1,11 +1,13 @@ -import Encoder from "../../encoder.js"; +import { EncoderOptions } from "../../type.js"; -export default { +/** @type EncoderOptions */ +const options = { normalize: function (str) { return str.toLowerCase(); }, - dedupe: /* suggest */ /* append: */ /* enrich */!1 + dedupe: !1 }; +export default options; // import { pipeline } from "../../lang.js"; // diff --git a/dist/module-debug/charset/latin/exact.js b/dist/module-debug/charset/latin/exact.js new file mode 100644 index 0000000..0ba66b6 --- /dev/null +++ b/dist/module-debug/charset/latin/exact.js @@ -0,0 +1,8 @@ +import { EncoderOptions } from "../../type.js"; + +/** @type EncoderOptions */ +const options = { + normalize: !1, + dedupe: !1 +}; +export default options; \ No newline at end of file diff --git a/dist/module-debug/lang/latin/extra.js b/dist/module-debug/charset/latin/extra.js similarity index 86% rename from dist/module-debug/lang/latin/extra.js rename to dist/module-debug/charset/latin/extra.js index ec53420..d1368bd 100644 --- a/dist/module-debug/lang/latin/extra.js +++ b/dist/module-debug/charset/latin/extra.js @@ -1,18 +1,19 @@ -import Encoder from "../../encoder.js"; +import { EncoderOptions } from "../../type.js"; import { soundex } from "./balance.js"; import { matcher, replacer } from "./advanced.js"; export const compact = [/(?!^)[aeoy]/g, "" // old: aioy ]; -export default { - normalize: /* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */ /* skip deletion */ // splice: - !0 /*await rows.hasNext()*/ /*await rows.hasNext()*/ /*await rows.hasNext()*/, +/** @type EncoderOptions */ +const options = { + normalize: !0, dedupe: !0, mapper: soundex, replacer: replacer.concat(compact), matcher: matcher }; +export default options; // import { regex, replace, collapse } from "../../lang.js"; // import { encode as encode_advanced } from "./advanced.js"; diff --git a/dist/module-debug/lang/latin/simple.js b/dist/module-debug/charset/latin/simple.js similarity index 95% rename from dist/module-debug/lang/latin/simple.js rename to dist/module-debug/charset/latin/simple.js index b01236e..3ee046c 100644 --- a/dist/module-debug/lang/latin/simple.js +++ b/dist/module-debug/charset/latin/simple.js @@ -1,10 +1,11 @@ -import Encoder from "../../encoder.js"; +import { EncoderOptions } from "../../type.js"; -export default { - normalize: /* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */ /* skip deletion */ // splice: - !0 /*await rows.hasNext()*/ /*await rows.hasNext()*/ /*await rows.hasNext()*/, +/** @type EncoderOptions */ +const options = { + normalize: !0, dedupe: !0 }; +export default options; // import { pipeline, regex } from "../../lang.js"; // diff --git a/dist/module/lang/latin/soundex.js b/dist/module-debug/charset/latin/soundex.js similarity index 80% rename from dist/module/lang/latin/soundex.js rename to dist/module-debug/charset/latin/soundex.js index 0a38309..9766d70 100644 --- a/dist/module/lang/latin/soundex.js +++ b/dist/module-debug/charset/latin/soundex.js @@ -1,7 +1,9 @@ -export default { - normalize: /* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */ /* skip deletion */ // splice: - !0 /*await rows.hasNext()*/ /*await rows.hasNext()*/ /*await rows.hasNext()*/, - dedupe: /* suggest */ /* append: */ /* enrich */!1, +import { EncoderOptions } from "../../type.js"; + +/** @type EncoderOptions */ +const options = { + normalize: !0, + dedupe: !1, include: { letter: !0 }, @@ -11,6 +13,7 @@ export default { } } }; +export default options; const codes = { a: "", e: "", i: "", o: "", u: "", y: "", diff --git a/dist/module-debug/charset/normalize.js b/dist/module-debug/charset/normalize.js new file mode 100644 index 0000000..749d40b --- /dev/null +++ b/dist/module-debug/charset/normalize.js @@ -0,0 +1,29 @@ +export default [ + +// Charset Normalization + +["ª", "a"], ["²", "2"], ["³", "3"], ["¹", "1"], ["º", "o"], ["¼", "1⁄4"], ["½", "1⁄2"], ["¾", "3⁄4"], ["à", "a"], ["á", "a"], ["â", "a"], ["ã", "a"], ["ä", "a"], ["å", "a"], ["ç", "c"], ["è", "e"], ["é", "e"], ["ê", "e"], ["ë", "e"], ["ì", "i"], ["í", "i"], ["î", "i"], ["ï", "i"], ["ñ", "n"], ["ò", "o"], ["ó", "o"], ["ô", "o"], ["õ", "o"], ["ö", "o"], ["ù", "u"], ["ú", "u"], ["û", "u"], ["ü", "u"], ["ý", "y"], ["ÿ", "y"], ["ā", "a"], ["ă", "a"], ["ą", "a"], ["ć", "c"], ["ĉ", "c"], ["ċ", "c"], ["č", "c"], ["ď", "d"], ["ē", "e"], ["ĕ", "e"], ["ė", "e"], ["ę", "e"], ["ě", "e"], ["ĝ", "g"], ["ğ", "g"], ["ġ", "g"], ["ģ", "g"], ["ĥ", "h"], ["ĩ", "i"], ["ī", "i"], ["ĭ", "i"], ["į", "i"], ["ij", "ij"], ["ĵ", "j"], ["ķ", "k"], ["ĺ", "l"], ["ļ", "l"], ["ľ", "l"], ["ŀ", "l"], ["ń", "n"], ["ņ", "n"], ["ň", "n"], ["ʼn", "n"], ["ō", "o"], ["ŏ", "o"], ["ő", "o"], ["ŕ", "r"], ["ŗ", "r"], ["ř", "r"], ["ś", "s"], ["ŝ", "s"], ["ş", "s"], ["š", "s"], ["ţ", "t"], ["ť", "t"], ["ũ", "u"], ["ū", "u"], ["ŭ", "u"], ["ů", "u"], ["ű", "u"], ["ų", "u"], ["ŵ", "w"], ["ŷ", "y"], ["ź", "z"], ["ż", "z"], ["ž", "z"], ["ſ", "s"], ["ơ", "o"], ["ư", "u"], ["dž", "dz"], ["lj", "lj"], ["nj", "nj"], ["ǎ", "a"], ["ǐ", "i"], ["ǒ", "o"], ["ǔ", "u"], ["ǖ", "u"], ["ǘ", "u"], ["ǚ", "u"], ["ǜ", "u"], ["ǟ", "a"], ["ǡ", "a"], ["ǣ", "ae"], ["æ", "ae"], ["ǽ", "ae"], ["ǧ", "g"], ["ǩ", "k"], ["ǫ", "o"], ["ǭ", "o"], ["ǯ", "ʒ"], ["ǰ", "j"], ["dz", "dz"], ["ǵ", "g"], ["ǹ", "n"], ["ǻ", "a"], ["ǿ", "ø"], ["ȁ", "a"], ["ȃ", "a"], ["ȅ", "e"], ["ȇ", "e"], ["ȉ", "i"], ["ȋ", "i"], ["ȍ", "o"], ["ȏ", "o"], ["ȑ", "r"], ["ȓ", "r"], ["ȕ", "u"], ["ȗ", "u"], ["ș", "s"], ["ț", "t"], ["ȟ", "h"], ["ȧ", "a"], ["ȩ", "e"], ["ȫ", "o"], ["ȭ", "o"], ["ȯ", "o"], ["ȱ", "o"], ["ȳ", "y"], ["ʰ", "h"], ["ʱ", "h"], ["ɦ", "h"], ["ʲ", "j"], ["ʳ", "r"], ["ʴ", "ɹ"], ["ʵ", "ɻ"], ["ʶ", "ʁ"], ["ʷ", "w"], ["ʸ", "y"], ["ˠ", "ɣ"], ["ˡ", "l"], ["ˢ", "s"], ["ˣ", "x"], ["ˤ", "ʕ"], ["ΐ", "ι"], ["ά", "α"], ["έ", "ε"], ["ή", "η"], ["ί", "ι"], ["ΰ", "υ"], ["ϊ", "ι"], ["ϋ", "υ"], ["ό", "ο"], ["ύ", "υ"], ["ώ", "ω"], ["ϐ", "β"], ["ϑ", "θ"], ["ϒ", "Υ"], ["ϓ", "Υ"], ["ϔ", "Υ"], ["ϕ", "φ"], ["ϖ", "π"], ["ϰ", "κ"], ["ϱ", "ρ"], ["ϲ", "ς"], ["ϵ", "ε"], ["й", "и"], ["ѐ", "е"], ["ё", "е"], ["ѓ", "г"], ["ї", "і"], ["ќ", "к"], ["ѝ", "и"], ["ў", "у"], ["ѷ", "ѵ"], ["ӂ", "ж"], ["ӑ", "а"], ["ӓ", "а"], ["ӗ", "е"], ["ӛ", "ә"], ["ӝ", "ж"], ["ӟ", "з"], ["ӣ", "и"], ["ӥ", "и"], ["ӧ", "о"], ["ӫ", "ө"], ["ӭ", "э"], ["ӯ", "у"], ["ӱ", "у"], ["ӳ", "у"], ["ӵ", "ч"] + +// Term Separators + +// ["'", ""], // it's -> its +// ["´", ""], +// ["`", ""], +// ["’", ""], +// ["ʼ", ""], + +// Numeric-Separators Chars Removal + +// [",", ""], +// [".", ""] + +// Non-Whitespace Separators + +// already was split by default via p{P} +// ["-", " "], +// [":", " "], +// ["_", " "], +// ["|", " "], +// ["/", " "], +// ["\\", " "] +]; \ No newline at end of file diff --git a/dist/module-debug/db/clickhouse/index.js b/dist/module-debug/db/clickhouse/index.js index 51bfa95..8be0f35 100644 --- a/dist/module-debug/db/clickhouse/index.js +++ b/dist/module-debug/db/clickhouse/index.js @@ -57,7 +57,7 @@ let DB; */ export default function ClickhouseDB(name, config = {}) { - if (!(this instanceof ClickhouseDB)) { + if (!this) { return new ClickhouseDB(name, config); } if ("object" == typeof name) { @@ -83,7 +83,7 @@ export default function ClickhouseDB(name, config = {}) { } ClickhouseDB.prototype.mount = function (flexsearch) { - if (flexsearch instanceof Document) { + if (flexsearch.constructor === Document) { return flexsearch.mount(this); } defaults.resolution = Math.max(flexsearch.resolution, flexsearch.resolution_ctx); diff --git a/dist/module-debug/db/indexeddb/index.js b/dist/module-debug/db/indexeddb/index.js index e7c6fee..bfe60e5 100644 --- a/dist/module-debug/db/indexeddb/index.js +++ b/dist/module-debug/db/indexeddb/index.js @@ -20,7 +20,7 @@ function sanitize(str) { */ export default function IdxDB(name, config = {}) { - if (!(this instanceof IdxDB)) { + if (!this) { return new IdxDB(name, config); } if ("object" == typeof name) { @@ -38,7 +38,7 @@ export default function IdxDB(name, config = {}) { } IdxDB.prototype.mount = function (flexsearch) { - if (flexsearch instanceof Document) { + if (flexsearch.constructor === Document) { return flexsearch.mount(this); } flexsearch.db = this; diff --git a/dist/module-debug/db/mongo/index.js b/dist/module-debug/db/mongo/index.js index 0475df2..f385c06 100644 --- a/dist/module-debug/db/mongo/index.js +++ b/dist/module-debug/db/mongo/index.js @@ -26,7 +26,7 @@ let CLIENT, */ export default function MongoDB(name, config = {}) { - if (!(this instanceof MongoDB)) { + if (!this) { return new MongoDB(name, config); } if ("object" == typeof name) { @@ -51,7 +51,7 @@ export default function MongoDB(name, config = {}) { // }; MongoDB.prototype.mount = function (flexsearch) { - if (flexsearch instanceof Document) { + if (flexsearch.constructor === Document) { return flexsearch.mount(this); } flexsearch.db = this; diff --git a/dist/module-debug/db/postgres/index.js b/dist/module-debug/db/postgres/index.js index 33cb63a..7328891 100644 --- a/dist/module-debug/db/postgres/index.js +++ b/dist/module-debug/db/postgres/index.js @@ -51,7 +51,7 @@ let DB, TRX; */ export default function PostgresDB(name, config = {}) { - if (!(this instanceof PostgresDB)) { + if (!this) { return new PostgresDB(name, config); } if ("object" == typeof name) { @@ -72,7 +72,7 @@ export default function PostgresDB(name, config = {}) { } PostgresDB.prototype.mount = function (flexsearch) { - if (flexsearch instanceof Document) { + if (flexsearch.constructor === Document) { return flexsearch.mount(this); } flexsearch.db = this; diff --git a/dist/module-debug/db/redis/index.js b/dist/module-debug/db/redis/index.js index dbf7c83..ce54eb6 100644 --- a/dist/module-debug/db/redis/index.js +++ b/dist/module-debug/db/redis/index.js @@ -26,7 +26,7 @@ let DB, TRX; */ export default function RedisDB(name, config = {}) { - if (!(this instanceof RedisDB)) { + if (!this) { return new RedisDB(name, config); } if ("object" == typeof name) { @@ -52,7 +52,7 @@ export default function RedisDB(name, config = {}) { // }; RedisDB.prototype.mount = function (flexsearch) { - if (flexsearch instanceof Document) { + if (flexsearch.constructor === Document) { return flexsearch.mount(this); } flexsearch.db = this; diff --git a/dist/module-debug/db/sqlite/index.js b/dist/module-debug/db/sqlite/index.js index c71a6dd..2d02eaa 100644 --- a/dist/module-debug/db/sqlite/index.js +++ b/dist/module-debug/db/sqlite/index.js @@ -48,7 +48,7 @@ const TRX = Object.create(null), */ export default function SqliteDB(name, config = {}) { - if (!(this instanceof SqliteDB)) { + if (!this) { return new SqliteDB(name, config); } if ("object" == typeof name) { @@ -68,7 +68,7 @@ export default function SqliteDB(name, config = {}) { } SqliteDB.prototype.mount = function (flexsearch) { - if (flexsearch instanceof Document) { + if (flexsearch.constructor === Document) { return flexsearch.mount(this); } flexsearch.db = this; diff --git a/dist/module-debug/document.js b/dist/module-debug/document.js index 605ef9b..d16c71f 100644 --- a/dist/module-debug/document.js +++ b/dist/module-debug/document.js @@ -6,7 +6,7 @@ * https://github.com/nextapps-de/flexsearch */ -import { DocumentOptions } from "./type.js"; +import { DocumentOptions, DocumentDescriptor, DocumentIndexOptions, StoreOptions } from "./type.js"; import Index from "./index.js"; import WorkerIndex from "./worker/index.js"; import Cache, { searchCache } from "./cache.js"; @@ -19,15 +19,16 @@ import "./document/search.js"; /** * @constructor - * @param {DocumentOptions=} options + * @param {!DocumentOptions} options */ export default function Document(options) { - if (!(this instanceof Document)) { + if (!this) { return new Document(options); } + /** @type DocumentDescriptor */ const document = options.document || options.doc || options; let tmp, keystore; @@ -39,8 +40,8 @@ export default function Document(options) { keystore = options.keystore || 0; keystore && (this.keystore = keystore); this.fastupdate = !!options.fastupdate; - this.reg = this.fastupdate ? keystore && /* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */ /* skip deletion */!0 - /*await rows.hasNext()*/ /*await rows.hasNext()*/ /*await rows.hasNext()*/ ? new KeystoreMap(keystore) : new Map() : keystore && !0 ? new KeystoreSet(keystore) : new Set(); + this.reg = this.fastupdate ? keystore && /* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ + /* skip_update: */ /* skip deletion */!0 /*await rows.hasNext()*/ /*await rows.hasNext()*/ /*await rows.hasNext()*/ ? new KeystoreMap(keystore) : new Map() : keystore && !0 ? new KeystoreSet(keystore) : new Set(); // todo support custom filter function this.storetree = (tmp = document.store || null) && !0 !== tmp && []; @@ -192,12 +193,11 @@ function parse_descriptor(options, document) { key = key.field; } - opt = is_object(opt) ? Object.assign({}, options, opt) : options; + opt = /** @type DocumentIndexOptions */is_object(opt) ? Object.assign({}, options, opt) : options; if (this.worker) { const worker = new WorkerIndex(opt); - index.set(key, worker); - if (!worker.worker) { + index.set(key, worker);if (!worker.worker) { // fallback when not supported this.worker = !1; } @@ -229,7 +229,7 @@ function parse_descriptor(options, document) { if (is_string(stores)) stores = [stores]; for (let i = 0, store, field; i < stores.length; i++) { - store = stores[i]; + store = /** @type Array */stores[i]; field = store.field || store; if (store.custom) { this.storetree[i] = store.custom; diff --git a/dist/module-debug/document/add.js b/dist/module-debug/document/add.js index e0cf5c9..5c826a6 100644 --- a/dist/module-debug/document/add.js +++ b/dist/module-debug/document/add.js @@ -34,14 +34,15 @@ Document.prototype.add = function (id, content, _append) { if ("function" == typeof tree) { const tmp = tree(content); if (tmp) { - index.add(id, tmp, /* suggest */ /* append: */!1, /* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */!0 /*await rows.hasNext()*/ /*await rows.hasNext()*/ /*await rows.hasNext()*/); + index.add(id, tmp, /* suggest */ /* append: */!1, /* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */!0 /*await rows.hasNext()*/ + /*await rows.hasNext()*/ /*await rows.hasNext()*/); } } else { const filter = tree._filter; if (filter && !filter(content)) { continue; } - if (tree instanceof String) { + if (tree.constructor === String) { tree = ["" + tree]; } else if (is_string(tree)) { tree = [tree]; @@ -70,7 +71,7 @@ Document.prototype.add = function (id, content, _append) { if (filter && !filter(content)) { continue; } - if (tree instanceof String) { + if (tree.constructor === String) { tree = "" + tree; } tags = parse_simple(content, tree); @@ -146,7 +147,7 @@ Document.prototype.add = function (id, content, _append) { custom = tree(content); if (!custom) continue; tree = [tree._field]; - } else if (is_string(tree) || tree instanceof String) { + } else if (is_string(tree) || tree.constructor === String) { payload[tree] = content[tree]; continue; } diff --git a/dist/module-debug/encoder.js b/dist/module-debug/encoder.js index 8be0bee..510d597 100644 --- a/dist/module-debug/encoder.js +++ b/dist/module-debug/encoder.js @@ -1,5 +1,7 @@ import { parse_option } from "./common.js"; +import normalize_polyfill from "./charset/normalize.js"; +import { EncoderOptions } from "./type.js"; /* @@ -42,51 +44,24 @@ const whitespace = /[^\p{L}\p{N}]+/u, numeric_split_length = /(\d{3})/g, numeric_split_prev_char = /(\D)(\d{3})/g, numeric_split_next_char = /(\d{3})(\D)/g, - normalize = /[\u0300-\u036f]/g, - normalize_mapper = !normalize && [ - -// Charset Normalization - -["ª", "a"], ["²", "2"], ["³", "3"], ["¹", "1"], ["º", "o"], ["¼", "1⁄4"], ["½", "1⁄2"], ["¾", "3⁄4"], ["à", "a"], ["á", "a"], ["â", "a"], ["ã", "a"], ["ä", "a"], ["å", "a"], ["ç", "c"], ["è", "e"], ["é", "e"], ["ê", "e"], ["ë", "e"], ["ì", "i"], ["í", "i"], ["î", "i"], ["ï", "i"], ["ñ", "n"], ["ò", "o"], ["ó", "o"], ["ô", "o"], ["õ", "o"], ["ö", "o"], ["ù", "u"], ["ú", "u"], ["û", "u"], ["ü", "u"], ["ý", "y"], ["ÿ", "y"], ["ā", "a"], ["ă", "a"], ["ą", "a"], ["ć", "c"], ["ĉ", "c"], ["ċ", "c"], ["č", "c"], ["ď", "d"], ["ē", "e"], ["ĕ", "e"], ["ė", "e"], ["ę", "e"], ["ě", "e"], ["ĝ", "g"], ["ğ", "g"], ["ġ", "g"], ["ģ", "g"], ["ĥ", "h"], ["ĩ", "i"], ["ī", "i"], ["ĭ", "i"], ["į", "i"], ["ij", "ij"], ["ĵ", "j"], ["ķ", "k"], ["ĺ", "l"], ["ļ", "l"], ["ľ", "l"], ["ŀ", "l"], ["ń", "n"], ["ņ", "n"], ["ň", "n"], ["ʼn", "n"], ["ō", "o"], ["ŏ", "o"], ["ő", "o"], ["ŕ", "r"], ["ŗ", "r"], ["ř", "r"], ["ś", "s"], ["ŝ", "s"], ["ş", "s"], ["š", "s"], ["ţ", "t"], ["ť", "t"], ["ũ", "u"], ["ū", "u"], ["ŭ", "u"], ["ů", "u"], ["ű", "u"], ["ų", "u"], ["ŵ", "w"], ["ŷ", "y"], ["ź", "z"], ["ż", "z"], ["ž", "z"], ["ſ", "s"], ["ơ", "o"], ["ư", "u"], ["dž", "dz"], ["lj", "lj"], ["nj", "nj"], ["ǎ", "a"], ["ǐ", "i"], ["ǒ", "o"], ["ǔ", "u"], ["ǖ", "u"], ["ǘ", "u"], ["ǚ", "u"], ["ǜ", "u"], ["ǟ", "a"], ["ǡ", "a"], ["ǣ", "ae"], ["æ", "ae"], ["ǽ", "ae"], ["ǧ", "g"], ["ǩ", "k"], ["ǫ", "o"], ["ǭ", "o"], ["ǯ", "ʒ"], ["ǰ", "j"], ["dz", "dz"], ["ǵ", "g"], ["ǹ", "n"], ["ǻ", "a"], ["ǿ", "ø"], ["ȁ", "a"], ["ȃ", "a"], ["ȅ", "e"], ["ȇ", "e"], ["ȉ", "i"], ["ȋ", "i"], ["ȍ", "o"], ["ȏ", "o"], ["ȑ", "r"], ["ȓ", "r"], ["ȕ", "u"], ["ȗ", "u"], ["ș", "s"], ["ț", "t"], ["ȟ", "h"], ["ȧ", "a"], ["ȩ", "e"], ["ȫ", "o"], ["ȭ", "o"], ["ȯ", "o"], ["ȱ", "o"], ["ȳ", "y"], ["ʰ", "h"], ["ʱ", "h"], ["ɦ", "h"], ["ʲ", "j"], ["ʳ", "r"], ["ʴ", "ɹ"], ["ʵ", "ɻ"], ["ʶ", "ʁ"], ["ʷ", "w"], ["ʸ", "y"], ["ˠ", "ɣ"], ["ˡ", "l"], ["ˢ", "s"], ["ˣ", "x"], ["ˤ", "ʕ"], ["ΐ", "ι"], ["ά", "α"], ["έ", "ε"], ["ή", "η"], ["ί", "ι"], ["ΰ", "υ"], ["ϊ", "ι"], ["ϋ", "υ"], ["ό", "ο"], ["ύ", "υ"], ["ώ", "ω"], ["ϐ", "β"], ["ϑ", "θ"], ["ϒ", "Υ"], ["ϓ", "Υ"], ["ϔ", "Υ"], ["ϕ", "φ"], ["ϖ", "π"], ["ϰ", "κ"], ["ϱ", "ρ"], ["ϲ", "ς"], ["ϵ", "ε"], ["й", "и"], ["ѐ", "е"], ["ё", "е"], ["ѓ", "г"], ["ї", "і"], ["ќ", "к"], ["ѝ", "и"], ["ў", "у"], ["ѷ", "ѵ"], ["ӂ", "ж"], ["ӑ", "а"], ["ӓ", "а"], ["ӗ", "е"], ["ӛ", "ә"], ["ӝ", "ж"], ["ӟ", "з"], ["ӣ", "и"], ["ӥ", "и"], ["ӧ", "о"], ["ӫ", "ө"], ["ӭ", "э"], ["ӯ", "у"], ["ӱ", "у"], ["ӳ", "у"], ["ӵ", "ч"] - -// Term Separators - -// ["'", ""], // it's -> its -// ["´", ""], -// ["`", ""], -// ["’", ""], -// ["ʼ", ""], - -// Numeric-Separators Chars Removal - -// [",", ""], -// [".", ""] - -// Non-Whitespace Separators - -// already was split by default via p{P} -// ["-", " "], -// [":", " "], -// ["_", " "], -// ["|", " "], -// ["/", " "], -// ["\\", " "] -]; // /[\p{Z}\p{S}\p{P}\p{C}]+/u; + normalize = /[\u0300-\u036f]/g; // /[\p{Z}\p{S}\p{P}\p{C}]+/u; //const numeric_split = /(\d{3})/g; //.replace(/(\d{3})/g, "$1 ") //.replace(/([^\d])([\d])/g, "$1 $2") //.replace(/([\d])([^\d])/g, "$1 $2") + // '´`’ʼ., +//const normalize_mapper = SUPPORT_CHARSET && !normalize && normalize_polyfill; /** - * @param options + * @param {EncoderOptions=} options * @constructor */ -export default function Encoder(options = {}) { +export default function Encoder() { - if (!(this instanceof Encoder)) { + if (!this) { return new Encoder(...arguments); } @@ -95,25 +70,11 @@ export default function Encoder(options = {}) { } } +/** + * @param {!EncoderOptions} options + */ Encoder.prototype.assign = function (options) { - // if(options.assign){ - // //options = Object.assign({}, options.assign, options); - // this.assign(options.assign); - // } - - // let tmp = options["normalize"]; - // if(typeof tmp === "function"){ - // const old = this.normalize; - // if(typeof old === "function"){ - // const current = tmp; - // tmp = function(){ - // old(); - // current(); - // } - // } - // } - /** * pre-processing string input * @type {Function|boolean} @@ -183,16 +144,15 @@ Encoder.prototype.assign = function (options) { */ this.finalize = /** @type {Function|null} */parse_option(options.finalize, null, this.finalize); - // move the normalization fallback to the mapper - if (normalize_mapper) { + // assign the normalization fallback to the mapper + if (!normalize) { this.mapper = new Map( - /** @type {Array>} */normalize_mapper); + /** @type {Array>} */normalize_polyfill); } // options - this.rtl = options.rtl || - /* suggest */ /* append: */ /* enrich */!1; + this.rtl = options.rtl || /* suggest */ /* append: */ /* enrich */!1; this.dedupe = parse_option(options.dedupe, !0, this.dedupe); this.filter = parse_option((tmp = options.filter) && new Set(tmp), null, this.filter); this.matcher = parse_option((tmp = options.matcher) && new Map(tmp), null, this.matcher); diff --git a/dist/module-debug/index.js b/dist/module-debug/index.js index d253a26..06ef547 100644 --- a/dist/module-debug/index.js +++ b/dist/module-debug/index.js @@ -6,13 +6,14 @@ * https://github.com/nextapps-de/flexsearch */ -import { IndexOptions } from "./type.js"; +import { IndexOptions, ContextOptions } from "./type.js"; import Encoder from "./encoder.js"; import Cache, { searchCache } from "./cache.js"; +import Charset from "./charset.js"; import { KeystoreMap, KeystoreSet } from "./keystore.js"; import { is_array, is_string } from "./common.js"; import { exportIndex, importIndex } from "./serialize.js"; -import default_encoder from "./lang/latin/default.js"; +import default_encoder from "./charset/latin/default.js"; import apply_preset from "./preset.js"; import apply_async from "./async.js"; import tick from "./profiler.js"; @@ -22,21 +23,23 @@ import "./index/remove.js"; /** * @constructor - * @param {IndexOptions|string=} options + * @param {IndexOptions|string=} options Options or preset as string * @param {Map|Set|KeystoreSet|KeystoreMap=} _register */ export default function Index(options, _register) { - if (!(this instanceof Index)) { + if (!this) { return new Index(options); } options = options ? apply_preset(options) : {}; + /** @type ContextOptions */ const context = options.context || {}, encoder = options.encode || options.encoder || default_encoder; + /** @type Encoder */ this.encoder = encoder.encode ? encoder : "object" == typeof encoder ? new Encoder(encoder) : { encode: encoder }; this.compress = options.compress || options.compression || /* suggest */ /* append: */ /* enrich */!1; diff --git a/dist/module-debug/index/search.js b/dist/module-debug/index/search.js index 44b4cb1..341892f 100644 --- a/dist/module-debug/index/search.js +++ b/dist/module-debug/index/search.js @@ -48,8 +48,7 @@ Index.prototype.search = function (query, limit, options) { offset = options.offset || 0; context = options.context; suggest = options.suggest; - resolve = global_resolve && - /* suggest */ /* append: */ /* enrich */!1 !== options.resolve; + resolve = global_resolve && /* suggest */ /* append: */ /* enrich */!1 !== options.resolve; resolve || (global_resolve = 0); enrich = resolve && options.enrich; tag = this.db && options.tag; diff --git a/dist/module-debug/intersect.js b/dist/module-debug/intersect.js index 8b5575a..6db61cf 100644 --- a/dist/module-debug/intersect.js +++ b/dist/module-debug/intersect.js @@ -103,7 +103,8 @@ export function intersect(arrays, limit, offset, suggest) { check_new[id] = 1; } - found = /* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */ /* skip deletion */!0 /*await rows.hasNext()*/ /*await rows.hasNext()*/ /*await rows.hasNext()*/; + found = /* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */ /* skip deletion */!0 /*await rows.hasNext()*/ + /*await rows.hasNext()*/ /*await rows.hasNext()*/; } if (suggest) { diff --git a/dist/module-debug/keystore.js b/dist/module-debug/keystore.js index 3f7b6d7..9f222ac 100644 --- a/dist/module-debug/keystore.js +++ b/dist/module-debug/keystore.js @@ -7,7 +7,7 @@ import { create_object } from "./common.js"; export function KeystoreObj(bitlength = 8) { - if (!(this instanceof KeystoreObj)) { + if (!this) { return new KeystoreObj(bitlength); } @@ -37,10 +37,10 @@ export function KeystoreObj(bitlength = 8) { target.keys.push(address); } obj[key] = value; - return (/* tag? */ /* stringify */ - /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */ /* skip deletion */ + return (/* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */ /* skip deletion */ // splice: - !0 /*await rows.hasNext()*/ /*await rows.hasNext()*/ /*await rows.hasNext()*/ + !0 /*await rows.hasNext()*/ + /*await rows.hasNext()*/ /*await rows.hasNext()*/ ); }, delete(target, key) { @@ -94,7 +94,7 @@ function _slice(self, start, end, splice) { export function KeystoreArray(arr) { - if (!(this instanceof KeystoreArray)) { + if (!this) { return new KeystoreArray(arr); } @@ -196,7 +196,7 @@ KeystoreArray.prototype.push = function () {}; export function KeystoreMap(bitlength = 8) { - if (!(this instanceof KeystoreMap)) { + if (!this) { return new KeystoreMap(bitlength); } @@ -241,7 +241,7 @@ KeystoreMap.prototype.set = function (key, value) { export function KeystoreSet(bitlength = 8) { - if (!(this instanceof KeystoreSet)) { + if (!this) { return new KeystoreSet(bitlength); } @@ -372,6 +372,7 @@ function lcg(str) { function lcg64(str) { let range = BigInt(2) ** /** @type {!BigInt} */this.bit - BigInt(1), type = typeof str; + if ("bigint" == type) { return (/** @type {!BigInt} */str & range ); diff --git a/dist/module-debug/lang/de.js b/dist/module-debug/lang/de.js index 717c431..55eb8ef 100644 --- a/dist/module-debug/lang/de.js +++ b/dist/module-debug/lang/de.js @@ -1,3 +1,5 @@ +import { EncoderOptions } from "../type.js"; + /** * Filter are also known as "stopwords", they completely filter out words from being indexed. * Source: http://www.ranks.nl/stopwords @@ -22,18 +24,20 @@ export const stemmer = new Map([["niss", ""], ["isch", ""], ["lich", ""], ["heit * Object Definition: the key represents the target term, the value contains the search string which should be replaced (could also be an array of multiple terms). * @type {Map} */ -const map = new Map([["_", " "], ["ä", "ae"], ["ö", "oe"], ["ü", "ue"], ["ß", "ss"], ["&", " und "], ["€", " EUR "]]); - -export default { - normalize: function (str) { - return str.toLowerCase(); - }, +const map = new Map([["_", " "], ["ä", "ae"], ["ö", "oe"], ["ü", "ue"], ["ß", "ss"], ["&", " und "], ["€", " EUR "]]), + options = { prepare: function (str) { // normalization if (/[_äöüß&€]/.test(str)) str = str.replace(/[_äöüß&€]/g, match => map.get(match)); // street names - return str.replace(/str\b/g, "strasse").replace(/(?!\b)strasse\b/g, " strasse").replace(/\bst\b/g, "sankt"); + return str.replace(/str\b/g, "strasse").replace(/(?!\b)strasse\b/g, " strasse"); }, filter: filter, stemmer: stemmer -}; \ No newline at end of file +}; + +/** + * @type EncoderOptions + */ + +export default options; \ No newline at end of file diff --git a/dist/module-debug/lang/en.js b/dist/module-debug/lang/en.js index 65b6220..c651a81 100644 --- a/dist/module-debug/lang/en.js +++ b/dist/module-debug/lang/en.js @@ -1,9 +1,11 @@ +import { EncoderOptions } from "../type.js"; + +// todo filter out minlength + /** * http://www.ranks.nl/stopwords * @type {Set} */ - -// todo filter out minlength export const filter = new Set(["a", "about", "above", "after", "again", "against", "all", "also", "am", "an", "and", "any", "are", "arent", "as", "at", "back", "be", "because", "been", "before", "being", "below", "between", "both", "but", "by", "can", "cannot", "cant", "come", "could", "couldnt", //"day", "did", "didnt", "do", "does", "doesnt", "doing", "dont", "down", "during", "each", "even", "few", @@ -21,15 +23,11 @@ export const filter = new Set(["a", "about", "above", "after", "again", "against "ya", "you", "youd", "youll", "your", "youre", "yours", "yourself", "yourselves", "youve"]); /** - * @type {Object} + * @type {Map} */ export const stemmer = new Map([["ational", ""], ["iveness", ""], ["fulness", ""], ["ousness", ""], ["ization", ""], ["tional", ""], ["biliti", ""], ["icate", ""], ["ative", ""], ["alize", ""], ["iciti", ""], ["entli", ""], ["ousli", ""], ["alism", ""], ["ation", ""], ["aliti", ""], ["iviti", ""], ["ement", ""], ["izer", ""], ["able", ""], ["alli", ""], ["ator", ""], ["logi", ""], ["ical", ""], ["ance", ""], ["ence", ""], ["ness", ""], ["ble", ""], ["ment", ""], ["eli", ""], ["bli", ""], ["ful", ""], ["ant", ""], ["ent", ""], ["ism", ""], ["ate", ""], ["iti", ""], ["ous", ""], ["ive", ""], ["ize", ""], ["ing", ""], ["ion", ""], ["al", ""], ["ou", ""], ["er", ""], ["ic", ""], ["ly", ""]]); -// export const replacer = new Map([ -// ["&", " and "], -// ]); - /* he’s (= he is / he has) she’s (= she is / she has) @@ -58,71 +56,18 @@ export const stemmer = new Map([["ational", ""], ["iveness", ""], ["fulness", "" // ["'d$", " would had"], // ]); -// const pairs = [ -// /´`’ʼ/, /´`’ʼ/g, "'", -// /_/, /_+/g, " ", -// /&/, /&/g, " and ", -// /\bi'm\b/, /\bi'm\b/g, "i am", -// /\b(can't|cannot)\b/, /\b(can't|cannot)\b/g, "can not", -// /\bwon't\b/, /\bwon't\b/g, "will not", -// /[a-z]n't\b/, /[a-z]n't\b/g, "$1 not", -// /[a-z]'s\b/, /([a-z])'s\b/g, "$1 is has", -// /[a-z]'ll\b/, /[a-z]'ll\b/g, "$1 will", -// /[a-z]'re\b/, /[a-z]'re\b/g, "$1 are", -// /[a-z]'ve\b/, /[a-z]'ve\b/g, "$1 have", -// /[a-z]'d\b/, /[a-z]'d\b/g, "$1 is has" -// ]; - -// const map = new Map([ -// ["´", "'"], -// ["`", "'"], -// ["’", "'"], -// ["ʼ", "'"], -// ["_", " "], -// ["&", " and "] -// ]); - -export default { +/** + * @type EncoderOptions + */ +const options = { prepare: function (str) { - // if(/[´`’ʼ_&]/.test(str)) - // str = str.replace(/[´`’ʼ_&]/g, match => map.get(match)); - - // if(/´`’ʼ/.test(str)) - // str = str.replace(/´`’ʼ/g, "'"); - // if(/_/.test(str)) - // str = str.replace(/_+/g, " "); - // if(/&/.test(str)) - // str = str.replace(/&/g, " and "); - - // if(/\bi'm\b/.test(str)) - // str = str.replace(/\bi'm\b/g, "i am"); - // if(/\b(can't|cannot)\b/.test(str)) - // str = str.replace(/\b(can't|cannot)\b/g, "can not"); - // if(/\bwon't\b/.test(str)) - // str = str.replace(/\bwon't\b/g, "will not"); - // if(/[a-z]n't\b/.test(str)) - // str = str.replace(/([a-z])n't\b/g, "$1 not"); - // if(/[a-z]'s\b/.test(str)) - // str = str.replace(/([a-z])'s\b/g, "$1 is has"); - // if(/[a-z]'ll\b/.test(str)) - // str = str.replace(/([a-z])'ll\b/g, "$1 will"); - // if(/[a-z]'re\b/.test(str)) - // str = str.replace(/([a-z])'re\b/g, "$1 are"); - // if(/[a-z]'ve\b/.test(str)) - // str = str.replace(/([a-z])'ve\b/g, "$1 have"); - // if(/[a-z]'d\b/.test(str)) - // str = str.replace(/([a-z])'d\b/g, "$1 would had"); - // return str; - - return str //.replace(/[´`’ʼ_&]/g, match => map.get(match)) + return str // normalization - .replace(/´`’ʼ/g, "'").replace(/_+/g, " ").replace(/&/g, " and ") - //.replace(/([0-9 ]|^)\$([0-9 ]|$)/g, "$1 USD $2") - //.replace(/([0-9 ]|^)£([0-9 ]|$)/g, "$1 GBP $2") - .replace(/\$/g, " USD ").replace(/£/g, " GBP ") + .replace(/´`’ʼ/g, "'").replace(/_+/g, " ").replace(/&/g, " and ").replace(/\$/g, " USD ").replace(/£/g, " GBP ") // explode short forms .replace(/([a-z])'s\b/g, "$1 is").replace(/\bi'm\b/g, "i am").replace(/\b(can't|cannot)\b/g, "can not").replace(/\bwon't\b/g, "will not").replace(/([a-z])n't\b/g, "$1 not").replace(/([a-z])'ll\b/g, "$1 will").replace(/([a-z])'re\b/g, "$1 are").replace(/([a-z])'ve\b/g, "$1 have").replace(/([a-z])'d\b/g, "$1 would"); }, filter: filter, stemmer: stemmer -}; \ No newline at end of file +}; +export default options; \ No newline at end of file diff --git a/dist/module-debug/lang/fr.js b/dist/module-debug/lang/fr.js index 573652f..51050f7 100644 --- a/dist/module-debug/lang/fr.js +++ b/dist/module-debug/lang/fr.js @@ -1,3 +1,5 @@ +import { EncoderOptions } from "../type.js"; + /** * http://www.ranks.nl/stopwords * http://snowball.tartarus.org/algorithms/french/stop.txt @@ -12,10 +14,14 @@ export const filter = new Set(["au", "aux", "avec", "ce", "ces", "dans", "de", " export const stemmer = new Map([["lement", ""], ["ient", ""], ["nera", ""], ["ment", ""], ["ais", ""], ["ait", ""], ["ant", ""], ["ent", ""], ["iez", ""], ["ion", ""], ["nez", ""], ["ai", ""], ["es", ""], ["er", ""], ["ez", ""], ["le", ""], ["na", ""], ["ne", ""], ["a", ""], ["e", ""]]); -export default { +/** + * @type EncoderOptions + */ +const options = { prepare: function (str) { return str.replace(/´`’ʼ/g, "'").replace(/_+/g, " ").replace(/&/g, " et ").replace(/€/g, " EUR ").replace(/\bl'([^\b])/g, "la le $1").replace(/\bt'([^\b])/g, "ta te $1").replace(/\bc'([^\b])/g, "ca ce $1").replace(/\bd'([^\b])/g, "da de $1").replace(/\bj'([^\b])/g, "ja je $1").replace(/\bn'([^\b])/g, "na ne $1").replace(/\bm'([^\b])/g, "ma me $1").replace(/\bs'([^\b])/g, "sa se $1").replace(/\bau\b/g, "a le").replace(/\baux\b/g, "a les").replace(/\bdu\b/g, "de le").replace(/\bdes\b/g, "de les"); }, filter: filter, stemmer: stemmer -}; \ No newline at end of file +}; +export default options; \ No newline at end of file diff --git a/dist/module-debug/lang/latin/exact.js b/dist/module-debug/lang/latin/exact.js deleted file mode 100644 index 1ef1dae..0000000 --- a/dist/module-debug/lang/latin/exact.js +++ /dev/null @@ -1,4 +0,0 @@ -export default { - normalize: /* suggest */ /* append: */ /* enrich */!1, - dedupe: !1 -}; \ No newline at end of file diff --git a/dist/module-debug/lang/us.js b/dist/module-debug/lang/us.js deleted file mode 100644 index 7b1ba3a..0000000 --- a/dist/module-debug/lang/us.js +++ /dev/null @@ -1,100 +0,0 @@ -/** - * http://www.ranks.nl/stopwords - * @type {Array} - */ - -export const filter = ["a", "about", "above", "after", "again", "against", "all", "also", "am", "an", "and", "any", "are", "aren't", "as", "at", -//"back", -"be", "because", "been", "before", "being", "below", -//"between", -"both", "but", "by", "can", "cannot", "can't", "come", "could", "couldn't", -//"day", -"did", "didn't", "do", "does", "doesn't", "doing", "dont", "down", "during", "each", "even", "few", "first", "for", "from", "further", "get", -//"give", -"go", -//"good", -"had", "hadn't", "has", "hasn't", "have", "haven't", "having", "he", "hed", -//"hell", -"her", "here", "here's", "hers", "herself", "hes", "him", "himself", "his", "how", "how's", "i", "id", "if", "ill", "im", "in", "into", "is", "isn't", "it", "it's", "itself", "i've", "just", "know", "let's", "like", -//"look", -"make", "me", "more", "most", "mustn't", "my", "myself", "new", "no", "nor", "not", "now", "of", "off", "on", "once", -//"one", -"only", "or", "other", "ought", "our", "our's", "ourselves", "out", "over", "own", -//"people", -"same", "say", "see", "shan't", "she", "she'd", "shell", "shes", "should", "shouldn't", "so", "some", "such", -//"take", -"than", "that", "that's", "the", "their", "theirs", "them", "themselves", "then", "there", "there's", "these", "they", "they'd", "they'll", "they're", "they've", -//"think", -"this", "those", "through", "time", "to", "too", -//"two", -//"under", -"until", "up", "us", -//"use", -"very", "want", "was", "wasn't", "way", "we", "wed", "well", "were", "weren't", "we've", "what", "what's", "when", "when's", "where", "where's", "which", "while", "who", "whom", "who's", "why", "why's", "will", "with", "won't", -//"work", -"would", "wouldn't", -//"year", -"you", "you'd", "you'll", "your", "you're", "your's", "yourself", "yourselves", "you've"]; - -/** - * @type {Object} - */ - -export const stemmer = { - - ational: "ate", - iveness: "ive", - fulness: "ful", - ousness: "ous", - ization: "ize", - tional: "tion", - biliti: "ble", - icate: "ic", - ative: "", - alize: "al", - iciti: "ic", - entli: "ent", - ousli: "ous", - alism: "al", - ation: "ate", - aliti: "al", - iviti: "ive", - ement: "", - enci: "ence", - anci: "ance", - izer: "ize", - alli: "al", - ator: "ate", - logi: "log", - ical: "ic", - ance: "", - ence: "", - ness: "", - able: "", - ible: "", - ment: "", - eli: "e", - bli: "ble", - ful: "", - ant: "", - ent: "", - ism: "", - ate: "", - iti: "", - ous: "", - ive: "", - ize: "", - al: "", - ou: "", - er: "", - ic: "" -}; - -export const matcher = {}; - -export default { - - filter: filter, - stemmer: stemmer, - matcher: matcher -}; \ No newline at end of file diff --git a/dist/module-debug/resolve/and.js b/dist/module-debug/resolve/and.js index 9e7994f..80eab54 100644 --- a/dist/module-debug/resolve/and.js +++ b/dist/module-debug/resolve/and.js @@ -13,7 +13,7 @@ Resolver.prototype.and = function () { first_argument = args[0]; - if (first_argument instanceof Promise) { + if (first_argument.then) { return first_argument.then(function () { return self.and.apply(self, args); }); @@ -51,7 +51,7 @@ Resolver.prototype.and = function () { if (query = args[i]) { let result; - if (query instanceof Resolver) { + if (query.constructor === Resolver) { result = query.result; } else if (query.constructor === Array) { result = query; @@ -74,7 +74,7 @@ Resolver.prototype.and = function () { final[i] = result; - if (result instanceof Promise) { + if (result.then) { promises.push(result); //{ query, result }; } } diff --git a/dist/module-debug/resolve/not.js b/dist/module-debug/resolve/not.js index 13364fb..2a5e1e4 100644 --- a/dist/module-debug/resolve/not.js +++ b/dist/module-debug/resolve/not.js @@ -11,7 +11,7 @@ Resolver.prototype.not = function () { first_argument = args[0]; - if (first_argument instanceof Promise) { + if (first_argument.then) { return first_argument.then(function () { return self.not.apply(self, args); }); @@ -35,7 +35,7 @@ Resolver.prototype.not = function () { if (query = args[i]) { let result; - if (query instanceof Resolver) { + if (query.constructor === Resolver) { result = query.result; } else if (query.constructor === Array) { result = query; @@ -58,7 +58,7 @@ Resolver.prototype.not = function () { final[i] = result; - if (result instanceof Promise) { + if (result.then) { promises.push(result); //{ query, result }; } } diff --git a/dist/module-debug/resolve/or.js b/dist/module-debug/resolve/or.js index d14981c..1423149 100644 --- a/dist/module-debug/resolve/or.js +++ b/dist/module-debug/resolve/or.js @@ -12,7 +12,7 @@ Resolver.prototype.or = function () { first_argument = args[0]; - if (first_argument instanceof Promise) { + if (first_argument.then) { return first_argument.then(function () { return self.or.apply(self, args); }); @@ -62,7 +62,7 @@ Resolver.prototype.or = function () { if (query = args[i]) { let result; - if (query instanceof Resolver) { + if (query.constructor === Resolver) { result = query.result; } else if (query.constructor === Array) { result = query; @@ -85,7 +85,7 @@ Resolver.prototype.or = function () { final[i] = result; - if (result instanceof Promise) { + if (result.then) { promises.push(result); //{ query, result }; } } diff --git a/dist/module-debug/resolve/xor.js b/dist/module-debug/resolve/xor.js index 1eed9e8..3e76ef8 100644 --- a/dist/module-debug/resolve/xor.js +++ b/dist/module-debug/resolve/xor.js @@ -11,7 +11,7 @@ Resolver.prototype.xor = function () { first_argument = args[0]; - if (first_argument instanceof Promise) { + if (first_argument.then) { return first_argument.then(function () { return self.xor.apply(self, args); }); @@ -36,7 +36,7 @@ Resolver.prototype.xor = function () { if (query = args[i]) { let result; - if (query instanceof Resolver) { + if (query.constructor === Resolver) { result = query.result; } else if (query.constructor === Array) { result = query; @@ -59,7 +59,7 @@ Resolver.prototype.xor = function () { final[i] = result; - if (result instanceof Promise) { + if (result.then) { promises.push(result); //{ query, result }; } } diff --git a/dist/module-debug/resolver.js b/dist/module-debug/resolver.js index fbd1bf7..b6057e3 100644 --- a/dist/module-debug/resolver.js +++ b/dist/module-debug/resolver.js @@ -1,9 +1,5 @@ import default_resolver from "./resolve/default.js"; import { set_resolve } from "./index/search.js"; -// import or from "./resolve/or.js"; -// import and from "./resolve/and.js"; -// import xor from "./resolve/xor.js"; -// import not from "./resolve/not.js"; import "./resolve/or.js"; import "./resolve/and.js"; import "./resolve/xor.js"; @@ -15,16 +11,16 @@ import "./resolve/not.js"; */ export default function Resolver(result) { + if (!this) { + return new Resolver(result); + } if (result && result.index) { result.resolve = /* suggest */ /* append: */ /* enrich */!1; this.index = result.index; return result.index.search(result); } - if (!(this instanceof Resolver)) { - return new Resolver(result); - } - if (result instanceof Resolver) { - // todo remove + if (result.constructor === Resolver) { + // todo test this branch //console.log("Resolver Loopback") return result; } @@ -33,11 +29,6 @@ export default function Resolver(result) { this.boostval = 0; } -// Resolver.prototype.or = or; -// Resolver.prototype.and = and; -// Resolver.prototype.not = not; -// Resolver.prototype.xor = xor; - Resolver.prototype.limit = function (limit) { if (this.result.length) { const final = []; diff --git a/dist/module-debug/type.js b/dist/module-debug/type.js index 0c377c0..d9e2165 100644 --- a/dist/module-debug/type.js +++ b/dist/module-debug/type.js @@ -23,6 +23,29 @@ import StorageInterface from "./db/interface.js"; */ export let IndexOptions; +/** + * @typedef DocumentIndexOptions {{ + * preset: string|undefined, + * context: (IndexOptions|undefined), + * encoder: Encoder|Function|Object|undefined, + * encode: Function|undefined, + * resolution: [number=9], + * tokenize: [string="strict"], + * fastupdate: [boolean:false], + * score: Function]|undefined, + * keystore: [number=0], + * rtl: [boolean=false], + * cache: [number=null], + * db: StorageInterface|undefined, + * config: string|undefined, + * + * field: string, + * filter: Function|undefined, + * custom: Function|undefined + * }} + */ +export let DocumentIndexOptions; + /** * @typedef DocumentOptions {{ * context: (IndexOptions|undefined), @@ -57,33 +80,11 @@ export let ContextOptions; * field: FieldOptions|Array|undefined, * index: FieldOptions|Array|undefined, * tag: TagOptions|Array|undefined, - * store: StoreOptions|Array|undefined, - * config: string|undefined + * store: StoreOptions|Array|undefined * }} */ export let DocumentDescriptor; -/** - * @typedef FieldOptions {{ - * field: string, - * filter: Function|undefined, - * custom: Function|undefined, - * context: (IndexOptions|undefined), - * encoder: Encoder|Function|Object|undefined, - * encode: Function|undefined, - * resolution: [number=9], - * tokenize: [string="strict"], - * fastupdate: [boolean:false], - * score: Function]|undefined, - * keystore: [number=0], - * rtl: [boolean=false], - * cache: [number=null], - * db: StorageInterface|undefined, - * config: string|undefined - * }} - */ -export let FieldOptions; - /** * @typedef TagOptions {{ * field: string, @@ -102,8 +103,6 @@ export let TagOptions; * field: string, * filter: Function|undefined, * custom: Function|undefined, - * keystore: [number=0], - * db: StorageInterface|undefined, * config: string|undefined * }} */ @@ -118,10 +117,10 @@ export let StoreOptions; * suggest: [boolean=false], * resolve: [boolean=true], * enrich: [boolean=false], - * tag: Array|undefined * }} */ export let SearchOptions; +// tag: Array|undefined /** * @typedef DocumentSearchOptions {{ @@ -131,14 +130,44 @@ export let SearchOptions; * context: boolean|undefined, * suggest: [boolean=false], * enrich: [boolean=false], - * tag: Array|undefined, - * field: FieldOptions|Array|undefined, - * index: FieldOptions|Array|undefined, + * tag: Object|Array|undefined, + * field: Array|undefined, + * index: Array|undefined, * pluck: boolean|undefined, * merge: [boolean=false] * }} */ export let DocumentSearchOptions; +/** + * @typedef EncoderOptions {{ + * rtl: boolean=false, + * dedupe: boolean=true, + * split: string|undefined, + * include: EncoderSplitOptions|undefined, + * exclude: EncoderSplitOptions|undefined, + * prepare: function(string):string|undefined, + * finalize: function(Array<>string>):Array|undefined, + * filter: Set|undefined, + * matcher: Map|undefined, + * mapper: Map|undefined, + * stemmer: Map|undefined, + * replacer: Array|undefined, + * minlength: number=1, + * maxlength: number|undefined, + * cache: boolean=true, + * }} + */ export let EncoderOptions; -export let ResolverOptions; \ No newline at end of file + +/** + * @typedef EncoderSplitOptions {{ + * letter: boolean=false, + * number: boolean=false, + * symbol: boolean=false, + * punctuation: boolean=false, + * control: boolean=false, + * char: string|Array|undefined, + * }} + */ +export let EncoderSplitOptions; \ No newline at end of file diff --git a/dist/module-debug/webpack.js b/dist/module-debug/webpack.js index ea7a391..8c4ccb9 100644 --- a/dist/module-debug/webpack.js +++ b/dist/module-debug/webpack.js @@ -1,19 +1,12 @@ -import { SearchOptions, ContextOptions, DocumentDescriptor, DocumentSearchOptions, FieldOptions, IndexOptions, DocumentOptions } from "./type.js"; +import { SearchOptions, ContextOptions, DocumentDescriptor, DocumentSearchOptions, DocumentIndexOptions, IndexOptions, DocumentOptions, TagOptions, StoreOptions, EncoderOptions, EncoderSplitOptions } from "./type.js"; import Document from "./document.js"; import Index from "./index.js"; import WorkerIndex from "./worker/index.js"; import Resolver from "./resolver.js"; import Encoder from "./encoder.js"; import IdxDB from "./db/indexeddb/index.js"; -import { global_charset } from "./charset.js"; -import charset_exact from "./lang/latin/exact.js"; -import charset_default from "./lang/latin/default.js"; -import charset_simple from "./lang/latin/simple.js"; -import charset_balance from "./lang/latin/balance.js"; -import charset_advanced from "./lang/latin/advanced.js"; -import charset_extra from "./lang/latin/extra.js"; -import charset_soundex from "./lang/latin/soundex.js"; +import Charset from "./charset.js"; /** @export */Index.prototype.add; /** @export */Index.prototype.append; @@ -23,6 +16,17 @@ import charset_soundex from "./lang/latin/soundex.js"; /** @export */Index.prototype.contain; /** @export */Index.prototype.clear; /** @export */Index.prototype.cleanup; +/** @export */Index.prototype.searchCache; +/** @export */Index.prototype.addAsync; +/** @export */Index.prototype.appendAsync; +/** @export */Index.prototype.searchAsync; +/** @export */Index.prototype.updateAsync; +/** @export */Index.prototype.removeAsync; +/** @export */Index.prototype.export; +/** @export */Index.prototype.import; +/** @export */Index.prototype.mount; +/** @export */Index.prototype.commit; +/** @export */Index.db; /** @export */Document.prototype.add; /** @export */Document.prototype.append; @@ -32,39 +36,39 @@ import charset_soundex from "./lang/latin/soundex.js"; /** @export */Document.prototype.contain; /** @export */Document.prototype.clear; /** @export */Document.prototype.cleanup; - -/** @export */Document.prototype.get; -/** @export */Document.prototype.set; - -/** @export */Index.prototype.searchCache; - -/** @export */Document.prototype.searchCache; - -/** @export */Index.prototype.addAsync; -/** @export */Index.prototype.appendAsync; -/** @export */Index.prototype.searchAsync; -/** @export */Index.prototype.updateAsync; -/** @export */Index.prototype.removeAsync; - /** @export */Document.prototype.addAsync; /** @export */Document.prototype.appendAsync; /** @export */Document.prototype.searchAsync; /** @export */Document.prototype.updateAsync; /** @export */Document.prototype.removeAsync; - -/** @export */Index.prototype.export; -/** @export */Index.prototype.import; - -/** @export */Document.prototype.export; -/** @export */Document.prototype.import; - -/** @export */Index.prototype.mount; -/** @export */Index.prototype.commit; -/** @export */Index.db; - /** @export */Document.prototype.mount; /** @export */Document.prototype.commit; /** @export */Document.db; +/** @export */Document.prototype.export; +/** @export */Document.prototype.import; +/** @export */Document.prototype.searchCache; +/** @export */Document.prototype.get; +/** @export */Document.prototype.set; + +/** @export */Resolver.prototype.limit; +/** @export */Resolver.prototype.offset; +/** @export */Resolver.prototype.boost; +/** @export */Resolver.prototype.resolve; +/** @export */Resolver.prototype.or; +/** @export */Resolver.prototype.and; +/** @export */Resolver.prototype.xor; +/** @export */Resolver.prototype.not; + +/** @export */Charset.LatinExact; +/** @export */Charset.LatinDefault; +/** @export */Charset.LatinSimple; +/** @export */Charset.LatinBalance; +/** @export */Charset.LatinAdvanced; +/** @export */Charset.LatinExtra; +/** @export */Charset.LatinSoundex; +/** @export */Charset.ArabicDefault; +/** @export */Charset.CjkDefault; +/** @export */Charset.CyrillicDefault; /** @export */IndexOptions.preset; /** @export */IndexOptions.context; @@ -79,6 +83,25 @@ import charset_soundex from "./lang/latin/soundex.js"; /** @export */IndexOptions.cache; /** @export */IndexOptions.resolve; /** @export */IndexOptions.db; +/** @export */IndexOptions.config; + +/** @export */DocumentIndexOptions.preset; +/** @export */DocumentIndexOptions.context; +/** @export */DocumentIndexOptions.encoder; +/** @export */DocumentIndexOptions.encode; +/** @export */DocumentIndexOptions.resolution; +/** @export */DocumentIndexOptions.tokenize; +/** @export */DocumentIndexOptions.fastupdate; +/** @export */DocumentIndexOptions.score; +/** @export */DocumentIndexOptions.keystore; +/** @export */DocumentIndexOptions.rtl; +/** @export */DocumentIndexOptions.cache; +/** @export */DocumentIndexOptions.db; +/** @export */DocumentIndexOptions.config; +// /** @export */ DocumentIndexOptions.resolve; +/** @export */DocumentIndexOptions.field; +/** @export */DocumentIndexOptions.filter; +/** @export */DocumentIndexOptions.custom; /** @export */DocumentOptions.context; /** @export */DocumentOptions.encoder; @@ -95,14 +118,27 @@ import charset_soundex from "./lang/latin/soundex.js"; /** @export */DocumentOptions.document; /** @export */DocumentOptions.worker; +/** @export */ContextOptions.depth; +/** @export */ContextOptions.bidirectional; +/** @export */ContextOptions.resolution; + /** @export */DocumentDescriptor.field; /** @export */DocumentDescriptor.index; /** @export */DocumentDescriptor.tag; /** @export */DocumentDescriptor.store; -/** @export */ContextOptions.depth; -/** @export */ContextOptions.bidirectional; -/** @export */ContextOptions.resolution; +/** @export */TagOptions.field; +/** @export */TagOptions.tag; +/** @export */TagOptions.filter; +/** @export */TagOptions.custom; +/** @export */TagOptions.keystore; +/** @export */TagOptions.db; +/** @export */TagOptions.config; + +/** @export */StoreOptions.field; +/** @export */StoreOptions.filter; +/** @export */StoreOptions.custom; +/** @export */StoreOptions.config; /** @export */SearchOptions.query; /** @export */SearchOptions.limit; @@ -111,7 +147,6 @@ import charset_soundex from "./lang/latin/soundex.js"; /** @export */SearchOptions.suggest; /** @export */SearchOptions.resolve; /** @export */SearchOptions.enrich; -/** @export */SearchOptions.tag; /** @export */DocumentSearchOptions.query; /** @export */DocumentSearchOptions.limit; @@ -125,25 +160,38 @@ import charset_soundex from "./lang/latin/soundex.js"; /** @export */DocumentSearchOptions.pluck; /** @export */DocumentSearchOptions.merge; -global_charset["latin:exact"] = charset_exact; -global_charset["latin:default"] = charset_default; -global_charset["latin:simple"] = charset_simple; -global_charset["latin:balance"] = charset_balance; -global_charset["latin:advanced"] = charset_advanced; -global_charset["latin:extra"] = charset_extra; -global_charset["latin:soundex"] = charset_soundex; +/** @export */EncoderOptions.rtl; +/** @export */EncoderOptions.dedupe; +/** @export */EncoderOptions.split; +/** @export */EncoderOptions.include; +/** @export */EncoderOptions.exclude; +/** @export */EncoderOptions.prepare; +/** @export */EncoderOptions.finalize; +/** @export */EncoderOptions.filter; +/** @export */EncoderOptions.matcher; +/** @export */EncoderOptions.mapper; +/** @export */EncoderOptions.stemmer; +/** @export */EncoderOptions.replacer; +/** @export */EncoderOptions.minlength; +/** @export */EncoderOptions.maxlength; +/** @export */EncoderOptions.cache; +/** @export */EncoderSplitOptions.letter; +/** @export */EncoderSplitOptions.number; +/** @export */EncoderSplitOptions.symbol; +/** @export */EncoderSplitOptions.punctuation; +/** @export */EncoderSplitOptions.control; +/** @export */EncoderSplitOptions.char; const FlexSearch = { Index: Index, - Charset: global_charset, + Charset: Charset, Encoder: Encoder, Document: Document, Worker: WorkerIndex, Resolver: Resolver, - IndexedDB: IdxDB - //"registerCharset": registerCharset, - //"registerLanguage": registerLanguage + IndexedDB: IdxDB, + Language: {} }; // Export as library (Bundle) @@ -151,6 +199,8 @@ const FlexSearch = { { + FlexSearch.Language = {}; + const root = self; let prop; diff --git a/dist/module-debug/worker/index.js b/dist/module-debug/worker/index.js index 8d7df5e..3dab909 100644 --- a/dist/module-debug/worker/index.js +++ b/dist/module-debug/worker/index.js @@ -12,7 +12,7 @@ let pid = 0; function WorkerIndex(options) { - if (!(this instanceof WorkerIndex)) { + if (!this) { return new WorkerIndex(options); } diff --git a/dist/module-min/cache.js b/dist/module-min/cache.js index 9e51568..0d2a783 100644 --- a/dist/module-min/cache.js +++ b/dist/module-min/cache.js @@ -1 +1 @@ -import Index from"./index.js";import Document from"./document.js";export function searchCache(a,b,c){a=("object"==typeof a?""+a.query:""+a).toLowerCase();let d=this.cache.get(a);if(!d){if(d=this.search(a,b,c),d instanceof Promise){const b=this;d.then(function(c){b.cache.set(a,c)})}this.cache.set(a,d)}return d}export default function CacheClass(a){this.limit=a&&!0!==a?a:1000,this.cache=new Map,this.last=""}CacheClass.prototype.set=function(a,b){this.cache.has(a)||(this.cache.set(this.last=a,b),this.limit&&this.cache.size>this.limit&&this.cache.delete(this.cache.keys().next().value))},CacheClass.prototype.get=function(a){const b=this.cache.get(a);return b&&this.limit&&this.last!==a&&(this.cache.delete(a),this.cache.set(this.last=a,b)),b},CacheClass.prototype.remove=function(a){for(const b of this.cache){const c=b[0],d=b[1];d.includes(a)&&this.cache.delete(c)}},CacheClass.prototype.clear=function(){this.cache.clear(),this.last=""}; \ No newline at end of file +import Index from"./index.js";import Document from"./document.js";export function searchCache(a,b,c){a=("object"==typeof a?""+a.query:""+a).toLowerCase();let d=this.cache.get(a);if(!d){if(d=this.search(a,b,c),d.then){const b=this;d.then(function(c){return b.cache.set(a,c),c})}this.cache.set(a,d)}return d}export default function CacheClass(a){this.limit=a&&!0!==a?a:1000,this.cache=new Map,this.last=""}CacheClass.prototype.set=function(a,b){this.cache.set(this.last=a,b),this.cache.size>this.limit&&this.cache.delete(this.cache.keys().next().value)},CacheClass.prototype.get=function(a){const b=this.cache.get(a);return b&&this.last!==a&&(this.cache.delete(a),this.cache.set(this.last=a,b)),b},CacheClass.prototype.remove=function(a){for(const b of this.cache){const c=b[0],d=b[1];d.includes(a)&&this.cache.delete(c)}},CacheClass.prototype.clear=function(){this.cache.clear(),this.last=""}; \ No newline at end of file diff --git a/dist/module-min/charset.js b/dist/module-min/charset.js index 08e30f3..7c7956b 100644 --- a/dist/module-min/charset.js +++ b/dist/module-min/charset.js @@ -1 +1 @@ -import{create_object}from"./common.js";export const global_charset=create_object();export function registerCharset(a,b){global_charset[a]=b} \ No newline at end of file +import charset_latin_exact from"./charset/latin/exact.js";import charset_latin_default from"./charset/latin/default.js";import charset_latin_simple from"./charset/latin/simple.js";import charset_latin_balance from"./charset/latin/balance.js";import charset_latin_advanced from"./charset/latin/advanced.js";import charset_latin_extra from"./charset/latin/extra.js";import charset_latin_soundex from"./charset/latin/soundex.js";import charset_arabic_default from"./charset/arabic/default.js";import charset_cjk_default from"./charset/cjk/default.js";import charset_cyrillic_default from"./charset/cyrillic/default.js";export const LatinExact=charset_latin_exact;export const LatinDefault=charset_latin_default;export const LatinSimple=charset_latin_simple;export const LatinBalance=charset_latin_balance;export const LatinAdvanced=charset_latin_advanced;export const LatinExtra=charset_latin_extra;export const LatinSoundex=charset_latin_soundex;export const ArabicDefault=charset_arabic_default;export const CjkDefault=charset_cjk_default;export const CyrillicDefault=charset_cyrillic_default;export default{LatinExact:charset_latin_exact,LatinDefault:charset_latin_default,LatinSimple:charset_latin_simple,LatinBalance:charset_latin_balance,LatinAdvanced:charset_latin_advanced,LatinExtra:charset_latin_extra,LatinSoundex:charset_latin_soundex,ArabicDefault:charset_arabic_default,CjkDefault:charset_cjk_default,CyrillicDefault:charset_cyrillic_default}; \ No newline at end of file diff --git a/dist/module-min/charset/arabic/default.js b/dist/module-min/charset/arabic/default.js new file mode 100644 index 0000000..c81f2ae --- /dev/null +++ b/dist/module-min/charset/arabic/default.js @@ -0,0 +1 @@ +import{EncoderOptions}from"../../type.js";const regex=/[\x00-\x7F]+/g,split=/\s+/,options={rtl:!0,normalize:!1,dedupe:!0,prepare:function(a){return(""+a).replace(regex," ")}};export default options; \ No newline at end of file diff --git a/dist/module-min/charset/cjk/default.js b/dist/module-min/charset/cjk/default.js new file mode 100644 index 0000000..e48bd09 --- /dev/null +++ b/dist/module-min/charset/cjk/default.js @@ -0,0 +1 @@ +import{EncoderOptions}from"../../type.js";const regex=/[\x00-\x7F]+/g,options={normalize:!1,dedupe:!0,split:"",prepare:function(a){return(""+a).replace(regex,"")}};export default options; \ No newline at end of file diff --git a/dist/module-min/charset/cyrillic/default.js b/dist/module-min/charset/cyrillic/default.js new file mode 100644 index 0000000..b66591a --- /dev/null +++ b/dist/module-min/charset/cyrillic/default.js @@ -0,0 +1 @@ +import{EncoderOptions}from"../../type.js";const regex=/[\x00-\x7F]+/g,options={normalize:!1,dedupe:!0,prepare:function(a){return(""+a).replace(regex," ")}};export default options; \ No newline at end of file diff --git a/dist/module-min/charset/latin/advanced.js b/dist/module-min/charset/latin/advanced.js new file mode 100644 index 0000000..266d4fd --- /dev/null +++ b/dist/module-min/charset/latin/advanced.js @@ -0,0 +1 @@ +import{EncoderOptions}from"../../type.js";import{soundex}from"./balance.js";export const matcher=new Map([["ai","ei"],["ae","a"],["oe","o"],["ue","u"],["sh","s"],["ch","c"],["th","t"],["ph","f"],["pf","f"]]);export const replacer=[/([^aeo])h([aeo$])/g,"$1$2",/([aeo])h([^aeo]|$)/g,"$1$2"];const options={normalize:!0,dedupe:!0,mapper:soundex,replacer:replacer,matcher:matcher};export default options; \ No newline at end of file diff --git a/dist/module-min/charset/latin/balance.js b/dist/module-min/charset/latin/balance.js new file mode 100644 index 0000000..0236992 --- /dev/null +++ b/dist/module-min/charset/latin/balance.js @@ -0,0 +1 @@ +import{EncoderOptions}from"../../type.js";export const soundex=new Map([["b","p"],["v","f"],["w","f"],["z","s"],["x","s"],["d","t"],["n","m"],["c","k"],["g","k"],["j","k"],["q","k"],["i","e"],["y","e"],["u","o"]]);const options={normalize:!0,dedupe:!0,mapper:soundex};export default options; \ No newline at end of file diff --git a/dist/module-min/charset/latin/default.js b/dist/module-min/charset/latin/default.js new file mode 100644 index 0000000..6d6e97f --- /dev/null +++ b/dist/module-min/charset/latin/default.js @@ -0,0 +1 @@ +import{EncoderOptions}from"../../type.js";const options={normalize:function(a){return a.toLowerCase()},dedupe:!1};export default options; \ No newline at end of file diff --git a/dist/module-min/charset/latin/exact.js b/dist/module-min/charset/latin/exact.js new file mode 100644 index 0000000..9999f54 --- /dev/null +++ b/dist/module-min/charset/latin/exact.js @@ -0,0 +1 @@ +import{EncoderOptions}from"../../type.js";const options={normalize:!1,dedupe:!1};export default options; \ No newline at end of file diff --git a/dist/module-min/charset/latin/extra.js b/dist/module-min/charset/latin/extra.js new file mode 100644 index 0000000..694a97b --- /dev/null +++ b/dist/module-min/charset/latin/extra.js @@ -0,0 +1 @@ +import{EncoderOptions}from"../../type.js";import{soundex}from"./balance.js";import{matcher,replacer}from"./advanced.js";export const compact=[/(?!^)[aeoy]/g,""];const options={normalize:!0,dedupe:!0,mapper:soundex,replacer:replacer.concat(compact),matcher:matcher};export default options; \ No newline at end of file diff --git a/dist/module-min/charset/latin/simple.js b/dist/module-min/charset/latin/simple.js new file mode 100644 index 0000000..9692634 --- /dev/null +++ b/dist/module-min/charset/latin/simple.js @@ -0,0 +1 @@ +import{EncoderOptions}from"../../type.js";const options={normalize:!0,dedupe:!0};export default options; \ No newline at end of file diff --git a/dist/module-min/charset/latin/soundex.js b/dist/module-min/charset/latin/soundex.js new file mode 100644 index 0000000..fec07c5 --- /dev/null +++ b/dist/module-min/charset/latin/soundex.js @@ -0,0 +1 @@ +import{EncoderOptions}from"../../type.js";const options={normalize:!0,dedupe:!1,include:{letter:!0},finalize:function(a){for(let b=0;b{b.objectStoreNames.contains(a)||b.createObjectStore(a)})},d.onblocked=function(a){console.error("blocked",a),c()},d.onerror=function(a){console.error(this.error,a),c()},d.onsuccess=function(){a.db=this.result,a.db.onversionchange=function(){a.close()},b(a)}})},IdxDB.prototype.close=function(){this.db.close(),this.db=null},IdxDB.prototype.destroy=function(){return this.db&&this.close(),IndexedDB.deleteDatabase(this.id+(this.field?":"+this.field:""))},IdxDB.prototype.clear=function(){const a=this.db.transaction(fields,"readwrite");for(let b=0;b=e.length){d-=e.length;continue}const a=c?d+Math.min(e.length-d,c):e.length;for(let c=d;c=a.length)return[];if(!b&&!c)return a;const e=a.slice(c,c+b);return d?h.enrich(e):e})},IdxDB.prototype.enrich=function(a){"object"!=typeof a&&(a=[a]);const b=this.db.transaction("reg","readonly"),c=b.objectStore("reg"),d=[];for(let b=0;b(e.onerror=c=>{this.trx[a+":"+b]=null,e.abort(),e=d=null,g(c)},e.oncomplete=c=>{this.trx[a+":"+b]=null,e=d=null,f(c||!0)},c.call(this,d)))},IdxDB.prototype.commit=async function(a,b,c){if(b)await this.clear(),a.commit_task=[];else{let d=a.commit_task;a.commit_task=[];for(let a,c=0;ca)||f||"string"!=typeof c||isNaN(c)||(a=h.indexOf(parseInt(c,10)),a&&(f=1)),0<=a)if(e=1,1{a.onsuccess=function(){b&&b(this.result),c(this.result)},a.oncomplete=function(){b&&b(this.result),c(this.result)},a.onerror=d,a=null})} \ No newline at end of file +import Document from"../../document.js";const VERSION=1,IndexedDB="undefined"!=typeof window&&(window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB),IDBTransaction="undefined"!=typeof window&&(window.IDBTransaction||window.webkitIDBTransaction||window.msIDBTransaction),IDBKeyRange="undefined"!=typeof window&&(window.IDBKeyRange||window.webkitIDBKeyRange||window.msIDBKeyRange),fields=["map","ctx","tag","reg","cfg"];import StorageInterface from"../interface.js";import{toArray}from"../../common.js";function sanitize(a){return a.toLowerCase().replace(/[^a-z0-9_\-]/g,"")}export default function IdxDB(a,b={}){return this?void("object"==typeof a&&(a=a.name,b=a),!a&&console.info("Default storage space was used, because a name was not passed."),this.id="flexsearch"+(a?":"+sanitize(a):""),this.field=b.field?sanitize(b.field):"",this.support_tag_search=!1,this.db=null,this.trx={}):new IdxDB(a,b)}IdxDB.prototype.mount=function(a){return a.constructor===Document?a.mount(this):(a.db=this,this.open())},IdxDB.prototype.open=function(){let a=this;return navigator.storage&&navigator.storage.persist(),this.db||new Promise(function(b,c){const d=IndexedDB.open(a.id+(a.field?":"+a.field:""),VERSION);d.onupgradeneeded=function(){const b=a.db=this.result;fields.forEach(a=>{b.objectStoreNames.contains(a)||b.createObjectStore(a)})},d.onblocked=function(a){console.error("blocked",a),c()},d.onerror=function(a){console.error(this.error,a),c()},d.onsuccess=function(){a.db=this.result,a.db.onversionchange=function(){a.close()},b(a)}})},IdxDB.prototype.close=function(){this.db.close(),this.db=null},IdxDB.prototype.destroy=function(){return this.db&&this.close(),IndexedDB.deleteDatabase(this.id+(this.field?":"+this.field:""))},IdxDB.prototype.clear=function(){const a=this.db.transaction(fields,"readwrite");for(let b=0;b=e.length){d-=e.length;continue}const a=c?d+Math.min(e.length-d,c):e.length;for(let c=d;c=a.length)return[];if(!b&&!c)return a;const e=a.slice(c,c+b);return d?h.enrich(e):e})},IdxDB.prototype.enrich=function(a){"object"!=typeof a&&(a=[a]);const b=this.db.transaction("reg","readonly"),c=b.objectStore("reg"),d=[];for(let b=0;b(e.onerror=c=>{this.trx[a+":"+b]=null,e.abort(),e=d=null,g(c)},e.oncomplete=c=>{this.trx[a+":"+b]=null,e=d=null,f(c||!0)},c.call(this,d)))},IdxDB.prototype.commit=async function(a,b,c){if(b)await this.clear(),a.commit_task=[];else{let d=a.commit_task;a.commit_task=[];for(let a,c=0;ca)||f||"string"!=typeof c||isNaN(c)||(a=h.indexOf(parseInt(c,10)),a&&(f=1)),0<=a)if(e=1,1{a.onsuccess=function(){b&&b(this.result),c(this.result)},a.oncomplete=function(){b&&b(this.result),c(this.result)},a.onerror=d,a=null})} \ No newline at end of file diff --git a/dist/module-min/db/mongo/index.js b/dist/module-min/db/mongo/index.js index 498114b..277e2ff 100644 --- a/dist/module-min/db/mongo/index.js +++ b/dist/module-min/db/mongo/index.js @@ -1 +1 @@ -import{MongoClient}from"mongodb";const defaults={host:"localhost",port:"27017",user:null,pass:null},VERSION=1,fields=["map","ctx","tag","reg","cfg"];import StorageInterface from"../interface.js";import Document from"../../document.js";import{toArray}from"../../common.js";function sanitize(a){return a.toLowerCase().replace(/[^a-z0-9_\-]/g,"")}let CLIENT,DB=Object.create(null);export default function MongoDB(a,b={}){return this instanceof MongoDB?void("object"==typeof a&&(a=a.name,b=a),!a&&console.info("Default storage space was used, because a name was not passed."),this.id="flexsearch"+(a?"-"+sanitize(a):""),this.field=b.field?"-"+sanitize(b.field):"",this.type=b.type||"",this.db=b.db||DB[this.id]||CLIENT||null,this.trx=!1,this.support_tag_search=!0,Object.assign(defaults,b),this.db&&delete defaults.db):new MongoDB(a,b)}MongoDB.prototype.mount=function(a){return a instanceof Document?a.mount(this):(a.db=this,this.open())};async function createCollection(a,b,c){"map"===b?(await a.createCollection("map"+c),await a.collection("map"+c).createIndex({key:1}),await a.collection("map"+c).createIndex({id:1})):"ctx"===b?(await a.createCollection("ctx"+c),await a.collection("ctx"+c).createIndex({ctx:1,key:1}),await a.collection("ctx"+c).createIndex({id:1})):"tag"===b?(await a.createCollection("tag"+c),await a.collection("tag"+c).createIndex({tag:1}),await a.collection("tag"+c).createIndex({id:1})):"reg"===b?(await a.createCollection("reg"),await a.collection("reg").createIndex({id:1})):"cfg"===b?await a.createCollection("cfg"+c):void 0}MongoDB.prototype.open=async function(){if(!this.db&&!(this.db=DB[this.id])&&!(this.db=CLIENT)){let a=defaults.url;a||(a=defaults.user?`mongodb://${defaults.user}:${defaults.pass}@${defaults.host}:${defaults.port}`:`mongodb://${defaults.host}:${defaults.port}`),this.db=CLIENT=new MongoClient(a),await this.db.connect()}this.db.db&&(this.db=DB[this.id]=this.db.db(this.id));const a=await this.db.listCollections().toArray();for(let b,c=0;cl;k.push({ctx:d?j:l,key:d?l:j}),l=j}let m=f?{_id:1}:{_id:1,res:1};const n=[{$match:{$or:k}},{$group:{_id:"$id",res:e?{$sum:1}:{$min:1},count:{$sum:1}}}];if(e||n.push({$match:{count:b.length-1}}),g&&(m.doc="$doc.doc",n.push({$lookup:{from:"reg",localField:"_id",foreignField:"id",as:"doc"}},{$unwind:{path:"$doc",preserveNullAndEmptyArrays:!0}})),h){const a={};for(let b=0,c=1;bl;k.push({ctx:d?j:l,key:d?l:j}),l=j}let m=f?{_id:1}:{_id:1,res:1};const n=[{$match:{$or:k}},{$group:{_id:"$id",res:e?{$sum:1}:{$min:1},count:{$sum:1}}}];if(e||n.push({$match:{count:b.length-1}}),g&&(m.doc="$doc.doc",n.push({$lookup:{from:"reg",localField:"_id",foreignField:"id",as:"doc"}},{$unwind:{path:"$doc",preserveNullAndEmptyArrays:!0}})),h){const a={};for(let b=0,c=1;bconsole.error(a)).connect()},RedisDB.prototype.close=async function(){return await this.db.disconnect(),this.db=null,this},RedisDB.prototype.clear=function(){return this.db.unlink([this.id+"map"+this.field,this.id+"ctx"+this.field,this.id+"tag"+this.field,this.id+"cfg"+this.field,this.id+"doc",this.id+"reg"])};function create_result(a,b,c,d){if(c){for(let c,d,e=0;e=a.length)return[];if(!b&&!c)return a;const f=a.slice(c,c+b);return d?e.enrich(f):f})},RedisDB.prototype.enrich=function(a){return"object"!=typeof a&&(a=[a]),this.db.hmGet(this.id+"doc",a).then(function(b){for(let c=0;cf,e.push(c+(g?d:f)+":"+(g?f:d)),f=d;b=e}else{const a=this.id+"map"+this.field+":";for(let c=0;cconsole.error(a)).connect()},RedisDB.prototype.close=async function(){return await this.db.disconnect(),this.db=null,this},RedisDB.prototype.clear=function(){return this.db.unlink([this.id+"map"+this.field,this.id+"ctx"+this.field,this.id+"tag"+this.field,this.id+"cfg"+this.field,this.id+"doc",this.id+"reg"])};function create_result(a,b,c,d){if(c){for(let c,d,e=0;e=a.length)return[];if(!b&&!c)return a;const f=a.slice(c,c+b);return d?e.enrich(f):f})},RedisDB.prototype.enrich=function(a){return"object"!=typeof a&&(a=[a]),this.db.hmGet(this.id+"doc",a).then(function(b){for(let c=0;cf,e.push(c+(g?d:f)+":"+(g?f:d)),f=d;b=e}else{const a=this.id+"map"+this.field+":";for(let c=0;ca.length?this.addMapper(a,b):(this.matcher||(this.matcher=new Map),this.matcher.set(a,b),this.matcher_str+=(this.matcher_str?"|":"")+a,this.matcher_test=null,this.cache&&this.invalidate(),this)},Encoder.prototype.addStemmer=function(a,b){return this.stemmer||(this.stemmer=new Map),this.stemmer.set(a,b),this.stemmer_str+=(this.stemmer_str?"|":"")+a,this.stemmer_test=null,this.cache&&this.invalidate(),this},Encoder.prototype.addFilter=function(a){return this.filter||(this.filter=new Set),this.filter.add(a),this.cache&&this.invalidate(),this},Encoder.prototype.addMapper=function(a,b){return"object"==typeof a?this.addReplacer(a,b):1this.stemmer.get(a)),a=1),this.matcher&&1this.matcher.get(a)),a=1),e&&a&&(e.lengththis.cache_size&&(this.cache_prt.clear(),this.cache_prt_length=0|this.cache_prt_length/1.1)),e&&c.push(e)}return this.finalize&&(c=this.finalize(c)||c),this.cache&&a.length<=this.cache_enc_length&&(this.cache_enc.set(a,c),this.cache_enc.size>this.cache_size&&(this.cache_enc.clear(),this.cache_enc_length=0|this.cache_enc_length/1.1)),c};function clear(a){a.timer=null,a.cache_enc.clear(),a.cache_prt.clear()} \ No newline at end of file +import{parse_option}from"./common.js";import normalize_polyfill from"./charset/normalize.js";import{EncoderOptions}from"./type.js";const whitespace=/[^\p{L}\p{N}]+/u,numeric_split_length=/(\d{3})/g,numeric_split_prev_char=/(\D)(\d{3})/g,numeric_split_next_char=/(\d{3})(\D)/g,normalize=/[\u0300-\u036f]/g;export default function Encoder(){if(!this)return new Encoder(...arguments);for(let a=0;aa.length?this.addMapper(a,b):(this.matcher||(this.matcher=new Map),this.matcher.set(a,b),this.matcher_str+=(this.matcher_str?"|":"")+a,this.matcher_test=null,this.cache&&this.invalidate(),this)},Encoder.prototype.addStemmer=function(a,b){return this.stemmer||(this.stemmer=new Map),this.stemmer.set(a,b),this.stemmer_str+=(this.stemmer_str?"|":"")+a,this.stemmer_test=null,this.cache&&this.invalidate(),this},Encoder.prototype.addFilter=function(a){return this.filter||(this.filter=new Set),this.filter.add(a),this.cache&&this.invalidate(),this},Encoder.prototype.addMapper=function(a,b){return"object"==typeof a?this.addReplacer(a,b):1this.stemmer.get(a)),a=1),this.matcher&&1this.matcher.get(a)),a=1),e&&a&&(e.lengththis.cache_size&&(this.cache_prt.clear(),this.cache_prt_length=0|this.cache_prt_length/1.1)),e&&c.push(e)}return this.finalize&&(c=this.finalize(c)||c),this.cache&&a.length<=this.cache_enc_length&&(this.cache_enc.set(a,c),this.cache_enc.size>this.cache_size&&(this.cache_enc.clear(),this.cache_enc_length=0|this.cache_enc_length/1.1)),c};function clear(a){a.timer=null,a.cache_enc.clear(),a.cache_prt.clear()} \ No newline at end of file diff --git a/dist/module-min/index.js b/dist/module-min/index.js index 1d7df89..e34a00e 100644 --- a/dist/module-min/index.js +++ b/dist/module-min/index.js @@ -1 +1 @@ -import{IndexOptions}from"./type.js";import Encoder from"./encoder.js";import Cache,{searchCache}from"./cache.js";import{KeystoreMap,KeystoreSet}from"./keystore.js";import{is_array,is_string}from"./common.js";import{exportIndex,importIndex}from"./serialize.js";import default_encoder from"./lang/latin/default.js";import apply_preset from"./preset.js";import apply_async from"./async.js";import tick from"./profiler.js";import"./index/add.js";import"./index/search.js";import"./index/remove.js";export default function Index(a,b){if(!(this instanceof Index))return new Index(a);!1,a=a?apply_preset(a):{};const c=a.context||{},d=a.encode||a.encoder||default_encoder;this.encoder=d.encode?d:"object"==typeof d?new Encoder(d):{encode:d},this.compress=a.compress||a.compression||!1;let e;this.resolution=a.resolution||9,this.tokenize=e=a.tokenize||"strict",this.depth="strict"===e&&c.depth||0,this.bidirectional=!1!==c.bidirectional,this.fastupdate=!!a.fastupdate,this.score=a.score||null,e=a.keystore||0,e&&(this.keystore=e),this.map=e&&!0?new KeystoreMap(e):new Map,this.ctx=e&&!0?new KeystoreMap(e):new Map,this.reg=b||(this.fastupdate?e&&!0?new KeystoreMap(e):new Map:e&&!0?new KeystoreSet(e):new Set),this.resolution_ctx=c.resolution||1,this.rtl=d.rtl||a.rtl||!1,this.cache=(e=a.cache||null)&&new Cache(e),this.resolve=!1!==a.resolve,(e=a.db)&&(this.db=e.mount(this)),this.commit_auto=!1!==a.commit,this.commit_task=[],this.commit_timer=null}Index.prototype.mount=function(a){return this.commit_timer&&(clearTimeout(this.commit_timer),this.commit_timer=null),a.mount(this)},Index.prototype.commit=function(a,b){return this.commit_timer&&(clearTimeout(this.commit_timer),this.commit_timer=null),this.db.commit(this,a,b)};export function autoCommit(a,b,c){a.commit_timer||(a.commit_timer=setTimeout(function(){a.commit_timer=null,a.db.commit(a,b,c)},0))}Index.prototype.clear=function(){return this.map.clear(),this.ctx.clear(),this.reg.clear(),this.cache&&this.cache.clear(),this.db&&(this.commit_timer&&clearTimeout(this.commit_timer),this.commit_timer=null,this.commit_task=[{clear:!0}]),this},Index.prototype.append=function(a,b){return this.add(a,b,!0)},Index.prototype.contain=function(a){return this.db?this.db.has(a):this.reg.has(a)},Index.prototype.update=function(a,b){if(this.async){const c=this,d=this.remove(a);return d.then?d.then(()=>c.add(a,b)):this.add(a,b)}return this.remove(a).add(a,b)};function cleanup_index(a){let b=0;if(is_array(a))for(let c,d=0;dc.add(a,b)):this.add(a,b)}return this.remove(a).add(a,b)};function cleanup_index(a){let b=0;if(is_array(a))for(let c,d=0;d=f.length)b-=f.length;else{const g=f[d?"splice":"slice"](b,c),h=g.length;if(h&&(e=e.length?e.concat(g):g,c-=h,d&&(a.length-=h),!c))break;b=0}return e}export function KeystoreArray(a){if(!(this instanceof KeystoreArray))return new KeystoreArray(a);this.index=a?[a]:[],this.length=a?a.length:0;const b=this;return new Proxy([],{get(a,c){if("length"===c)return b.length;if("push"===c)return function(a){b.index[b.index.length-1].push(a),b.length++};if("pop"===c)return function(){if(b.length)return b.length--,b.index[b.index.length-1].pop()};if("indexOf"===c)return function(a){let c=0;for(let d,e,f=0;f=f.length)b-=f.length;else{const g=f[d?"splice":"slice"](b,c),h=g.length;if(h&&(e=e.length?e.concat(g):g,c-=h,d&&(a.length-=h),!c))break;b=0}return e}export function KeystoreArray(a){if(!this)return new KeystoreArray(a);this.index=a?[a]:[],this.length=a?a.length:0;const b=this;return new Proxy([],{get(a,c){if("length"===c)return b.length;if("push"===c)return function(a){b.index[b.index.length-1].push(a),b.length++};if("pop"===c)return function(){if(b.length)return b.length--,b.index[b.index.length-1].pop()};if("indexOf"===c)return function(a){let c=0;for(let d,e,f=0;fmap.get(a))),a.replace(/str\b/g,"strasse").replace(/(?!\b)strasse\b/g," strasse").replace(/\bst\b/g,"sankt")},filter:filter,stemmer:stemmer}; \ No newline at end of file +import{EncoderOptions}from"../type.js";export const filter=new Set(["aber","als","am","an","auch","auf","aus","bei","bin","bis","bist","da","dadurch","daher","darum","das","dass","dass","dein","deine","dem","den","der","des","dessen","deshalb","die","dies","dieser","dieses","doch","dort","du","durch","ein","eine","einem","einen","einer","eines","er","es","euer","eure","fuer","hatte","hatten","hattest","hattet","hier","hinter","ich","ihr","ihre","im","in","ist","ja","jede","jedem","jeden","jeder","jedes","jener","jenes","jetzt","ggf","kann","kannst","koennen","koennt","machen","mein","meine","mit","muss","musst","musst","muessen","muesst","nach","nachdem","nein","nicht","noch","nun","oder","seid","sein","seine","sich","sie","sind","soll","sollen","sollst","sollt","sonst","soweit","sowie","und","unser","unsere","unter","usw","uvm","vom","von","vor","wann","warum","was","weiter","weitere","wenn","wer","werde","werden","werdet","weshalb","wie","wieder","wieso","wir","wird","wirst","wo","woher","wohin","zu","zum","zur","ueber"]);export const stemmer=new Map([["niss",""],["isch",""],["lich",""],["heit",""],["keit",""],["ell",""],["bar",""],["end",""],["ung",""],["est",""],["ern",""],["em",""],["er",""],["en",""],["es",""],["st",""],["ig",""],["ik",""],["e",""],["s",""]]);const map=new Map([["_"," "],["\xE4","ae"],["\xF6","oe"],["\xFC","ue"],["\xDF","ss"],["&"," und "],["\u20AC"," EUR "]]),options={prepare:function(a){return /[_äöüß&€]/.test(a)&&(a=a.replace(/[_äöüß&€]/g,a=>map.get(a))),a.replace(/str\b/g,"strasse").replace(/(?!\b)strasse\b/g," strasse")},filter:filter,stemmer:stemmer};export default options; \ No newline at end of file diff --git a/dist/module-min/lang/en.js b/dist/module-min/lang/en.js index 9934d75..c57a8aa 100644 --- a/dist/module-min/lang/en.js +++ b/dist/module-min/lang/en.js @@ -1 +1 @@ -export const filter=new Set(["a","about","above","after","again","against","all","also","am","an","and","any","are","arent","as","at","back","be","because","been","before","being","below","between","both","but","by","can","cannot","cant","come","could","couldnt","did","didnt","do","does","doesnt","doing","dont","down","during","each","even","few","for","from","further","get","go","good","had","hadnt","has","hasnt","have","havent","having","he","hed","her","here","heres","hers","herself","hes","him","himself","his","how","hows","i","id","if","ill","im","in","into","is","isnt","it","its","itself","ive","just","know","lets","like","lot","make","made","me","more","most","mustnt","my","myself","new","no","nor","not","now","of","off","on","once","one","only","or","other","ought","our","ours","ourselves","out","over","own","people","same","say","see","shant","she","shed","shell","shes","should","shouldnt","so","some","such","take","than","that","thats","the","their","theirs","them","themselves","then","there","theres","these","they","theyd","theyll","theyre","theyve","think","this","those","through","time","times","to","too","under","until","up","us","use","very","want","was","wasnt","way","we","wed","well","were","werent","weve","what","whats","when","whens","where","wheres","which","while","who","whom","whos","why","whys","will","with","wont","work","would","wouldnt","ya","you","youd","youll","your","youre","yours","yourself","yourselves","youve"]);export const stemmer=new Map([["ational",""],["iveness",""],["fulness",""],["ousness",""],["ization",""],["tional",""],["biliti",""],["icate",""],["ative",""],["alize",""],["iciti",""],["entli",""],["ousli",""],["alism",""],["ation",""],["aliti",""],["iviti",""],["ement",""],["izer",""],["able",""],["alli",""],["ator",""],["logi",""],["ical",""],["ance",""],["ence",""],["ness",""],["ble",""],["ment",""],["eli",""],["bli",""],["ful",""],["ant",""],["ent",""],["ism",""],["ate",""],["iti",""],["ous",""],["ive",""],["ize",""],["ing",""],["ion",""],["al",""],["ou",""],["er",""],["ic",""],["ly",""]]);export default{prepare:function(a){return a.replace(/´`’ʼ/g,"'").replace(/_+/g," ").replace(/&/g," and ").replace(/\$/g," USD ").replace(/£/g," GBP ").replace(/([a-z])'s\b/g,"$1 is").replace(/\bi'm\b/g,"i am").replace(/\b(can't|cannot)\b/g,"can not").replace(/\bwon't\b/g,"will not").replace(/([a-z])n't\b/g,"$1 not").replace(/([a-z])'ll\b/g,"$1 will").replace(/([a-z])'re\b/g,"$1 are").replace(/([a-z])'ve\b/g,"$1 have").replace(/([a-z])'d\b/g,"$1 would")},filter:filter,stemmer:stemmer}; \ No newline at end of file +import{EncoderOptions}from"../type.js";export const filter=new Set(["a","about","above","after","again","against","all","also","am","an","and","any","are","arent","as","at","back","be","because","been","before","being","below","between","both","but","by","can","cannot","cant","come","could","couldnt","did","didnt","do","does","doesnt","doing","dont","down","during","each","even","few","for","from","further","get","go","good","had","hadnt","has","hasnt","have","havent","having","he","hed","her","here","heres","hers","herself","hes","him","himself","his","how","hows","i","id","if","ill","im","in","into","is","isnt","it","its","itself","ive","just","know","lets","like","lot","make","made","me","more","most","mustnt","my","myself","new","no","nor","not","now","of","off","on","once","one","only","or","other","ought","our","ours","ourselves","out","over","own","people","same","say","see","shant","she","shed","shell","shes","should","shouldnt","so","some","such","take","than","that","thats","the","their","theirs","them","themselves","then","there","theres","these","they","theyd","theyll","theyre","theyve","think","this","those","through","time","times","to","too","under","until","up","us","use","very","want","was","wasnt","way","we","wed","well","were","werent","weve","what","whats","when","whens","where","wheres","which","while","who","whom","whos","why","whys","will","with","wont","work","would","wouldnt","ya","you","youd","youll","your","youre","yours","yourself","yourselves","youve"]);export const stemmer=new Map([["ational",""],["iveness",""],["fulness",""],["ousness",""],["ization",""],["tional",""],["biliti",""],["icate",""],["ative",""],["alize",""],["iciti",""],["entli",""],["ousli",""],["alism",""],["ation",""],["aliti",""],["iviti",""],["ement",""],["izer",""],["able",""],["alli",""],["ator",""],["logi",""],["ical",""],["ance",""],["ence",""],["ness",""],["ble",""],["ment",""],["eli",""],["bli",""],["ful",""],["ant",""],["ent",""],["ism",""],["ate",""],["iti",""],["ous",""],["ive",""],["ize",""],["ing",""],["ion",""],["al",""],["ou",""],["er",""],["ic",""],["ly",""]]);const options={prepare:function(a){return a.replace(/´`’ʼ/g,"'").replace(/_+/g," ").replace(/&/g," and ").replace(/\$/g," USD ").replace(/£/g," GBP ").replace(/([a-z])'s\b/g,"$1 is").replace(/\bi'm\b/g,"i am").replace(/\b(can't|cannot)\b/g,"can not").replace(/\bwon't\b/g,"will not").replace(/([a-z])n't\b/g,"$1 not").replace(/([a-z])'ll\b/g,"$1 will").replace(/([a-z])'re\b/g,"$1 are").replace(/([a-z])'ve\b/g,"$1 have").replace(/([a-z])'d\b/g,"$1 would")},filter:filter,stemmer:stemmer};export default options; \ No newline at end of file diff --git a/dist/module-min/lang/fr.js b/dist/module-min/lang/fr.js index eb8da75..173bc4a 100644 --- a/dist/module-min/lang/fr.js +++ b/dist/module-min/lang/fr.js @@ -1 +1 @@ -export const filter=new Set(["au","aux","avec","ce","ces","dans","de","des","du","elle","en","et","eux","il","je","la","le","leur","lui","ma","mais","me","meme","mes","moi","mon","ne","nos","notre","nous","on","ou","par","pas","pour","qu","que","qui","sa","se","ses","son","sur","ta","te","tes","toi","ton","tu","un","une","vos","votre","vous","c","d","j","l","m","n","s","t","a","y","ete","etee","etees","etes","etant","suis","es","est","sommes","etes","sont","serai","seras","sera","serons","serez","seront","serais","serait","serions","seriez","seraient","etais","etait","etions","etiez","etaient","fus","fut","fumes","futes","furent","sois","soit","soyons","soyez","soient","fusse","fusses","fut","fussions","fussiez","fussent","ayant","eu","eue","eues","eus","ai","as","avons","avez","ont","aurai","auras","aura","aurons","aurez","auront","aurais","aurait","aurions","auriez","auraient","avais","avait","avions","aviez","avaient","eut","eumes","eutes","eurent","aie","aies","ait","ayons","ayez","aient","eusse","eusses","eut","eussions","eussiez","eussent","ceci","cela","cela","cet","cette","ici","ils","les","leurs","quel","quels","quelle","quelles","sans","soi"]);export const stemmer=new Map([["lement",""],["ient",""],["nera",""],["ment",""],["ais",""],["ait",""],["ant",""],["ent",""],["iez",""],["ion",""],["nez",""],["ai",""],["es",""],["er",""],["ez",""],["le",""],["na",""],["ne",""],["a",""],["e",""]]);export default{prepare:function(a){return a.replace(/´`’ʼ/g,"'").replace(/_+/g," ").replace(/&/g," et ").replace(/€/g," EUR ").replace(/\bl'([^\b])/g,"la le $1").replace(/\bt'([^\b])/g,"ta te $1").replace(/\bc'([^\b])/g,"ca ce $1").replace(/\bd'([^\b])/g,"da de $1").replace(/\bj'([^\b])/g,"ja je $1").replace(/\bn'([^\b])/g,"na ne $1").replace(/\bm'([^\b])/g,"ma me $1").replace(/\bs'([^\b])/g,"sa se $1").replace(/\bau\b/g,"a le").replace(/\baux\b/g,"a les").replace(/\bdu\b/g,"de le").replace(/\bdes\b/g,"de les")},filter:filter,stemmer:stemmer}; \ No newline at end of file +import{EncoderOptions}from"../type.js";export const filter=new Set(["au","aux","avec","ce","ces","dans","de","des","du","elle","en","et","eux","il","je","la","le","leur","lui","ma","mais","me","meme","mes","moi","mon","ne","nos","notre","nous","on","ou","par","pas","pour","qu","que","qui","sa","se","ses","son","sur","ta","te","tes","toi","ton","tu","un","une","vos","votre","vous","c","d","j","l","m","n","s","t","a","y","ete","etee","etees","etes","etant","suis","es","est","sommes","etes","sont","serai","seras","sera","serons","serez","seront","serais","serait","serions","seriez","seraient","etais","etait","etions","etiez","etaient","fus","fut","fumes","futes","furent","sois","soit","soyons","soyez","soient","fusse","fusses","fut","fussions","fussiez","fussent","ayant","eu","eue","eues","eus","ai","as","avons","avez","ont","aurai","auras","aura","aurons","aurez","auront","aurais","aurait","aurions","auriez","auraient","avais","avait","avions","aviez","avaient","eut","eumes","eutes","eurent","aie","aies","ait","ayons","ayez","aient","eusse","eusses","eut","eussions","eussiez","eussent","ceci","cela","cela","cet","cette","ici","ils","les","leurs","quel","quels","quelle","quelles","sans","soi"]);export const stemmer=new Map([["lement",""],["ient",""],["nera",""],["ment",""],["ais",""],["ait",""],["ant",""],["ent",""],["iez",""],["ion",""],["nez",""],["ai",""],["es",""],["er",""],["ez",""],["le",""],["na",""],["ne",""],["a",""],["e",""]]);const options={prepare:function(a){return a.replace(/´`’ʼ/g,"'").replace(/_+/g," ").replace(/&/g," et ").replace(/€/g," EUR ").replace(/\bl'([^\b])/g,"la le $1").replace(/\bt'([^\b])/g,"ta te $1").replace(/\bc'([^\b])/g,"ca ce $1").replace(/\bd'([^\b])/g,"da de $1").replace(/\bj'([^\b])/g,"ja je $1").replace(/\bn'([^\b])/g,"na ne $1").replace(/\bm'([^\b])/g,"ma me $1").replace(/\bs'([^\b])/g,"sa se $1").replace(/\bau\b/g,"a le").replace(/\baux\b/g,"a les").replace(/\bdu\b/g,"de le").replace(/\bdes\b/g,"de les")},filter:filter,stemmer:stemmer};export default options; \ No newline at end of file diff --git a/dist/module-min/lang/latin/advanced.js b/dist/module-min/lang/latin/advanced.js deleted file mode 100644 index 2380370..0000000 --- a/dist/module-min/lang/latin/advanced.js +++ /dev/null @@ -1 +0,0 @@ -import Encoder from"../../encoder.js";import{soundex}from"./balance.js";export const matcher=new Map([["ai","ei"],["ae","a"],["oe","o"],["ue","u"],["sh","s"],["ch","c"],["th","t"],["ph","f"],["pf","f"]]);export const replacer=[/([^aeo])h([aeo$])/g,"$1$2",/([aeo])h([^aeo]|$)/g,"$1$2"];export default{normalize:!0,dedupe:!0,mapper:soundex,replacer:replacer,matcher:matcher}; \ No newline at end of file diff --git a/dist/module-min/lang/latin/balance.js b/dist/module-min/lang/latin/balance.js deleted file mode 100644 index ac377bf..0000000 --- a/dist/module-min/lang/latin/balance.js +++ /dev/null @@ -1 +0,0 @@ -import Encoder from"../../encoder.js";export const soundex=new Map([["b","p"],["v","f"],["w","f"],["z","s"],["x","s"],["d","t"],["n","m"],["c","k"],["g","k"],["j","k"],["q","k"],["i","e"],["y","e"],["u","o"]]);export default{normalize:!0,dedupe:!0,mapper:soundex}; \ No newline at end of file diff --git a/dist/module-min/lang/latin/default.js b/dist/module-min/lang/latin/default.js deleted file mode 100644 index ff8ecb2..0000000 --- a/dist/module-min/lang/latin/default.js +++ /dev/null @@ -1 +0,0 @@ -import Encoder from"../../encoder.js";export default{normalize:function(a){return a.toLowerCase()},dedupe:!1}; \ No newline at end of file diff --git a/dist/module-min/lang/latin/exact.js b/dist/module-min/lang/latin/exact.js deleted file mode 100644 index a9c7487..0000000 --- a/dist/module-min/lang/latin/exact.js +++ /dev/null @@ -1 +0,0 @@ -export default{normalize:!1,dedupe:!1}; \ No newline at end of file diff --git a/dist/module-min/lang/latin/extra.js b/dist/module-min/lang/latin/extra.js deleted file mode 100644 index 6ea9850..0000000 --- a/dist/module-min/lang/latin/extra.js +++ /dev/null @@ -1 +0,0 @@ -import Encoder from"../../encoder.js";import{soundex}from"./balance.js";import{matcher,replacer}from"./advanced.js";export const compact=[/(?!^)[aeoy]/g,""];export default{normalize:!0,dedupe:!0,mapper:soundex,replacer:replacer.concat(compact),matcher:matcher}; \ No newline at end of file diff --git a/dist/module-min/lang/latin/simple.js b/dist/module-min/lang/latin/simple.js deleted file mode 100644 index 9f6118d..0000000 --- a/dist/module-min/lang/latin/simple.js +++ /dev/null @@ -1 +0,0 @@ -import Encoder from"../../encoder.js";export default{normalize:!0,dedupe:!0}; \ No newline at end of file diff --git a/dist/module-min/lang/latin/soundex.js b/dist/module-min/lang/latin/soundex.js deleted file mode 100644 index 6310c3e..0000000 --- a/dist/module-min/lang/latin/soundex.js +++ /dev/null @@ -1 +0,0 @@ -export default{normalize:!0,dedupe:!1,include:{letter:!0},finalize:function(a){for(let b=0;ba.length)return[];let g=[],h=0,l=create_object(),m=get_max_len(a);if(!m)return g;for(let k,n=0;na.length)return[];let g=[],h=0,l=create_object(),m=get_max_len(a);if(!m)return g;for(let k,n=0;na.length)return e?default_resolver(a[0],b,c,d):a[0];let g=[],h=0,l=create_object(),m=get_max_len(a);for(let n,o=0;oa.length)return e?default_resolver(a[0],b,c,d):a[0];let g=[],h=0,l=create_object(),m=get_max_len(a);for(let n,o=0;oa.length)return e?default_resolver(a[0],b,c,d):a[0];const g=[],h=create_object();for(let g,j=0;ja.length)return e?default_resolver(a[0],b,c,d):a[0];const g=[],h=create_object();for(let g,j=0;j this.limit) { - this.cache.delete(this.cache.keys().next().value); - } + //if(!this.cache.has(key)){ + this.cache.set(this.last = key, value); + if (this.cache.size > this.limit) { + this.cache.delete(this.cache.keys().next().value); } + //} }; CacheClass.prototype.get = function (key) { const cache = this.cache.get(key); - if (cache && this.limit && this.last !== key) { + if (cache && this.last !== key) { this.cache.delete(key); this.cache.set(this.last = key, cache); } diff --git a/dist/module/charset.js b/dist/module/charset.js index 9a6e07f..ae8cf2a 100644 --- a/dist/module/charset.js +++ b/dist/module/charset.js @@ -1,16 +1,58 @@ -import { create_object } from "./common.js"; +import charset_latin_exact from "./charset/latin/exact.js"; +import charset_latin_default from "./charset/latin/default.js"; +import charset_latin_simple from "./charset/latin/simple.js"; +import charset_latin_balance from "./charset/latin/balance.js"; +import charset_latin_advanced from "./charset/latin/advanced.js"; +import charset_latin_extra from "./charset/latin/extra.js"; +import charset_latin_soundex from "./charset/latin/soundex.js"; +import charset_arabic_default from "./charset/arabic/default.js"; +import charset_cjk_default from "./charset/cjk/default.js"; +import charset_cyrillic_default from "./charset/cyrillic/default.js"; + +export const LatinExact = charset_latin_exact; +export const LatinDefault = charset_latin_default; +export const LatinSimple = charset_latin_simple; +export const LatinBalance = charset_latin_balance; +export const LatinAdvanced = charset_latin_advanced; +export const LatinExtra = charset_latin_extra; +export const LatinSoundex = charset_latin_soundex; +export const ArabicDefault = charset_arabic_default; +export const CjkDefault = charset_cjk_default; +export const CyrillicDefault = charset_cyrillic_default; // export const global_lang = create_object(); -export const global_charset = create_object(); +//export const global_charset = create_object(); + +export default { + LatinExact: charset_latin_exact, + LatinDefault: charset_latin_default, + LatinSimple: charset_latin_simple, + LatinBalance: charset_latin_balance, + LatinAdvanced: charset_latin_advanced, + LatinExtra: charset_latin_extra, + LatinSoundex: charset_latin_soundex, + ArabicDefault: charset_arabic_default, + CjkDefault: charset_cjk_default, + CyrillicDefault: charset_cyrillic_default +}; + +// global_charset["latin:exact"] = charset_exact; +// global_charset["latin:default"] = charset_default; +// global_charset["latin:simple"] = charset_simple; +// global_charset["latin:balance"] = charset_balance; +// global_charset["latin:advanced"] = charset_advanced; +// global_charset["latin:extra"] = charset_extra; +// global_charset["latin:soundex"] = charset_soundex; + /** * @param {!string} name * @param {Object} charset */ -export function registerCharset(name, charset) { - global_charset[name] = charset; -} +// export function registerCharset(name, charset){ +// global_charset[name] = charset; +// } /** * @param {!string} name diff --git a/dist/module/charset/arabic/default.js b/dist/module/charset/arabic/default.js new file mode 100644 index 0000000..028abbd --- /dev/null +++ b/dist/module/charset/arabic/default.js @@ -0,0 +1,42 @@ +import { EncoderOptions } from "../../type.js"; +const regex = /[\x00-\x7F]+/g, + split = /\s+/, + options = { + rtl: !0, + normalize: !1, + dedupe: !0, + prepare: function (str) { + return ("" + str).replace(regex, " "); + } +}; + +/** @type EncoderOptions */ + +export default options; + +// import { pipeline } from "../../lang.js"; +// +// export const rtl = true; +// export const tokenize = ""; +// export default { +// encode: encode, +// rtl: rtl +// } +// +// +// +// /** +// * @param {string|number} str +// */ +// +// export function encode(str){ +// +// return pipeline.call( +// +// this, +// /* string: */ ("" + str).replace(regex, " "), +// /* normalize: */ false, +// /* split: */ split, +// /* collapse: */ false +// ); +// } \ No newline at end of file diff --git a/dist/module/charset/cjk/default.js b/dist/module/charset/cjk/default.js new file mode 100644 index 0000000..c4caf70 --- /dev/null +++ b/dist/module/charset/cjk/default.js @@ -0,0 +1,42 @@ +import { EncoderOptions } from "../../type.js"; +const regex = /[\x00-\x7F]+/g, + options = { + normalize: !1, + dedupe: !0, + split: "", + prepare: function (str) { + return ("" + str).replace(regex, ""); + } +}; + +/** @type EncoderOptions */ + +export default options; + +// import { pipeline } from "../../lang.js"; +// +// export const rtl = false; +// export const tokenize = "strict"; +// export default { +// encode: encode, +// rtl: rtl, +// tokenize: tokenize +// } +// +// const regex = /[\x00-\x7F]+/g; +// +// /** +// * @param {string|number} str +// */ +// +// export function encode(str){ +// +// return pipeline.call( +// +// this, +// /* string: */ ("" + str).replace(regex, ""), +// /* normalize: */ false, +// /* split: */ "", +// /* collapse: */ false +// ); +// } \ No newline at end of file diff --git a/dist/module/charset/cyrillic/default.js b/dist/module/charset/cyrillic/default.js new file mode 100644 index 0000000..6de8b9a --- /dev/null +++ b/dist/module/charset/cyrillic/default.js @@ -0,0 +1,41 @@ +import { EncoderOptions } from "../../type.js"; +const regex = /[\x00-\x7F]+/g, + options = { + normalize: !1, + dedupe: !0, + prepare: function (str) { + return ("" + str).replace(regex, " "); + } +}; + +/** @type EncoderOptions */ + +export default options; + +// import { pipeline } from "../../lang.js"; +// +// export const rtl = false; +// export const tokenize = ""; +// export default { +// encode: encode, +// rtl: rtl +// } +// +// const regex = /[\x00-\x7F]+/g; +// const split = /\s+/; +// +// /** +// * @param {string|number} str +// */ +// +// export function encode(str){ +// +// return pipeline.call( +// +// this, +// /* string: */ ("" + str).replace(regex, " "), +// /* normalize: */ false, +// /* split: */ split, +// /* collapse: */ false +// ); +// } \ No newline at end of file diff --git a/dist/module/lang/latin/advanced.js b/dist/module/charset/latin/advanced.js similarity index 91% rename from dist/module/lang/latin/advanced.js rename to dist/module/charset/latin/advanced.js index fa5ea16..9d30352 100644 --- a/dist/module/lang/latin/advanced.js +++ b/dist/module/charset/latin/advanced.js @@ -1,4 +1,4 @@ -import Encoder from "../../encoder.js"; +import { EncoderOptions } from "../../type.js"; import { soundex } from "./balance.js"; // const soundex = new Map([ @@ -43,14 +43,15 @@ export const matcher = new Map([["ai", "ei"], ["ae", "a"], ["oe", "o"], ["ue", " export const replacer = [/([^aeo])h([aeo$])/g, "$1$2", /([aeo])h([^aeo]|$)/g, "$1$2"]; -export default { - normalize: /* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */ /* skip deletion */ // splice: - !0 /*await rows.hasNext()*/ /*await rows.hasNext()*/ /*await rows.hasNext()*/, +/** @type EncoderOptions */ +const options = { + normalize: !0, dedupe: !0, mapper: soundex, replacer: replacer, matcher: matcher }; +export default options; // import { regex, replace, collapse } from "../../lang.js"; // import { encode as encode_balance } from "./balance.js"; diff --git a/dist/module/lang/latin/balance.js b/dist/module/charset/latin/balance.js similarity index 94% rename from dist/module/lang/latin/balance.js rename to dist/module/charset/latin/balance.js index bd8fd20..f6791e8 100644 --- a/dist/module/lang/latin/balance.js +++ b/dist/module/charset/latin/balance.js @@ -1,4 +1,4 @@ -import Encoder from "../../encoder.js"; +import { EncoderOptions } from "../../type.js"; export const soundex = new Map([["b", "p"], //["p", "p"], @@ -28,12 +28,13 @@ export const soundex = new Map([["b", "p"], //["o", "o"], ["u", "o"]]); -export default { - normalize: /* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */ /* skip deletion */ // splice: - !0 /*await rows.hasNext()*/ /*await rows.hasNext()*/ /*await rows.hasNext()*/, +/** @type EncoderOptions */ +const options = { + normalize: !0, dedupe: !0, mapper: soundex }; +export default options; // //import { encode as encode_simple } from "./simple.js"; // import { pipeline } from "../../lang.js"; diff --git a/dist/module/lang/latin/default.js b/dist/module/charset/latin/default.js similarity index 82% rename from dist/module/lang/latin/default.js rename to dist/module/charset/latin/default.js index a7e126e..5501ee9 100644 --- a/dist/module/lang/latin/default.js +++ b/dist/module/charset/latin/default.js @@ -1,11 +1,13 @@ -import Encoder from "../../encoder.js"; +import { EncoderOptions } from "../../type.js"; -export default { +/** @type EncoderOptions */ +const options = { normalize: function (str) { return str.toLowerCase(); }, - dedupe: /* suggest */ /* append: */ /* enrich */!1 + dedupe: !1 }; +export default options; // import { pipeline } from "../../lang.js"; // diff --git a/dist/module/charset/latin/exact.js b/dist/module/charset/latin/exact.js new file mode 100644 index 0000000..0ba66b6 --- /dev/null +++ b/dist/module/charset/latin/exact.js @@ -0,0 +1,8 @@ +import { EncoderOptions } from "../../type.js"; + +/** @type EncoderOptions */ +const options = { + normalize: !1, + dedupe: !1 +}; +export default options; \ No newline at end of file diff --git a/dist/module/lang/latin/extra.js b/dist/module/charset/latin/extra.js similarity index 86% rename from dist/module/lang/latin/extra.js rename to dist/module/charset/latin/extra.js index ec53420..d1368bd 100644 --- a/dist/module/lang/latin/extra.js +++ b/dist/module/charset/latin/extra.js @@ -1,18 +1,19 @@ -import Encoder from "../../encoder.js"; +import { EncoderOptions } from "../../type.js"; import { soundex } from "./balance.js"; import { matcher, replacer } from "./advanced.js"; export const compact = [/(?!^)[aeoy]/g, "" // old: aioy ]; -export default { - normalize: /* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */ /* skip deletion */ // splice: - !0 /*await rows.hasNext()*/ /*await rows.hasNext()*/ /*await rows.hasNext()*/, +/** @type EncoderOptions */ +const options = { + normalize: !0, dedupe: !0, mapper: soundex, replacer: replacer.concat(compact), matcher: matcher }; +export default options; // import { regex, replace, collapse } from "../../lang.js"; // import { encode as encode_advanced } from "./advanced.js"; diff --git a/dist/module/lang/latin/simple.js b/dist/module/charset/latin/simple.js similarity index 95% rename from dist/module/lang/latin/simple.js rename to dist/module/charset/latin/simple.js index b01236e..3ee046c 100644 --- a/dist/module/lang/latin/simple.js +++ b/dist/module/charset/latin/simple.js @@ -1,10 +1,11 @@ -import Encoder from "../../encoder.js"; +import { EncoderOptions } from "../../type.js"; -export default { - normalize: /* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */ /* skip deletion */ // splice: - !0 /*await rows.hasNext()*/ /*await rows.hasNext()*/ /*await rows.hasNext()*/, +/** @type EncoderOptions */ +const options = { + normalize: !0, dedupe: !0 }; +export default options; // import { pipeline, regex } from "../../lang.js"; // diff --git a/dist/module-debug/lang/latin/soundex.js b/dist/module/charset/latin/soundex.js similarity index 80% rename from dist/module-debug/lang/latin/soundex.js rename to dist/module/charset/latin/soundex.js index 0a38309..9766d70 100644 --- a/dist/module-debug/lang/latin/soundex.js +++ b/dist/module/charset/latin/soundex.js @@ -1,7 +1,9 @@ -export default { - normalize: /* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */ /* skip deletion */ // splice: - !0 /*await rows.hasNext()*/ /*await rows.hasNext()*/ /*await rows.hasNext()*/, - dedupe: /* suggest */ /* append: */ /* enrich */!1, +import { EncoderOptions } from "../../type.js"; + +/** @type EncoderOptions */ +const options = { + normalize: !0, + dedupe: !1, include: { letter: !0 }, @@ -11,6 +13,7 @@ export default { } } }; +export default options; const codes = { a: "", e: "", i: "", o: "", u: "", y: "", diff --git a/dist/module/charset/normalize.js b/dist/module/charset/normalize.js new file mode 100644 index 0000000..749d40b --- /dev/null +++ b/dist/module/charset/normalize.js @@ -0,0 +1,29 @@ +export default [ + +// Charset Normalization + +["ª", "a"], ["²", "2"], ["³", "3"], ["¹", "1"], ["º", "o"], ["¼", "1⁄4"], ["½", "1⁄2"], ["¾", "3⁄4"], ["à", "a"], ["á", "a"], ["â", "a"], ["ã", "a"], ["ä", "a"], ["å", "a"], ["ç", "c"], ["è", "e"], ["é", "e"], ["ê", "e"], ["ë", "e"], ["ì", "i"], ["í", "i"], ["î", "i"], ["ï", "i"], ["ñ", "n"], ["ò", "o"], ["ó", "o"], ["ô", "o"], ["õ", "o"], ["ö", "o"], ["ù", "u"], ["ú", "u"], ["û", "u"], ["ü", "u"], ["ý", "y"], ["ÿ", "y"], ["ā", "a"], ["ă", "a"], ["ą", "a"], ["ć", "c"], ["ĉ", "c"], ["ċ", "c"], ["č", "c"], ["ď", "d"], ["ē", "e"], ["ĕ", "e"], ["ė", "e"], ["ę", "e"], ["ě", "e"], ["ĝ", "g"], ["ğ", "g"], ["ġ", "g"], ["ģ", "g"], ["ĥ", "h"], ["ĩ", "i"], ["ī", "i"], ["ĭ", "i"], ["į", "i"], ["ij", "ij"], ["ĵ", "j"], ["ķ", "k"], ["ĺ", "l"], ["ļ", "l"], ["ľ", "l"], ["ŀ", "l"], ["ń", "n"], ["ņ", "n"], ["ň", "n"], ["ʼn", "n"], ["ō", "o"], ["ŏ", "o"], ["ő", "o"], ["ŕ", "r"], ["ŗ", "r"], ["ř", "r"], ["ś", "s"], ["ŝ", "s"], ["ş", "s"], ["š", "s"], ["ţ", "t"], ["ť", "t"], ["ũ", "u"], ["ū", "u"], ["ŭ", "u"], ["ů", "u"], ["ű", "u"], ["ų", "u"], ["ŵ", "w"], ["ŷ", "y"], ["ź", "z"], ["ż", "z"], ["ž", "z"], ["ſ", "s"], ["ơ", "o"], ["ư", "u"], ["dž", "dz"], ["lj", "lj"], ["nj", "nj"], ["ǎ", "a"], ["ǐ", "i"], ["ǒ", "o"], ["ǔ", "u"], ["ǖ", "u"], ["ǘ", "u"], ["ǚ", "u"], ["ǜ", "u"], ["ǟ", "a"], ["ǡ", "a"], ["ǣ", "ae"], ["æ", "ae"], ["ǽ", "ae"], ["ǧ", "g"], ["ǩ", "k"], ["ǫ", "o"], ["ǭ", "o"], ["ǯ", "ʒ"], ["ǰ", "j"], ["dz", "dz"], ["ǵ", "g"], ["ǹ", "n"], ["ǻ", "a"], ["ǿ", "ø"], ["ȁ", "a"], ["ȃ", "a"], ["ȅ", "e"], ["ȇ", "e"], ["ȉ", "i"], ["ȋ", "i"], ["ȍ", "o"], ["ȏ", "o"], ["ȑ", "r"], ["ȓ", "r"], ["ȕ", "u"], ["ȗ", "u"], ["ș", "s"], ["ț", "t"], ["ȟ", "h"], ["ȧ", "a"], ["ȩ", "e"], ["ȫ", "o"], ["ȭ", "o"], ["ȯ", "o"], ["ȱ", "o"], ["ȳ", "y"], ["ʰ", "h"], ["ʱ", "h"], ["ɦ", "h"], ["ʲ", "j"], ["ʳ", "r"], ["ʴ", "ɹ"], ["ʵ", "ɻ"], ["ʶ", "ʁ"], ["ʷ", "w"], ["ʸ", "y"], ["ˠ", "ɣ"], ["ˡ", "l"], ["ˢ", "s"], ["ˣ", "x"], ["ˤ", "ʕ"], ["ΐ", "ι"], ["ά", "α"], ["έ", "ε"], ["ή", "η"], ["ί", "ι"], ["ΰ", "υ"], ["ϊ", "ι"], ["ϋ", "υ"], ["ό", "ο"], ["ύ", "υ"], ["ώ", "ω"], ["ϐ", "β"], ["ϑ", "θ"], ["ϒ", "Υ"], ["ϓ", "Υ"], ["ϔ", "Υ"], ["ϕ", "φ"], ["ϖ", "π"], ["ϰ", "κ"], ["ϱ", "ρ"], ["ϲ", "ς"], ["ϵ", "ε"], ["й", "и"], ["ѐ", "е"], ["ё", "е"], ["ѓ", "г"], ["ї", "і"], ["ќ", "к"], ["ѝ", "и"], ["ў", "у"], ["ѷ", "ѵ"], ["ӂ", "ж"], ["ӑ", "а"], ["ӓ", "а"], ["ӗ", "е"], ["ӛ", "ә"], ["ӝ", "ж"], ["ӟ", "з"], ["ӣ", "и"], ["ӥ", "и"], ["ӧ", "о"], ["ӫ", "ө"], ["ӭ", "э"], ["ӯ", "у"], ["ӱ", "у"], ["ӳ", "у"], ["ӵ", "ч"] + +// Term Separators + +// ["'", ""], // it's -> its +// ["´", ""], +// ["`", ""], +// ["’", ""], +// ["ʼ", ""], + +// Numeric-Separators Chars Removal + +// [",", ""], +// [".", ""] + +// Non-Whitespace Separators + +// already was split by default via p{P} +// ["-", " "], +// [":", " "], +// ["_", " "], +// ["|", " "], +// ["/", " "], +// ["\\", " "] +]; \ No newline at end of file diff --git a/dist/module/db/clickhouse/index.js b/dist/module/db/clickhouse/index.js index 51bfa95..8be0f35 100644 --- a/dist/module/db/clickhouse/index.js +++ b/dist/module/db/clickhouse/index.js @@ -57,7 +57,7 @@ let DB; */ export default function ClickhouseDB(name, config = {}) { - if (!(this instanceof ClickhouseDB)) { + if (!this) { return new ClickhouseDB(name, config); } if ("object" == typeof name) { @@ -83,7 +83,7 @@ export default function ClickhouseDB(name, config = {}) { } ClickhouseDB.prototype.mount = function (flexsearch) { - if (flexsearch instanceof Document) { + if (flexsearch.constructor === Document) { return flexsearch.mount(this); } defaults.resolution = Math.max(flexsearch.resolution, flexsearch.resolution_ctx); diff --git a/dist/module/db/indexeddb/index.js b/dist/module/db/indexeddb/index.js index e7c6fee..bfe60e5 100644 --- a/dist/module/db/indexeddb/index.js +++ b/dist/module/db/indexeddb/index.js @@ -20,7 +20,7 @@ function sanitize(str) { */ export default function IdxDB(name, config = {}) { - if (!(this instanceof IdxDB)) { + if (!this) { return new IdxDB(name, config); } if ("object" == typeof name) { @@ -38,7 +38,7 @@ export default function IdxDB(name, config = {}) { } IdxDB.prototype.mount = function (flexsearch) { - if (flexsearch instanceof Document) { + if (flexsearch.constructor === Document) { return flexsearch.mount(this); } flexsearch.db = this; diff --git a/dist/module/db/mongo/index.js b/dist/module/db/mongo/index.js index 0475df2..f385c06 100644 --- a/dist/module/db/mongo/index.js +++ b/dist/module/db/mongo/index.js @@ -26,7 +26,7 @@ let CLIENT, */ export default function MongoDB(name, config = {}) { - if (!(this instanceof MongoDB)) { + if (!this) { return new MongoDB(name, config); } if ("object" == typeof name) { @@ -51,7 +51,7 @@ export default function MongoDB(name, config = {}) { // }; MongoDB.prototype.mount = function (flexsearch) { - if (flexsearch instanceof Document) { + if (flexsearch.constructor === Document) { return flexsearch.mount(this); } flexsearch.db = this; diff --git a/dist/module/db/postgres/index.js b/dist/module/db/postgres/index.js index 33cb63a..7328891 100644 --- a/dist/module/db/postgres/index.js +++ b/dist/module/db/postgres/index.js @@ -51,7 +51,7 @@ let DB, TRX; */ export default function PostgresDB(name, config = {}) { - if (!(this instanceof PostgresDB)) { + if (!this) { return new PostgresDB(name, config); } if ("object" == typeof name) { @@ -72,7 +72,7 @@ export default function PostgresDB(name, config = {}) { } PostgresDB.prototype.mount = function (flexsearch) { - if (flexsearch instanceof Document) { + if (flexsearch.constructor === Document) { return flexsearch.mount(this); } flexsearch.db = this; diff --git a/dist/module/db/redis/index.js b/dist/module/db/redis/index.js index dbf7c83..ce54eb6 100644 --- a/dist/module/db/redis/index.js +++ b/dist/module/db/redis/index.js @@ -26,7 +26,7 @@ let DB, TRX; */ export default function RedisDB(name, config = {}) { - if (!(this instanceof RedisDB)) { + if (!this) { return new RedisDB(name, config); } if ("object" == typeof name) { @@ -52,7 +52,7 @@ export default function RedisDB(name, config = {}) { // }; RedisDB.prototype.mount = function (flexsearch) { - if (flexsearch instanceof Document) { + if (flexsearch.constructor === Document) { return flexsearch.mount(this); } flexsearch.db = this; diff --git a/dist/module/db/sqlite/index.js b/dist/module/db/sqlite/index.js index c71a6dd..2d02eaa 100644 --- a/dist/module/db/sqlite/index.js +++ b/dist/module/db/sqlite/index.js @@ -48,7 +48,7 @@ const TRX = Object.create(null), */ export default function SqliteDB(name, config = {}) { - if (!(this instanceof SqliteDB)) { + if (!this) { return new SqliteDB(name, config); } if ("object" == typeof name) { @@ -68,7 +68,7 @@ export default function SqliteDB(name, config = {}) { } SqliteDB.prototype.mount = function (flexsearch) { - if (flexsearch instanceof Document) { + if (flexsearch.constructor === Document) { return flexsearch.mount(this); } flexsearch.db = this; diff --git a/dist/module/document.js b/dist/module/document.js index 605ef9b..d16c71f 100644 --- a/dist/module/document.js +++ b/dist/module/document.js @@ -6,7 +6,7 @@ * https://github.com/nextapps-de/flexsearch */ -import { DocumentOptions } from "./type.js"; +import { DocumentOptions, DocumentDescriptor, DocumentIndexOptions, StoreOptions } from "./type.js"; import Index from "./index.js"; import WorkerIndex from "./worker/index.js"; import Cache, { searchCache } from "./cache.js"; @@ -19,15 +19,16 @@ import "./document/search.js"; /** * @constructor - * @param {DocumentOptions=} options + * @param {!DocumentOptions} options */ export default function Document(options) { - if (!(this instanceof Document)) { + if (!this) { return new Document(options); } + /** @type DocumentDescriptor */ const document = options.document || options.doc || options; let tmp, keystore; @@ -39,8 +40,8 @@ export default function Document(options) { keystore = options.keystore || 0; keystore && (this.keystore = keystore); this.fastupdate = !!options.fastupdate; - this.reg = this.fastupdate ? keystore && /* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */ /* skip deletion */!0 - /*await rows.hasNext()*/ /*await rows.hasNext()*/ /*await rows.hasNext()*/ ? new KeystoreMap(keystore) : new Map() : keystore && !0 ? new KeystoreSet(keystore) : new Set(); + this.reg = this.fastupdate ? keystore && /* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ + /* skip_update: */ /* skip deletion */!0 /*await rows.hasNext()*/ /*await rows.hasNext()*/ /*await rows.hasNext()*/ ? new KeystoreMap(keystore) : new Map() : keystore && !0 ? new KeystoreSet(keystore) : new Set(); // todo support custom filter function this.storetree = (tmp = document.store || null) && !0 !== tmp && []; @@ -192,12 +193,11 @@ function parse_descriptor(options, document) { key = key.field; } - opt = is_object(opt) ? Object.assign({}, options, opt) : options; + opt = /** @type DocumentIndexOptions */is_object(opt) ? Object.assign({}, options, opt) : options; if (this.worker) { const worker = new WorkerIndex(opt); - index.set(key, worker); - if (!worker.worker) { + index.set(key, worker);if (!worker.worker) { // fallback when not supported this.worker = !1; } @@ -229,7 +229,7 @@ function parse_descriptor(options, document) { if (is_string(stores)) stores = [stores]; for (let i = 0, store, field; i < stores.length; i++) { - store = stores[i]; + store = /** @type Array */stores[i]; field = store.field || store; if (store.custom) { this.storetree[i] = store.custom; diff --git a/dist/module/document/add.js b/dist/module/document/add.js index d59ac77..17cce81 100644 --- a/dist/module/document/add.js +++ b/dist/module/document/add.js @@ -34,14 +34,15 @@ Document.prototype.add = function (id, content, _append) { if ("function" == typeof tree) { const tmp = tree(content); if (tmp) { - index.add(id, tmp, /* suggest */ /* append: */!1, /* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */!0 /*await rows.hasNext()*/ /*await rows.hasNext()*/ /*await rows.hasNext()*/); + index.add(id, tmp, /* suggest */ /* append: */!1, /* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */!0 /*await rows.hasNext()*/ + /*await rows.hasNext()*/ /*await rows.hasNext()*/); } } else { const filter = tree._filter; if (filter && !filter(content)) { continue; } - if (tree instanceof String) { + if (tree.constructor === String) { tree = ["" + tree]; } else if (is_string(tree)) { tree = [tree]; @@ -70,7 +71,7 @@ Document.prototype.add = function (id, content, _append) { if (filter && !filter(content)) { continue; } - if (tree instanceof String) { + if (tree.constructor === String) { tree = "" + tree; } tags = parse_simple(content, tree); @@ -145,7 +146,7 @@ Document.prototype.add = function (id, content, _append) { custom = tree(content); if (!custom) continue; tree = [tree._field]; - } else if (is_string(tree) || tree instanceof String) { + } else if (is_string(tree) || tree.constructor === String) { payload[tree] = content[tree]; continue; } diff --git a/dist/module/encoder.js b/dist/module/encoder.js index 8be0bee..510d597 100644 --- a/dist/module/encoder.js +++ b/dist/module/encoder.js @@ -1,5 +1,7 @@ import { parse_option } from "./common.js"; +import normalize_polyfill from "./charset/normalize.js"; +import { EncoderOptions } from "./type.js"; /* @@ -42,51 +44,24 @@ const whitespace = /[^\p{L}\p{N}]+/u, numeric_split_length = /(\d{3})/g, numeric_split_prev_char = /(\D)(\d{3})/g, numeric_split_next_char = /(\d{3})(\D)/g, - normalize = /[\u0300-\u036f]/g, - normalize_mapper = !normalize && [ - -// Charset Normalization - -["ª", "a"], ["²", "2"], ["³", "3"], ["¹", "1"], ["º", "o"], ["¼", "1⁄4"], ["½", "1⁄2"], ["¾", "3⁄4"], ["à", "a"], ["á", "a"], ["â", "a"], ["ã", "a"], ["ä", "a"], ["å", "a"], ["ç", "c"], ["è", "e"], ["é", "e"], ["ê", "e"], ["ë", "e"], ["ì", "i"], ["í", "i"], ["î", "i"], ["ï", "i"], ["ñ", "n"], ["ò", "o"], ["ó", "o"], ["ô", "o"], ["õ", "o"], ["ö", "o"], ["ù", "u"], ["ú", "u"], ["û", "u"], ["ü", "u"], ["ý", "y"], ["ÿ", "y"], ["ā", "a"], ["ă", "a"], ["ą", "a"], ["ć", "c"], ["ĉ", "c"], ["ċ", "c"], ["č", "c"], ["ď", "d"], ["ē", "e"], ["ĕ", "e"], ["ė", "e"], ["ę", "e"], ["ě", "e"], ["ĝ", "g"], ["ğ", "g"], ["ġ", "g"], ["ģ", "g"], ["ĥ", "h"], ["ĩ", "i"], ["ī", "i"], ["ĭ", "i"], ["į", "i"], ["ij", "ij"], ["ĵ", "j"], ["ķ", "k"], ["ĺ", "l"], ["ļ", "l"], ["ľ", "l"], ["ŀ", "l"], ["ń", "n"], ["ņ", "n"], ["ň", "n"], ["ʼn", "n"], ["ō", "o"], ["ŏ", "o"], ["ő", "o"], ["ŕ", "r"], ["ŗ", "r"], ["ř", "r"], ["ś", "s"], ["ŝ", "s"], ["ş", "s"], ["š", "s"], ["ţ", "t"], ["ť", "t"], ["ũ", "u"], ["ū", "u"], ["ŭ", "u"], ["ů", "u"], ["ű", "u"], ["ų", "u"], ["ŵ", "w"], ["ŷ", "y"], ["ź", "z"], ["ż", "z"], ["ž", "z"], ["ſ", "s"], ["ơ", "o"], ["ư", "u"], ["dž", "dz"], ["lj", "lj"], ["nj", "nj"], ["ǎ", "a"], ["ǐ", "i"], ["ǒ", "o"], ["ǔ", "u"], ["ǖ", "u"], ["ǘ", "u"], ["ǚ", "u"], ["ǜ", "u"], ["ǟ", "a"], ["ǡ", "a"], ["ǣ", "ae"], ["æ", "ae"], ["ǽ", "ae"], ["ǧ", "g"], ["ǩ", "k"], ["ǫ", "o"], ["ǭ", "o"], ["ǯ", "ʒ"], ["ǰ", "j"], ["dz", "dz"], ["ǵ", "g"], ["ǹ", "n"], ["ǻ", "a"], ["ǿ", "ø"], ["ȁ", "a"], ["ȃ", "a"], ["ȅ", "e"], ["ȇ", "e"], ["ȉ", "i"], ["ȋ", "i"], ["ȍ", "o"], ["ȏ", "o"], ["ȑ", "r"], ["ȓ", "r"], ["ȕ", "u"], ["ȗ", "u"], ["ș", "s"], ["ț", "t"], ["ȟ", "h"], ["ȧ", "a"], ["ȩ", "e"], ["ȫ", "o"], ["ȭ", "o"], ["ȯ", "o"], ["ȱ", "o"], ["ȳ", "y"], ["ʰ", "h"], ["ʱ", "h"], ["ɦ", "h"], ["ʲ", "j"], ["ʳ", "r"], ["ʴ", "ɹ"], ["ʵ", "ɻ"], ["ʶ", "ʁ"], ["ʷ", "w"], ["ʸ", "y"], ["ˠ", "ɣ"], ["ˡ", "l"], ["ˢ", "s"], ["ˣ", "x"], ["ˤ", "ʕ"], ["ΐ", "ι"], ["ά", "α"], ["έ", "ε"], ["ή", "η"], ["ί", "ι"], ["ΰ", "υ"], ["ϊ", "ι"], ["ϋ", "υ"], ["ό", "ο"], ["ύ", "υ"], ["ώ", "ω"], ["ϐ", "β"], ["ϑ", "θ"], ["ϒ", "Υ"], ["ϓ", "Υ"], ["ϔ", "Υ"], ["ϕ", "φ"], ["ϖ", "π"], ["ϰ", "κ"], ["ϱ", "ρ"], ["ϲ", "ς"], ["ϵ", "ε"], ["й", "и"], ["ѐ", "е"], ["ё", "е"], ["ѓ", "г"], ["ї", "і"], ["ќ", "к"], ["ѝ", "и"], ["ў", "у"], ["ѷ", "ѵ"], ["ӂ", "ж"], ["ӑ", "а"], ["ӓ", "а"], ["ӗ", "е"], ["ӛ", "ә"], ["ӝ", "ж"], ["ӟ", "з"], ["ӣ", "и"], ["ӥ", "и"], ["ӧ", "о"], ["ӫ", "ө"], ["ӭ", "э"], ["ӯ", "у"], ["ӱ", "у"], ["ӳ", "у"], ["ӵ", "ч"] - -// Term Separators - -// ["'", ""], // it's -> its -// ["´", ""], -// ["`", ""], -// ["’", ""], -// ["ʼ", ""], - -// Numeric-Separators Chars Removal - -// [",", ""], -// [".", ""] - -// Non-Whitespace Separators - -// already was split by default via p{P} -// ["-", " "], -// [":", " "], -// ["_", " "], -// ["|", " "], -// ["/", " "], -// ["\\", " "] -]; // /[\p{Z}\p{S}\p{P}\p{C}]+/u; + normalize = /[\u0300-\u036f]/g; // /[\p{Z}\p{S}\p{P}\p{C}]+/u; //const numeric_split = /(\d{3})/g; //.replace(/(\d{3})/g, "$1 ") //.replace(/([^\d])([\d])/g, "$1 $2") //.replace(/([\d])([^\d])/g, "$1 $2") + // '´`’ʼ., +//const normalize_mapper = SUPPORT_CHARSET && !normalize && normalize_polyfill; /** - * @param options + * @param {EncoderOptions=} options * @constructor */ -export default function Encoder(options = {}) { +export default function Encoder() { - if (!(this instanceof Encoder)) { + if (!this) { return new Encoder(...arguments); } @@ -95,25 +70,11 @@ export default function Encoder(options = {}) { } } +/** + * @param {!EncoderOptions} options + */ Encoder.prototype.assign = function (options) { - // if(options.assign){ - // //options = Object.assign({}, options.assign, options); - // this.assign(options.assign); - // } - - // let tmp = options["normalize"]; - // if(typeof tmp === "function"){ - // const old = this.normalize; - // if(typeof old === "function"){ - // const current = tmp; - // tmp = function(){ - // old(); - // current(); - // } - // } - // } - /** * pre-processing string input * @type {Function|boolean} @@ -183,16 +144,15 @@ Encoder.prototype.assign = function (options) { */ this.finalize = /** @type {Function|null} */parse_option(options.finalize, null, this.finalize); - // move the normalization fallback to the mapper - if (normalize_mapper) { + // assign the normalization fallback to the mapper + if (!normalize) { this.mapper = new Map( - /** @type {Array>} */normalize_mapper); + /** @type {Array>} */normalize_polyfill); } // options - this.rtl = options.rtl || - /* suggest */ /* append: */ /* enrich */!1; + this.rtl = options.rtl || /* suggest */ /* append: */ /* enrich */!1; this.dedupe = parse_option(options.dedupe, !0, this.dedupe); this.filter = parse_option((tmp = options.filter) && new Set(tmp), null, this.filter); this.matcher = parse_option((tmp = options.matcher) && new Map(tmp), null, this.matcher); diff --git a/dist/module/index.js b/dist/module/index.js index 4604438..23c01db 100644 --- a/dist/module/index.js +++ b/dist/module/index.js @@ -6,13 +6,14 @@ * https://github.com/nextapps-de/flexsearch */ -import { IndexOptions } from "./type.js"; +import { IndexOptions, ContextOptions } from "./type.js"; import Encoder from "./encoder.js"; import Cache, { searchCache } from "./cache.js"; +import Charset from "./charset.js"; import { KeystoreMap, KeystoreSet } from "./keystore.js"; import { is_array, is_string } from "./common.js"; import { exportIndex, importIndex } from "./serialize.js"; -import default_encoder from "./lang/latin/default.js"; +import default_encoder from "./charset/latin/default.js"; import apply_preset from "./preset.js"; import apply_async from "./async.js"; import tick from "./profiler.js"; @@ -22,21 +23,23 @@ import "./index/remove.js"; /** * @constructor - * @param {IndexOptions|string=} options + * @param {IndexOptions|string=} options Options or preset as string * @param {Map|Set|KeystoreSet|KeystoreMap=} _register */ export default function Index(options, _register) { - if (!(this instanceof Index)) { + if (!this) { return new Index(options); } options = options ? apply_preset(options) : {}; + /** @type ContextOptions */ const context = options.context || {}, encoder = options.encode || options.encoder || default_encoder; + /** @type Encoder */ this.encoder = encoder.encode ? encoder : "object" == typeof encoder ? new Encoder(encoder) : { encode: encoder }; this.compress = options.compress || options.compression || /* suggest */ /* append: */ /* enrich */!1; diff --git a/dist/module/index/search.js b/dist/module/index/search.js index 44b4cb1..341892f 100644 --- a/dist/module/index/search.js +++ b/dist/module/index/search.js @@ -48,8 +48,7 @@ Index.prototype.search = function (query, limit, options) { offset = options.offset || 0; context = options.context; suggest = options.suggest; - resolve = global_resolve && - /* suggest */ /* append: */ /* enrich */!1 !== options.resolve; + resolve = global_resolve && /* suggest */ /* append: */ /* enrich */!1 !== options.resolve; resolve || (global_resolve = 0); enrich = resolve && options.enrich; tag = this.db && options.tag; diff --git a/dist/module/intersect.js b/dist/module/intersect.js index 8b5575a..6db61cf 100644 --- a/dist/module/intersect.js +++ b/dist/module/intersect.js @@ -103,7 +103,8 @@ export function intersect(arrays, limit, offset, suggest) { check_new[id] = 1; } - found = /* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */ /* skip deletion */!0 /*await rows.hasNext()*/ /*await rows.hasNext()*/ /*await rows.hasNext()*/; + found = /* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */ /* skip deletion */!0 /*await rows.hasNext()*/ + /*await rows.hasNext()*/ /*await rows.hasNext()*/; } if (suggest) { diff --git a/dist/module/keystore.js b/dist/module/keystore.js index 3f7b6d7..9f222ac 100644 --- a/dist/module/keystore.js +++ b/dist/module/keystore.js @@ -7,7 +7,7 @@ import { create_object } from "./common.js"; export function KeystoreObj(bitlength = 8) { - if (!(this instanceof KeystoreObj)) { + if (!this) { return new KeystoreObj(bitlength); } @@ -37,10 +37,10 @@ export function KeystoreObj(bitlength = 8) { target.keys.push(address); } obj[key] = value; - return (/* tag? */ /* stringify */ - /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */ /* skip deletion */ + return (/* tag? */ /* stringify */ /* stringify */ /* skip update: */ /* append: */ /* skip update: */ /* skip_update: */ /* skip deletion */ // splice: - !0 /*await rows.hasNext()*/ /*await rows.hasNext()*/ /*await rows.hasNext()*/ + !0 /*await rows.hasNext()*/ + /*await rows.hasNext()*/ /*await rows.hasNext()*/ ); }, delete(target, key) { @@ -94,7 +94,7 @@ function _slice(self, start, end, splice) { export function KeystoreArray(arr) { - if (!(this instanceof KeystoreArray)) { + if (!this) { return new KeystoreArray(arr); } @@ -196,7 +196,7 @@ KeystoreArray.prototype.push = function () {}; export function KeystoreMap(bitlength = 8) { - if (!(this instanceof KeystoreMap)) { + if (!this) { return new KeystoreMap(bitlength); } @@ -241,7 +241,7 @@ KeystoreMap.prototype.set = function (key, value) { export function KeystoreSet(bitlength = 8) { - if (!(this instanceof KeystoreSet)) { + if (!this) { return new KeystoreSet(bitlength); } @@ -372,6 +372,7 @@ function lcg(str) { function lcg64(str) { let range = BigInt(2) ** /** @type {!BigInt} */this.bit - BigInt(1), type = typeof str; + if ("bigint" == type) { return (/** @type {!BigInt} */str & range ); diff --git a/dist/module/lang/de.js b/dist/module/lang/de.js index 717c431..55eb8ef 100644 --- a/dist/module/lang/de.js +++ b/dist/module/lang/de.js @@ -1,3 +1,5 @@ +import { EncoderOptions } from "../type.js"; + /** * Filter are also known as "stopwords", they completely filter out words from being indexed. * Source: http://www.ranks.nl/stopwords @@ -22,18 +24,20 @@ export const stemmer = new Map([["niss", ""], ["isch", ""], ["lich", ""], ["heit * Object Definition: the key represents the target term, the value contains the search string which should be replaced (could also be an array of multiple terms). * @type {Map} */ -const map = new Map([["_", " "], ["ä", "ae"], ["ö", "oe"], ["ü", "ue"], ["ß", "ss"], ["&", " und "], ["€", " EUR "]]); - -export default { - normalize: function (str) { - return str.toLowerCase(); - }, +const map = new Map([["_", " "], ["ä", "ae"], ["ö", "oe"], ["ü", "ue"], ["ß", "ss"], ["&", " und "], ["€", " EUR "]]), + options = { prepare: function (str) { // normalization if (/[_äöüß&€]/.test(str)) str = str.replace(/[_äöüß&€]/g, match => map.get(match)); // street names - return str.replace(/str\b/g, "strasse").replace(/(?!\b)strasse\b/g, " strasse").replace(/\bst\b/g, "sankt"); + return str.replace(/str\b/g, "strasse").replace(/(?!\b)strasse\b/g, " strasse"); }, filter: filter, stemmer: stemmer -}; \ No newline at end of file +}; + +/** + * @type EncoderOptions + */ + +export default options; \ No newline at end of file diff --git a/dist/module/lang/en.js b/dist/module/lang/en.js index 65b6220..c651a81 100644 --- a/dist/module/lang/en.js +++ b/dist/module/lang/en.js @@ -1,9 +1,11 @@ +import { EncoderOptions } from "../type.js"; + +// todo filter out minlength + /** * http://www.ranks.nl/stopwords * @type {Set} */ - -// todo filter out minlength export const filter = new Set(["a", "about", "above", "after", "again", "against", "all", "also", "am", "an", "and", "any", "are", "arent", "as", "at", "back", "be", "because", "been", "before", "being", "below", "between", "both", "but", "by", "can", "cannot", "cant", "come", "could", "couldnt", //"day", "did", "didnt", "do", "does", "doesnt", "doing", "dont", "down", "during", "each", "even", "few", @@ -21,15 +23,11 @@ export const filter = new Set(["a", "about", "above", "after", "again", "against "ya", "you", "youd", "youll", "your", "youre", "yours", "yourself", "yourselves", "youve"]); /** - * @type {Object} + * @type {Map} */ export const stemmer = new Map([["ational", ""], ["iveness", ""], ["fulness", ""], ["ousness", ""], ["ization", ""], ["tional", ""], ["biliti", ""], ["icate", ""], ["ative", ""], ["alize", ""], ["iciti", ""], ["entli", ""], ["ousli", ""], ["alism", ""], ["ation", ""], ["aliti", ""], ["iviti", ""], ["ement", ""], ["izer", ""], ["able", ""], ["alli", ""], ["ator", ""], ["logi", ""], ["ical", ""], ["ance", ""], ["ence", ""], ["ness", ""], ["ble", ""], ["ment", ""], ["eli", ""], ["bli", ""], ["ful", ""], ["ant", ""], ["ent", ""], ["ism", ""], ["ate", ""], ["iti", ""], ["ous", ""], ["ive", ""], ["ize", ""], ["ing", ""], ["ion", ""], ["al", ""], ["ou", ""], ["er", ""], ["ic", ""], ["ly", ""]]); -// export const replacer = new Map([ -// ["&", " and "], -// ]); - /* he’s (= he is / he has) she’s (= she is / she has) @@ -58,71 +56,18 @@ export const stemmer = new Map([["ational", ""], ["iveness", ""], ["fulness", "" // ["'d$", " would had"], // ]); -// const pairs = [ -// /´`’ʼ/, /´`’ʼ/g, "'", -// /_/, /_+/g, " ", -// /&/, /&/g, " and ", -// /\bi'm\b/, /\bi'm\b/g, "i am", -// /\b(can't|cannot)\b/, /\b(can't|cannot)\b/g, "can not", -// /\bwon't\b/, /\bwon't\b/g, "will not", -// /[a-z]n't\b/, /[a-z]n't\b/g, "$1 not", -// /[a-z]'s\b/, /([a-z])'s\b/g, "$1 is has", -// /[a-z]'ll\b/, /[a-z]'ll\b/g, "$1 will", -// /[a-z]'re\b/, /[a-z]'re\b/g, "$1 are", -// /[a-z]'ve\b/, /[a-z]'ve\b/g, "$1 have", -// /[a-z]'d\b/, /[a-z]'d\b/g, "$1 is has" -// ]; - -// const map = new Map([ -// ["´", "'"], -// ["`", "'"], -// ["’", "'"], -// ["ʼ", "'"], -// ["_", " "], -// ["&", " and "] -// ]); - -export default { +/** + * @type EncoderOptions + */ +const options = { prepare: function (str) { - // if(/[´`’ʼ_&]/.test(str)) - // str = str.replace(/[´`’ʼ_&]/g, match => map.get(match)); - - // if(/´`’ʼ/.test(str)) - // str = str.replace(/´`’ʼ/g, "'"); - // if(/_/.test(str)) - // str = str.replace(/_+/g, " "); - // if(/&/.test(str)) - // str = str.replace(/&/g, " and "); - - // if(/\bi'm\b/.test(str)) - // str = str.replace(/\bi'm\b/g, "i am"); - // if(/\b(can't|cannot)\b/.test(str)) - // str = str.replace(/\b(can't|cannot)\b/g, "can not"); - // if(/\bwon't\b/.test(str)) - // str = str.replace(/\bwon't\b/g, "will not"); - // if(/[a-z]n't\b/.test(str)) - // str = str.replace(/([a-z])n't\b/g, "$1 not"); - // if(/[a-z]'s\b/.test(str)) - // str = str.replace(/([a-z])'s\b/g, "$1 is has"); - // if(/[a-z]'ll\b/.test(str)) - // str = str.replace(/([a-z])'ll\b/g, "$1 will"); - // if(/[a-z]'re\b/.test(str)) - // str = str.replace(/([a-z])'re\b/g, "$1 are"); - // if(/[a-z]'ve\b/.test(str)) - // str = str.replace(/([a-z])'ve\b/g, "$1 have"); - // if(/[a-z]'d\b/.test(str)) - // str = str.replace(/([a-z])'d\b/g, "$1 would had"); - // return str; - - return str //.replace(/[´`’ʼ_&]/g, match => map.get(match)) + return str // normalization - .replace(/´`’ʼ/g, "'").replace(/_+/g, " ").replace(/&/g, " and ") - //.replace(/([0-9 ]|^)\$([0-9 ]|$)/g, "$1 USD $2") - //.replace(/([0-9 ]|^)£([0-9 ]|$)/g, "$1 GBP $2") - .replace(/\$/g, " USD ").replace(/£/g, " GBP ") + .replace(/´`’ʼ/g, "'").replace(/_+/g, " ").replace(/&/g, " and ").replace(/\$/g, " USD ").replace(/£/g, " GBP ") // explode short forms .replace(/([a-z])'s\b/g, "$1 is").replace(/\bi'm\b/g, "i am").replace(/\b(can't|cannot)\b/g, "can not").replace(/\bwon't\b/g, "will not").replace(/([a-z])n't\b/g, "$1 not").replace(/([a-z])'ll\b/g, "$1 will").replace(/([a-z])'re\b/g, "$1 are").replace(/([a-z])'ve\b/g, "$1 have").replace(/([a-z])'d\b/g, "$1 would"); }, filter: filter, stemmer: stemmer -}; \ No newline at end of file +}; +export default options; \ No newline at end of file diff --git a/dist/module/lang/fr.js b/dist/module/lang/fr.js index 573652f..51050f7 100644 --- a/dist/module/lang/fr.js +++ b/dist/module/lang/fr.js @@ -1,3 +1,5 @@ +import { EncoderOptions } from "../type.js"; + /** * http://www.ranks.nl/stopwords * http://snowball.tartarus.org/algorithms/french/stop.txt @@ -12,10 +14,14 @@ export const filter = new Set(["au", "aux", "avec", "ce", "ces", "dans", "de", " export const stemmer = new Map([["lement", ""], ["ient", ""], ["nera", ""], ["ment", ""], ["ais", ""], ["ait", ""], ["ant", ""], ["ent", ""], ["iez", ""], ["ion", ""], ["nez", ""], ["ai", ""], ["es", ""], ["er", ""], ["ez", ""], ["le", ""], ["na", ""], ["ne", ""], ["a", ""], ["e", ""]]); -export default { +/** + * @type EncoderOptions + */ +const options = { prepare: function (str) { return str.replace(/´`’ʼ/g, "'").replace(/_+/g, " ").replace(/&/g, " et ").replace(/€/g, " EUR ").replace(/\bl'([^\b])/g, "la le $1").replace(/\bt'([^\b])/g, "ta te $1").replace(/\bc'([^\b])/g, "ca ce $1").replace(/\bd'([^\b])/g, "da de $1").replace(/\bj'([^\b])/g, "ja je $1").replace(/\bn'([^\b])/g, "na ne $1").replace(/\bm'([^\b])/g, "ma me $1").replace(/\bs'([^\b])/g, "sa se $1").replace(/\bau\b/g, "a le").replace(/\baux\b/g, "a les").replace(/\bdu\b/g, "de le").replace(/\bdes\b/g, "de les"); }, filter: filter, stemmer: stemmer -}; \ No newline at end of file +}; +export default options; \ No newline at end of file diff --git a/dist/module/lang/latin/exact.js b/dist/module/lang/latin/exact.js deleted file mode 100644 index 1ef1dae..0000000 --- a/dist/module/lang/latin/exact.js +++ /dev/null @@ -1,4 +0,0 @@ -export default { - normalize: /* suggest */ /* append: */ /* enrich */!1, - dedupe: !1 -}; \ No newline at end of file diff --git a/dist/module/lang/us.js b/dist/module/lang/us.js deleted file mode 100644 index 7b1ba3a..0000000 --- a/dist/module/lang/us.js +++ /dev/null @@ -1,100 +0,0 @@ -/** - * http://www.ranks.nl/stopwords - * @type {Array} - */ - -export const filter = ["a", "about", "above", "after", "again", "against", "all", "also", "am", "an", "and", "any", "are", "aren't", "as", "at", -//"back", -"be", "because", "been", "before", "being", "below", -//"between", -"both", "but", "by", "can", "cannot", "can't", "come", "could", "couldn't", -//"day", -"did", "didn't", "do", "does", "doesn't", "doing", "dont", "down", "during", "each", "even", "few", "first", "for", "from", "further", "get", -//"give", -"go", -//"good", -"had", "hadn't", "has", "hasn't", "have", "haven't", "having", "he", "hed", -//"hell", -"her", "here", "here's", "hers", "herself", "hes", "him", "himself", "his", "how", "how's", "i", "id", "if", "ill", "im", "in", "into", "is", "isn't", "it", "it's", "itself", "i've", "just", "know", "let's", "like", -//"look", -"make", "me", "more", "most", "mustn't", "my", "myself", "new", "no", "nor", "not", "now", "of", "off", "on", "once", -//"one", -"only", "or", "other", "ought", "our", "our's", "ourselves", "out", "over", "own", -//"people", -"same", "say", "see", "shan't", "she", "she'd", "shell", "shes", "should", "shouldn't", "so", "some", "such", -//"take", -"than", "that", "that's", "the", "their", "theirs", "them", "themselves", "then", "there", "there's", "these", "they", "they'd", "they'll", "they're", "they've", -//"think", -"this", "those", "through", "time", "to", "too", -//"two", -//"under", -"until", "up", "us", -//"use", -"very", "want", "was", "wasn't", "way", "we", "wed", "well", "were", "weren't", "we've", "what", "what's", "when", "when's", "where", "where's", "which", "while", "who", "whom", "who's", "why", "why's", "will", "with", "won't", -//"work", -"would", "wouldn't", -//"year", -"you", "you'd", "you'll", "your", "you're", "your's", "yourself", "yourselves", "you've"]; - -/** - * @type {Object} - */ - -export const stemmer = { - - ational: "ate", - iveness: "ive", - fulness: "ful", - ousness: "ous", - ization: "ize", - tional: "tion", - biliti: "ble", - icate: "ic", - ative: "", - alize: "al", - iciti: "ic", - entli: "ent", - ousli: "ous", - alism: "al", - ation: "ate", - aliti: "al", - iviti: "ive", - ement: "", - enci: "ence", - anci: "ance", - izer: "ize", - alli: "al", - ator: "ate", - logi: "log", - ical: "ic", - ance: "", - ence: "", - ness: "", - able: "", - ible: "", - ment: "", - eli: "e", - bli: "ble", - ful: "", - ant: "", - ent: "", - ism: "", - ate: "", - iti: "", - ous: "", - ive: "", - ize: "", - al: "", - ou: "", - er: "", - ic: "" -}; - -export const matcher = {}; - -export default { - - filter: filter, - stemmer: stemmer, - matcher: matcher -}; \ No newline at end of file diff --git a/dist/module/resolve/and.js b/dist/module/resolve/and.js index 9e7994f..80eab54 100644 --- a/dist/module/resolve/and.js +++ b/dist/module/resolve/and.js @@ -13,7 +13,7 @@ Resolver.prototype.and = function () { first_argument = args[0]; - if (first_argument instanceof Promise) { + if (first_argument.then) { return first_argument.then(function () { return self.and.apply(self, args); }); @@ -51,7 +51,7 @@ Resolver.prototype.and = function () { if (query = args[i]) { let result; - if (query instanceof Resolver) { + if (query.constructor === Resolver) { result = query.result; } else if (query.constructor === Array) { result = query; @@ -74,7 +74,7 @@ Resolver.prototype.and = function () { final[i] = result; - if (result instanceof Promise) { + if (result.then) { promises.push(result); //{ query, result }; } } diff --git a/dist/module/resolve/not.js b/dist/module/resolve/not.js index 13364fb..2a5e1e4 100644 --- a/dist/module/resolve/not.js +++ b/dist/module/resolve/not.js @@ -11,7 +11,7 @@ Resolver.prototype.not = function () { first_argument = args[0]; - if (first_argument instanceof Promise) { + if (first_argument.then) { return first_argument.then(function () { return self.not.apply(self, args); }); @@ -35,7 +35,7 @@ Resolver.prototype.not = function () { if (query = args[i]) { let result; - if (query instanceof Resolver) { + if (query.constructor === Resolver) { result = query.result; } else if (query.constructor === Array) { result = query; @@ -58,7 +58,7 @@ Resolver.prototype.not = function () { final[i] = result; - if (result instanceof Promise) { + if (result.then) { promises.push(result); //{ query, result }; } } diff --git a/dist/module/resolve/or.js b/dist/module/resolve/or.js index d14981c..1423149 100644 --- a/dist/module/resolve/or.js +++ b/dist/module/resolve/or.js @@ -12,7 +12,7 @@ Resolver.prototype.or = function () { first_argument = args[0]; - if (first_argument instanceof Promise) { + if (first_argument.then) { return first_argument.then(function () { return self.or.apply(self, args); }); @@ -62,7 +62,7 @@ Resolver.prototype.or = function () { if (query = args[i]) { let result; - if (query instanceof Resolver) { + if (query.constructor === Resolver) { result = query.result; } else if (query.constructor === Array) { result = query; @@ -85,7 +85,7 @@ Resolver.prototype.or = function () { final[i] = result; - if (result instanceof Promise) { + if (result.then) { promises.push(result); //{ query, result }; } } diff --git a/dist/module/resolve/xor.js b/dist/module/resolve/xor.js index 1eed9e8..3e76ef8 100644 --- a/dist/module/resolve/xor.js +++ b/dist/module/resolve/xor.js @@ -11,7 +11,7 @@ Resolver.prototype.xor = function () { first_argument = args[0]; - if (first_argument instanceof Promise) { + if (first_argument.then) { return first_argument.then(function () { return self.xor.apply(self, args); }); @@ -36,7 +36,7 @@ Resolver.prototype.xor = function () { if (query = args[i]) { let result; - if (query instanceof Resolver) { + if (query.constructor === Resolver) { result = query.result; } else if (query.constructor === Array) { result = query; @@ -59,7 +59,7 @@ Resolver.prototype.xor = function () { final[i] = result; - if (result instanceof Promise) { + if (result.then) { promises.push(result); //{ query, result }; } } diff --git a/dist/module/resolver.js b/dist/module/resolver.js index fbd1bf7..b6057e3 100644 --- a/dist/module/resolver.js +++ b/dist/module/resolver.js @@ -1,9 +1,5 @@ import default_resolver from "./resolve/default.js"; import { set_resolve } from "./index/search.js"; -// import or from "./resolve/or.js"; -// import and from "./resolve/and.js"; -// import xor from "./resolve/xor.js"; -// import not from "./resolve/not.js"; import "./resolve/or.js"; import "./resolve/and.js"; import "./resolve/xor.js"; @@ -15,16 +11,16 @@ import "./resolve/not.js"; */ export default function Resolver(result) { + if (!this) { + return new Resolver(result); + } if (result && result.index) { result.resolve = /* suggest */ /* append: */ /* enrich */!1; this.index = result.index; return result.index.search(result); } - if (!(this instanceof Resolver)) { - return new Resolver(result); - } - if (result instanceof Resolver) { - // todo remove + if (result.constructor === Resolver) { + // todo test this branch //console.log("Resolver Loopback") return result; } @@ -33,11 +29,6 @@ export default function Resolver(result) { this.boostval = 0; } -// Resolver.prototype.or = or; -// Resolver.prototype.and = and; -// Resolver.prototype.not = not; -// Resolver.prototype.xor = xor; - Resolver.prototype.limit = function (limit) { if (this.result.length) { const final = []; diff --git a/dist/module/type.js b/dist/module/type.js index 0c377c0..d9e2165 100644 --- a/dist/module/type.js +++ b/dist/module/type.js @@ -23,6 +23,29 @@ import StorageInterface from "./db/interface.js"; */ export let IndexOptions; +/** + * @typedef DocumentIndexOptions {{ + * preset: string|undefined, + * context: (IndexOptions|undefined), + * encoder: Encoder|Function|Object|undefined, + * encode: Function|undefined, + * resolution: [number=9], + * tokenize: [string="strict"], + * fastupdate: [boolean:false], + * score: Function]|undefined, + * keystore: [number=0], + * rtl: [boolean=false], + * cache: [number=null], + * db: StorageInterface|undefined, + * config: string|undefined, + * + * field: string, + * filter: Function|undefined, + * custom: Function|undefined + * }} + */ +export let DocumentIndexOptions; + /** * @typedef DocumentOptions {{ * context: (IndexOptions|undefined), @@ -57,33 +80,11 @@ export let ContextOptions; * field: FieldOptions|Array|undefined, * index: FieldOptions|Array|undefined, * tag: TagOptions|Array|undefined, - * store: StoreOptions|Array|undefined, - * config: string|undefined + * store: StoreOptions|Array|undefined * }} */ export let DocumentDescriptor; -/** - * @typedef FieldOptions {{ - * field: string, - * filter: Function|undefined, - * custom: Function|undefined, - * context: (IndexOptions|undefined), - * encoder: Encoder|Function|Object|undefined, - * encode: Function|undefined, - * resolution: [number=9], - * tokenize: [string="strict"], - * fastupdate: [boolean:false], - * score: Function]|undefined, - * keystore: [number=0], - * rtl: [boolean=false], - * cache: [number=null], - * db: StorageInterface|undefined, - * config: string|undefined - * }} - */ -export let FieldOptions; - /** * @typedef TagOptions {{ * field: string, @@ -102,8 +103,6 @@ export let TagOptions; * field: string, * filter: Function|undefined, * custom: Function|undefined, - * keystore: [number=0], - * db: StorageInterface|undefined, * config: string|undefined * }} */ @@ -118,10 +117,10 @@ export let StoreOptions; * suggest: [boolean=false], * resolve: [boolean=true], * enrich: [boolean=false], - * tag: Array|undefined * }} */ export let SearchOptions; +// tag: Array|undefined /** * @typedef DocumentSearchOptions {{ @@ -131,14 +130,44 @@ export let SearchOptions; * context: boolean|undefined, * suggest: [boolean=false], * enrich: [boolean=false], - * tag: Array|undefined, - * field: FieldOptions|Array|undefined, - * index: FieldOptions|Array|undefined, + * tag: Object|Array|undefined, + * field: Array|undefined, + * index: Array|undefined, * pluck: boolean|undefined, * merge: [boolean=false] * }} */ export let DocumentSearchOptions; +/** + * @typedef EncoderOptions {{ + * rtl: boolean=false, + * dedupe: boolean=true, + * split: string|undefined, + * include: EncoderSplitOptions|undefined, + * exclude: EncoderSplitOptions|undefined, + * prepare: function(string):string|undefined, + * finalize: function(Array<>string>):Array|undefined, + * filter: Set|undefined, + * matcher: Map|undefined, + * mapper: Map|undefined, + * stemmer: Map|undefined, + * replacer: Array|undefined, + * minlength: number=1, + * maxlength: number|undefined, + * cache: boolean=true, + * }} + */ export let EncoderOptions; -export let ResolverOptions; \ No newline at end of file + +/** + * @typedef EncoderSplitOptions {{ + * letter: boolean=false, + * number: boolean=false, + * symbol: boolean=false, + * punctuation: boolean=false, + * control: boolean=false, + * char: string|Array|undefined, + * }} + */ +export let EncoderSplitOptions; \ No newline at end of file diff --git a/dist/module/webpack.js b/dist/module/webpack.js index ea7a391..8c4ccb9 100644 --- a/dist/module/webpack.js +++ b/dist/module/webpack.js @@ -1,19 +1,12 @@ -import { SearchOptions, ContextOptions, DocumentDescriptor, DocumentSearchOptions, FieldOptions, IndexOptions, DocumentOptions } from "./type.js"; +import { SearchOptions, ContextOptions, DocumentDescriptor, DocumentSearchOptions, DocumentIndexOptions, IndexOptions, DocumentOptions, TagOptions, StoreOptions, EncoderOptions, EncoderSplitOptions } from "./type.js"; import Document from "./document.js"; import Index from "./index.js"; import WorkerIndex from "./worker/index.js"; import Resolver from "./resolver.js"; import Encoder from "./encoder.js"; import IdxDB from "./db/indexeddb/index.js"; -import { global_charset } from "./charset.js"; -import charset_exact from "./lang/latin/exact.js"; -import charset_default from "./lang/latin/default.js"; -import charset_simple from "./lang/latin/simple.js"; -import charset_balance from "./lang/latin/balance.js"; -import charset_advanced from "./lang/latin/advanced.js"; -import charset_extra from "./lang/latin/extra.js"; -import charset_soundex from "./lang/latin/soundex.js"; +import Charset from "./charset.js"; /** @export */Index.prototype.add; /** @export */Index.prototype.append; @@ -23,6 +16,17 @@ import charset_soundex from "./lang/latin/soundex.js"; /** @export */Index.prototype.contain; /** @export */Index.prototype.clear; /** @export */Index.prototype.cleanup; +/** @export */Index.prototype.searchCache; +/** @export */Index.prototype.addAsync; +/** @export */Index.prototype.appendAsync; +/** @export */Index.prototype.searchAsync; +/** @export */Index.prototype.updateAsync; +/** @export */Index.prototype.removeAsync; +/** @export */Index.prototype.export; +/** @export */Index.prototype.import; +/** @export */Index.prototype.mount; +/** @export */Index.prototype.commit; +/** @export */Index.db; /** @export */Document.prototype.add; /** @export */Document.prototype.append; @@ -32,39 +36,39 @@ import charset_soundex from "./lang/latin/soundex.js"; /** @export */Document.prototype.contain; /** @export */Document.prototype.clear; /** @export */Document.prototype.cleanup; - -/** @export */Document.prototype.get; -/** @export */Document.prototype.set; - -/** @export */Index.prototype.searchCache; - -/** @export */Document.prototype.searchCache; - -/** @export */Index.prototype.addAsync; -/** @export */Index.prototype.appendAsync; -/** @export */Index.prototype.searchAsync; -/** @export */Index.prototype.updateAsync; -/** @export */Index.prototype.removeAsync; - /** @export */Document.prototype.addAsync; /** @export */Document.prototype.appendAsync; /** @export */Document.prototype.searchAsync; /** @export */Document.prototype.updateAsync; /** @export */Document.prototype.removeAsync; - -/** @export */Index.prototype.export; -/** @export */Index.prototype.import; - -/** @export */Document.prototype.export; -/** @export */Document.prototype.import; - -/** @export */Index.prototype.mount; -/** @export */Index.prototype.commit; -/** @export */Index.db; - /** @export */Document.prototype.mount; /** @export */Document.prototype.commit; /** @export */Document.db; +/** @export */Document.prototype.export; +/** @export */Document.prototype.import; +/** @export */Document.prototype.searchCache; +/** @export */Document.prototype.get; +/** @export */Document.prototype.set; + +/** @export */Resolver.prototype.limit; +/** @export */Resolver.prototype.offset; +/** @export */Resolver.prototype.boost; +/** @export */Resolver.prototype.resolve; +/** @export */Resolver.prototype.or; +/** @export */Resolver.prototype.and; +/** @export */Resolver.prototype.xor; +/** @export */Resolver.prototype.not; + +/** @export */Charset.LatinExact; +/** @export */Charset.LatinDefault; +/** @export */Charset.LatinSimple; +/** @export */Charset.LatinBalance; +/** @export */Charset.LatinAdvanced; +/** @export */Charset.LatinExtra; +/** @export */Charset.LatinSoundex; +/** @export */Charset.ArabicDefault; +/** @export */Charset.CjkDefault; +/** @export */Charset.CyrillicDefault; /** @export */IndexOptions.preset; /** @export */IndexOptions.context; @@ -79,6 +83,25 @@ import charset_soundex from "./lang/latin/soundex.js"; /** @export */IndexOptions.cache; /** @export */IndexOptions.resolve; /** @export */IndexOptions.db; +/** @export */IndexOptions.config; + +/** @export */DocumentIndexOptions.preset; +/** @export */DocumentIndexOptions.context; +/** @export */DocumentIndexOptions.encoder; +/** @export */DocumentIndexOptions.encode; +/** @export */DocumentIndexOptions.resolution; +/** @export */DocumentIndexOptions.tokenize; +/** @export */DocumentIndexOptions.fastupdate; +/** @export */DocumentIndexOptions.score; +/** @export */DocumentIndexOptions.keystore; +/** @export */DocumentIndexOptions.rtl; +/** @export */DocumentIndexOptions.cache; +/** @export */DocumentIndexOptions.db; +/** @export */DocumentIndexOptions.config; +// /** @export */ DocumentIndexOptions.resolve; +/** @export */DocumentIndexOptions.field; +/** @export */DocumentIndexOptions.filter; +/** @export */DocumentIndexOptions.custom; /** @export */DocumentOptions.context; /** @export */DocumentOptions.encoder; @@ -95,14 +118,27 @@ import charset_soundex from "./lang/latin/soundex.js"; /** @export */DocumentOptions.document; /** @export */DocumentOptions.worker; +/** @export */ContextOptions.depth; +/** @export */ContextOptions.bidirectional; +/** @export */ContextOptions.resolution; + /** @export */DocumentDescriptor.field; /** @export */DocumentDescriptor.index; /** @export */DocumentDescriptor.tag; /** @export */DocumentDescriptor.store; -/** @export */ContextOptions.depth; -/** @export */ContextOptions.bidirectional; -/** @export */ContextOptions.resolution; +/** @export */TagOptions.field; +/** @export */TagOptions.tag; +/** @export */TagOptions.filter; +/** @export */TagOptions.custom; +/** @export */TagOptions.keystore; +/** @export */TagOptions.db; +/** @export */TagOptions.config; + +/** @export */StoreOptions.field; +/** @export */StoreOptions.filter; +/** @export */StoreOptions.custom; +/** @export */StoreOptions.config; /** @export */SearchOptions.query; /** @export */SearchOptions.limit; @@ -111,7 +147,6 @@ import charset_soundex from "./lang/latin/soundex.js"; /** @export */SearchOptions.suggest; /** @export */SearchOptions.resolve; /** @export */SearchOptions.enrich; -/** @export */SearchOptions.tag; /** @export */DocumentSearchOptions.query; /** @export */DocumentSearchOptions.limit; @@ -125,25 +160,38 @@ import charset_soundex from "./lang/latin/soundex.js"; /** @export */DocumentSearchOptions.pluck; /** @export */DocumentSearchOptions.merge; -global_charset["latin:exact"] = charset_exact; -global_charset["latin:default"] = charset_default; -global_charset["latin:simple"] = charset_simple; -global_charset["latin:balance"] = charset_balance; -global_charset["latin:advanced"] = charset_advanced; -global_charset["latin:extra"] = charset_extra; -global_charset["latin:soundex"] = charset_soundex; +/** @export */EncoderOptions.rtl; +/** @export */EncoderOptions.dedupe; +/** @export */EncoderOptions.split; +/** @export */EncoderOptions.include; +/** @export */EncoderOptions.exclude; +/** @export */EncoderOptions.prepare; +/** @export */EncoderOptions.finalize; +/** @export */EncoderOptions.filter; +/** @export */EncoderOptions.matcher; +/** @export */EncoderOptions.mapper; +/** @export */EncoderOptions.stemmer; +/** @export */EncoderOptions.replacer; +/** @export */EncoderOptions.minlength; +/** @export */EncoderOptions.maxlength; +/** @export */EncoderOptions.cache; +/** @export */EncoderSplitOptions.letter; +/** @export */EncoderSplitOptions.number; +/** @export */EncoderSplitOptions.symbol; +/** @export */EncoderSplitOptions.punctuation; +/** @export */EncoderSplitOptions.control; +/** @export */EncoderSplitOptions.char; const FlexSearch = { Index: Index, - Charset: global_charset, + Charset: Charset, Encoder: Encoder, Document: Document, Worker: WorkerIndex, Resolver: Resolver, - IndexedDB: IdxDB - //"registerCharset": registerCharset, - //"registerLanguage": registerLanguage + IndexedDB: IdxDB, + Language: {} }; // Export as library (Bundle) @@ -151,6 +199,8 @@ const FlexSearch = { { + FlexSearch.Language = {}; + const root = self; let prop; diff --git a/dist/module/worker/index.js b/dist/module/worker/index.js index 8d7df5e..3dab909 100644 --- a/dist/module/worker/index.js +++ b/dist/module/worker/index.js @@ -12,7 +12,7 @@ let pid = 0; function WorkerIndex(options) { - if (!(this instanceof WorkerIndex)) { + if (!this) { return new WorkerIndex(options); } diff --git a/package.json b/package.json index 6b8ac59..6fb35da 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,12 @@ "email": "info@nextapps.de" }, "main": "dist/flexsearch.bundle.min.js", + "exports": { + ".": "./dist/flexsearch.bundle.min.js", + "./lang/*": "./dist/lang/*.min.js", + "./lang/*.js": "./dist/lang/*.min.js", + "./db/*/*.js": "./dist/db/*/*.js" + }, "module": "dist/flexsearch.bundle.module.min.js", "browser": { "dist/flexsearch.bundle.min.js": "./dist/flexsearch.bundle.min.js", @@ -48,6 +54,7 @@ "build:es5": "node task/build RELEASE=es5 DEBUG=false PROFILER=false SUPPORT_WORKER=true SUPPORT_ENCODER=true SUPPORT_CHARSET=true SUPPORT_CACHE=true SUPPORT_ASYNC=true SUPPORT_STORE=true SUPPORT_TAGS=true SUPPORT_SUGGESTION=true SUPPORT_SERIALIZE=true SUPPORT_DOCUMENT=true POLYFILL=true SUPPORT_PERSISTENT=true SUPPORT_RESOLVER=true SUPPORT_KEYSTORE=true SUPPORT_COMPRESSION=false LANGUAGE_OUT=ECMASCRIPT5_STRICT", "build:es5:debug": "node task/build RELEASE=es5 DEBUG=true PROFILER=false SUPPORT_WORKER=true SUPPORT_ENCODER=true SUPPORT_CHARSET=true SUPPORT_CACHE=true SUPPORT_ASYNC=true SUPPORT_STORE=true SUPPORT_TAGS=true SUPPORT_SUGGESTION=true SUPPORT_SERIALIZE=true SUPPORT_DOCUMENT=true POLYFILL=true SUPPORT_PERSISTENT=true SUPPORT_RESOLVER=true SUPPORT_KEYSTORE=true SUPPORT_COMPRESSION=false FORMATTING=PRETTY_PRINT LANGUAGE_OUT=ECMASCRIPT5_STRICT", "build:lang": "node task/build RELEASE=lang", + "build:db": "npx rollup src/db/postgres/index.js --file dist/db/postgres/index.cjs --format cjs && npx rollup src/db/sqlite/index.js --file dist/db/sqlite/index.cjs --format cjs && npx rollup src/db/mongo/index.js --file dist/db/mongo/index.cjs --format cjs && npx rollup src/db/redis/index.js --file dist/db/redis/index.cjs --format cjs && npx rollup src/db/clickhouse/index.js --file dist/db/clickhouse/index.cjs --format cjs && npx rollup src/db/indexeddb/index.js --file dist/db/indexeddb/index.cjs --format cjs", "build:module": "node task/babel && exit 0", "build:module:debug": "node task/babel DEBUG=true && exit 0", "build:module:min": "node task/babel RELEASE=min && exit 0", @@ -57,7 +64,7 @@ "build:module:compact:debug": "node task/build RELEASE=compact.module DEBUG=true PROFILER=false SUPPORT_WORKER=false SUPPORT_ENCODER=true SUPPORT_CHARSET=true SUPPORT_CACHE=false SUPPORT_ASYNC=true SUPPORT_STORE=true SUPPORT_TAGS=false SUPPORT_SUGGESTION=true SUPPORT_SERIALIZE=true SUPPORT_DOCUMENT=true POLYFILL=false SUPPORT_PERSISTENT=false SUPPORT_RESOLVER=false SUPPORT_KEYSTORE=false SUPPORT_COMPRESSION=false FORMATTING=PRETTY_PRINT", "build:module:light": "node task/build RELEASE=light.module DEBUG=false PROFILER=false SUPPORT_WORKER=false SUPPORT_ENCODER=false SUPPORT_CHARSET=true SUPPORT_CACHE=true SUPPORT_ASYNC=false SUPPORT_STORE=false SUPPORT_TAGS=false SUPPORT_SUGGESTION=false SUPPORT_SERIALIZE=false SUPPORT_DOCUMENT=false POLYFILL=false SUPPORT_PERSISTENT=false SUPPORT_RESOLVER=false SUPPORT_KEYSTORE=false SUPPORT_COMPRESSION=false", "build:module:light:debug": "node task/build RELEASE=light.module DEBUG=true PROFILER=false SUPPORT_WORKER=false SUPPORT_ENCODER=false SUPPORT_CHARSET=true SUPPORT_CACHE=true SUPPORT_ASYNC=false SUPPORT_STORE=false SUPPORT_TAGS=false SUPPORT_SUGGESTION=false SUPPORT_SERIALIZE=false SUPPORT_DOCUMENT=false POLYFILL=false SUPPORT_PERSISTENT=false SUPPORT_RESOLVER=false SUPPORT_KEYSTORE=false SUPPORT_COMPRESSION=false FORMATTING=PRETTY_PRINT", - "build:all": "npm run build:bundle && npm run build:bundle:debug && npm run build:light && npm run build:light:debug && npm run build:compact && npm run build:compact:debug && npm run build:module && npm run build:module:debug && npm run build:module:min && npm run build:module:bundle && npm run build:module:bundle:debug && npm run build:module:light && npm run build:module:light:debug && npm run build:module:compact && npm run build:module:compact:debug && npm run build:es5 && npm run build:es5:debug", + "build:all": "npm run build:bundle && npm run build:bundle:debug && npm run build:light && npm run build:light:debug && npm run build:compact && npm run build:compact:debug && npm run build:module && npm run build:module:debug && npm run build:module:min && npm run build:module:bundle && npm run build:module:bundle:debug && npm run build:module:light && npm run build:module:light:debug && npm run build:module:compact && npm run build:module:compact:debug && npm run build:lang && npm run build:es5 && npm run build:es5:debug", "test": "cd test && npm install && npm run test" }, "files": [ @@ -87,6 +94,7 @@ "babel-plugin-transform-property-literals": "^6.9.4", "babel-plugin-transform-simplify-comparison-operators": "^6.9.4", "babel-plugin-transform-undefined-to-void": "^6.9.4", - "google-closure-compiler": "^20240317.0.0" + "google-closure-compiler": "^20240317.0.0", + "rollup": "^4.35.0" } } diff --git a/src/cache.js b/src/cache.js index 65cc890..7e28332 100644 --- a/src/cache.js +++ b/src/cache.js @@ -20,10 +20,11 @@ export function searchCache(query, limit, options){ let cache = this.cache.get(query); if(!cache){ cache = this.search(query, limit, options); - if(cache instanceof Promise){ + if(cache.then){ const self = this; cache.then(function(cache){ self.cache.set(query, cache); + return cache; }); } this.cache.set(query, cache); @@ -46,17 +47,17 @@ export default function CacheClass(limit){ } CacheClass.prototype.set = function(key, value){ - if(!this.cache.has(key)){ + //if(!this.cache.has(key)){ this.cache.set(this.last = key, value); - if(this.limit && this.cache.size > this.limit){ + if(this.cache.size > this.limit){ this.cache.delete(this.cache.keys().next().value); } - } + //} }; CacheClass.prototype.get = function(key){ const cache = this.cache.get(key); - if(cache && this.limit && this.last !== key){ + if(cache && this.last !== key){ this.cache.delete(key); this.cache.set(this.last = key, cache); } diff --git a/src/charset.js b/src/charset.js index 8eade76..a3acb5a 100644 --- a/src/charset.js +++ b/src/charset.js @@ -1,16 +1,58 @@ -import { create_object } from "./common.js"; +import charset_latin_exact from "./charset/latin/exact.js"; +import charset_latin_default from "./charset/latin/default.js"; +import charset_latin_simple from "./charset/latin/simple.js"; +import charset_latin_balance from "./charset/latin/balance.js"; +import charset_latin_advanced from "./charset/latin/advanced.js"; +import charset_latin_extra from "./charset/latin/extra.js"; +import charset_latin_soundex from "./charset/latin/soundex.js"; +import charset_arabic_default from "./charset/arabic/default.js"; +import charset_cjk_default from "./charset/cjk/default.js"; +import charset_cyrillic_default from "./charset/cyrillic/default.js"; + +export const LatinExact = charset_latin_exact; +export const LatinDefault = charset_latin_default; +export const LatinSimple = charset_latin_simple; +export const LatinBalance = charset_latin_balance; +export const LatinAdvanced = charset_latin_advanced; +export const LatinExtra = charset_latin_extra; +export const LatinSoundex = charset_latin_soundex; +export const ArabicDefault = charset_arabic_default; +export const CjkDefault = charset_cjk_default; +export const CyrillicDefault = charset_cyrillic_default; // export const global_lang = create_object(); -export const global_charset = create_object(); +//export const global_charset = create_object(); + +export default { + LatinExact: charset_latin_exact, + LatinDefault: charset_latin_default, + LatinSimple: charset_latin_simple, + LatinBalance: charset_latin_balance, + LatinAdvanced: charset_latin_advanced, + LatinExtra: charset_latin_extra, + LatinSoundex: charset_latin_soundex, + ArabicDefault: charset_arabic_default, + CjkDefault: charset_cjk_default, + CyrillicDefault: charset_cyrillic_default +}; + +// global_charset["latin:exact"] = charset_exact; +// global_charset["latin:default"] = charset_default; +// global_charset["latin:simple"] = charset_simple; +// global_charset["latin:balance"] = charset_balance; +// global_charset["latin:advanced"] = charset_advanced; +// global_charset["latin:extra"] = charset_extra; +// global_charset["latin:soundex"] = charset_soundex; + /** * @param {!string} name * @param {Object} charset */ -export function registerCharset(name, charset){ - global_charset[name] = charset; -} +// export function registerCharset(name, charset){ +// global_charset[name] = charset; +// } /** * @param {!string} name diff --git a/src/charset/arabic/default.js b/src/charset/arabic/default.js new file mode 100644 index 0000000..7ef9bc2 --- /dev/null +++ b/src/charset/arabic/default.js @@ -0,0 +1,41 @@ +import { EncoderOptions } from "../../type.js"; +const regex = /[\x00-\x7F]+/g; +const split = /\s+/; + +/** @type EncoderOptions */ +const options = { + rtl: true, + normalize: false, + dedupe: true, + prepare: function(str){ + return ("" + str).replace(regex, " ") + } +}; +export default options; + +// import { pipeline } from "../../lang.js"; +// +// export const rtl = true; +// export const tokenize = ""; +// export default { +// encode: encode, +// rtl: rtl +// } +// +// +// +// /** +// * @param {string|number} str +// */ +// +// export function encode(str){ +// +// return pipeline.call( +// +// this, +// /* string: */ ("" + str).replace(regex, " "), +// /* normalize: */ false, +// /* split: */ split, +// /* collapse: */ false +// ); +// } diff --git a/src/charset/cjk/default.js b/src/charset/cjk/default.js new file mode 100644 index 0000000..9bfb87d --- /dev/null +++ b/src/charset/cjk/default.js @@ -0,0 +1,41 @@ +import { EncoderOptions } from "../../type.js"; +const regex = /[\x00-\x7F]+/g; + +/** @type EncoderOptions */ +const options = { + normalize: false, + dedupe: true, + split: "", + prepare: function(str){ + return ("" + str).replace(regex, "") + } +}; +export default options; + +// import { pipeline } from "../../lang.js"; +// +// export const rtl = false; +// export const tokenize = "strict"; +// export default { +// encode: encode, +// rtl: rtl, +// tokenize: tokenize +// } +// +// const regex = /[\x00-\x7F]+/g; +// +// /** +// * @param {string|number} str +// */ +// +// export function encode(str){ +// +// return pipeline.call( +// +// this, +// /* string: */ ("" + str).replace(regex, ""), +// /* normalize: */ false, +// /* split: */ "", +// /* collapse: */ false +// ); +// } diff --git a/src/charset/cyrillic/default.js b/src/charset/cyrillic/default.js new file mode 100644 index 0000000..ce26c16 --- /dev/null +++ b/src/charset/cyrillic/default.js @@ -0,0 +1,40 @@ +import { EncoderOptions } from "../../type.js"; +const regex = /[\x00-\x7F]+/g; + +/** @type EncoderOptions */ +const options = { + normalize: false, + dedupe: true, + prepare: function(str){ + return ("" + str).replace(regex, " ") + } +}; +export default options; + +// import { pipeline } from "../../lang.js"; +// +// export const rtl = false; +// export const tokenize = ""; +// export default { +// encode: encode, +// rtl: rtl +// } +// +// const regex = /[\x00-\x7F]+/g; +// const split = /\s+/; +// +// /** +// * @param {string|number} str +// */ +// +// export function encode(str){ +// +// return pipeline.call( +// +// this, +// /* string: */ ("" + str).replace(regex, " "), +// /* normalize: */ false, +// /* split: */ split, +// /* collapse: */ false +// ); +// } diff --git a/src/lang/latin/advanced.js b/src/charset/latin/advanced.js similarity index 96% rename from src/lang/latin/advanced.js rename to src/charset/latin/advanced.js index 3732d41..df6db83 100644 --- a/src/lang/latin/advanced.js +++ b/src/charset/latin/advanced.js @@ -1,4 +1,4 @@ -import Encoder from "../../encoder.js"; +import { EncoderOptions } from "../../type.js"; import { soundex } from "./balance.js"; // const soundex = new Map([ @@ -56,13 +56,15 @@ export const replacer = [ /([aeo])h([^aeo]|$)/g, "$1$2", ]; -export default { +/** @type EncoderOptions */ +const options = { normalize: true, dedupe: true, mapper: soundex, replacer: replacer, matcher: matcher }; +export default options; // import { regex, replace, collapse } from "../../lang.js"; // import { encode as encode_balance } from "./balance.js"; diff --git a/src/lang/latin/balance.js b/src/charset/latin/balance.js similarity index 98% rename from src/lang/latin/balance.js rename to src/charset/latin/balance.js index c129226..35369a2 100644 --- a/src/lang/latin/balance.js +++ b/src/charset/latin/balance.js @@ -1,4 +1,4 @@ -import Encoder from "../../encoder.js"; +import { EncoderOptions } from "../../type.js"; export const soundex = new Map([ ["b", "p"], @@ -38,11 +38,13 @@ export const soundex = new Map([ ["u", "o"] ]); -export default { +/** @type EncoderOptions */ +const options = { normalize: true, dedupe: true, mapper: soundex }; +export default options; // //import { encode as encode_simple } from "./simple.js"; // import { pipeline } from "../../lang.js"; diff --git a/src/lang/latin/default.js b/src/charset/latin/default.js similarity index 84% rename from src/lang/latin/default.js rename to src/charset/latin/default.js index 76eef6b..87b0a74 100644 --- a/src/lang/latin/default.js +++ b/src/charset/latin/default.js @@ -1,11 +1,13 @@ -import Encoder from "../../encoder.js"; +import { EncoderOptions } from "../../type.js"; -export default { +/** @type EncoderOptions */ +const options = { normalize: function(str){ return str.toLowerCase(); }, dedupe: false }; +export default options; // import { pipeline } from "../../lang.js"; // diff --git a/src/charset/latin/exact.js b/src/charset/latin/exact.js new file mode 100644 index 0000000..522c04c --- /dev/null +++ b/src/charset/latin/exact.js @@ -0,0 +1,8 @@ +import { EncoderOptions } from "../../type.js"; + +/** @type EncoderOptions */ +const options = { + normalize: false, + dedupe: false +}; +export default options; \ No newline at end of file diff --git a/src/lang/latin/extra.js b/src/charset/latin/extra.js similarity index 94% rename from src/lang/latin/extra.js rename to src/charset/latin/extra.js index a351059..0d8b589 100644 --- a/src/lang/latin/extra.js +++ b/src/charset/latin/extra.js @@ -1,4 +1,4 @@ -import Encoder from "../../encoder.js"; +import { EncoderOptions } from "../../type.js"; import { soundex } from "./balance.js"; import { matcher, replacer } from "./advanced.js"; @@ -6,13 +6,15 @@ export const compact = [ /(?!^)[aeoy]/g, "" // old: aioy ]; -export default { +/** @type EncoderOptions */ +const options = { normalize: true, dedupe: true, mapper: soundex, replacer: replacer.concat(compact), matcher: matcher }; +export default options; // import { regex, replace, collapse } from "../../lang.js"; // import { encode as encode_advanced } from "./advanced.js"; diff --git a/src/lang/latin/simple.js b/src/charset/latin/simple.js similarity index 98% rename from src/lang/latin/simple.js rename to src/charset/latin/simple.js index f2da1b3..d310495 100644 --- a/src/lang/latin/simple.js +++ b/src/charset/latin/simple.js @@ -1,9 +1,11 @@ -import Encoder from "../../encoder.js"; +import { EncoderOptions } from "../../type.js"; -export default { +/** @type EncoderOptions */ +const options = { normalize: true, dedupe: true }; +export default options; // import { pipeline, regex } from "../../lang.js"; // diff --git a/src/lang/latin/soundex.js b/src/charset/latin/soundex.js similarity index 92% rename from src/lang/latin/soundex.js rename to src/charset/latin/soundex.js index b1524b6..3176db5 100644 --- a/src/lang/latin/soundex.js +++ b/src/charset/latin/soundex.js @@ -1,4 +1,7 @@ -export default { +import { EncoderOptions } from "../../type.js"; + +/** @type EncoderOptions */ +const options = { normalize: true, dedupe: false, include: { @@ -10,6 +13,7 @@ export default { } } }; +export default options; const codes = { "a": "", "e": "", "i": "", "o": "", "u": "", "y": "", diff --git a/src/charset/normalize.js b/src/charset/normalize.js new file mode 100644 index 0000000..f1e487d --- /dev/null +++ b/src/charset/normalize.js @@ -0,0 +1,233 @@ +export default [ + + // Charset Normalization + + ["ª","a"], + ["²","2"], + ["³","3"], + ["¹","1"], + ["º","o"], + ["¼","1⁄4"], + ["½","1⁄2"], + ["¾","3⁄4"], + ["à","a"], + ["á","a"], + ["â","a"], + ["ã","a"], + ["ä","a"], + ["å","a"], + ["ç","c"], + ["è","e"], + ["é","e"], + ["ê","e"], + ["ë","e"], + ["ì","i"], + ["í","i"], + ["î","i"], + ["ï","i"], + ["ñ","n"], + ["ò","o"], + ["ó","o"], + ["ô","o"], + ["õ","o"], + ["ö","o"], + ["ù","u"], + ["ú","u"], + ["û","u"], + ["ü","u"], + ["ý","y"], + ["ÿ","y"], + ["ā","a"], + ["ă","a"], + ["ą","a"], + ["ć","c"], + ["ĉ","c"], + ["ċ","c"], + ["č","c"], + ["ď","d"], + ["ē","e"], + ["ĕ","e"], + ["ė","e"], + ["ę","e"], + ["ě","e"], + ["ĝ","g"], + ["ğ","g"], + ["ġ","g"], + ["ģ","g"], + ["ĥ","h"], + ["ĩ","i"], + ["ī","i"], + ["ĭ","i"], + ["į","i"], + ["ij","ij"], + ["ĵ","j"], + ["ķ","k"], + ["ĺ","l"], + ["ļ","l"], + ["ľ","l"], + ["ŀ","l"], + ["ń","n"], + ["ņ","n"], + ["ň","n"], + ["ʼn","n"], + ["ō","o"], + ["ŏ","o"], + ["ő","o"], + ["ŕ","r"], + ["ŗ","r"], + ["ř","r"], + ["ś","s"], + ["ŝ","s"], + ["ş","s"], + ["š","s"], + ["ţ","t"], + ["ť","t"], + ["ũ","u"], + ["ū","u"], + ["ŭ","u"], + ["ů","u"], + ["ű","u"], + ["ų","u"], + ["ŵ","w"], + ["ŷ","y"], + ["ź","z"], + ["ż","z"], + ["ž","z"], + ["ſ","s"], + ["ơ","o"], + ["ư","u"], + ["dž","dz"], + ["lj","lj"], + ["nj","nj"], + ["ǎ","a"], + ["ǐ","i"], + ["ǒ","o"], + ["ǔ","u"], + ["ǖ","u"], + ["ǘ","u"], + ["ǚ","u"], + ["ǜ","u"], + ["ǟ","a"], + ["ǡ","a"], + ["ǣ","ae"], + ["æ","ae"], + ["ǽ","ae"], + ["ǧ","g"], + ["ǩ","k"], + ["ǫ","o"], + ["ǭ","o"], + ["ǯ","ʒ"], + ["ǰ","j"], + ["dz","dz"], + ["ǵ","g"], + ["ǹ","n"], + ["ǻ","a"], + ["ǿ","ø"], + ["ȁ","a"], + ["ȃ","a"], + ["ȅ","e"], + ["ȇ","e"], + ["ȉ","i"], + ["ȋ","i"], + ["ȍ","o"], + ["ȏ","o"], + ["ȑ","r"], + ["ȓ","r"], + ["ȕ","u"], + ["ȗ","u"], + ["ș","s"], + ["ț","t"], + ["ȟ","h"], + ["ȧ","a"], + ["ȩ","e"], + ["ȫ","o"], + ["ȭ","o"], + ["ȯ","o"], + ["ȱ","o"], + ["ȳ","y"], + ["ʰ","h"], + ["ʱ","h"], + ["ɦ","h"], + ["ʲ","j"], + ["ʳ","r"], + ["ʴ","ɹ"], + ["ʵ","ɻ"], + ["ʶ","ʁ"], + ["ʷ","w"], + ["ʸ","y"], + ["ˠ","ɣ"], + ["ˡ","l"], + ["ˢ","s"], + ["ˣ","x"], + ["ˤ","ʕ"], + ["ΐ","ι"], + ["ά","α"], + ["έ","ε"], + ["ή","η"], + ["ί","ι"], + ["ΰ","υ"], + ["ϊ","ι"], + ["ϋ","υ"], + ["ό","ο"], + ["ύ","υ"], + ["ώ","ω"], + ["ϐ","β"], + ["ϑ","θ"], + ["ϒ","Υ"], + ["ϓ","Υ"], + ["ϔ","Υ"], + ["ϕ","φ"], + ["ϖ","π"], + ["ϰ","κ"], + ["ϱ","ρ"], + ["ϲ","ς"], + ["ϵ","ε"], + ["й","и"], + ["ѐ","е"], + ["ё","е"], + ["ѓ","г"], + ["ї","і"], + ["ќ","к"], + ["ѝ","и"], + ["ў","у"], + ["ѷ","ѵ"], + ["ӂ","ж"], + ["ӑ","а"], + ["ӓ","а"], + ["ӗ","е"], + ["ӛ","ә"], + ["ӝ","ж"], + ["ӟ","з"], + ["ӣ","и"], + ["ӥ","и"], + ["ӧ","о"], + ["ӫ","ө"], + ["ӭ","э"], + ["ӯ","у"], + ["ӱ","у"], + ["ӳ","у"], + ["ӵ","ч"] + + // Term Separators + + // ["'", ""], // it's -> its + // ["´", ""], + // ["`", ""], + // ["’", ""], + // ["ʼ", ""], + + // Numeric-Separators Chars Removal + + // [",", ""], + // [".", ""] + + // Non-Whitespace Separators + + // already was split by default via p{P} + // ["-", " "], + // [":", " "], + // ["_", " "], + // ["|", " "], + // ["/", " "], + // ["\\", " "] +]; \ No newline at end of file diff --git a/src/db/clickhouse/index.js b/src/db/clickhouse/index.js index 224231a..79d05b2 100644 --- a/src/db/clickhouse/index.js +++ b/src/db/clickhouse/index.js @@ -59,7 +59,7 @@ let DB; */ export default function ClickhouseDB(name, config = {}){ - if(!(this instanceof ClickhouseDB)){ + if(!this){ return new ClickhouseDB(name, config); } if(typeof name === "object"){ @@ -85,7 +85,7 @@ export default function ClickhouseDB(name, config = {}){ }; ClickhouseDB.prototype.mount = function(flexsearch){ - if(flexsearch instanceof Document){ + if(flexsearch.constructor === Document){ return flexsearch.mount(this); } defaults.resolution = Math.max(flexsearch.resolution, flexsearch.resolution_ctx); diff --git a/src/db/indexeddb/index.js b/src/db/indexeddb/index.js index 22abc63..e09f40f 100644 --- a/src/db/indexeddb/index.js +++ b/src/db/indexeddb/index.js @@ -38,7 +38,7 @@ function sanitize(str) { */ export default function IdxDB(name, config = {}){ - if(!(this instanceof IdxDB)){ + if(!this){ return new IdxDB(name, config); } if(typeof name === "object"){ @@ -56,7 +56,7 @@ export default function IdxDB(name, config = {}){ }; IdxDB.prototype.mount = function(flexsearch){ - if(flexsearch instanceof Document){ + if(flexsearch.constructor === Document){ return flexsearch.mount(this); } flexsearch.db = this; diff --git a/src/db/mongo/index.js b/src/db/mongo/index.js index e119e07..2ed4731 100644 --- a/src/db/mongo/index.js +++ b/src/db/mongo/index.js @@ -24,7 +24,7 @@ let DB = Object.create(null); */ export default function MongoDB(name, config = {}){ - if(!(this instanceof MongoDB)){ + if(!this){ return new MongoDB(name, config); } if(typeof name === "object"){ @@ -49,7 +49,7 @@ export default function MongoDB(name, config = {}){ // }; MongoDB.prototype.mount = function(flexsearch){ - if(flexsearch instanceof Document){ + if(flexsearch.constructor === Document){ return flexsearch.mount(this); } flexsearch.db = this; diff --git a/src/db/postgres/index.js b/src/db/postgres/index.js index 77f4f12..cc2cdec 100644 --- a/src/db/postgres/index.js +++ b/src/db/postgres/index.js @@ -53,7 +53,7 @@ let DB, TRX; */ export default function PostgresDB(name, config = {}){ - if(!(this instanceof PostgresDB)){ + if(!this){ return new PostgresDB(name, config); } if(typeof name === "object"){ @@ -74,7 +74,7 @@ export default function PostgresDB(name, config = {}){ }; PostgresDB.prototype.mount = function(flexsearch){ - if(flexsearch instanceof Document){ + if(flexsearch.constructor === Document){ return flexsearch.mount(this); } flexsearch.db = this; diff --git a/src/db/redis/index.js b/src/db/redis/index.js index 4e7199c..1fd9ce3 100644 --- a/src/db/redis/index.js +++ b/src/db/redis/index.js @@ -27,7 +27,7 @@ let DB, TRX; */ export default function RedisDB(name, config = {}){ - if(!(this instanceof RedisDB)){ + if(!this){ return new RedisDB(name, config); } if(typeof name === "object"){ @@ -53,7 +53,7 @@ export default function RedisDB(name, config = {}){ // }; RedisDB.prototype.mount = function(flexsearch){ - if(flexsearch instanceof Document){ + if(flexsearch.constructor === Document){ return flexsearch.mount(this); } flexsearch.db = this; diff --git a/src/db/sqlite/index.js b/src/db/sqlite/index.js index 2785ca2..c640dd2 100644 --- a/src/db/sqlite/index.js +++ b/src/db/sqlite/index.js @@ -48,7 +48,7 @@ const DB = Object.create(null); */ export default function SqliteDB(name, config = {}){ - if(!(this instanceof SqliteDB)){ + if(!this){ return new SqliteDB(name, config); } if(typeof name === "object"){ @@ -72,7 +72,7 @@ export default function SqliteDB(name, config = {}){ }; SqliteDB.prototype.mount = function(flexsearch){ - if(flexsearch instanceof Document){ + if(flexsearch.constructor === Document){ return flexsearch.mount(this); } flexsearch.db = this; diff --git a/src/document.js b/src/document.js index 1740441..ff5e429 100644 --- a/src/document.js +++ b/src/document.js @@ -20,7 +20,7 @@ import { } from "./config.js"; // <-- COMPILER BLOCK -import { DocumentOptions } from "./type.js"; +import { DocumentOptions, DocumentDescriptor, DocumentIndexOptions, StoreOptions } from "./type.js"; import Index from "./index.js"; import WorkerIndex from "./worker/index.js"; import Cache, { searchCache } from "./cache.js"; @@ -33,15 +33,16 @@ import "./document/search.js"; /** * @constructor - * @param {DocumentOptions=} options + * @param {!DocumentOptions} options */ export default function Document(options){ - if(!(this instanceof Document)) { + if(!this) { return new Document(options); } + /** @type DocumentDescriptor */ const document = options.document || options.doc || options; let tmp, keystore; @@ -227,7 +228,11 @@ function parse_descriptor(options, document){ key = key.field; } - opt = is_object(opt) ? Object.assign({}, options, opt) : options; + opt = /** @type DocumentIndexOptions */ ( + is_object(opt) + ? Object.assign({}, options, opt) + : options + ); if(SUPPORT_WORKER && this.worker){ const worker = new WorkerIndex(opt); @@ -265,7 +270,7 @@ function parse_descriptor(options, document){ if(is_string(stores)) stores = [stores]; for(let i = 0, store, field; i < stores.length; i++){ - store = stores[i]; + store = /** @type Array */ (stores[i]); field = store.field || store; if(store.custom){ this.storetree[i] = store.custom; diff --git a/src/document/add.js b/src/document/add.js index aef0fb0..6a6474d 100644 --- a/src/document/add.js +++ b/src/document/add.js @@ -50,7 +50,7 @@ Document.prototype.add = function(id, content, _append){ if(filter && !filter(content)){ continue; } - if(tree instanceof String){ + if(tree.constructor === String){ tree = ["" + tree]; } else if(is_string(tree)){ @@ -81,7 +81,7 @@ Document.prototype.add = function(id, content, _append){ if(filter && !filter(content)){ continue; } - if(tree instanceof String){ + if(tree.constructor === String){ tree = "" + tree; } tags = parse_simple(content, tree); @@ -160,7 +160,7 @@ Document.prototype.add = function(id, content, _append){ if(!custom) continue; tree = [tree._field]; } - else if(is_string(tree) || tree instanceof String){ + else if(is_string(tree) || tree.constructor === String){ payload[tree] = content[tree]; continue; } diff --git a/src/encoder.js b/src/encoder.js index 4ddfba9..999da54 100644 --- a/src/encoder.js +++ b/src/encoder.js @@ -1,12 +1,12 @@ // COMPILER BLOCK --> import { SUPPORT_CACHE, - SUPPORT_CHARSET, - SUPPORT_COMPRESSION, - SUPPORT_ENCODER + SUPPORT_CHARSET } from "./config.js"; // <-- COMPILER BLOCK import { parse_option } from "./common.js"; +import normalize_polyfill from "./charset/normalize.js"; +import { EncoderOptions } from "./type.js"; /* @@ -54,248 +54,16 @@ const numeric_split_next_char = /(\d{3})(\D)/g; //.replace(/([^\d])([\d])/g, "$1 $2") //.replace(/([\d])([^\d])/g, "$1 $2") const normalize = "".normalize && /[\u0300-\u036f]/g; // '´`’ʼ., -const normalize_mapper = SUPPORT_CHARSET && !normalize && [ - - // Charset Normalization - - ["ª","a"], - ["²","2"], - ["³","3"], - ["¹","1"], - ["º","o"], - ["¼","1⁄4"], - ["½","1⁄2"], - ["¾","3⁄4"], - ["à","a"], - ["á","a"], - ["â","a"], - ["ã","a"], - ["ä","a"], - ["å","a"], - ["ç","c"], - ["è","e"], - ["é","e"], - ["ê","e"], - ["ë","e"], - ["ì","i"], - ["í","i"], - ["î","i"], - ["ï","i"], - ["ñ","n"], - ["ò","o"], - ["ó","o"], - ["ô","o"], - ["õ","o"], - ["ö","o"], - ["ù","u"], - ["ú","u"], - ["û","u"], - ["ü","u"], - ["ý","y"], - ["ÿ","y"], - ["ā","a"], - ["ă","a"], - ["ą","a"], - ["ć","c"], - ["ĉ","c"], - ["ċ","c"], - ["č","c"], - ["ď","d"], - ["ē","e"], - ["ĕ","e"], - ["ė","e"], - ["ę","e"], - ["ě","e"], - ["ĝ","g"], - ["ğ","g"], - ["ġ","g"], - ["ģ","g"], - ["ĥ","h"], - ["ĩ","i"], - ["ī","i"], - ["ĭ","i"], - ["į","i"], - ["ij","ij"], - ["ĵ","j"], - ["ķ","k"], - ["ĺ","l"], - ["ļ","l"], - ["ľ","l"], - ["ŀ","l"], - ["ń","n"], - ["ņ","n"], - ["ň","n"], - ["ʼn","n"], - ["ō","o"], - ["ŏ","o"], - ["ő","o"], - ["ŕ","r"], - ["ŗ","r"], - ["ř","r"], - ["ś","s"], - ["ŝ","s"], - ["ş","s"], - ["š","s"], - ["ţ","t"], - ["ť","t"], - ["ũ","u"], - ["ū","u"], - ["ŭ","u"], - ["ů","u"], - ["ű","u"], - ["ų","u"], - ["ŵ","w"], - ["ŷ","y"], - ["ź","z"], - ["ż","z"], - ["ž","z"], - ["ſ","s"], - ["ơ","o"], - ["ư","u"], - ["dž","dz"], - ["lj","lj"], - ["nj","nj"], - ["ǎ","a"], - ["ǐ","i"], - ["ǒ","o"], - ["ǔ","u"], - ["ǖ","u"], - ["ǘ","u"], - ["ǚ","u"], - ["ǜ","u"], - ["ǟ","a"], - ["ǡ","a"], - ["ǣ","ae"], - ["æ","ae"], - ["ǽ","ae"], - ["ǧ","g"], - ["ǩ","k"], - ["ǫ","o"], - ["ǭ","o"], - ["ǯ","ʒ"], - ["ǰ","j"], - ["dz","dz"], - ["ǵ","g"], - ["ǹ","n"], - ["ǻ","a"], - ["ǿ","ø"], - ["ȁ","a"], - ["ȃ","a"], - ["ȅ","e"], - ["ȇ","e"], - ["ȉ","i"], - ["ȋ","i"], - ["ȍ","o"], - ["ȏ","o"], - ["ȑ","r"], - ["ȓ","r"], - ["ȕ","u"], - ["ȗ","u"], - ["ș","s"], - ["ț","t"], - ["ȟ","h"], - ["ȧ","a"], - ["ȩ","e"], - ["ȫ","o"], - ["ȭ","o"], - ["ȯ","o"], - ["ȱ","o"], - ["ȳ","y"], - ["ʰ","h"], - ["ʱ","h"], - ["ɦ","h"], - ["ʲ","j"], - ["ʳ","r"], - ["ʴ","ɹ"], - ["ʵ","ɻ"], - ["ʶ","ʁ"], - ["ʷ","w"], - ["ʸ","y"], - ["ˠ","ɣ"], - ["ˡ","l"], - ["ˢ","s"], - ["ˣ","x"], - ["ˤ","ʕ"], - ["ΐ","ι"], - ["ά","α"], - ["έ","ε"], - ["ή","η"], - ["ί","ι"], - ["ΰ","υ"], - ["ϊ","ι"], - ["ϋ","υ"], - ["ό","ο"], - ["ύ","υ"], - ["ώ","ω"], - ["ϐ","β"], - ["ϑ","θ"], - ["ϒ","Υ"], - ["ϓ","Υ"], - ["ϔ","Υ"], - ["ϕ","φ"], - ["ϖ","π"], - ["ϰ","κ"], - ["ϱ","ρ"], - ["ϲ","ς"], - ["ϵ","ε"], - ["й","и"], - ["ѐ","е"], - ["ё","е"], - ["ѓ","г"], - ["ї","і"], - ["ќ","к"], - ["ѝ","и"], - ["ў","у"], - ["ѷ","ѵ"], - ["ӂ","ж"], - ["ӑ","а"], - ["ӓ","а"], - ["ӗ","е"], - ["ӛ","ә"], - ["ӝ","ж"], - ["ӟ","з"], - ["ӣ","и"], - ["ӥ","и"], - ["ӧ","о"], - ["ӫ","ө"], - ["ӭ","э"], - ["ӯ","у"], - ["ӱ","у"], - ["ӳ","у"], - ["ӵ","ч"] - - // Term Separators - - // ["'", ""], // it's -> its - // ["´", ""], - // ["`", ""], - // ["’", ""], - // ["ʼ", ""], - - // Numeric-Separators Chars Removal - - // [",", ""], - // [".", ""] - - // Non-Whitespace Separators - - // already was split by default via p{P} - // ["-", " "], - // [":", " "], - // ["_", " "], - // ["|", " "], - // ["/", " "], - // ["\\", " "] -]; +//const normalize_mapper = SUPPORT_CHARSET && !normalize && normalize_polyfill; /** - * @param options + * @param {EncoderOptions=} options * @constructor */ -export default function Encoder(options = {}){ +export default function Encoder(options){ - if(!(this instanceof Encoder)){ + if(!this){ return new Encoder(...arguments); } @@ -304,25 +72,11 @@ export default function Encoder(options = {}){ } }; +/** + * @param {!EncoderOptions} options + */ Encoder.prototype.assign = function(options){ - // if(options.assign){ - // //options = Object.assign({}, options.assign, options); - // this.assign(options.assign); - // } - - // let tmp = options["normalize"]; - // if(typeof tmp === "function"){ - // const old = this.normalize; - // if(typeof old === "function"){ - // const current = tmp; - // tmp = function(){ - // old(); - // current(); - // } - // } - // } - /** * pre-processing string input * @type {Function|boolean} @@ -399,11 +153,11 @@ Encoder.prototype.assign = function(options){ parse_option(options.finalize, null, this.finalize) ); - // move the normalization fallback to the mapper - if(normalize_mapper){ + // assign the normalization fallback to the mapper + if(SUPPORT_CHARSET && !normalize){ this.mapper = new Map( /** @type {Array>} */ ( - normalize_mapper + normalize_polyfill ) ); } diff --git a/src/index.js b/src/index.js index e055623..fa6be4d 100644 --- a/src/index.js +++ b/src/index.js @@ -18,18 +18,20 @@ import { SUPPORT_PERSISTENT, SUPPORT_COMPRESSION, SUPPORT_KEYSTORE, - SUPPORT_RESOLVER + SUPPORT_RESOLVER, + SUPPORT_CHARSET } from "./config.js"; // <-- COMPILER BLOCK -import { IndexOptions } from "./type.js"; +import { IndexOptions, ContextOptions } from "./type.js"; import Encoder from "./encoder.js"; import Cache, { searchCache } from "./cache.js"; +import Charset from "./charset.js"; import { KeystoreMap, KeystoreSet } from "./keystore.js"; import { is_array, is_string } from "./common.js"; import { exportIndex, importIndex } from "./serialize.js"; -import default_encoder from "./lang/latin/default.js"; +import default_encoder from "./charset/latin/default.js"; import apply_preset from "./preset.js"; import apply_async from "./async.js"; import tick from "./profiler.js"; @@ -39,13 +41,13 @@ import "./index/remove.js"; /** * @constructor - * @param {IndexOptions|string=} options + * @param {IndexOptions|string=} options Options or preset as string * @param {Map|Set|KeystoreSet|KeystoreMap=} _register */ export default function Index(options, _register){ - if(!(this instanceof Index)){ + if(!this){ return new Index(options); } @@ -55,12 +57,14 @@ export default function Index(options, _register){ ? apply_preset(options) : {}; + /** @type ContextOptions */ const context = options.context || {}; const encoder = options.encode || options.encoder || ( SUPPORT_ENCODER ? default_encoder : function(str){ return str.toLowerCase().trim().split(/\s+/); } ); + /** @type Encoder */ this.encoder = encoder.encode ? encoder : typeof encoder === "object" diff --git a/src/keystore.js b/src/keystore.js index 83afb90..a39510c 100644 --- a/src/keystore.js +++ b/src/keystore.js @@ -7,7 +7,7 @@ import { create_object } from "./common.js"; export function KeystoreObj(bitlength = 8){ - if(!(this instanceof KeystoreObj)){ + if(!this){ return new KeystoreObj(bitlength); } @@ -90,7 +90,7 @@ function _slice(self, start, end, splice){ export function KeystoreArray(arr){ - if(!(this instanceof KeystoreArray)){ + if(!this){ return new KeystoreArray(arr); } @@ -200,7 +200,7 @@ KeystoreArray.prototype.push = function(val){}; export function KeystoreMap(bitlength = 8){ - if(!(this instanceof KeystoreMap)){ + if(!this){ return new KeystoreMap(bitlength); } @@ -246,7 +246,7 @@ KeystoreMap.prototype.set = function(key, value){ export function KeystoreSet(bitlength = 8){ - if(!(this instanceof KeystoreSet)){ + if(!this){ return new KeystoreSet(bitlength); } diff --git a/src/lang/arabic/default.js b/src/lang/arabic/default.js deleted file mode 100644 index 4dee96d..0000000 --- a/src/lang/arabic/default.js +++ /dev/null @@ -1,27 +0,0 @@ -import { pipeline } from "../../lang.js"; - -export const rtl = true; -export const tokenize = ""; -export default { - encode: encode, - rtl: rtl -} - -const regex = /[\x00-\x7F]+/g; -const split = /\s+/; - -/** - * @param {string|number} str - */ - -export function encode(str){ - - return pipeline.call( - - this, - /* string: */ ("" + str).replace(regex, " "), - /* normalize: */ false, - /* split: */ split, - /* collapse: */ false - ); -} diff --git a/src/lang/cjk/default.js b/src/lang/cjk/default.js deleted file mode 100644 index 4b1a1ea..0000000 --- a/src/lang/cjk/default.js +++ /dev/null @@ -1,27 +0,0 @@ -import { pipeline } from "../../lang.js"; - -export const rtl = false; -export const tokenize = "strict"; -export default { - encode: encode, - rtl: rtl, - tokenize: tokenize -} - -const regex = /[\x00-\x7F]+/g; - -/** - * @param {string|number} str - */ - -export function encode(str){ - - return pipeline.call( - - this, - /* string: */ ("" + str).replace(regex, ""), - /* normalize: */ false, - /* split: */ "", - /* collapse: */ false - ); -} diff --git a/src/lang/cyrillic/default.js b/src/lang/cyrillic/default.js deleted file mode 100644 index e331a4e..0000000 --- a/src/lang/cyrillic/default.js +++ /dev/null @@ -1,27 +0,0 @@ -import { pipeline } from "../../lang.js"; - -export const rtl = false; -export const tokenize = ""; -export default { - encode: encode, - rtl: rtl -} - -const regex = /[\x00-\x7F]+/g; -const split = /\s+/; - -/** - * @param {string|number} str - */ - -export function encode(str){ - - return pipeline.call( - - this, - /* string: */ ("" + str).replace(regex, " "), - /* normalize: */ false, - /* split: */ split, - /* collapse: */ false - ); -} diff --git a/src/lang/de.js b/src/lang/de.js index 86c7152..af9d9bb 100644 --- a/src/lang/de.js +++ b/src/lang/de.js @@ -1,3 +1,5 @@ +import { EncoderOptions } from "../type.js"; + /** * Filter are also known as "stopwords", they completely filter out words from being indexed. * Source: http://www.ranks.nl/stopwords @@ -187,19 +189,19 @@ const map = new Map([ ["€", " EUR "] ]); -export default { - normalize: function(str){ - return str.toLowerCase(); - }, +/** + * @type EncoderOptions + */ +const options = { prepare: function(str){ // normalization if(/[_äöüß&€]/.test(str)) str = str.replace(/[_äöüß&€]/g, match => map.get(match)); // street names return str.replace(/str\b/g, "strasse") - .replace(/(?!\b)strasse\b/g, " strasse") - .replace(/\bst\b/g, "sankt"); + .replace(/(?!\b)strasse\b/g, " strasse"); }, filter: filter, stemmer: stemmer -}; \ No newline at end of file +}; +export default options; \ No newline at end of file diff --git a/src/lang/en.js b/src/lang/en.js index 32c0257..0052366 100644 --- a/src/lang/en.js +++ b/src/lang/en.js @@ -1,10 +1,11 @@ +import { EncoderOptions } from "../type.js"; + +// todo filter out minlength + /** * http://www.ranks.nl/stopwords * @type {Set} */ - - -// todo filter out minlength export const filter = new Set([ "a", "about", @@ -218,7 +219,7 @@ export const filter = new Set([ ]); /** - * @type {Object} + * @type {Map} */ export const stemmer = new Map([ @@ -271,10 +272,6 @@ export const stemmer = new Map([ ["ly", ""] ]); -// export const replacer = new Map([ -// ["&", " and "], -// ]); - /* he’s (= he is / he has) she’s (= she is / she has) @@ -303,82 +300,30 @@ export const stemmer = new Map([ // ["'d$", " would had"], // ]); -// const pairs = [ -// /´`’ʼ/, /´`’ʼ/g, "'", -// /_/, /_+/g, " ", -// /&/, /&/g, " and ", -// /\bi'm\b/, /\bi'm\b/g, "i am", -// /\b(can't|cannot)\b/, /\b(can't|cannot)\b/g, "can not", -// /\bwon't\b/, /\bwon't\b/g, "will not", -// /[a-z]n't\b/, /[a-z]n't\b/g, "$1 not", -// /[a-z]'s\b/, /([a-z])'s\b/g, "$1 is has", -// /[a-z]'ll\b/, /[a-z]'ll\b/g, "$1 will", -// /[a-z]'re\b/, /[a-z]'re\b/g, "$1 are", -// /[a-z]'ve\b/, /[a-z]'ve\b/g, "$1 have", -// /[a-z]'d\b/, /[a-z]'d\b/g, "$1 is has" -// ]; - -// const map = new Map([ -// ["´", "'"], -// ["`", "'"], -// ["’", "'"], -// ["ʼ", "'"], -// ["_", " "], -// ["&", " and "] -// ]); - -export default { +/** + * @type EncoderOptions + */ +const options = { prepare: function(str){ - // if(/[´`’ʼ_&]/.test(str)) - // str = str.replace(/[´`’ʼ_&]/g, match => map.get(match)); - - // if(/´`’ʼ/.test(str)) - // str = str.replace(/´`’ʼ/g, "'"); - // if(/_/.test(str)) - // str = str.replace(/_+/g, " "); - // if(/&/.test(str)) - // str = str.replace(/&/g, " and "); - - // if(/\bi'm\b/.test(str)) - // str = str.replace(/\bi'm\b/g, "i am"); - // if(/\b(can't|cannot)\b/.test(str)) - // str = str.replace(/\b(can't|cannot)\b/g, "can not"); - // if(/\bwon't\b/.test(str)) - // str = str.replace(/\bwon't\b/g, "will not"); - // if(/[a-z]n't\b/.test(str)) - // str = str.replace(/([a-z])n't\b/g, "$1 not"); - // if(/[a-z]'s\b/.test(str)) - // str = str.replace(/([a-z])'s\b/g, "$1 is has"); - // if(/[a-z]'ll\b/.test(str)) - // str = str.replace(/([a-z])'ll\b/g, "$1 will"); - // if(/[a-z]'re\b/.test(str)) - // str = str.replace(/([a-z])'re\b/g, "$1 are"); - // if(/[a-z]'ve\b/.test(str)) - // str = str.replace(/([a-z])'ve\b/g, "$1 have"); - // if(/[a-z]'d\b/.test(str)) - // str = str.replace(/([a-z])'d\b/g, "$1 would had"); - // return str; - - return str//.replace(/[´`’ʼ_&]/g, match => map.get(match)) - // normalization - .replace(/´`’ʼ/g, "'") - .replace(/_+/g, " ") - .replace(/&/g, " and ") - //.replace(/([0-9 ]|^)\$([0-9 ]|$)/g, "$1 USD $2") - //.replace(/([0-9 ]|^)£([0-9 ]|$)/g, "$1 GBP $2") - .replace(/\$/g, " USD ") - .replace(/£/g, " GBP ") - // explode short forms - .replace(/([a-z])'s\b/g, "$1 is") - .replace(/\bi'm\b/g, "i am") - .replace(/\b(can't|cannot)\b/g, "can not") - .replace(/\bwon't\b/g, "will not") - .replace(/([a-z])n't\b/g, "$1 not") - .replace(/([a-z])'ll\b/g, "$1 will") - .replace(/([a-z])'re\b/g, "$1 are") - .replace(/([a-z])'ve\b/g, "$1 have") - .replace(/([a-z])'d\b/g, "$1 would"); + return str + // normalization + .replace(/´`’ʼ/g, "'") + .replace(/_+/g, " ") + .replace(/&/g, " and ") + .replace(/\$/g, " USD ") + .replace(/£/g, " GBP ") + // explode short forms + .replace(/([a-z])'s\b/g, "$1 is") + .replace(/\bi'm\b/g, "i am") + .replace(/\b(can't|cannot)\b/g, "can not") + .replace(/\bwon't\b/g, "will not") + .replace(/([a-z])n't\b/g, "$1 not") + .replace(/([a-z])'ll\b/g, "$1 will") + .replace(/([a-z])'re\b/g, "$1 are") + .replace(/([a-z])'ve\b/g, "$1 have") + .replace(/([a-z])'d\b/g, "$1 would"); }, filter: filter, stemmer: stemmer }; +export default options; diff --git a/src/lang/fr.js b/src/lang/fr.js index eef6e37..83e7b20 100644 --- a/src/lang/fr.js +++ b/src/lang/fr.js @@ -1,3 +1,5 @@ +import { EncoderOptions } from "../type.js"; + /** * http://www.ranks.nl/stopwords * http://snowball.tartarus.org/algorithms/french/stop.txt @@ -202,25 +204,30 @@ export const stemmer = new Map([ ["e", ""] ]); -export default { +/** + * @type EncoderOptions + */ +const options = { prepare: function(str){ - return str.replace(/´`’ʼ/g, "'") - .replace(/_+/g, " ") - .replace(/&/g, " et ") - .replace(/€/g, " EUR ") - .replace(/\bl'([^\b])/g, "la le $1") - .replace(/\bt'([^\b])/g, "ta te $1") - .replace(/\bc'([^\b])/g, "ca ce $1") - .replace(/\bd'([^\b])/g, "da de $1") - .replace(/\bj'([^\b])/g, "ja je $1") - .replace(/\bn'([^\b])/g, "na ne $1") - .replace(/\bm'([^\b])/g, "ma me $1") - .replace(/\bs'([^\b])/g, "sa se $1") - .replace(/\bau\b/g, "a le") - .replace(/\baux\b/g, "a les") - .replace(/\bdu\b/g, "de le") - .replace(/\bdes\b/g, "de les") + return str + .replace(/´`’ʼ/g, "'") + .replace(/_+/g, " ") + .replace(/&/g, " et ") + .replace(/€/g, " EUR ") + .replace(/\bl'([^\b])/g, "la le $1") + .replace(/\bt'([^\b])/g, "ta te $1") + .replace(/\bc'([^\b])/g, "ca ce $1") + .replace(/\bd'([^\b])/g, "da de $1") + .replace(/\bj'([^\b])/g, "ja je $1") + .replace(/\bn'([^\b])/g, "na ne $1") + .replace(/\bm'([^\b])/g, "ma me $1") + .replace(/\bs'([^\b])/g, "sa se $1") + .replace(/\bau\b/g, "a le") + .replace(/\baux\b/g, "a les") + .replace(/\bdu\b/g, "de le") + .replace(/\bdes\b/g, "de les") }, filter: filter, stemmer: stemmer }; +export default options; diff --git a/src/lang/latin/exact.js b/src/lang/latin/exact.js deleted file mode 100644 index c76ee55..0000000 --- a/src/lang/latin/exact.js +++ /dev/null @@ -1,4 +0,0 @@ -export default { - normalize: false, - dedupe: false -}; diff --git a/src/lang/us.js b/src/lang/us.js deleted file mode 100644 index c7e9870..0000000 --- a/src/lang/us.js +++ /dev/null @@ -1,276 +0,0 @@ -/** - * http://www.ranks.nl/stopwords - * @type {Array} - */ - -export const filter = [ - - "a", - "about", - "above", - "after", - "again", - "against", - "all", - "also", - "am", - "an", - "and", - "any", - "are", - "aren't", - "as", - "at", - //"back", - "be", - "because", - "been", - "before", - "being", - "below", - //"between", - "both", - "but", - "by", - "can", - "cannot", - "can't", - "come", - "could", - "couldn't", - //"day", - "did", - "didn't", - "do", - "does", - "doesn't", - "doing", - "dont", - "down", - "during", - "each", - "even", - "few", - "first", - "for", - "from", - "further", - "get", - //"give", - "go", - //"good", - "had", - "hadn't", - "has", - "hasn't", - "have", - "haven't", - "having", - "he", - "hed", - //"hell", - "her", - "here", - "here's", - "hers", - "herself", - "hes", - "him", - "himself", - "his", - "how", - "how's", - "i", - "id", - "if", - "ill", - "im", - "in", - "into", - "is", - "isn't", - "it", - "it's", - "itself", - "i've", - "just", - "know", - "let's", - "like", - //"look", - "make", - "me", - "more", - "most", - "mustn't", - "my", - "myself", - "new", - "no", - "nor", - "not", - "now", - "of", - "off", - "on", - "once", - //"one", - "only", - "or", - "other", - "ought", - "our", - "our's", - "ourselves", - "out", - "over", - "own", - //"people", - "same", - "say", - "see", - "shan't", - "she", - "she'd", - "shell", - "shes", - "should", - "shouldn't", - "so", - "some", - "such", - //"take", - "than", - "that", - "that's", - "the", - "their", - "theirs", - "them", - "themselves", - "then", - "there", - "there's", - "these", - "they", - "they'd", - "they'll", - "they're", - "they've", - //"think", - "this", - "those", - "through", - "time", - "to", - "too", - //"two", - //"under", - "until", - "up", - "us", - //"use", - "very", - "want", - "was", - "wasn't", - "way", - "we", - "wed", - "well", - "were", - "weren't", - "we've", - "what", - "what's", - "when", - "when's", - "where", - "where's", - "which", - "while", - "who", - "whom", - "who's", - "why", - "why's", - "will", - "with", - "won't", - //"work", - "would", - "wouldn't", - //"year", - "you", - "you'd", - "you'll", - "your", - "you're", - "your's", - "yourself", - "yourselves", - "you've" -]; - -/** - * @type {Object} - */ - -export const stemmer = { - - "ational": "ate", - "iveness": "ive", - "fulness": "ful", - "ousness": "ous", - "ization": "ize", - "tional": "tion", - "biliti": "ble", - "icate": "ic", - "ative": "", - "alize": "al", - "iciti": "ic", - "entli": "ent", - "ousli": "ous", - "alism": "al", - "ation": "ate", - "aliti": "al", - "iviti": "ive", - "ement": "", - "enci": "ence", - "anci": "ance", - "izer": "ize", - "alli": "al", - "ator": "ate", - "logi": "log", - "ical": "ic", - "ance": "", - "ence": "", - "ness": "", - "able": "", - "ible": "", - "ment": "", - "eli": "e", - "bli": "ble", - "ful": "", - "ant": "", - "ent": "", - "ism": "", - "ate": "", - "iti": "", - "ous": "", - "ive": "", - "ize": "", - "al": "", - "ou": "", - "er": "", - "ic": "" -}; - -export const matcher = {}; - -export default { - - filter: filter, - stemmer: stemmer, - matcher: matcher -} diff --git a/src/resolve/and.js b/src/resolve/and.js index 5056ea4..c419bab 100644 --- a/src/resolve/and.js +++ b/src/resolve/and.js @@ -12,7 +12,7 @@ Resolver.prototype.and = function(){ let args = arguments; let first_argument = args[0]; - if(first_argument instanceof Promise){ + if(first_argument.then){ return first_argument.then(function(){ return self.and.apply(self, args); }); @@ -46,7 +46,7 @@ Resolver.prototype.and = function(){ if((query = args[i])){ let result; - if(query instanceof Resolver){ + if(query.constructor === Resolver){ result = query.result; } else if(query.constructor === Array){ @@ -75,7 +75,7 @@ Resolver.prototype.and = function(){ final[i] = result; - if(result instanceof Promise){ + if(result.then){ promises.push(result); //{ query, result }; } } diff --git a/src/resolve/not.js b/src/resolve/not.js index aeacf61..d52f228 100644 --- a/src/resolve/not.js +++ b/src/resolve/not.js @@ -10,7 +10,7 @@ Resolver.prototype.not = function(){ let args = arguments; let first_argument = args[0]; - if(first_argument instanceof Promise){ + if(first_argument.then){ return first_argument.then(function(){ return self.not.apply(self, args); }); @@ -31,7 +31,7 @@ Resolver.prototype.not = function(){ if((query = args[i])){ let result; - if(query instanceof Resolver){ + if(query.constructor === Resolver){ result = query.result; } else if(query.constructor === Array){ @@ -60,7 +60,7 @@ Resolver.prototype.not = function(){ final[i] = result; - if(result instanceof Promise){ + if(result.then){ promises.push(result); //{ query, result }; } } diff --git a/src/resolve/or.js b/src/resolve/or.js index ae701b9..89f4c42 100644 --- a/src/resolve/or.js +++ b/src/resolve/or.js @@ -11,7 +11,7 @@ Resolver.prototype.or = function(){ let args = arguments; let first_argument = args[0]; - if(first_argument instanceof Promise){ + if(first_argument.then){ return first_argument.then(function(){ return self.or.apply(self, args); }); @@ -57,7 +57,7 @@ Resolver.prototype.or = function(){ if((query = args[i])){ let result; - if(query instanceof Resolver){ + if(query.constructor === Resolver){ result = query.result; } else if(query.constructor === Array){ @@ -86,7 +86,7 @@ Resolver.prototype.or = function(){ final[i] = result; - if(result instanceof Promise){ + if(result.then){ promises.push(result); //{ query, result }; } } diff --git a/src/resolve/xor.js b/src/resolve/xor.js index f85f8ec..ff0e1ee 100644 --- a/src/resolve/xor.js +++ b/src/resolve/xor.js @@ -10,7 +10,7 @@ Resolver.prototype.xor = function(){ let args = arguments; let first_argument = args[0]; - if(first_argument instanceof Promise){ + if(first_argument.then){ return first_argument.then(function(){ return self.xor.apply(self, args); }); @@ -31,7 +31,7 @@ Resolver.prototype.xor = function(){ if((query = args[i])){ let result; - if(query instanceof Resolver){ + if(query.constructor === Resolver){ result = query.result; } else if(query.constructor === Array){ @@ -60,7 +60,7 @@ Resolver.prototype.xor = function(){ final[i] = result; - if(result instanceof Promise){ + if(result.then){ promises.push(result); //{ query, result }; } } diff --git a/src/resolver.js b/src/resolver.js index f6c62d4..cf38f60 100644 --- a/src/resolver.js +++ b/src/resolver.js @@ -1,9 +1,5 @@ import default_resolver from "./resolve/default.js"; import { set_resolve } from "./index/search.js"; -// import or from "./resolve/or.js"; -// import and from "./resolve/and.js"; -// import xor from "./resolve/xor.js"; -// import not from "./resolve/not.js"; import "./resolve/or.js"; import "./resolve/and.js"; import "./resolve/xor.js"; @@ -15,16 +11,16 @@ import "./resolve/not.js"; */ export default function Resolver(result){ + if(!this){ + return new Resolver(result); + } if(result && result.index){ result.resolve = false; this.index = result.index; return result.index.search(result); } - if(!(this instanceof Resolver)){ - return new Resolver(result); - } - if(result instanceof Resolver){ - // todo remove + if(result.constructor === Resolver){ + // todo test this branch //console.log("Resolver Loopback") return result; } @@ -33,11 +29,6 @@ export default function Resolver(result){ this.boostval = 0; } -// Resolver.prototype.or = or; -// Resolver.prototype.and = and; -// Resolver.prototype.not = not; -// Resolver.prototype.xor = xor; - Resolver.prototype.limit = function(limit){ if(this.result.length){ const final = []; diff --git a/src/type.js b/src/type.js index 0c377c0..6218213 100644 --- a/src/type.js +++ b/src/type.js @@ -23,6 +23,29 @@ import StorageInterface from "./db/interface.js"; */ export let IndexOptions; +/** + * @typedef DocumentIndexOptions {{ + * preset: string|undefined, + * context: (IndexOptions|undefined), + * encoder: Encoder|Function|Object|undefined, + * encode: Function|undefined, + * resolution: [number=9], + * tokenize: [string="strict"], + * fastupdate: [boolean:false], + * score: Function]|undefined, + * keystore: [number=0], + * rtl: [boolean=false], + * cache: [number=null], + * db: StorageInterface|undefined, + * config: string|undefined, + * + * field: string, + * filter: Function|undefined, + * custom: Function|undefined + * }} + */ +export let DocumentIndexOptions; + /** * @typedef DocumentOptions {{ * context: (IndexOptions|undefined), @@ -57,33 +80,11 @@ export let ContextOptions; * field: FieldOptions|Array|undefined, * index: FieldOptions|Array|undefined, * tag: TagOptions|Array|undefined, - * store: StoreOptions|Array|undefined, - * config: string|undefined + * store: StoreOptions|Array|undefined * }} */ export let DocumentDescriptor; -/** - * @typedef FieldOptions {{ - * field: string, - * filter: Function|undefined, - * custom: Function|undefined, - * context: (IndexOptions|undefined), - * encoder: Encoder|Function|Object|undefined, - * encode: Function|undefined, - * resolution: [number=9], - * tokenize: [string="strict"], - * fastupdate: [boolean:false], - * score: Function]|undefined, - * keystore: [number=0], - * rtl: [boolean=false], - * cache: [number=null], - * db: StorageInterface|undefined, - * config: string|undefined - * }} - */ -export let FieldOptions; - /** * @typedef TagOptions {{ * field: string, @@ -102,8 +103,6 @@ export let TagOptions; * field: string, * filter: Function|undefined, * custom: Function|undefined, - * keystore: [number=0], - * db: StorageInterface|undefined, * config: string|undefined * }} */ @@ -118,10 +117,10 @@ export let StoreOptions; * suggest: [boolean=false], * resolve: [boolean=true], * enrich: [boolean=false], - * tag: Array|undefined * }} */ export let SearchOptions; +// tag: Array|undefined /** * @typedef DocumentSearchOptions {{ @@ -131,14 +130,44 @@ export let SearchOptions; * context: boolean|undefined, * suggest: [boolean=false], * enrich: [boolean=false], - * tag: Array|undefined, - * field: FieldOptions|Array|undefined, - * index: FieldOptions|Array|undefined, + * tag: Object|Array|undefined, + * field: Array|undefined, + * index: Array|undefined, * pluck: boolean|undefined, * merge: [boolean=false] * }} */ export let DocumentSearchOptions; +/** + * @typedef EncoderOptions {{ + * rtl: boolean=false, + * dedupe: boolean=true, + * split: string|undefined, + * include: EncoderSplitOptions|undefined, + * exclude: EncoderSplitOptions|undefined, + * prepare: function(string):string|undefined, + * finalize: function(Array<>string>):Array|undefined, + * filter: Set|undefined, + * matcher: Map|undefined, + * mapper: Map|undefined, + * stemmer: Map|undefined, + * replacer: Array|undefined, + * minlength: number=1, + * maxlength: number|undefined, + * cache: boolean=true, + * }} + */ export let EncoderOptions; -export let ResolverOptions; \ No newline at end of file + +/** + * @typedef EncoderSplitOptions {{ + * letter: boolean=false, + * number: boolean=false, + * symbol: boolean=false, + * punctuation: boolean=false, + * control: boolean=false, + * char: string|Array|undefined, + * }} + */ +export let EncoderSplitOptions; diff --git a/src/webpack.js b/src/webpack.js index 71b95c0..8c1451d 100644 --- a/src/webpack.js +++ b/src/webpack.js @@ -1,16 +1,12 @@ // COMPILER BLOCK --> import { RELEASE, - SUPPORT_ASYNC, SUPPORT_DOCUMENT, - SUPPORT_CACHE, - SUPPORT_SERIALIZE, SUPPORT_WORKER, SUPPORT_ENCODER, SUPPORT_CHARSET, SUPPORT_PERSISTENT, - SUPPORT_RESOLVER, - SUPPORT_STORE + SUPPORT_RESOLVER } from "./config.js"; // <-- COMPILER BLOCK import { @@ -18,9 +14,13 @@ import { ContextOptions, DocumentDescriptor, DocumentSearchOptions, - FieldOptions, + DocumentIndexOptions, IndexOptions, - DocumentOptions + DocumentOptions, + TagOptions, + StoreOptions, + EncoderOptions, + EncoderSplitOptions } from "./type.js"; import Document from "./document.js"; import Index from "./index.js"; @@ -28,14 +28,7 @@ import WorkerIndex from "./worker/index.js"; import Resolver from "./resolver.js"; import Encoder from "./encoder.js"; import IdxDB from "./db/indexeddb/index.js"; -import { global_charset } from "./charset.js"; -import charset_exact from "./lang/latin/exact.js" -import charset_default from "./lang/latin/default.js" -import charset_simple from "./lang/latin/simple.js" -import charset_balance from "./lang/latin/balance.js" -import charset_advanced from "./lang/latin/advanced.js" -import charset_extra from "./lang/latin/extra.js" -import charset_soundex from "./lang/latin/soundex.js" +import Charset from "./charset.js"; /** @export */ Index.prototype.add; /** @export */ Index.prototype.append; @@ -45,8 +38,18 @@ import charset_soundex from "./lang/latin/soundex.js" /** @export */ Index.prototype.contain; /** @export */ Index.prototype.clear; /** @export */ Index.prototype.cleanup; +/** @export */ Index.prototype.searchCache; +/** @export */ Index.prototype.addAsync; +/** @export */ Index.prototype.appendAsync; +/** @export */ Index.prototype.searchAsync; +/** @export */ Index.prototype.updateAsync; +/** @export */ Index.prototype.removeAsync; +/** @export */ Index.prototype.export; +/** @export */ Index.prototype.import; +/** @export */ Index.prototype.mount; +/** @export */ Index.prototype.commit; +/** @export */ Index.db; -if(SUPPORT_DOCUMENT){ /** @export */ Document.prototype.add; /** @export */ Document.prototype.append; /** @export */ Document.prototype.search; @@ -55,53 +58,39 @@ if(SUPPORT_DOCUMENT){ /** @export */ Document.prototype.contain; /** @export */ Document.prototype.clear; /** @export */ Document.prototype.cleanup; -} -if(SUPPORT_DOCUMENT && SUPPORT_STORE){ -/** @export */ Document.prototype.get; -/** @export */ Document.prototype.set; -} - -if(SUPPORT_CACHE){ -/** @export */ Index.prototype.searchCache; -} -if(SUPPORT_CACHE && SUPPORT_DOCUMENT){ -/** @export */ Document.prototype.searchCache; -} - -if(SUPPORT_ASYNC){ -/** @export */ Index.prototype.addAsync; -/** @export */ Index.prototype.appendAsync; -/** @export */ Index.prototype.searchAsync; -/** @export */ Index.prototype.updateAsync; -/** @export */ Index.prototype.removeAsync; -} -if(SUPPORT_ASYNC && SUPPORT_DOCUMENT){ /** @export */ Document.prototype.addAsync; /** @export */ Document.prototype.appendAsync; /** @export */ Document.prototype.searchAsync; /** @export */ Document.prototype.updateAsync; /** @export */ Document.prototype.removeAsync; -} - -if(SUPPORT_SERIALIZE){ -/** @export */ Index.prototype.export; -/** @export */ Index.prototype.import; -} -if(SUPPORT_SERIALIZE && SUPPORT_DOCUMENT){ -/** @export */ Document.prototype.export; -/** @export */ Document.prototype.import; -} - -if(SUPPORT_PERSISTENT){ -/** @export */ Index.prototype.mount; -/** @export */ Index.prototype.commit; -/** @export */ Index.db; -} -if(SUPPORT_PERSISTENT && SUPPORT_DOCUMENT){ /** @export */ Document.prototype.mount; /** @export */ Document.prototype.commit; /** @export */ Document.db; -} +/** @export */ Document.prototype.export; +/** @export */ Document.prototype.import; +/** @export */ Document.prototype.searchCache; +/** @export */ Document.prototype.get; +/** @export */ Document.prototype.set; + +/** @export */ Resolver.prototype.limit; +/** @export */ Resolver.prototype.offset; +/** @export */ Resolver.prototype.boost; +/** @export */ Resolver.prototype.resolve; +/** @export */ Resolver.prototype.or; +/** @export */ Resolver.prototype.and; +/** @export */ Resolver.prototype.xor; +/** @export */ Resolver.prototype.not; + +/** @export */ Charset.LatinExact; +/** @export */ Charset.LatinDefault; +/** @export */ Charset.LatinSimple; +/** @export */ Charset.LatinBalance; +/** @export */ Charset.LatinAdvanced; +/** @export */ Charset.LatinExtra; +/** @export */ Charset.LatinSoundex; +/** @export */ Charset.ArabicDefault; +/** @export */ Charset.CjkDefault; +/** @export */ Charset.CyrillicDefault; /** @export */ IndexOptions.preset; /** @export */ IndexOptions.context; @@ -116,6 +105,25 @@ if(SUPPORT_PERSISTENT && SUPPORT_DOCUMENT){ /** @export */ IndexOptions.cache; /** @export */ IndexOptions.resolve; /** @export */ IndexOptions.db; +/** @export */ IndexOptions.config; + +/** @export */ DocumentIndexOptions.preset; +/** @export */ DocumentIndexOptions.context; +/** @export */ DocumentIndexOptions.encoder; +/** @export */ DocumentIndexOptions.encode; +/** @export */ DocumentIndexOptions.resolution; +/** @export */ DocumentIndexOptions.tokenize; +/** @export */ DocumentIndexOptions.fastupdate; +/** @export */ DocumentIndexOptions.score; +/** @export */ DocumentIndexOptions.keystore; +/** @export */ DocumentIndexOptions.rtl; +/** @export */ DocumentIndexOptions.cache; +/** @export */ DocumentIndexOptions.db; +/** @export */ DocumentIndexOptions.config; +// /** @export */ DocumentIndexOptions.resolve; +/** @export */ DocumentIndexOptions.field; +/** @export */ DocumentIndexOptions.filter; +/** @export */ DocumentIndexOptions.custom; /** @export */ DocumentOptions.context; /** @export */ DocumentOptions.encoder; @@ -132,14 +140,27 @@ if(SUPPORT_PERSISTENT && SUPPORT_DOCUMENT){ /** @export */ DocumentOptions.document; /** @export */ DocumentOptions.worker; +/** @export */ ContextOptions.depth; +/** @export */ ContextOptions.bidirectional; +/** @export */ ContextOptions.resolution; + /** @export */ DocumentDescriptor.field; /** @export */ DocumentDescriptor.index; /** @export */ DocumentDescriptor.tag; /** @export */ DocumentDescriptor.store; -/** @export */ ContextOptions.depth; -/** @export */ ContextOptions.bidirectional; -/** @export */ ContextOptions.resolution; +/** @export */ TagOptions.field; +/** @export */ TagOptions.tag; +/** @export */ TagOptions.filter; +/** @export */ TagOptions.custom; +/** @export */ TagOptions.keystore; +/** @export */ TagOptions.db; +/** @export */ TagOptions.config; + +/** @export */ StoreOptions.field; +/** @export */ StoreOptions.filter; +/** @export */ StoreOptions.custom; +/** @export */ StoreOptions.config; /** @export */ SearchOptions.query; /** @export */ SearchOptions.limit; @@ -148,7 +169,6 @@ if(SUPPORT_PERSISTENT && SUPPORT_DOCUMENT){ /** @export */ SearchOptions.suggest; /** @export */ SearchOptions.resolve; /** @export */ SearchOptions.enrich; -/** @export */ SearchOptions.tag; /** @export */ DocumentSearchOptions.query; /** @export */ DocumentSearchOptions.limit; @@ -162,26 +182,38 @@ if(SUPPORT_PERSISTENT && SUPPORT_DOCUMENT){ /** @export */ DocumentSearchOptions.pluck; /** @export */ DocumentSearchOptions.merge; -if(SUPPORT_CHARSET){ - global_charset["latin:exact"] = charset_exact; - global_charset["latin:default"] = charset_default; - global_charset["latin:simple"] = charset_simple; - global_charset["latin:balance"] = charset_balance; - global_charset["latin:advanced"] = charset_advanced; - global_charset["latin:extra"] = charset_extra; - global_charset["latin:soundex"] = charset_soundex; -} +/** @export */ EncoderOptions.rtl; +/** @export */ EncoderOptions.dedupe; +/** @export */ EncoderOptions.split; +/** @export */ EncoderOptions.include; +/** @export */ EncoderOptions.exclude; +/** @export */ EncoderOptions.prepare; +/** @export */ EncoderOptions.finalize; +/** @export */ EncoderOptions.filter; +/** @export */ EncoderOptions.matcher; +/** @export */ EncoderOptions.mapper; +/** @export */ EncoderOptions.stemmer; +/** @export */ EncoderOptions.replacer; +/** @export */ EncoderOptions.minlength; +/** @export */ EncoderOptions.maxlength; +/** @export */ EncoderOptions.cache; + +/** @export */ EncoderSplitOptions.letter; +/** @export */ EncoderSplitOptions.number; +/** @export */ EncoderSplitOptions.symbol; +/** @export */ EncoderSplitOptions.punctuation; +/** @export */ EncoderSplitOptions.control; +/** @export */ EncoderSplitOptions.char; const FlexSearch = { "Index": Index, - "Charset": global_charset, + "Charset": SUPPORT_CHARSET ? Charset : null, "Encoder": SUPPORT_ENCODER ? Encoder : null, "Document": SUPPORT_DOCUMENT ? Document : null, "Worker": SUPPORT_WORKER ? WorkerIndex : null, "Resolver": SUPPORT_RESOLVER ? Resolver : null, "IndexedDB": SUPPORT_PERSISTENT ? IdxDB : null, - //"registerCharset": registerCharset, - //"registerLanguage": registerLanguage + "Language": {} }; // Export as library (Bundle) @@ -192,6 +224,8 @@ if(RELEASE !== "bundle.module" && RELEASE !== "compact.module" && RELEASE !== "custom.module"){ + FlexSearch["Language"] = {}; + const root = self; let prop; diff --git a/src/worker/index.js b/src/worker/index.js index c6be06c..eb82383 100644 --- a/src/worker/index.js +++ b/src/worker/index.js @@ -12,7 +12,7 @@ let pid = 0; function WorkerIndex(options){ - if(!(this instanceof WorkerIndex)) { + if(!this) { return new WorkerIndex(options); } diff --git a/task/babel.js b/task/babel.js index 85cc128..8e8b10b 100644 --- a/task/babel.js +++ b/task/babel.js @@ -23,8 +23,10 @@ fs.existsSync("dist") || fs.mkdirSync("dist"); fs.existsSync("./tmp/db") || fs.mkdirSync("./tmp/db/"); fs.existsSync("./tmp/lang") || fs.mkdirSync("./tmp/lang/"); + fs.existsSync("./tmp/charset") || fs.mkdirSync("./tmp/charset/"); - ["db/clickhouse", + ["db/", + "db/clickhouse", "db/indexeddb", "db/mongo", "db/postgres", @@ -35,7 +37,11 @@ fs.existsSync("dist") || fs.mkdirSync("dist"); "resolve", "worker", "lang", - "lang/latin", + "charset/", + "charset/latin", + "charset/arabic", + "charset/cjk", + "charset/cyrillic" ].forEach(await async function(path){ fs.existsSync("./tmp/" + path + "/") || fs.mkdirSync("./tmp/" + path + "/"); files = await fs.promises.readdir("./src/" + path + "/"); @@ -50,9 +56,9 @@ fs.existsSync("dist") || fs.mkdirSync("dist"); }); }); - fs.copyFileSync("src/db/interface.js", "tmp/db/interface.js"); + //fs.copyFileSync("src/db/interface.js", "tmp/db/interface.js"); fs.copyFileSync("task/babel." + (debug ? "debug": (minify ? "min" : "bundle")) + ".json", "tmp/.babelrc"); - fs.rmSync("dist/module" + (debug ? "-debug" : (minify ? "-min" : "")), { recursive: true }); + fs.existsSync("dist/module" + (debug ? "-debug" : (minify ? "-min" : ""))) && fs.rmSync("dist/module" + (debug ? "-debug" : (minify ? "-min" : "")), { recursive: true }); fs.mkdirSync("dist/module" + (debug ? "-debug" : (minify ? "-min" : ""))); exec("npx babel tmp -d dist/module" + (debug ? "-debug" : (minify ? "-min --minified --compact true" : "")) + " --config-file tmp/.babelrc && exit 0", function(){ diff --git a/task/build.js b/task/build.js index 7394301..83c3846 100644 --- a/task/build.js +++ b/task/build.js @@ -10,12 +10,11 @@ fs.existsSync("dist") || fs.mkdirSync("dist"); let supported_lang = [ 'en', 'de', - 'fr', - 'us' + 'fr' ]; let supported_charset = { - 'latin': ["default", "advanced", "balance", "extra", "simple", "extreme"], + 'latin': ["exact", "default", "simple", "balance", "advanced", "extra", "soundex"], 'cjk': ["default"], 'cyrillic': ["default"], 'arabic': ["default"], @@ -75,9 +74,9 @@ let parameter = (function(opt){ if(opt.hasOwnProperty(index)){ - if(release !== "lang"){ + //if(release !== "lang"){ parameter += ' --' + index + '=' + opt[index]; - } + //} } } @@ -102,7 +101,7 @@ let parameter = (function(opt){ module_resolution: "BROWSER", //dependency_mode: "SORT_ONLY", //js_module_root: "./", - entry_point: "./tmp/webpack.js", + entry_point: release === "lang" ? "./tmp/lang.js" : "./tmp/webpack.js", //manage_closure_dependencies: true, dependency_mode: "PRUNE", // PRUNE_LEGACY rewrite_polyfills: use_polyfill || false, @@ -122,77 +121,104 @@ if(!release.endsWith(".module")){ parameter += ' --emit_use_strict=true'; } -// if(language_out === "ECMASCRIPT5_STRICT"){ -// parameter += " --js='!tmp/keystore.js'"; -// } - const custom = (!release || release.startsWith("custom")) && hashCode(parameter + flag_str).replace(/[^a-zA-Z0-9]/g, "").toLowerCase(); if(custom){ - release || (options["RELEASE"] = release = "custom"); } -// if(release === "lang"){ -// -// const charsets = Object.keys(supported_charset); -// -// (function next(x, y, z){ -// -// if(x < supported_lang.length){ -// -// (function(lang){ -// -// fs.writeFileSync("tmp/" + lang + ".js", ` -// import lang from "../src/lang/${lang}.js"; -// self["FlexSearch"]["registerLanguage"]("${lang}", lang); -// `); -// -// exec("java -jar node_modules/google-closure-compiler-java/compiler.jar" + parameter + " --entry_point='tmp/" + lang + ".js' --js='tmp/" + lang + ".js' --js='src/**.js'" + flag_str + " --js_output_file='dist/lang/" + lang + ".min.js' && exit 0", function(){ -// -// console.log("Build Complete: " + lang + ".min.js"); -// next(++x, y, z); -// }); -// -// })(supported_lang[x]); -// } -// else if(y < charsets.length){ -// -// const charset = charsets[y]; -// const variants = supported_charset[charset]; -// -// if(z < variants.length){ -// -// (function(charset, variant){ -// -// fs.writeFileSync("tmp/" + charset + "_" + variant + ".js", ` -// import charset from "../src/lang/${charset}/${variant}.js"; -// /*try{if(module)self=module}catch(e){}*/ -// self["FlexSearch"]["registerCharset"]("${charset}:${variant}", charset); -// `); -// -// exec("java -jar node_modules/google-closure-compiler-java/compiler.jar" + parameter + " --entry_point='tmp/" + charset + "_" + variant + ".js' --js='tmp/" + charset + "_" + variant + ".js' --js='src/**.js'" + flag_str + " --js_output_file='dist/lang/" + charset + "/" + variant + ".min.js' && exit 0", function(){ -// -// console.log("Build Complete: " + charset + "/" + variant + ".min.js"); -// next(x, y, ++z); -// }); -// -// })(charset, variants[z]); -// } -// else{ -// -// next(x, ++y, 0); -// } -// } -// -// }(0, 0, 0)); -// } -// else{ +if(release === "lang"){ -if(release === "lang") throw new Error("disabled"); + //const charsets = Object.keys(supported_charset); -(async function(){ + //fs.existsSync("tmp/lang/") || fs.mkdirSync("tmp/lang/"); + fs.cpSync("src/", "tmp/", { recursive: true }); + fs.copyFileSync("src/db/interface.js", "tmp/db/interface.js"); + + (function next(x, y, z){ + + if(x < supported_lang.length){ + + (function(lang){ + + //fs.copyFileSync("src/lang/" + lang + ".js", "tmp/lang/" + lang + ".js"); + + //console.log(lang) + + fs.writeFileSync("tmp/lang.js", ` + import { EncoderOptions, EncoderSplitOptions } from "./type.js"; + import lang from "./lang/${lang}.js"; + /** @export */ EncoderOptions.rtl; + /** @export */ EncoderOptions.dedupe; + /** @export */ EncoderOptions.split; + /** @export */ EncoderOptions.include; + /** @export */ EncoderOptions.exclude; + /** @export */ EncoderOptions.prepare; + /** @export */ EncoderOptions.finalize; + /** @export */ EncoderOptions.filter; + /** @export */ EncoderOptions.matcher; + /** @export */ EncoderOptions.mapper; + /** @export */ EncoderOptions.stemmer; + /** @export */ EncoderOptions.replacer; + /** @export */ EncoderOptions.minlength; + /** @export */ EncoderOptions.maxlength; + /** @export */ EncoderOptions.cache; + + /** @export */ EncoderSplitOptions.letter; + /** @export */ EncoderSplitOptions.number; + /** @export */ EncoderSplitOptions.symbol; + /** @export */ EncoderSplitOptions.punctuation; + /** @export */ EncoderSplitOptions.control; + /** @export */ EncoderSplitOptions.char; + if(typeof module !== "undefined" && module["exports"]) module["exports"] = lang; + else if(self["FlexSearch"]) self["FlexSearch"]["Language"]['${lang}'] = lang; + `); + + const executable = process.platform === "win32" ? "\"node_modules/google-closure-compiler-windows/compiler.exe\"" : + process.platform === "darwin" ? "\"node_modules/google-closure-compiler-osx/compiler\"" : + "java -jar node_modules/google-closure-compiler-java/compiler.jar"; + + exec(executable + parameter + " --js='tmp/**.js' --js='!tmp/db/**.js' --js='tmp/db/interface.js'" + flag_str + " --js_output_file='dist/lang/" + lang + ".min.js' && exit 0", function(){ + + console.log("Build Complete: " + lang + ".min.js"); + next(++x, y, z); + }); + + })(supported_lang[x]); + } + // else if(y < charsets.length){ + // + // const charset = charsets[y]; + // const variants = supported_charset[charset]; + // + // if(z < variants.length){ + // + // (function(charset, variant){ + // + // fs.writeFileSync("tmp/" + charset + "_" + variant + ".js", ` + // import charset from "../src/lang/${charset}/${variant}.js"; + // /*try{if(module)self=module}catch(e){}*/ + // self["FlexSearch"]["registerCharset"]("${charset}:${variant}", charset); + // `); + // + // exec("java -jar node_modules/google-closure-compiler-java/compiler.jar" + parameter + " --entry_point='tmp/" + charset + "_" + variant + ".js' --js='tmp/" + charset + "_" + variant + ".js' --js='src/**.js'" + flag_str + " --js_output_file='dist/lang/" + charset + "/" + variant + ".min.js' && exit 0", function(){ + // + // console.log("Build Complete: " + charset + "/" + variant + ".min.js"); + // next(x, y, ++z); + // }); + // + // })(charset, variants[z]); + // } + // else{ + // + // next(x, ++y, 0); + // } + // } + + }(0, 0, 0)); +} +else (async function(){ const files = await fs.promises.readdir("./src/"); @@ -274,6 +300,7 @@ if(release === "lang") throw new Error("disabled"); fs.cpSync("src/index/", "tmp/index/", { recursive: true }); fs.cpSync("src/document/", "tmp/document/", { recursive: true }); fs.cpSync("src/resolve/", "tmp/resolve/", { recursive: true }); + fs.cpSync("src/charset/", "tmp/charset/", { recursive: true }); const filename = "dist/flexsearch." + (release + (custom ? "." + custom : "")) + (options["DEBUG"] ? ".debug" : ".min") + ".js"; const executable = process.platform === "win32" ? "\"node_modules/google-closure-compiler-windows/compiler.exe\"" : @@ -341,6 +368,7 @@ if(release === "lang") throw new Error("disabled"); build = build.replace(/\(0,eval\)\('([^']+)'\)/, "$1"); fs.writeFileSync(filename, build); + fs.existsSync("dist/node/") || fs.mkdirSync("dist/node/"); fs.copyFileSync("src/worker/node.js", "dist/node/node.js"); console.log("Saved to " + filename);