mirror of
https://github.com/nextapps-de/flexsearch.git
synced 2025-09-25 04:51:29 +02:00
792 lines
26 KiB
JavaScript
792 lines
26 KiB
JavaScript
//const sqlite3 = require("sqlite3").verbose();
|
|
import sqlite3 from "sqlite3";
|
|
import path from "path";
|
|
import StorageInterface from "../interface.js";
|
|
import { concat, toArray } from "../../common.js";
|
|
|
|
const VERSION = 1,
|
|
MAXIMUM_QUERY_VARS = 16000,
|
|
fields = ["map", "ctx", "reg", "tag", "cfg"],
|
|
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),
|
|
DB = Object.create(null);
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
* @implements StorageInterface
|
|
*/
|
|
|
|
export default function SqliteDB(name, config = {}) {
|
|
if (!this) {
|
|
return new SqliteDB(name, config);
|
|
}
|
|
if ("object" == typeof name) {
|
|
config = name;
|
|
name = name.name;
|
|
}
|
|
if (!name) {
|
|
console.info("Default storage space was used, because a name was not passed.");
|
|
}
|
|
//field = "Test-456";
|
|
this.id = config.path || (":memory:" === name ? name : "flexsearch" + (name ? "-" + sanitize(name) : "") + ".sqlite");
|
|
this.field = config.field ? "_" + sanitize(config.field) : "";
|
|
this.support_tag_search = /* tag? */ /* stringify */ /* stringify */ /* single param */!0 /*await rows.hasNext()*/ /*await rows.hasNext()*/ /*await rows.hasNext()*/;
|
|
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){
|
|
if (!flexsearch.encoder) {
|
|
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 (":memory:" !== filepath) {
|
|
// 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] + ("reg" === fields[i] ? "" : 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 && this.db.close();
|
|
this.db = null;
|
|
DB[this.id] = null;
|
|
TRX[this.id] = null;
|
|
return this;
|
|
};
|
|
|
|
SqliteDB.prototype.destroy = function () {
|
|
return 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;");
|
|
});
|
|
};
|
|
|
|
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 = !0, enrich = /* suggest */!1, tags) {
|
|
let result,
|
|
stmt = '',
|
|
params = ctx ? [ctx, key] : [key],
|
|
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 = !1) {
|
|
const table = "main.tag" + this.field,
|
|
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, !0, !1);
|
|
});
|
|
return promise;
|
|
};
|
|
|
|
function build_params(length, single_param) {
|
|
|
|
let stmt = single_param ? ",(?)" : ",?";
|
|
for (let i = 1; i < length;) {
|
|
if (i <= length - i) {
|
|
stmt += stmt;
|
|
i *= 2;
|
|
} else {
|
|
stmt += stmt.substring(0, (length - i) * (single_param ? 4 : 2));
|
|
break;
|
|
}
|
|
}
|
|
return stmt.substring(1);
|
|
}
|
|
|
|
SqliteDB.prototype.enrich = function (ids) {
|
|
const result = [],
|
|
promises = [];
|
|
|
|
if ("object" != typeof ids) {
|
|
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 1 === result.length ? result[0] : 1 < result.length ? 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 = !1, resolve = !0, enrich = !1, tags) {
|
|
|
|
let rows;
|
|
|
|
if (1 < query.length && flexsearch.depth) {
|
|
let stmt = "",
|
|
params = [],
|
|
keyword = query[0],
|
|
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" : "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 = "",
|
|
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" : "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) {
|
|
|
|
if (TRX[this.id]) {
|
|
return task.call(this);
|
|
}
|
|
|
|
const db = this.db,
|
|
self = this;
|
|
|
|
|
|
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 = !0;
|
|
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],
|
|
arr = item[1];
|
|
|
|
|
|
for (let i = 0, ids; i < arr.length; i++) {
|
|
if ((ids = arr[i]) && ids.length) {
|
|
let stmt = "",
|
|
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],
|
|
ctx_value = ctx[1];
|
|
|
|
|
|
for (const item of ctx_value) {
|
|
const key = item[0],
|
|
arr = item[1];
|
|
|
|
|
|
for (let i = 0, ids; i < arr.length; i++) {
|
|
if ((ids = arr[i]) && ids.length) {
|
|
let stmt = "",
|
|
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 = "",
|
|
chunk = [];
|
|
|
|
for (const item of flexsearch.store.entries()) {
|
|
const id = item[0],
|
|
doc = item[1];
|
|
|
|
stmt += (stmt ? "," : "") + "(?,?)";
|
|
chunk.push(id, "object" == typeof doc ? 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, !0);
|
|
this.db.run("INSERT INTO main.reg (id) VALUES " + stmt, chunk);
|
|
}
|
|
}
|
|
//});
|
|
//await this.transaction(function(){
|
|
|
|
if (flexsearch.tag) {
|
|
let stmt = "",
|
|
chunk = [];
|
|
|
|
for (const item of flexsearch.tag) {
|
|
const tag = item[0],
|
|
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 ("object" != typeof ids) {
|
|
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 (result) {
|
|
return next ? self.remove(next) : result;
|
|
});
|
|
};
|
|
|
|
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);
|
|
});
|
|
});
|
|
}; |