import pg_promise from "pg-promise"; import StorageInterface from "../interface.js"; import { concat, toArray } from "../../common.js"; const defaults = { schema: "flexsearch", user: "postgres", pass: "postgres", name: "postgres", host: "localhost", port: "5432" }, pgp = pg_promise(), 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, ""); } let DB, TRX; /** * @constructor * @implements StorageInterface */ export default function PostgresDB(name, config = {}) { if (!this || this.constructor !== PostgresDB) { return new PostgresDB(name, config); } if ("object" == typeof name) { config = 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 = /* tag? */!0 /*await rows.hasNext()*/ /*await rows.hasNext()*/ /*await rows.hasNext()*/; 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){ if (flexsearch.index) { 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] + ("reg" !== fields[i] ? this.field : "")}' ); `); if (exist && exist.exists) continue; const type = "text" === this.type ? "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(() => { // 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 () { //DB && DB.close && DB.close(); this.db = /*DB =*/null; return this; }; PostgresDB.prototype.destroy = function () { return 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; `); }; 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 = !0, enrich = !1, tags) { let rows, stmt = '', params = ctx ? [ctx, key] : [key], 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 = !1) { const table = this.id + ".tag" + this.field, 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, !0, !1); }); return promise; }; PostgresDB.prototype.enrich = async function (ids) { let result = []; 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 += (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 1 === result.length ? result[0] : 1 < result.length ? concat(result) : result; }; PostgresDB.prototype.has = function (id) { return this.db.oneOrNone("SELECT EXISTS(SELECT 1 FROM " + this.id + ".reg WHERE id = $1)", [id]).then(function (result) { return !!(result && result.exists); }); }; PostgresDB.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 where = "", params = [], keyword = query[0], term, 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" : "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 = "", count = 1, query_length = query.length; for (let i = 0; i < query_length; i++) { where += (where ? "," : "") + "$" + count++; } where = "key " + (1 < query_length ? "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" : "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) { const self = this; return this.db.tx(function (trx) { return task.call(self, trx); }); }; // PostgresDB.prototype.transaction = async function(task){ // if(TRX){ // return await task.call(this, TRX); // } // const self = this; // return this.db.tx(async function(trx){ // await task.call(self, TRX = trx); // 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 = !0; 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 = [], stmt = new pgp.helpers.ColumnSet(["id", "doc"], { table: this.id + ".reg" }); for (const item of flexsearch.store.entries()) { const id = item[0], 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 = [], 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 = [], stmt = new pgp.helpers.ColumnSet(["key", "res", "id"], { table: this.id + ".map" + this.field }); 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) { //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 = [], 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], 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 = "(?,?,?)"; // 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 = [], stmt = new pgp.helpers.ColumnSet(["tag", "id"], { table: this.id + ".tag" + this.field }); for (const item of flexsearch.tag) { const tag = item[0], 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 && 0 !== ids) { return; } if ("object" != typeof ids) { 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; // `); // }