larvitorder
Version:
Generic order system
441 lines • 18.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Orders = void 0;
const larvitutils_1 = require("larvitutils");
const helpers_1 = require("./helpers");
class Orders {
constructor(options) {
if (!options?.db)
throw new Error('Missing required option "db"');
this.db = options.db;
this.log = options.log ?? new larvitutils_1.Log();
this.lUtils = options.lUtils ?? new larvitutils_1.Utils();
this.helpers = options.helpers ?? new helpers_1.Helpers({
db: this.db,
log: this.log,
lUtils: this.lUtils,
});
this.uuids = this.helpers.arrayify(options.uuids);
this.matchDates = options.matchDates;
this.matchFieldDates = options.matchFieldDates;
this.createdAfter = options.createdAfter;
this.updatedAfter = options.updatedAfter;
this.matchFieldHasValue = options.matchFieldHasValue;
this.matchFieldHasNoValue = options.matchFieldHasNoValue;
this.q = options.q;
this.matchAllFields = options.matchAllFields;
this.fieldNotEqualTo = options.fieldNotEqualTo;
this.fieldGreaterThanOrEqualTo = options.fieldGreaterThanOrEqualTo;
this.fieldLessThanOrEqualTo = options.fieldLessThanOrEqualTo;
this.matchAllRowFields = options.matchAllRowFields;
this.limit = options.limit;
this.offset = options.offset;
this.returnFields = options.returnFields;
this.returnRowFields = options.returnRowFields;
}
async get() {
// Get basic orders and total hits
const { orders, hits } = await this.getBasicOrders();
// Get fields
await this.getAndPopulateOrderFields(orders);
// Get rows
await this.getAndPopulateOrderRows(orders);
return { orders, hits };
}
async getBasicOrders() {
const orders = {};
// Create sql with filters
let sql = ' FROM orders WHERE 1';
let dbFields = [];
({ sql, dbFields } = this.concatSqlUuidsFilter(sql, dbFields));
({ sql, dbFields } = this.concatSqlCreatedAfterFilter(sql, dbFields));
({ sql, dbFields } = this.concatSqlUpdatedAfterFilter(sql, dbFields));
({ sql, dbFields } = await this.concatSqlDateFilter(sql, dbFields));
({ sql, dbFields } = await this.concatSqlFieldDateFilter(sql, dbFields));
({ sql, dbFields } = this.concatSqlQFilter(sql, dbFields));
({ sql, dbFields } = this.concatSqlMatchAllFilter(sql, dbFields));
({ sql, dbFields } = this.concatSqlFieldNotEqualToFilter(sql, dbFields));
({ sql, dbFields } = this.concatSqlFieldGreaterThanOrEqualToFilter(sql, dbFields));
({ sql, dbFields } = this.concatSqlFieldLessThanOrEqualToFilter(sql, dbFields));
({ sql, dbFields } = this.concatSqlFieldHasValue(sql, dbFields));
({ sql, dbFields } = this.concatSqlFieldHasNoValue(sql, dbFields));
({ sql, dbFields } = this.concatSqlMatchAllRowsFieldsFilter(sql, dbFields));
sql += ' ORDER BY created DESC';
// Hits sql without limit and offset
const hitsSql = 'SELECT COUNT(*) AS hits' + sql;
// Finalize sql with limit, offset and select
sql = this.concatSqlLimitAndOffset(sql);
sql = 'SELECT *' + sql;
// Query db
let hits = 0;
const queryOrders = async () => {
const { rows } = await this.db.query(sql, dbFields);
for (const row of rows) {
const uuid = this.helpers.formatUuid(row.uuid);
orders[uuid] = {
uuid,
created: row.created,
updated: row.updated,
fields: {},
rows: [],
};
}
};
const queryOrderCount = async () => {
const { rows } = await this.db.query(hitsSql, dbFields);
hits = rows[0].hits;
};
await Promise.all([queryOrders(), queryOrderCount()]);
return { orders, hits };
}
concatSqlUuidsFilter(sql, dbFields) {
if (!this.uuids)
return { sql, dbFields };
// Match nothing if uuids is empty array
if (!this.uuids.length) {
sql += ' AND 0';
return { sql, dbFields };
}
// Match against uuids
sql += ' AND uuid IN (';
for (const uuid of this.uuids) {
const buffer = this.helpers.uuidToBuffer(uuid);
sql += '?,';
dbFields.push(buffer);
}
sql = sql.substring(0, sql.length - 1) + ')';
return { sql, dbFields };
}
concatSqlCreatedAfterFilter(sql, dbFields) {
if (!this.createdAfter)
return { sql, dbFields };
if (!this.helpers.isDateIsh(this.createdAfter)) {
sql += ' AND created IS NULL';
return { sql, dbFields };
}
sql += ' AND created >= ?';
dbFields.push(this.createdAfter);
return { sql, dbFields };
}
concatSqlUpdatedAfterFilter(sql, dbFields) {
if (!this.updatedAfter)
return { sql, dbFields };
if (!this.helpers.isDateIsh(this.updatedAfter)) {
sql += ' AND created IS NULL';
return { sql, dbFields };
}
sql += ' AND updated >= ?';
dbFields.push(this.updatedAfter);
return { sql, dbFields };
}
async concatSqlDateFilter(sql, dbFields) {
if (!this.matchDates || !this.matchDates.length)
return { sql, dbFields };
const dbCon = await this.db.getConnection();
try {
// Check headerDates
for (const matchExistingDate of this.matchDates) {
const operation = matchExistingDate.operation || 'eq';
const value = matchExistingDate.value;
const field = matchExistingDate.field;
if (!value)
continue;
if (!field)
continue;
if (['eq', 'gt', 'lt'].indexOf(operation) === -1)
continue;
sql += ' AND ' + dbCon.escapeId(field);
if (operation === 'eq')
sql += ' = CAST(? AS DATETIME)\n';
else if (operation === 'gt')
sql += ' > CAST(? AS DATETIME)\n';
else if (operation === 'lt')
sql += ' < CAST(? AS DATETIME)\n';
dbFields.push(value);
}
}
finally {
dbCon.release();
}
return { sql, dbFields };
}
async concatSqlFieldDateFilter(sql, dbFields) {
if (!this.matchFieldDates || !this.matchFieldDates.length)
return { sql, dbFields };
// Check field dates
for (const matchExistingDate of this.matchFieldDates) {
const operation = matchExistingDate.operation || 'eq';
const value = matchExistingDate.value;
const field = matchExistingDate.field;
if (!value)
continue;
if (!field)
continue;
if (['eq', 'gt', 'lt'].indexOf(operation) === -1)
continue;
dbFields.push(field);
dbFields.push(value);
sql += ' AND orders.uuid IN (\n';
sql += ' SELECT DISTINCT orderUuid\n';
sql += ' FROM orders_orders_fields\n';
sql += ' WHERE fieldUuid = (SELECT uuid FROM orders_orderFields WHERE name = ?)\n';
if (operation === 'eq')
sql += ' AND CAST(fieldValue AS DATETIME) = CAST(? AS DATETIME)\n';
else if (operation === 'gt')
sql += ' AND CAST(fieldValue AS DATETIME) > CAST(? AS DATETIME)\n';
else if (operation === 'lt')
sql += ' AND CAST(fieldValue AS DATETIME) < CAST(? AS DATETIME)\n';
sql += ')';
}
return { sql, dbFields };
}
concatSqlQFilter(sql, dbFields) {
if (this.q === undefined) {
return { sql, dbFields };
}
sql += ' AND (\n';
sql += ' (\n';
sql += ' uuid IN (SELECT DISTINCT orderUuid FROM orders_orders_fields WHERE MATCH (fieldValue) AGAINST (?))\n';
sql += ' )\n';
dbFields.push('"' + this.q + '"');
sql += ' OR uuid IN (\n';
sql += ' SELECT DISTINCT orderUuid\n';
sql += ' FROM orders_rows WHERE rowUuid IN (\n';
sql += ' SELECT rowUuid FROM orders_rows_fields WHERE MATCH (rowStrValue) AGAINST (?)\n';
sql += ' )\n';
sql += ' )\n';
dbFields.push('"' + this.q + '"');
sql += ' )\n';
return { sql, dbFields };
}
concatSqlMatchAllFilter(sql, dbFields) {
if (this.matchAllFields === undefined) {
return { sql, dbFields };
}
for (const fieldName in this.matchAllFields) {
dbFields.push(fieldName);
sql += ' AND orders.uuid IN (\n';
sql += ' SELECT DISTINCT orderUuid\n';
sql += ' FROM orders_orders_fields\n';
if (Array.isArray(this.matchAllFields[fieldName])) {
sql += ' WHERE fieldUuid = (SELECT uuid FROM orders_orderFields WHERE name = ?) AND fieldValue IN (';
for (const fieldValue of this.matchAllFields[fieldName]) {
dbFields.push(fieldValue);
sql += '?,';
}
sql = sql.substring(0, sql.length - 1);
sql += ')\n';
}
else {
dbFields.push(this.matchAllFields[fieldName]);
sql += ' WHERE fieldUuid = (SELECT uuid FROM orders_orderFields WHERE name = ?) AND fieldValue = ?\n';
}
sql += ')';
}
return { sql, dbFields };
}
concatSqlFieldNotEqualToFilter(sql, dbFields) {
if (this.fieldNotEqualTo === undefined) {
return { sql, dbFields };
}
for (const fieldName in this.fieldNotEqualTo) {
sql += ' AND orders.uuid NOT IN (\n';
sql += ' SELECT DISTINCT orderUuid\n';
sql += ' FROM orders_orders_fields\n';
sql += ' WHERE fieldUuid = (SELECT uuid FROM orders_orderFields WHERE name = ?) AND fieldValue = ?\n';
sql += ')';
dbFields.push(fieldName);
dbFields.push(this.fieldNotEqualTo[fieldName]);
}
return { sql, dbFields };
}
concatSqlFieldGreaterThanOrEqualToFilter(sql, dbFields) {
if (this.fieldGreaterThanOrEqualTo === undefined) {
return { sql, dbFields };
}
for (const fieldName in this.fieldGreaterThanOrEqualTo) {
sql += ' AND orders.uuid IN (\n';
sql += ' SELECT DISTINCT orderUuid\n';
sql += ' FROM orders_orders_fields\n';
sql += ' WHERE fieldUuid = (SELECT uuid FROM orders_orderFields WHERE name = ?) AND fieldValue >= ?\n';
sql += ')';
dbFields.push(fieldName);
dbFields.push(this.fieldGreaterThanOrEqualTo[fieldName]);
}
return { sql, dbFields };
}
concatSqlFieldLessThanOrEqualToFilter(sql, dbFields) {
if (this.fieldLessThanOrEqualTo === undefined) {
return { sql, dbFields };
}
for (const fieldName in this.fieldLessThanOrEqualTo) {
sql += ' AND orders.uuid IN (\n';
sql += ' SELECT DISTINCT orderUuid\n';
sql += ' FROM orders_orders_fields\n';
sql += ' WHERE fieldUuid = (SELECT uuid FROM orders_orderFields WHERE name = ?) AND fieldValue <= ?\n';
sql += ')';
dbFields.push(fieldName);
dbFields.push(this.fieldLessThanOrEqualTo[fieldName]);
}
return { sql, dbFields };
}
concatSqlFieldHasValue(sql, dbFields) {
if (!this.matchFieldHasValue || !this.matchFieldHasValue.length)
return { sql, dbFields };
for (const matchFieldHasValue of this.matchFieldHasValue) {
sql += ' AND orders.uuid IN (\n';
sql += ' SELECT orderUuid FROM orders_orders_fields WHERE fieldUuid IN (\n';
sql += ' SELECT uuid FROM orders_orderFields WHERE\n';
sql += ' name = ?\n';
sql += ' )\n';
sql += ' AND fieldValue IS NOT NULL AND fieldValue != ""\n';
sql += ' )\n';
dbFields.push(matchFieldHasValue);
}
return { sql, dbFields };
}
concatSqlFieldHasNoValue(sql, dbFields) {
if (!this.matchFieldHasNoValue || !this.matchFieldHasNoValue.length)
return { sql, dbFields };
sql += ' AND (\n';
sql += ' orders.uuid IN (\n';
sql += ' SELECT orderUuid FROM orders_orders_fields WHERE fieldUuid IN (\n';
sql += ' SELECT uuid FROM orders_orderFields WHERE\n';
for (const matchFieldHasNoValue of this.matchFieldHasNoValue) {
sql += 'name = ? OR ';
dbFields.push(matchFieldHasNoValue);
}
sql = sql.substring(0, sql.length - 4);
sql += ' )\n';
sql += ' AND (fieldValue IS NULL OR fieldValue = "")\n';
sql += ' )\n';
sql += ' OR (\n';
sql += ' orders.uuid NOT IN (\n';
sql += ' SELECT orderUuid FROM orders_orders_fields WHERE fieldUuid IN (\n';
sql += ' SELECT uuid FROM orders_orderFields WHERE\n';
for (const matchFieldHasNoValue of this.matchFieldHasNoValue) {
sql += 'name = ? OR ';
dbFields.push(matchFieldHasNoValue);
}
sql = sql.substring(0, sql.length - 4);
sql += ' )\n';
sql += ' )\n';
sql += ' )\n';
sql += ')\n';
return { sql, dbFields };
}
concatSqlMatchAllRowsFieldsFilter(sql, dbFields) {
if (this.matchAllRowFields === undefined) {
return { sql, dbFields };
}
for (const rowFieldName in this.matchAllRowFields) {
sql += ' AND orders.uuid IN (\n';
sql += ' SELECT DISTINCT orderUuid\n';
sql += ' FROM orders_rows\n';
sql += ' WHERE rowUuid IN (\n';
sql += ' SELECT rowUuid FROM orders_rows_fields WHERE rowFieldUuid = (SELECT uuid FROM orders_rowFields WHERE name = ?) AND ';
if (this.helpers.isNumberIsh(this.matchAllRowFields[rowFieldName])) {
sql += 'rowIntValue = ?\n';
}
else {
sql += 'rowStrValue = ?\n';
}
sql += ' )';
sql += ')';
dbFields.push(rowFieldName);
dbFields.push(this.matchAllRowFields[rowFieldName]);
}
return { sql, dbFields };
}
concatSqlLimitAndOffset(sql) {
if (this.limit) {
sql += ` LIMIT ${Number(this.limit)}`;
if (this.offset) {
sql += ` OFFSET ${Number(this.offset)}`;
}
}
return sql;
}
async getAndPopulateOrderFields(orders) {
var _a, _b;
if (!this.returnFields || !Object.keys(orders).length)
return;
const dbFields = [];
let sql;
sql = 'SELECT orderUuid, name AS fieldName, fieldValue\n';
sql += 'FROM orders_orders_fields JOIN orders_orderFields ON fieldUuid = uuid\n';
sql += 'WHERE\n';
sql += ' orderUuid IN (';
for (const orderUuid in orders) {
const buffer = this.helpers.uuidToBuffer(orderUuid);
sql += '?,';
dbFields.push(buffer);
}
sql = sql.substring(0, sql.length - 1) + ')\n';
sql += ' AND name IN (';
for (const returnField of this.returnFields) {
sql += '?,';
dbFields.push(returnField);
}
sql = sql.substring(0, sql.length - 1) + ')\n';
const { rows: dbRows } = await this.db.query(sql, dbFields);
for (const dbRow of dbRows) {
const orderUuid = this.helpers.formatUuid(dbRow.orderUuid);
const order = orders[orderUuid];
if (!order)
throw new Error(`Order field mismatch, got unexpected field for order uuid: "${orderUuid}", sql: ${sql}, dbFields: ${JSON.stringify(dbFields)}`);
order.fields ?? (order.fields = {});
(_a = order.fields)[_b = dbRow.fieldName] ?? (_a[_b] = []);
order.fields[dbRow.fieldName].push(dbRow.fieldValue);
}
}
async getAndPopulateOrderRows(orders) {
var _a;
const dbFields = [];
if (!this.returnRowFields || !Object.keys(orders).length)
return;
let sql;
sql = 'SELECT r.orderUuid, r.rowUuid, f.name AS fieldName, rf.rowIntValue, rf.rowStrValue\n';
sql += 'FROM orders_rows r\n';
sql += ' LEFT JOIN orders_rows_fields rf ON rf.rowUuid = r.rowUuid\n';
sql += ' LEFT JOIN orders_rowFields f ON f.uuid = rf.rowFieldUuid\n';
sql += 'WHERE r.orderUuid IN (';
for (const orderUuid in orders) {
const buffer = this.helpers.uuidToBuffer(orderUuid);
sql += '?,';
dbFields.push(buffer);
}
sql = sql.substring(0, sql.length - 1) + ')';
sql += ' AND f.name IN (';
for (const returnRowField of this.returnRowFields) {
sql += '?,';
dbFields.push(returnRowField);
}
sql = sql.substring(0, sql.length - 1) + ')';
const { rows: dbRows } = await this.db.query(sql, dbFields);
for (const dbRow of dbRows) {
const orderUuid = this.helpers.formatUuid(dbRow.orderUuid);
const rowUuid = this.helpers.formatUuid(dbRow.rowUuid);
const order = orders[orderUuid];
if (!order)
throw new Error(`Order field mismatch, got unexpected rows for order uuid: "${orderUuid}", sql: ${sql}, dbFields: ${JSON.stringify(dbFields)}`);
order.rows ?? (order.rows = []);
let row = order.rows.find(r => r.uuid === rowUuid);
if (!row) {
row = {
uuid: rowUuid,
};
order.rows.push(row);
}
row[_a = dbRow.fieldName] ?? (row[_a] = []);
if (dbRow.rowIntValue !== null) {
row[dbRow.fieldName].push(dbRow.rowIntValue);
}
else if (dbRow.rowStrValue !== null) {
row[dbRow.fieldName].push(dbRow.rowStrValue);
}
}
}
}
exports.Orders = Orders;
//# sourceMappingURL=orders.js.map