@nano-sql/plugin-fuzzy-search
Version:
Integrate Elastic Search style indexing in NanoSQL 2!
821 lines • 55.2 kB
JavaScript
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
var utilities_1 = require("@nano-sql/core/lib/utilities");
var metaphone = require("metaphone");
var stemmer = require("stemmer");
exports.stopWords = [
"a", "about", "after", "all", "also", "am", "an", "and", "andor", "another", "any",
"are", "as", "at", "be", "because", "been", "before", "being", "between",
"both", "but", "by", "came", "can", "come", "could", "did", "do", "each",
"for", "from", "get", "got", "had", "has", "have", "he", "her", "here",
"him", "himself", "his", "how", "i", "if", "in", "into", "is", "it", "like",
"make", "many", "me", "might", "more", "most", "much", "must", "my", "never",
"now", "of", "on", "only", "or", "other", "our", "out", "over", "said", "same",
"see", "should", "since", "some", "still", "such", "take", "than", "that", "the",
"their", "them", "then", "there", "these", "they", "this", "those", "through",
"to", "too", "under", "up", "very", "was", "way", "we", "well", "were", "what",
"where", "which", "while", "who", "with", "would", "you", "your"
];
exports.defaultTokenizer = function (type, stpWrds, decimalPoints) {
if (decimalPoints === void 0) { decimalPoints = 4; }
return function (tableName, tableId, path, value) {
var isStopWord = function (word) {
return !word || word === null ? true : // is this word falsey? (ie no length, undefined, etc);
String(word).length === 1 ? true : // is this word 1 length long?
stpWrds.indexOf(word) !== -1; // does word match something in the stop word list?
};
// Step 1, Clean up and normalize the text
var words = String(value || "")
// everything to lowercase
.toLowerCase()
// normalize fractions and numbers (1/4 => 0.2500, 1,000,235 => 100235.0000)
.replace(/(\d+)\/(\d+)|(?:\d+(?:,\d+)*|\d+)(?:\.\d+)?/gmi, function (all, top, bottom) { return top || bottom ? (parseInt(top) / parseInt(bottom)).toFixed(decimalPoints) : (parseFloat(all.replace(/\,/gmi, ""))).toFixed(decimalPoints); })
// replace dashes, underscores, anything like parantheses, slashes, newlines and tabs with a single whitespace
.replace(/\-|\_|\[|\]|\(|\)|\{|\}|\r?\n|\r|\t/gmi, " ")
// remove anything but letters, numbers and decimals inside numbers with nothing.
.replace(/[^\w\s]|(\d\.)/gmi, "$1")
// remove white spaces larger than 1 with 1 white space.
.replace(/\s+/g, " ")
.split(" ");
// Step 2, tokenize!
switch (type) {
case "english": return words.map(function (w, i) { return ({
i: i,
w: isNaN(w) ? (isStopWord(w) ? "" : metaphone(stemmer(w))) : w
}); }).filter(function (f) { return f.w; });
case "english-stem": return words.map(function (w, i) { return ({
i: i,
w: isNaN(w) ? (isStopWord(w) ? "" : stemmer(w)) : w
}); }).filter(function (f) { return f.w; });
case "english-meta": return words.map(function (w, i) { return ({
i: i,
w: isNaN(w) ? (isStopWord(w) ? "" : metaphone(w)) : w
}); }).filter(function (f) { return f.w; });
}
// no tokenization: 2,684 words/ms
return words.map(function (w, i) { return ({ w: w, i: i }); });
};
};
var searchIndexes = {};
var indexLocks = {};
var addRowToFuzzy = function (newRow, tableId, pkPath, nSQL, query, complete) {
var newRowData = newRow;
var newRowPK = utilities_1.deepGet(pkPath, newRowData);
var filters = utilities_1.adapterFilters(query.databaseID, nSQL, query);
utilities_1.allAsync(searchIndexes[tableId], function (item, i, next, err) {
var phrase = utilities_1.deepGet(item.path, newRowData);
if (typeof phrase !== "string" || !phrase) { // nothing to index
next();
return;
}
var phraseHash = utilities_1.hash(phrase);
var tokens = item.tokenizer(item.tableName, item.tableId, item.path, phrase);
var mergedTokens = tokens.reduce(function (prev, cur) {
if (!prev[cur.w]) {
prev[cur.w] = [];
}
prev[cur.w].push(cur.i);
return prev;
}, {});
var tokenArray = tokens.map(function (s) { return s.i + ":" + s.w; });
var indexTableNameWords = "_" + tableId + "_fuzzy_words_" + item.path.join(".");
var indexTableNameWordsId = nSQL.getDB(query.databaseID)._tableIds[indexTableNameWords];
// write cache of row index data
filters.write(indexTableNameWords, newRowPK, {
id: newRowPK,
hash: phraseHash,
tokens: tokenArray
}, function () {
// write words to index
var indexTable = "_" + tableId + "_fuzzy_" + item.path.join(".");
var indexTableId = nSQL.getDB(query.databaseID)._tableIds[indexTable];
utilities_1.allAsync(Object.keys(mergedTokens), function (word, k, nextToken, errToken) {
var writeToken = function () {
filters.read(indexTable, word, function (tokenRow) {
var useRow = utilities_1.maybeAssign(tokenRow) || {
wrd: word,
ids: []
};
useRow.ids.push({ id: newRowPK, i: mergedTokens[word] });
filters.write(indexTable, word, useRow, function () {
delete indexLocks[tableId][word];
nextToken();
}, function (err) {
delete indexLocks[tableId][word];
errToken(err);
});
}, function (err) {
delete indexLocks[tableId][word];
errToken(err);
});
};
var checkTokenLock = function () {
if (indexLocks[tableId][word]) {
setTimeout(function () {
checkTokenLock();
}, 2);
}
else {
indexLocks[tableId][word] = true;
writeToken();
}
};
checkTokenLock();
}).then(next).catch(err);
}, err);
}).then(function () {
complete();
}).catch(complete);
};
var rmRowFromFuzzy = function (newRowData, pkPath, databaseID, nSQL, query, tableId, complete) {
var newRowPK = utilities_1.deepGet(pkPath, newRowData);
var filters = utilities_1.adapterFilters(databaseID, nSQL, query);
utilities_1.allAsync(searchIndexes[tableId], function (item, i, next, err) {
var indexTableNameWords = "_" + tableId + "_fuzzy_words_" + item.path.join(".");
var indexTableNameWordsId = nSQL.getDB(databaseID)._tableIds[indexTableNameWords];
// remove row data cache
filters.delete(indexTableNameWords, newRowPK, function () {
var phrase = utilities_1.deepGet(item.path, newRowData);
if (typeof phrase !== "string" || !phrase) { // nothing to delete
next();
return;
}
var indexTable = "_" + tableId + "_fuzzy_" + item.path.join(".");
var indexTableId = nSQL.getDB(databaseID)._tableIds[indexTable];
var tokens = item.tokenizer(item.tableName, item.tableId, item.path, phrase);
var mergedTokens = tokens.reduce(function (prev, cur) {
if (!prev[cur.w]) {
prev[cur.w] = [];
}
prev[cur.w].push(cur.i);
return prev;
}, {});
utilities_1.allAsync(Object.keys(mergedTokens), function (word, k, nextToken, errToken) {
var writeToken = function () {
filters.read(indexTable, word, function (tokenRow) {
var useRow = utilities_1.maybeAssign(tokenRow) || {
wrd: word,
ids: []
};
var idx = -1;
var i = useRow.ids.length;
while (i-- && idx === -1) {
if (useRow.ids[i].id === newRowPK) {
idx === i;
}
}
if (idx !== -1) {
useRow.ids.splice(idx, 1);
if (!useRow.ids.length) { // no rows left for this token
filters.delete(indexTable, word, function () {
delete indexLocks[tableId][word];
nextToken();
}, function (err) {
delete indexLocks[tableId][word];
errToken(err);
});
}
else { // write other row data back
filters.write(indexTable, word, useRow, function () {
delete indexLocks[tableId][word];
nextToken();
}, function (err) {
delete indexLocks[tableId][word];
errToken(err);
});
}
}
else {
delete indexLocks[tableId][word];
nextToken();
}
}, function (err) {
delete indexLocks[tableId][word];
errToken(err);
});
};
var checkTokenLock = function () {
if (indexLocks[tableId][word]) {
setTimeout(function () {
checkTokenLock();
}, 2);
}
else {
indexLocks[tableId][word] = true;
writeToken();
}
};
checkTokenLock();
}).then(next).catch(err);
}, err);
}).then(function () {
complete();
}).catch(complete);
};
exports.FuzzyUserSanitize = function (str) {
return String(str).replace(/\'|\,/gmi, "");
};
exports.FuzzySearch = function () {
var nSQL;
return {
name: "Fuzzy Search",
version: 2.02,
filters: [
{
name: "willConnect",
priority: 1000,
call: function (inputArgs, complete, cancel) {
nSQL = inputArgs.res;
complete(inputArgs);
nSQL.functions["SEARCH"] = {
type: "S",
call: function (query, row, prev, gpsCol, lat, lon) {
// no standard usage
return {
result: 0
};
},
checkIndex: function (query, fnArgs, where) {
var tableId = nSQL.getDB(query.databaseID)._tableIds[query.table];
// no indexes on this table
if (!searchIndexes[tableId] || !searchIndexes[tableId].length) {
return false;
}
// search all fuzzy indexes
if (fnArgs[0] === "*") {
return {
index: "*",
parsedFn: { name: "SEARCH", args: fnArgs },
comp: where[1],
value: where[2],
col: String(-1)
};
}
var indexPath = utilities_1.resolvePath(fnArgs[0]);
var indexJoined = indexPath.join(".");
var idx = -1;
var i = searchIndexes[tableId].length;
while (i-- && idx === -1) {
if (searchIndexes[tableId][i].path.join(".") == indexJoined) {
idx = i;
}
}
// no matching index found
if (idx === -1) {
return false;
}
return {
index: searchIndexes[tableId][idx].indexId,
parsedFn: { name: "SEARCH", args: fnArgs },
comp: where[1],
value: where[2],
col: String(idx)
};
},
queryIndex: function (query, where, onlyPKs, onRow, complete, error) {
var filters = utilities_1.adapterFilters(query.databaseID, nSQL, query);
var getRows = function (PKs) {
PKs = PKs.filter(function (v, i, s) { return s.indexOf(v) === i; });
if (!PKs.length) {
complete();
return;
}
if (onlyPKs) {
var kk = 0;
while (kk < PKs.length) {
onRow(PKs[kk], kk);
kk++;
}
complete();
}
utilities_1.chainAsync(PKs, function (rowKey, i, next, err) {
var counter = 0;
filters.read(query.table, rowKey, function (row) {
if (!row) {
next();
return;
}
onRow(row, counter);
counter++;
next();
}, err);
}).then(function () {
complete();
}).catch(error);
};
var tableId = nSQL.getDB(query.databaseID)._tableIds[query.table];
var searchTheseIndexes = (parseInt(where.col) !== -1 ? [where.index] : searchIndexes[tableId].map(function (s) { return s.indexId; }));
var secondaryIndexPKS = [];
var getNextSearchTerm = function (searchIdx, indexNum) {
// search done
if (!searchTheseIndexes[indexNum]) {
getRows(secondaryIndexPKS);
return;
}
// out of search terms for this index
// move to next index
if (!where.parsedFn.args[searchIdx]) {
getNextSearchTerm(1, indexNum + 1);
return;
}
// strip trailing and leading quotes from search term
var searchValue = where.parsedFn.args[searchIdx];
var quoteRegex = /^[\"|\'](.*)[\"|\']$/gmi;
var hasQuotes = quoteRegex.exec(searchValue);
var useSeachValue = hasQuotes ? hasQuotes[1] : searchValue;
var useIndex = searchIndexes[tableId][indexNum];
if (!useIndex) {
error("Erro getting fuzzy index!");
return;
}
var phraseTokens = useIndex.tokenizer(useIndex.tableName, useIndex.tableId, useIndex.path, useSeachValue);
// blank search term, cya!
if (!phraseTokens.length) {
complete();
return;
}
var mergedTokens = phraseTokens.reduce(function (prev, cur) {
if (!prev[cur.w]) {
prev[cur.w] = [];
}
prev[cur.w].push(cur.i);
return prev;
}, {});
var tokenLocs = {};
var wordLocs = {};
// get exact matches from secondary index
filters.readIndexKey(query.table, searchTheseIndexes[indexNum], useSeachValue, function (pk) {
secondaryIndexPKS.push(pk);
}, function () {
// now do tokenization and search
utilities_1.allAsync(Object.keys(mergedTokens), function (word, i, nextWord, errWord) {
var indexTable = "_" + tableId + "_fuzzy_" + useIndex.path.join(".");
var indexTableId = nSQL.getDB(query.databaseID)._tableIds[indexTable];
filters.read(indexTable, word, function (tokenRow) {
var useRow = tokenRow || {
wrd: word,
ids: []
};
useRow.ids.forEach(function (rowData) {
if (!wordLocs[String(rowData.id)]) {
wordLocs[String(rowData.id)] = {
rowKey: rowData.id,
words: {}
};
}
wordLocs[String(rowData.id)].words[useRow.wrd] = rowData.i;
});
tokenLocs[word] = useRow;
nextWord();
}, errWord);
}).then(function () {
if (phraseTokens.length <= 1) { // single word search (easy and quick)
if ((where.comp === "=" || where.comp.indexOf("=") !== -1) && where.comp !== "!=" && where.value === 0) {
// doing exact match
Object.keys(tokenLocs).forEach(function (word) {
secondaryIndexPKS = secondaryIndexPKS.concat(tokenLocs[word].ids.map(function (r) { return r.id; }));
});
getNextSearchTerm(searchIdx + 1, indexNum);
}
else {
if (secondaryIndexPKS.length) {
getNextSearchTerm(searchIdx + 1, indexNum);
}
else {
// no matches
getNextSearchTerm(searchIdx + 1, indexNum);
}
}
}
else { // phrase search (oh boy)
var phraseWords_1 = phraseTokens.map(function (s) { return s.w; });
var phraseLocs_1 = phraseTokens.map(function (s) { return s.i; });
var rowSlots_1 = [];
Object.keys(wordLocs).forEach(function (rowID) {
var rowData = wordLocs[rowID];
var newSlot = {
rowKey: rowData.rowKey,
wordSlots: []
};
phraseWords_1.forEach(function (phraseWord, jj) {
newSlot.wordSlots[jj] = rowData.words[phraseWord] || [];
});
rowSlots_1.push(newSlot);
});
rowSlots_1.forEach(function (slot) {
// best score === 0;
var score = 100000;
/*
slot.wordSlots contains an array of phrase matches, in order of the search phrase, found in this row.
each wordSlot contains all the locations for the word match in the target row
we're going to scroll through every possible combination of matched phrases to find the best matches
*/
var recursiveMatch = function () {
var positions = [];
for (var _i = 0; _i < arguments.length; _i++) {
positions[_i] = arguments[_i];
}
var baseScore = 0;
// checkLocs contains the phrase locations we are testing
var checkLocs = slot.wordSlots.map(function (s, i) {
if (!slot.wordSlots[i].length) {
baseScore += 1;
}
return slot.wordSlots[i][positions[i]];
});
var firstPoint;
var phrasePoint;
for (var i = 0; i < checkLocs.length; i++) {
if (checkLocs[i] === undefined) {
baseScore++;
}
else {
if (firstPoint === undefined) {
firstPoint = checkLocs[i];
phrasePoint = phraseLocs_1[i];
}
else {
var diff = Math.abs((checkLocs[i] - firstPoint) - 1);
var phraseDiff = Math.abs((phraseLocs_1[i] - phrasePoint) - 1);
baseScore += Math.abs(diff - phraseDiff);
firstPoint = checkLocs[i];
phrasePoint = phraseLocs_1[i];
}
}
}
// set up score
score = Math.min(score, baseScore);
// setup next loop
// start jumping to the next item on the last phrase, then the next phrase...
var nextAdjustPos = slot.wordSlots.length - 1;
var nextIndex = positions[nextAdjustPos] + 1;
while (nextAdjustPos >= 0 && !slot.wordSlots[nextAdjustPos][nextIndex]) {
nextAdjustPos--;
nextIndex = positions[nextAdjustPos] + 1;
}
var newPos = positions.slice();
newPos[nextAdjustPos] = nextIndex;
// if this is undefined we've ran out of comparisons
if (newPos[nextAdjustPos]) {
recursiveMatch.apply(void 0, newPos);
}
};
// start at position 0, 0, 0, 0, 0...
recursiveMatch.apply(void 0, slot.wordSlots.map(function (s) { return 0; }));
switch (where.comp) {
case "=":
if (score === where.value) {
secondaryIndexPKS.push(slot.rowKey);
}
break;
case ">=":
if (score >= where.value) {
secondaryIndexPKS.push(slot.rowKey);
}
break;
case "<=":
if (score <= where.value) {
secondaryIndexPKS.push(slot.rowKey);
}
break;
case "<":
if (score < where.value) {
secondaryIndexPKS.push(slot.rowKey);
}
break;
case ">":
if (score > where.value) {
secondaryIndexPKS.push(slot.rowKey);
}
break;
case "!=":
if (score !== where.value) {
secondaryIndexPKS.push(slot.rowKey);
}
break;
}
});
getNextSearchTerm(searchIdx + 1, indexNum);
}
});
}, error);
};
getNextSearchTerm(1, 0);
}
};
}
},
{
name: "configTableSystem",
priority: 1000,
call: function (inputArgs, complete, cancel) {
var tableId = inputArgs.res.id;
var tableName = inputArgs.res.name;
var pkKey = "id:" + inputArgs.res.pkType;
if (inputArgs.res.name.indexOf("_") === 0) {
complete(inputArgs);
return;
}
var indexes = inputArgs.res.indexes || {};
searchIndexes[tableId] = [];
indexLocks[tableId] = {};
Object.keys(indexes).forEach(function (indexId) {
if (indexes[indexId].props.search) {
if (typeof indexes[indexId].props.search === "boolean") {
searchIndexes[tableId].push({
indexId: indexId,
tableName: tableName,
tableId: tableId,
path: indexes[indexId].path,
tokenizer: exports.defaultTokenizer("english", exports.stopWords)
});
}
else {
searchIndexes[tableId].push({
indexId: indexId,
tableName: tableName,
tableId: tableId,
path: indexes[indexId].path,
tokenizer: indexes[indexId].props.search.tokenizer || exports.defaultTokenizer("english", exports.stopWords)
});
}
}
});
utilities_1.allAsync(searchIndexes[tableId], function (item, i, next, err) {
var _a;
var indexTableName = "_" + tableId + "_fuzzy_" + item.path.join(".");
// store the data for each token
nSQL.triggerQuery(inputArgs.query.databaseID, __assign({}, utilities_1.buildQuery(inputArgs.query.databaseID, nSQL, "", "create table"), { actionArgs: {
name: indexTableName,
_internal: true,
model: {
"wrd:string": { pk: true },
"ids:any[]": {
notNull: true,
model: (_a = {},
_a[pkKey] = {},
_a["i:int[]"] = {},
_a)
}
}
} }), utilities_1.noop, function () {
var _a;
// store the locations of each token for every row
// allows us to diff updates
var indexTableNameWords = "_" + tableId + "_fuzzy_words_" + item.path.join(".");
nSQL.triggerQuery(inputArgs.query.databaseID, __assign({}, utilities_1.buildQuery(inputArgs.query.databaseID, nSQL, "", "create table"), { actionArgs: {
name: indexTableNameWords,
_internal: true,
model: (_a = {},
_a[pkKey] = { pk: true },
_a["hash:string"] = {},
_a["tokens:any"] = {},
_a)
} }), utilities_1.noop, next, err);
}, err);
}).then(function () {
complete(inputArgs);
}).catch(function (err) {
cancel(err);
});
}
},
{
name: "addRowEvent",
priority: 1000,
call: function (inputArgs, complete, cancel) {
var tableId = nSQL.getDB(inputArgs.query.databaseID)._tableIds[inputArgs.query.table];
var pkPath = nSQL.getDB(inputArgs.query.databaseID)._tables[inputArgs.query.table].pkCol;
if (!searchIndexes[tableId] || !searchIndexes[tableId].length) { // no indexes for this table
complete(inputArgs);
}
else { // add new row
addRowToFuzzy(inputArgs.res.result, tableId, pkPath, nSQL, inputArgs.query, function (err) {
if (err) {
cancel(err);
}
else {
complete(inputArgs);
}
});
}
}
},
{
name: "updateRow",
priority: 1000,
call: function (inputArgs, complete, cancel) {
var tableId = nSQL.getDB(inputArgs.query.databaseID)._tableIds[inputArgs.query.table];
var pkPath = nSQL.getDB(inputArgs.query.databaseID)._tables[inputArgs.query.table].pkCol;
if (!searchIndexes[tableId] || !searchIndexes[tableId].length) { // no indexes for this table
complete(inputArgs);
}
else { // update row data
var newRowData_1 = inputArgs.res;
var newRowPK_1 = utilities_1.deepGet(pkPath, newRowData_1);
var filters_1 = utilities_1.adapterFilters(inputArgs.query.databaseID, nSQL, inputArgs.query);
utilities_1.allAsync(searchIndexes[tableId], function (item, i, next, err) {
var indexTableNameWords = "_" + tableId + "_fuzzy_words_" + item.path.join(".");
var indexTableNameWordsId = nSQL.getDB(inputArgs.query.databaseID)._tableIds[indexTableNameWords];
// read row cache
filters_1.read(indexTableNameWords, newRowPK_1, function (row) {
var useRow = utilities_1.maybeAssign(row) || { id: newRowPK_1, hash: "", tokens: [] };
var phrase = utilities_1.deepGet(item.path, newRowData_1);
// new and old are both empty
if (!useRow.hash && (!phrase || typeof phrase !== "string")) {
next();
return;
}
var phraseHash = utilities_1.hash(String(phrase));
if (phraseHash === useRow.hash) { // no changes in index data
next();
return;
}
var indexTable = "_" + tableId + "_fuzzy_" + item.path.join(".");
var indexTableId = nSQL.getDB(inputArgs.query.databaseID)._tableIds[indexTable];
var tokens = item.tokenizer(item.tableName, item.tableId, item.path, phrase);
var tokenArray = tokens.map(function (s) { return s.i + ":" + s.w; });
var deleteTokens = useRow.tokens.filter(function (t) { return tokenArray.indexOf(t) === -1; });
var addTokens = tokenArray.filter(function (t) { return useRow.tokens.indexOf(t) === -1; });
// adjust tokens
utilities_1.allAsync([deleteTokens, addTokens], function (arr, kk, nextArr, errArr) {
var cleanedTokens = arr.map(function (s) {
var sp = s.split(":");
var thisPos = sp.shift(); // shift off index
return { w: sp.join(":"), i: parseInt(thisPos) };
});
var mergedTokens = cleanedTokens.reduce(function (prev, cur) {
if (!prev[cur.w]) {
prev[cur.w] = [];
}
prev[cur.w].push(cur.i);
return prev;
}, {});
utilities_1.allAsync(Object.keys(mergedTokens), function (word, k, nextToken, errToken) {
var writeToken = function () {
filters_1.read(indexTable, word, function (tokenRow) {
var useRow = utilities_1.maybeAssign(tokenRow) || {
wrd: word,
ids: []
};
var idx = -1;
var i = 0;
while (i < useRow.ids.length && idx === -1) {
if (useRow.ids[i].id === newRowPK_1) {
idx = i;
}
i++;
}
if (idx !== -1) {
if (kk === 0) { // deleting
useRow.ids[idx].i = useRow.ids[idx].i.filter(function (pos) {
return mergedTokens[word].indexOf(pos) === -1;
});
}
else { // adding
useRow.ids[idx].i = useRow.ids[idx].i.concat(mergedTokens[word]);
}
// remove row from index data if it has no index values left
if (!useRow.ids[idx].i.length) {
useRow.ids.splice(idx, 1);
}
// no row values left for this token
if (!useRow.ids.length) {
filters_1.delete(indexTable, word, function () {
delete indexLocks[tableId][word];
nextToken();
}, function (err) {
delete indexLocks[tableId][word];
errToken(err);
});
}
else {
filters_1.write(indexTable, word, useRow, function () {
delete indexLocks[tableId][word];
nextToken();
}, function (err) {
delete indexLocks[tableId][word];
errToken(err);
});
}
}
else {
if (kk === 1) { // adding
useRow.ids.push({
id: newRowPK_1,
i: mergedTokens[word]
});
filters_1.write(indexTable, word, useRow, function () {
delete indexLocks[tableId][word];
nextToken();
}, function (err) {
delete indexLocks[tableId][word];
errToken(err);
});
}
else { // deleting
delete indexLocks[tableId][word];
nextToken();
}
}
}, function (err) {
delete indexLocks[tableId][word];
errToken(err);
});
};
var checkTokenLock = function () {
if (indexLocks[tableId][word]) {
setTimeout(function () {
checkTokenLock();
}, 2);
}
else {
indexLocks[tableId][word] = true;
writeToken();
}
};
checkTokenLock();
}).then(next).catch(err);
}).then(function () {
// write new cache data
filters_1.write(indexTableNameWords, newRowPK_1, {
id: newRowPK_1,
hash: phraseHash,
tokens: tokenArray
}, next, err);
});
}, err);
}).then(function () {
complete(inputArgs);
}).catch(cancel);
}
}
},
{
name: "deleteRow",
priority: 1000,
call: function (inputArgs, complete, cancel) {
var tableId = nSQL.getDB(inputArgs.query.databaseID)._tableIds[inputArgs.query.table];
var pkPath = nSQL.getDB(inputArgs.query.databaseID)._tables[inputArgs.query.table].pkCol;
if (!searchIndexes[tableId] || !searchIndexes[tableId].length) { // no indexes for this table
complete(inputArgs);
}
else { // delete row data
rmRowFromFuzzy(inputArgs.res, pkPath, inputArgs.query.databaseID, nSQL, inputArgs.query, tableId, function (err) {
if (err) {
cancel(err);
}
else {
complete(inputArgs);
}
});
}
}
},
{
name: "customQuery",
priority: 1000,
call: function (inputArgs, complete, cancel) {
if (String(inputArgs.query.action).trim().toLowerCase() === "rebuild search") { // capture rebuild search
var table = inputArgs.query.table;
if (typeof table !== "string") {
inputArgs.error("Can't rebuild search on this table type!");
}
else {
var tableId_1 = nSQL.getDB(inputArgs.query.databaseID)._tableIds[inputArgs.query.table];
var pkPath_1 = nSQL.getDB(inputArgs.query.databaseID)._tables[inputArgs.query.table].pkCol;
if (!searchIndexes[tableId_1] || !searchIndexes[tableId_1].length) { // no indexes for this table
inputArgs.complete();
}
else { // rebuild indexes
if (inputArgs.query.where) { // rebuild specific indexes
var rebuildQ_1 = new utilities_1._nanoSQLQueue(function (item, i, done, error) {
rmRowFromFuzzy(item, pkPath_1, inputArgs.query.databaseID, nSQL, inputArgs.query, tableId_1, function (err) {
if (err) {
error(err);
}
else {
addRowToFuzzy(item, tableId_1, pkPath_1, nSQL, inputArgs.query, function (err2) {
if (err2) {
error(err2);
}
else {
done();
}
});
}
});
}, inputArgs.error, function () {
inputArgs.complete();
});
nSQL.triggerQuery(inputArgs