node-mysql-query-utils
Advanced tools
Comparing version 1.0.0 to 1.0.1
@@ -6,6 +6,6 @@ import mysql, { type ConnectionOptions, type Pool } from 'mysql2/promise'; | ||
private static instances; | ||
private config; | ||
private verbose; | ||
connectionName: string; | ||
config: ConnectionOptions; | ||
pool: Pool | null; | ||
verbose: boolean; | ||
constructor(connectionName: string, config: ConnectionOptions, options?: DatabaseManagementOptions); | ||
@@ -15,4 +15,4 @@ private logVerbose; | ||
private static initializeConnection; | ||
static connectSingleDatabase(identifierName: string, config: ConnectionOptions, options?: DatabaseManagementOptions): Promise<void>; | ||
static connectMultipleDatabases(configs: DatabaseConnectionConfig[]): Promise<void>; | ||
static connectSingleDatabase(identifierName: string, config: ConnectionOptions, options?: DatabaseManagementOptions): void; | ||
static connectMultipleDatabases(configs: DatabaseConnectionConfig[]): void; | ||
static getInstance(identifierName: string): DatabaseManagement; | ||
@@ -22,7 +22,11 @@ static getInstanceList(logInstanceNames?: boolean): Map<string, DatabaseManagement> | string[]; | ||
formatQuery(sql: string, params?: any[]): string; | ||
createTableModel<ColumnKeys extends string, PrimaryKey extends ColumnKeys>(BuildSQLConstructor: TableModelConstructor<ColumnKeys[], PrimaryKey>): TableModel<ColumnKeys, PrimaryKey>; | ||
createTableModel<ColumnKeys extends string, PrimaryKey extends ColumnKeys>(BuildSQLConstructor: Omit<TableModelConstructor<ColumnKeys[], PrimaryKey>, 'queryFn'>): TableModel<ColumnKeys, PrimaryKey>; | ||
createTransactionConnection(): Promise<{ | ||
query: (sql: string, params?: any[]) => Promise<mysql.QueryResult>; | ||
commit: (release?: boolean) => Promise<void>; | ||
rollback: (release?: boolean) => Promise<void>; | ||
commit: (options: { | ||
release: boolean; | ||
}) => Promise<void>; | ||
rollback: (options: { | ||
release: boolean; | ||
}) => Promise<void>; | ||
release: () => void; | ||
@@ -29,0 +33,0 @@ }>; |
@@ -36,6 +36,6 @@ "use strict"; | ||
static instances = new Map(); | ||
config; | ||
verbose; | ||
connectionName; | ||
config; | ||
pool; | ||
verbose; | ||
constructor(connectionName, config, options = {}) { | ||
@@ -51,3 +51,3 @@ this.connectionName = `[db::${connectionName}]`; | ||
} | ||
async initConnection() { | ||
initConnection() { | ||
try { | ||
@@ -59,4 +59,4 @@ this.pool = promise_1.default.createPool(this.config); | ||
// Test the connection, and release it back to the pool | ||
const connection = await this.pool.getConnection(); | ||
connection.release(); | ||
// const connection = await this.pool.getConnection(); | ||
// connection.release(); | ||
// if (this.verbose) { | ||
@@ -77,7 +77,7 @@ // logger.log(`Connection tested and released for ${this.connectionName}`); | ||
} | ||
static async initializeConnection(identifierName, config, options) { | ||
static initializeConnection(identifierName, config, options) { | ||
if (!DatabaseManagement.instances.has(identifierName)) { | ||
try { | ||
const instance = new DatabaseManagement(identifierName, config, options); | ||
await instance.initConnection(); // Initialize the connection | ||
instance.initConnection(); // Initialize the connection | ||
logger_1.default.info(`db.connection :: <${identifierName}> :: host >> ${config.host}, database >> ${config.database}`); | ||
@@ -92,8 +92,8 @@ DatabaseManagement.instances.set(identifierName, instance); | ||
} | ||
static async connectSingleDatabase(identifierName, config, options) { | ||
await DatabaseManagement.initializeConnection(identifierName, config, options); | ||
static connectSingleDatabase(identifierName, config, options) { | ||
DatabaseManagement.initializeConnection(identifierName, config, options); | ||
} | ||
static async connectMultipleDatabases(configs) { | ||
static connectMultipleDatabases(configs) { | ||
for (const { identifierName, config, options } of configs) { | ||
await DatabaseManagement.initializeConnection(identifierName, config, options); | ||
DatabaseManagement.initializeConnection(identifierName, config, options); | ||
} | ||
@@ -123,3 +123,3 @@ } | ||
createTableModel(BuildSQLConstructor) { | ||
return new table_model_1.TableModel(BuildSQLConstructor); | ||
return new table_model_1.TableModel({ ...BuildSQLConstructor, queryFn: this.executeQuery.bind(this) }); | ||
} | ||
@@ -140,3 +140,4 @@ async createTransactionConnection() { | ||
}, | ||
commit: async (release = false) => { | ||
commit: async (options) => { | ||
const { release = false } = options || {}; | ||
try { | ||
@@ -153,3 +154,4 @@ this.logVerbose(`${this.connectionName} :: transaction :: committing`); | ||
}, | ||
rollback: async (release = false) => { | ||
rollback: async (options) => { | ||
const { release = false } = options || {}; | ||
try { | ||
@@ -156,0 +158,0 @@ this.logVerbose(`${this.connectionName} :: transaction :: rolling back`); |
@@ -1,2 +0,2 @@ | ||
import { ColumnData, DeleteQueryBuilder, FromQueryBuilder, GroupByField, GroupByQueryBuilder, InsertOptions, InsertQueryBuilder, JoinQueryBuilder, JoinType, LimitQueryBuilder, OffsetQueryBuilder, OrderByField, OrderByQueryBuilder, QueryFunction, SelectFields, SelectQueryBuilder, SetQueryBuilder, SQL_CONSTRUCTORS, UpdateOptions, UpdateQueryBuilder, UpdateQueryBuilderWithoutSet, WhereCondition, WhereQueryBuilder } from './types'; | ||
import { ColumnData, DeleteQueryBuilder, FromQueryBuilder, GroupByField, GroupByQueryBuilder, InsertOptions, InsertQueryBuilder, InsertValue, JoinQueryBuilder, JoinType, LimitQueryBuilder, OffsetQueryBuilder, OrderByField, OrderByQueryBuilder, QueryFunction, SelectFields, SelectQueryBuilder, SetQueryBuilder, SQL_CONSTRUCTORS, UpdateOptions, UpdateQueryBuilder, UpdateQueryBuilderWithoutSet, WhereCondition, WhereQueryBuilder } from './types'; | ||
export declare class SQLBuilder<ColumnKeys extends string, QueryReturnType = any> implements SelectQueryBuilder<ColumnKeys, QueryReturnType>, FromQueryBuilder<ColumnKeys, QueryReturnType>, JoinQueryBuilder<ColumnKeys, QueryReturnType>, WhereQueryBuilder<ColumnKeys, QueryReturnType>, GroupByQueryBuilder<QueryReturnType>, OrderByQueryBuilder<QueryReturnType>, LimitQueryBuilder<QueryReturnType>, OffsetQueryBuilder<QueryReturnType>, DeleteQueryBuilder<ColumnKeys, QueryReturnType>, UpdateQueryBuilder<ColumnKeys, QueryReturnType>, UpdateQueryBuilderWithoutSet<ColumnKeys, QueryReturnType>, SetQueryBuilder<ColumnKeys, QueryReturnType>, InsertQueryBuilder<QueryReturnType> { | ||
@@ -33,3 +33,3 @@ queryParts: SQL_CONSTRUCTORS; | ||
set(values: ColumnData<ColumnKeys>): SetQueryBuilder<ColumnKeys, QueryReturnType>; | ||
insert(table: string, values: ColumnData<ColumnKeys>, options?: InsertOptions): InsertQueryBuilder<QueryReturnType>; | ||
insert(table: string, values: InsertValue<ColumnKeys>, options?: InsertOptions): InsertQueryBuilder<QueryReturnType>; | ||
deleteFrom(table: string): DeleteQueryBuilder<ColumnKeys, QueryReturnType>; | ||
@@ -36,0 +36,0 @@ buildQuery(): { |
@@ -133,11 +133,21 @@ "use strict"; | ||
for (const operator in value) { | ||
// Handle IN, BETWEEN, NOT_BETWEEN, =, !=, <, <=, >, >=, LIKE, IS_NULL, IS_NOT_NULL | ||
if (operator === 'IN' && Array.isArray(value[operator])) { | ||
if (value[operator].length === 0) { | ||
throw new Error(this.printPrefixMessage(`processConditions :: IN :: condition must be a non-empty array`)); | ||
} | ||
clauses.push(`${sanitizedKey} IN (${value[operator].map(() => '?').join(', ')})`); | ||
localParams.push(...value[operator]); | ||
} | ||
else if (operator === 'BETWEEN' && Array.isArray(value[operator]) && value[operator].length === 2) { | ||
else if (operator === 'BETWEEN' && Array.isArray(value[operator])) { | ||
if (value[operator].length !== 2) { | ||
throw new Error(this.printPrefixMessage(`processConditions :: BETWEEN :: condition must be an array with exactly 2 elements`)); | ||
} | ||
clauses.push(`${sanitizedKey} BETWEEN ? AND ?`); | ||
localParams.push(value[operator][0], value[operator][1]); | ||
} | ||
else if (operator === 'NOT_BETWEEN' && Array.isArray(value[operator]) && value[operator].length === 2) { | ||
else if (operator === 'NOT_BETWEEN' && Array.isArray(value[operator])) { | ||
if (value[operator].length !== 2) { | ||
throw new Error(this.printPrefixMessage(`processConditions :: NOT_BETWEEN :: condition must be an array with exactly 2 elements`)); | ||
} | ||
clauses.push(`${sanitizedKey} NOT BETWEEN ? AND ?`); | ||
@@ -151,2 +161,5 @@ localParams.push(value[operator][0], value[operator][1]); | ||
else if (['IS_NULL', 'IS_NOT_NULL'].includes(operator)) { | ||
if (value[operator] !== true) { | ||
throw new Error(this.printPrefixMessage(`processConditions :: ${operator} :: condition must be true`)); | ||
} | ||
clauses.push(`${sanitizedKey} ${operator.replace(/_/g, ' ')}`); | ||
@@ -307,12 +320,20 @@ } | ||
const { enableTimestamps = false, ctimeField = 'ctime', utimeField = 'utime', ctimeValue = this.getCurrentUnixTimestamp(), utimeValue = this.getCurrentUnixTimestamp(), } = options || {}; | ||
const isMultipleInsert = Array.isArray(values); | ||
const rows = isMultipleInsert ? values : [values]; | ||
if (isMultipleInsert && rows.length === 0) { | ||
throw new Error(this.printPrefixMessage('Insert :: Values cannot be empty')); | ||
} | ||
if (enableTimestamps) { | ||
values[ctimeField] = ctimeValue; | ||
values[utimeField] = utimeValue; | ||
rows.forEach((row) => { | ||
row[ctimeField] = ctimeValue; | ||
row[utimeField] = utimeValue; | ||
}); | ||
} | ||
const columns = Object.keys(values); // e.g. ['name', 'email', 'password'] | ||
const columns = Object.keys(rows[0]); // e.g. ['name', 'email', 'password'] | ||
const placeholders = columns.map(() => '?').join(', '); // e.g. '?, ?, ?' | ||
const columnPlaceholders = columns.map(() => '??').join(', '); // e.g. 'name, email, password' | ||
const valueParams = Object.values(values); // e.g. ['John Doe', ' | ||
const valueParams = rows.flatMap(row => Object.values(row)); // e.g. ['doe', 'doe@gmail.com', 'password'] | ||
const columnParams = columns; | ||
let insertClause = `INSERT ${options?.insertIgnore ? 'IGNORE ' : ''}INTO ?? (${columnPlaceholders}) VALUES (${placeholders})`; | ||
const valuesPlaceholders = rows.map(() => `(${placeholders})`).join(', '); // e.g. '(?, ?, ?), (?, ?, ?)' | ||
let insertClause = `INSERT ${options?.insertIgnore ? 'IGNORE ' : ''}INTO ?? (${columnPlaceholders}) VALUES ${valuesPlaceholders}`; | ||
if (options?.onDuplicateKeyUpdate) { | ||
@@ -389,3 +410,3 @@ const updateColumns = Object.keys(options.onDuplicateKeyUpdate).map(key => '?? = ?').join(', '); | ||
if (!this.queryFn) | ||
throw new Error(this.printPrefixMessage('executeQuery :: Query function is not defined / provided')); | ||
throw new Error(this.printPrefixMessage('executeQuery :: Query function is not defined / provided in the constructor')); | ||
const [sql, params] = this.buildQuery(); | ||
@@ -392,0 +413,0 @@ return this.queryFn(sql, params); |
@@ -97,2 +97,3 @@ import { ConnectionOptions } from "mysql2/promise"; | ||
export type ColumnData<T extends string> = Partial<Record<T, any>>; | ||
export type InsertValue<T extends string> = ColumnData<T> | ColumnData<T>[]; | ||
export type InsertOptions = { | ||
@@ -113,5 +114,3 @@ insertIgnore?: boolean; | ||
}; | ||
export type PatchOptions = Prettify<{ | ||
patchField?: string; | ||
} & Omit<UpdateOptions, 'primaryKey'>>; | ||
export type PatchOptions = Prettify<{} & Omit<UpdateOptions, 'primaryKey'>>; | ||
export type SoftDeleteOptions = Prettify<{ | ||
@@ -118,0 +117,0 @@ deleteField?: string; |
@@ -6,3 +6,4 @@ export * from "./src/database-management"; | ||
export * as sqlHelper from "./lib/helper"; | ||
export * as logger from "./lib/logger"; | ||
import logger from "./lib/logger"; | ||
export { logger }; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -28,2 +28,5 @@ "use strict"; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -36,3 +39,4 @@ exports.logger = exports.sqlHelper = exports.mysql2 = void 0; | ||
exports.sqlHelper = __importStar(require("./lib/helper")); | ||
exports.logger = __importStar(require("./lib/logger")); | ||
const logger_1 = __importDefault(require("./lib/logger")); | ||
exports.logger = logger_1.default; | ||
//# sourceMappingURL=index.js.map |
import { SQLBuilder } from "../../dto/sql-builder-class"; | ||
import { ColumnData, InsertOptions, LimitOffset, OrderByField, Prettify, QueryAction, UpdateOptions, WhereCondition, SoftDeleteOptions, FieldAlias, PatchOptions, TableModelConstructor, SelectFields } from "../../dto/types"; | ||
import { ColumnData, InsertOptions, LimitOffset, OrderByField, Prettify, QueryAction, UpdateOptions, WhereCondition, SoftDeleteOptions, FieldAlias, PatchOptions, TableModelConstructor, SelectFields, InsertValue } from "../../dto/types"; | ||
import type { QueryResult, ResultSetHeader, RowDataPacket } from "mysql2"; | ||
@@ -14,2 +14,3 @@ export declare class TableModel<ColumnKeys extends string, PrimaryKey extends ColumnKeys> { | ||
private throwEmptyObjectError; | ||
private throwEmptyArrayError; | ||
private printPrefixMessage; | ||
@@ -50,12 +51,13 @@ private removeExtraFieldsAndLog; | ||
}>): QueryAction<ResultSetHeader>; | ||
insertRecord(data: ColumnData<ColumnKeys>, options?: InsertOptions): import("../../dto/types").InsertQueryBuilder<ResultSetHeader>; | ||
insertRecord(data: InsertValue<ColumnKeys>, options?: InsertOptions): import("../../dto/types").InsertQueryBuilder<ResultSetHeader>; | ||
removeOne(values: { | ||
where: WhereCondition<ColumnKeys>; | ||
orderBy?: OrderByField<ColumnKeys>[]; | ||
}): import("../../dto/types").LimitQueryBuilder<ResultSetHeader>; | ||
}): QueryAction<ResultSetHeader>; | ||
remove(values: { | ||
where: WhereCondition<ColumnKeys>; | ||
orderBy?: OrderByField<ColumnKeys>[]; | ||
}): import("../../dto/types").OrderByQueryBuilder<ResultSetHeader>; | ||
patchActiveStatus<T>(values: { | ||
}): QueryAction<ResultSetHeader>; | ||
patchSingleField<T>(values: { | ||
patchField: ColumnKeys; | ||
where: WhereCondition<ColumnKeys>; | ||
@@ -62,0 +64,0 @@ value: T; |
@@ -52,6 +52,17 @@ "use strict"; | ||
} | ||
throwEmptyArrayError(arr, message) { | ||
if (arr.length === 0) { | ||
throw new Error(message || 'Array cannot be empty'); | ||
} | ||
} | ||
printPrefixMessage(message) { | ||
return `[Table :: ${this.tableName}] :: ${message}`; | ||
} | ||
removeExtraFieldsAndLog(structuredData) { | ||
removeExtraFieldsAndLog(structuredData, index) { | ||
if (Array.isArray(structuredData)) { | ||
structuredData.forEach((data, index) => { | ||
this.removeExtraFieldsAndLog(data, index); | ||
}); | ||
return; | ||
} | ||
const removedKeys = []; | ||
@@ -66,3 +77,3 @@ // Remove extra fields that are not in the columns | ||
if (removedKeys.length > 0) { | ||
logger_1.default.warn(this.printPrefixMessage(`Removed unknown fields: ${removedKeys.join(', ')}`)); | ||
logger_1.default.warn(this.printPrefixMessage(`Removed unknown fields: ${removedKeys.join(', ')} from data[${index}]`)); | ||
} | ||
@@ -158,4 +169,9 @@ } | ||
insertRecord(data, options) { | ||
this.throwEmptyObjectError(data, this.printPrefixMessage('Create :: Data cannot be empty')); | ||
const structuredData = { ...data }; | ||
if (Array.isArray(data)) { | ||
this.throwEmptyArrayError(data, this.printPrefixMessage('Create :: Data cannot be empty')); | ||
} | ||
else { | ||
this.throwEmptyObjectError(data, this.printPrefixMessage('Create :: Data cannot be empty')); | ||
} | ||
const structuredData = Array.isArray(data) ? data : [data]; | ||
this.removeExtraFieldsAndLog(structuredData); | ||
@@ -180,11 +196,13 @@ const SQLBuild = this.initSQLBuilder(); | ||
return SQLBuild.deleteFrom(this.tableName) | ||
.where(where) | ||
.orderBy(orderBy); | ||
.where(where); | ||
} | ||
patchActiveStatus(values) { | ||
const { where, value, options } = values || {}; | ||
const { patchField } = options || {}; | ||
patchSingleField(values) { | ||
const { where, value, options, patchField } = values || {}; | ||
// const { patchField } = options || {}; | ||
if (!patchField) { | ||
throw new Error(this.printPrefixMessage('PatchSingleField :: Patch field is required')); | ||
} | ||
this.throwEmptyObjectError(where, this.printPrefixMessage('PatchIsActive :: Where condition cannot be empty')); | ||
const SQLBuild = this.initSQLBuilder(); | ||
const data = { [patchField || this.centralFields.isActiveField]: value }; | ||
const data = { [patchField]: value }; | ||
return SQLBuild.update(this.tableName, data, options) | ||
@@ -191,0 +209,0 @@ .where(where); |
{ | ||
"name": "node-mysql-query-utils", | ||
"version": "1.0.0", | ||
"version": "1.0.1", | ||
"description": "A simple MySQL query utils for Node.js", | ||
"author": "John Tam <johntam718@gmail.com>", | ||
"license": "MIT", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/johntam718/node-mysql2-helper.git" | ||
}, | ||
"homepage": "https://github.com/johntam718/node-mysql2-helper#readme", | ||
"keywords": [ | ||
"mysql", | ||
"mysql2", | ||
"query", | ||
"utils", | ||
"node" | ||
], | ||
"main": "dist/index.js", | ||
@@ -12,2 +26,4 @@ "types": "dist/index.d.ts", | ||
"dev:node14": "nodemon --watch dist test/index.js", | ||
"pre:publish": "yarn run build", | ||
"publish": "npm publish", | ||
"build": "rm -rf dist && tsc --project tsconfig.build.json && tsc-alias -p tsconfig.build.json" | ||
@@ -14,0 +30,0 @@ }, |
# node-mysql-query-utils | ||
A MySQL query builder and helper for Node.js, written in TypeScript. | ||
A MySQL query builder and helper for Node.js. | ||
@@ -503,1 +503,4 @@ [![npm version](https://badge.fury.io/js/node-mysql-query-utils.svg)](https://badge.fury.io/js/node-mysql-query-utils) | ||
## Changelog | ||
Detailed changes for each version are documented in the [CHANGELOG.md](https://github.com/johntam718/node-mysql2-helper/blob/main/CHANGELOG.md) file. |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
No License Found
License(Experimental) License information could not be found.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
184933
62
0
2621
1
0
505
0