QueryGenerator.ts 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072
  1. import { CommonModel, CommonPageResult, NewCommonModel, ValidCommonModel } from "./CommonModel";
  2. import { DateUtils, ObjectUtils } from "@imengyu/imengyu-utils";
  3. import { query, queryAndReturnInsertId } from "./Query";
  4. export interface QueryData {
  5. [index: string]: any;
  6. }
  7. export interface QueryWhereArg {
  8. field : string;
  9. valueOrOperator : string | QueryOperator;
  10. valueSecond ?: string;
  11. }
  12. export interface QueryResult {
  13. [index: string]: any;
  14. }
  15. const logger = {
  16. log : (msg : string, data?: any) => console.log(msg, data),
  17. error : (msg : string, data?: any) => console.error(msg, data),
  18. }
  19. /**
  20. * 查询比较运算符
  21. */
  22. export type QueryOperator = ''|'='|'!='|'<'|'>'|'<>'|'<='|'>='|'is null'|'not null'|'like'|'between'|'in';
  23. /**
  24. * 查询条件
  25. */
  26. export type QueryCondition = 'and'|'or';
  27. export type QuerySubWhereCallback = (query : QueryGenerator) => void;
  28. type QueryAcceptValue = string|number|boolean|Date;
  29. interface QueryWhere {
  30. condition : QueryCondition;
  31. not: boolean;
  32. operator : QueryOperator;
  33. field: string;
  34. value: any;
  35. valueIsField?: boolean;
  36. valueIsRawSql?: boolean;
  37. valueChildren ?: QueryWhere[];
  38. }
  39. interface QueryOrderBy {
  40. field : string;
  41. direction : 'asc'|'desc';
  42. isRand: boolean;
  43. }
  44. class QuerySelectField {
  45. isRaw = false;
  46. field = '';
  47. static wrapKey(str: string) {
  48. const strs = str.replace(/\`/g, '').split('.');
  49. return strs.map(k => k == '*' ? k : `\`${k}\``).join('.');
  50. }
  51. public constructor(field : string, isRaw = false) {
  52. this.field = field?.toString() ?? '';
  53. this.isRaw = isRaw;
  54. }
  55. public toString() {
  56. return this.isRaw ? this.field : QuerySelectField.wrapKey(this.field);
  57. }
  58. }
  59. /**
  60. * 查询构造器。
  61. * 用于构建SQL查询语句的构造器。
  62. *
  63. * 可以从 DB 类快速构建查询语句:
  64. * ```ts
  65. * // 构建查询语句
  66. * DB.table('users')
  67. * .select('*')
  68. * .where('username', 'imengyu')
  69. * .orderBy('id', 'desc')
  70. * .limit(10)
  71. * .offset(0)
  72. * .get();
  73. * ```
  74. *
  75. * @example
  76. * // 基本查询示例
  77. * const users = await DB.table('users')
  78. * .select('id', 'username', 'email')
  79. * .get<UserModel>(UserModel);
  80. *
  81. * @example
  82. * // 条件查询示例
  83. * const user = await DB.table('users')
  84. * .where('id', '=', 1)
  85. * .orWhere('username', 'imengyu')
  86. * .where('status', 'active')
  87. * .first<UserModel>(UserModel);
  88. *
  89. * @example
  90. * // 复杂条件查询示例
  91. * const users = await DB.table('users')
  92. * .where('age', '>', 18)
  93. * .where('created_at', '>=', new Date('2023-01-01'))
  94. * .where('status', 'in', [1, 2, 3])
  95. * .where('name', 'like', '%张%')
  96. * .get<UserModel>(UserModel);
  97. *
  98. * @example
  99. * // 排序和分页示例
  100. * const users = await DB.table('users')
  101. * .select('*')
  102. * .orderBy('created_at', 'desc')
  103. * .orderBy('id', 'asc')
  104. * .limit(10)
  105. * .offset(20)
  106. * .get<UserModel>(UserModel);
  107. *
  108. * @example
  109. * // 分页查询示例
  110. * const pageResult = await DB.table('users')
  111. * .select('*')
  112. * .where('status', 1)
  113. * .orderBy('id', 'desc')
  114. * .paginate<UserModel>(2, 10, UserModel);
  115. *
  116. * @example
  117. * // 插入数据示例
  118. * await DB.table('users')
  119. * .insert({
  120. * username: 'imengyu',
  121. * email: 'imengyu@example.com',
  122. * created_at: new Date()
  123. * });
  124. *
  125. * // 插入并获取ID
  126. * const userId = await DB.table('users')
  127. * .insertGetId({
  128. * username: 'imengyu',
  129. * email: 'imengyu@example.com'
  130. * });
  131. *
  132. * @example
  133. * // 更新数据示例
  134. * await DB.table('users')
  135. * .where('id', 1)
  136. * .update({
  137. * username: 'new_username',
  138. * updated_at: new Date()
  139. * });
  140. *
  141. * @example
  142. * // 删除数据示例
  143. * await DB.table('users')
  144. * .where('id', 1)
  145. * .delete();
  146. *
  147. * @example
  148. * // 聚合函数示例
  149. * const count = await DB.table('users').count();
  150. * const maxAge = await DB.table('users').max('age');
  151. * const minAge = await DB.table('users').min('age');
  152. * const avgAge = await DB.table('users').avg('age');
  153. * const sumAge = await DB.table('users').sum('age');
  154. *
  155. * @example
  156. * // 分组查询示例
  157. * const userCounts = await DB.table('users')
  158. * .select('status', 'COUNT(*) as count')
  159. * .groupBy('status')
  160. * .get();
  161. *
  162. * @example
  163. * // 存在性检查示例
  164. * const exists = await DB.table('users')
  165. * .where('email', 'imengyu@example.com')
  166. * .exists();
  167. *
  168. * const notExists = await DB.table('users')
  169. * .where('email', 'nonexistent@example.com')
  170. * .doesntExist();
  171. *
  172. * @example
  173. * // 原生SQL示例
  174. * const users = await DB.table('users')
  175. * .selectRaw('id, username, email')
  176. * .whereRaw('created_at >= ? AND status = ?', [new Date('2023-01-01'), 1])
  177. * .get<UserModel>(UserModel);
  178. */
  179. export class QueryGenerator<O extends CommonModel = any> {
  180. /**
  181. * 当前表名
  182. */
  183. public tableName = '';
  184. public enableLogSQL = false;
  185. public dateFormat = 'yyyy-MM-dd HH:mm:ss';
  186. private tableSetCommonModel: NewCommonModel<O>|undefined;
  187. public enableLog() {
  188. this.enableLogSQL = true;
  189. return this;
  190. }
  191. public constructor(tableName : string|NewCommonModel<O>) {
  192. if (tableName instanceof Function) {
  193. this.tableSetCommonModel = tableName;
  194. this.tableName = new tableName()._tableName;
  195. } else if (typeof tableName === 'string')
  196. this.tableName = tableName;
  197. }
  198. //#region 工具方法
  199. /**
  200. * 清空所有已生成的SQL
  201. */
  202. public emptyAll() {
  203. this.whereConditions = [];
  204. this.orderByConditions = [];
  205. this.groupByConditions = [];
  206. this.limitCondition = -1;
  207. this.offsetCondition = -1;
  208. this.lastLimitCondition = '';
  209. this.lastWhereCondition = '';
  210. this.addDistinct = false;
  211. return this;
  212. }
  213. private solveQueryArrayData(arr : any[]) {
  214. let sqlValues = new Array<string>();
  215. for(let i = 0; i < arr.length; i++) {
  216. if(typeof arr[i] !== 'undefined')
  217. sqlValues.push(this.solveQueryVar(arr[i]))
  218. }
  219. return sqlValues
  220. }
  221. private solveQueryData(param : QueryData, acceptFields : string[], noId = false) {
  222. if (param instanceof CommonModel)
  223. param = param.toServerSide();
  224. const keys = Object.keys(param);
  225. const sqlKeys = new Array<string>();
  226. const sqlValues = new Array<string>();
  227. let key = '', emptyAccepts = acceptFields.length === 0, val : any = null;
  228. for(let i = 0; i < keys.length; i++) {
  229. key = keys[i], val = param[key];
  230. if (noId && key === 'id')
  231. continue;
  232. if((ObjectUtils.isDefined(val) && key !== 'id') && (emptyAccepts || acceptFields.includes(key))) {
  233. sqlKeys.push(key);
  234. sqlValues.push(this.solveQueryVar(val))
  235. }
  236. }
  237. return { sqlKeys, sqlValues }
  238. }
  239. private replaceForSQlite(v : string) {
  240. return v.replace(/\'/g, "\\'");
  241. }
  242. private solveQueryVar(v : any) {
  243. if(typeof v === 'undefined' || v === null)
  244. return 'null';
  245. else if(typeof v === 'string')
  246. v = `'${this.replaceForSQlite(v).replace(/\`/g,'\\\`')}'`;
  247. else if(typeof v === 'number' || typeof v === 'bigint')
  248. v = v.toString();
  249. else if(typeof v === 'object') {
  250. if (v instanceof Date)
  251. v = `'${DateUtils.formatDate(v, this.dateFormat)}'`;
  252. else if(typeof v === 'object' && v instanceof CommonModel)
  253. v = `'${this.replaceForSQlite(JSON.stringify(v.toServerSide()))}'`;
  254. else
  255. v = `'${this.replaceForSQlite(JSON.stringify(v))}'`;
  256. }
  257. return ('' + v)
  258. }
  259. private solveFields(arr : string[]) {
  260. let sqlValues = '';
  261. for(let i = 0; i < arr.length; i++) {
  262. if(i > 0) sqlValues += ',';
  263. sqlValues += QuerySelectField.wrapKey(arr[i]);
  264. }
  265. return sqlValues
  266. }
  267. //#endregion
  268. //#region insert 子句
  269. /**
  270. * 插入操作
  271. * @param param 插入的参数
  272. * @returns Promise 不返回数据
  273. */
  274. public async insert(param : O|QueryData | (O|QueryData)[], acceptFields : string[] = []) {
  275. if (param instanceof Array)
  276. await Promise.all(param.map(v => () => this.doInsert(v, false, acceptFields)));
  277. else
  278. await this.doInsert(param, false, acceptFields);
  279. }
  280. /**
  281. * 执行插入操作并返回新插入记录的ID
  282. * @param param 插入的参数
  283. * @returns Promise 返回新插入记录的ID
  284. */
  285. public insertGetId(param : O|QueryData, acceptFields : string[] = []) {
  286. return this.doInsert(param, true, acceptFields);
  287. }
  288. //插入
  289. private async doInsert(param : O|QueryData, returnId : boolean, acceptFields : string[]) {
  290. let { sqlKeys, sqlValues } = this.solveQueryData(param, acceptFields, true);
  291. let sql = `INSERT INTO \`${this.tableName}\` (${sqlKeys.map(k => QuerySelectField.wrapKey(k)).join(',')}) VALUES (${sqlValues.join(',')})`;
  292. if(this.enableLogSQL) logger.log('Query', sql);
  293. try {
  294. return returnId ? await queryAndReturnInsertId(sql) : null;
  295. } catch (err) {
  296. logger.log('Query', sql);
  297. throw err;
  298. }
  299. }
  300. //#endregion
  301. //#region update 子句
  302. private keyValueToUpdateSql(keys : string[], sqlValues : string[]) {
  303. let str = '', v = '';
  304. for(let i = 0, c = keys.length; i < c; i++) {
  305. v = sqlValues[i];
  306. if(i > 0) str += ',';
  307. str += `${QuerySelectField.wrapKey(keys[i])}=${v}`;
  308. }
  309. return str;
  310. }
  311. /**
  312. * 更新查询器指定字段的记录
  313. * @param param 要更新的字段和值
  314. */
  315. public async update(param : O|QueryData, acceptFields : string[] = []) {
  316. let whereCondition = this.makeWhereCondition();
  317. let limitCondition = this.makeLimitCondition();
  318. let { sqlKeys, sqlValues } = this.solveQueryData(param, acceptFields);
  319. let sql = `UPDATE \`${this.tableName}\` SET ${this.keyValueToUpdateSql(sqlKeys, sqlValues)}`;
  320. if(whereCondition !== '') sql += ' ' + whereCondition;
  321. if(limitCondition !== '') sql += ' ' + limitCondition;
  322. if(this.enableLogSQL) logger.log('Query', sql);
  323. try {
  324. return await query(sql);
  325. } catch(err) {
  326. logger.log('Query', sql);
  327. throw err;
  328. }
  329. }
  330. //#endregion
  331. //#region delete 子句
  332. /**
  333. * 删除当前查询器指定条件的记录
  334. */
  335. public async delete() {
  336. let whereCondition = this.makeWhereCondition();
  337. let sql = `DELETE FROM \`${this.tableName}\` ${whereCondition}`;
  338. if(this.enableLogSQL) logger.log('Query', sql);
  339. try {
  340. await query(sql);
  341. } catch(err) {
  342. logger.log('Query', sql);
  343. throw err;
  344. }
  345. }
  346. //#endregion
  347. //#region get 子句
  348. private makeCondition() {
  349. let whereCondition = this.makeWhereCondition();
  350. let orderByCondition = this.makeOrderByCondition();
  351. let groupByCondition = this.makeGroupByCondition();
  352. let limitCondition = this.makeLimitCondition();
  353. return { whereCondition, orderByCondition, groupByCondition, limitCondition }
  354. }
  355. private makeSelectSql(sql: string, whereCondition: string, orderByCondition: string, groupByCondition: string, limitCondition: string, limitOne = false) {
  356. if(this.leftJoinTable) sql += ' ' + this.makeLeftJoinConditions();
  357. if(whereCondition !== '') sql += ' ' + whereCondition;
  358. if(groupByCondition !== '') sql += ' ' + groupByCondition;
  359. if(orderByCondition !== '') sql += ' ' + orderByCondition;
  360. if(limitCondition !== '') sql += ' ' + limitCondition;
  361. else if(limitOne) sql += ' LIMIT 1';
  362. return sql;
  363. }
  364. private addDistinct = false;
  365. /**
  366. * 强制 SELECT 加上 DISTINCT
  367. */
  368. public distinct() {
  369. this.addDistinct = true;
  370. this.lastSelectCondition = '';
  371. }
  372. private toModel(model: NewCommonModel<any>|undefined, p: any) {
  373. return model && p ? new model().fromServerSide(p) : p;
  374. }
  375. /**
  376. * 进行查询获取数据
  377. * @returns Promise 成功则返回数据数组
  378. */
  379. public async get<T = O>(model: ValidCommonModel<T> = this.tableSetCommonModel as any) {
  380. let { whereCondition, orderByCondition, groupByCondition, limitCondition } = this.makeCondition();
  381. let selectCondition = this.makeSelectCondition();
  382. let sql = this.makeSelectSql(
  383. this.disableSelectCondition ? selectCondition : `SELECT ${selectCondition} FROM \`${this.tableName}\``,
  384. whereCondition, orderByCondition, groupByCondition, limitCondition);
  385. if(this.enableLogSQL) logger.log('Query', sql);
  386. try {
  387. const results = await query(sql);
  388. return results.map(p => this.toModel(model, p)) as T[];
  389. } catch(err) {
  390. logger.log('Query', sql);
  391. throw err;
  392. }
  393. }
  394. /**
  395. * 从数据表中获取单行数据
  396. * @returns Promise 成功则返回第一条数据
  397. */
  398. public async first<T = O>(model: ValidCommonModel<T> = this.tableSetCommonModel as any) {
  399. let { whereCondition, orderByCondition, groupByCondition, limitCondition } = this.makeCondition();
  400. let selectCondition = this.makeSelectCondition();
  401. let sql = this.makeSelectSql(
  402. this.disableSelectCondition ? selectCondition : `SELECT ${selectCondition} FROM \`${this.tableName}\``,
  403. whereCondition, orderByCondition, groupByCondition, limitCondition, true);
  404. if(this.enableLogSQL) logger.log('Query', sql);
  405. try {
  406. const result = (await query(sql))?.[0];
  407. return this.toModel(model, result) as T;
  408. } catch(err) {
  409. logger.log('Query', sql);
  410. throw err;
  411. }
  412. }
  413. /**
  414. * 从记录中获取单个字段的值。
  415. * @param field 字段
  416. */
  417. public async value<T extends QueryResult>(field : string) {
  418. let { whereCondition, orderByCondition, groupByCondition, limitCondition } = this.makeCondition();
  419. let sql = this.makeSelectSql(`SELECT \`${field}\` FROM \`${this.tableName}\``,
  420. whereCondition, orderByCondition, groupByCondition, limitCondition);
  421. if(this.enableLogSQL) logger.log('Query', sql);
  422. try {
  423. const result = (await query(sql))?.[0];
  424. return result ? result[field] as T : null;
  425. } catch(err) {
  426. logger.log('Query', sql);
  427. throw err;
  428. }
  429. }
  430. /**
  431. * 如果你想获取单列数据的集合。
  432. * @param fields 字段
  433. */
  434. public async pluck(...fields : string[]) {
  435. let { whereCondition, orderByCondition, groupByCondition, limitCondition } = this.makeCondition();
  436. let sql = this.makeSelectSql(`SELECT \`${this.solveFields(fields)}\` FROM \`${this.tableName}\``,
  437. whereCondition, orderByCondition, groupByCondition, limitCondition);
  438. if(this.enableLogSQL) logger.log('Query', sql);
  439. try {
  440. const results = await query(sql);
  441. if(fields.length === 1) {
  442. let field = fields[0];
  443. let result : any[] = [];
  444. for(let i = 0; i < results.length; i++)
  445. result.push(results[i][field]);
  446. return result;
  447. } else {
  448. let result : {[index: string]:any[]} = {};
  449. fields.forEach((field) => {
  450. let arr : any[] = [];
  451. for(let i = 0; i < results.length; i++)
  452. arr.push(results[i][field]);
  453. result[field] = arr;
  454. })
  455. return result;
  456. }
  457. } catch(err) {
  458. logger.log('Query', sql);
  459. throw err;
  460. }
  461. }
  462. /**
  463. * 禁用自动Select
  464. * @returns
  465. */
  466. public disableAutoSelectCondition() {
  467. this.disableSelectCondition = true;
  468. return this;
  469. }
  470. /**
  471. * 启用自动Select
  472. * @returns
  473. */
  474. public enableAutoSelectCondition() {
  475. this.disableSelectCondition = false;
  476. return this;
  477. }
  478. /**
  479. * 进行分页查询数据
  480. * @param pageNumber 页数,从1开始
  481. * @param pageSize 页大小
  482. */
  483. public async paginate<T = O>(pageNumber : number, pageSize : number, model: ValidCommonModel<T> = this.tableSetCommonModel as any) {
  484. let { whereCondition, orderByCondition, groupByCondition } = this.makeCondition();
  485. let selectCondition = this.makeSelectCondition();
  486. let sql = this.makeSelectSql(this.disableSelectCondition ? selectCondition : `SELECT ${selectCondition} FROM \`${this.tableName}\``,
  487. whereCondition, orderByCondition, groupByCondition, `LIMIT ${(pageNumber-1)*pageSize},${pageSize}`);
  488. if(this.enableLogSQL) logger.log('Query', sql);
  489. try {
  490. const results = await query(sql);
  491. const countSql = `SELECT COUNT(1) AS count FROM \`${this.tableName}\` ${whereCondition}`;
  492. const v = (await query(countSql))?.[0];
  493. return new CommonPageResult<T>(model, results, pageNumber, pageSize, v.count);
  494. } catch(err) {
  495. logger.log('Query', sql);
  496. throw err;
  497. }
  498. }
  499. //#endregion
  500. //#region 聚合子句
  501. private async commonAggregate(field : string, fun : string, distinct : boolean) {
  502. let { whereCondition, orderByCondition, groupByCondition, limitCondition } = this.makeCondition();
  503. let sql = this.makeSelectSql(`SELECT ${fun}(${distinct?'DISTINCT ':''}${field}) AS val FROM \`${this.tableName}\``,
  504. whereCondition, orderByCondition, groupByCondition, limitCondition);
  505. if(this.enableLogSQL) logger.log('Query', sql);
  506. try {
  507. const results = await query(sql);
  508. return results.length > 0 ? results[0].val : null;
  509. } catch(err) {
  510. logger.log('Query', sql);
  511. throw err;
  512. }
  513. }
  514. /**
  515. * 统计当前查询记录数
  516. */
  517. public count(field = '1', distinct = false) { return this.commonAggregate(field, 'COUNT', distinct); }
  518. public max(field : string, distinct = false) { return this.commonAggregate(field, 'MAX', distinct); }
  519. public min(field : string, distinct = false) { return this.commonAggregate(field, 'MIN', distinct); }
  520. public avg(field : string, distinct = false) { return this.commonAggregate(field, 'AVG', distinct); }
  521. public sum(field : string, distinct = false) { return this.commonAggregate(field, 'SUM', distinct); }
  522. /**
  523. * 查询当前的构造器是否存在指定记录
  524. * @returns
  525. */
  526. public async exists() {
  527. let { whereCondition, orderByCondition, groupByCondition, limitCondition } = this.makeCondition();
  528. let sql = this.makeSelectSql(`SELECT COUNT(1) AS val FROM \`${this.tableName}\``,
  529. whereCondition, orderByCondition, groupByCondition, limitCondition);
  530. if(this.enableLogSQL) logger.log('Query', sql);
  531. try {
  532. const results = await query(sql);
  533. return results.length > 0 ? (results[0].val > 0) : false;
  534. } catch(err) {
  535. logger.log('Query', sql);
  536. throw err;
  537. }
  538. }
  539. /**
  540. * 查询当前的构造器是否不存在指定记录
  541. * @returns
  542. */
  543. public async doesntExist() {
  544. let { whereCondition, orderByCondition, groupByCondition, limitCondition } = this.makeCondition();
  545. let sql = this.makeSelectSql(`SELECT COUNT(1) AS val FROM \`${this.tableName}\``,
  546. whereCondition, orderByCondition, groupByCondition, limitCondition);
  547. if(this.enableLogSQL) logger.log('Query', sql);
  548. try {
  549. const results = await query(sql);
  550. return results.length > 0 ? (results[0].val == 0) : false;
  551. } catch(err) {
  552. logger.log('Query', sql);
  553. throw err;
  554. }
  555. }
  556. //#endregion
  557. //#region select 子句
  558. private selectFields = new Array<QuerySelectField>();
  559. private lastSelectCondition = '';
  560. private disableSelectCondition = false;
  561. private makeSelectCondition() {
  562. if(this.lastSelectCondition === '') {
  563. let anyNonRawCondition = false;
  564. for(let i = 0; i < this.selectFields.length; i++) {
  565. if(!this.selectFields[i].isRaw) anyNonRawCondition = true;
  566. }
  567. if(this.selectFields.length === 0) return `*`;
  568. let ols = this.selectFields.join(',');
  569. this.lastSelectCondition =
  570. (this.addDistinct ? 'DISTINCT ' : '') +
  571. ((this.disableSelectCondition || anyNonRawCondition) ? '' : `*${(ols !== '' ? ',' : '')}`) +
  572. ols;
  573. }
  574. return this.lastSelectCondition;
  575. }
  576. /**
  577. * 清空当前查询器上的select条件
  578. */
  579. public selectClear() {
  580. this.selectFields = [];
  581. this.lastSelectCondition = '';
  582. }
  583. /**
  584. * 添加select字段
  585. * @param args 字段
  586. */
  587. public select(...args : any[]) { args.forEach((a) => this.selectFields.push(new QuerySelectField(a))); return this; }
  588. /**
  589. * 添加原生select语句
  590. * @param args 语句
  591. */
  592. public selectRaw(...args : any[]) { args.forEach((a) => this.selectFields.push(new QuerySelectField(a, true))); return this; }
  593. //#endregion
  594. //#region where 子句
  595. private whereConditions = new Array<QueryWhere>();
  596. private lastWhereCondition = '';
  597. private clearWhereCondition() {
  598. if(this.lastWhereCondition !== '')
  599. this.lastWhereCondition = '';
  600. }
  601. private QueryWhereToSQL(params: QueryWhere) {
  602. if(params.valueIsRawSql)
  603. return ''+params.value;
  604. else {
  605. switch(params.operator) {
  606. case '<':
  607. case '<=':
  608. case '=':
  609. case '!=':
  610. case '<>':
  611. case '>':
  612. case '>=':
  613. return `${QuerySelectField.wrapKey(params.field)}${params.not?' NOT ':''}${params.operator}` + (params.valueIsField ? `${QuerySelectField.wrapKey(params.value)}` : `${this.solveQueryVar(params.value)}`);
  614. case 'like':
  615. return `${QuerySelectField.wrapKey(params.field)} ${params.not?'NOT ':''}LIKE ${this.solveQueryVar(params.value)}`;
  616. case 'between':
  617. return `${QuerySelectField.wrapKey(params.field)} ${params.not?'NOT ':''}BETWEEN ${this.solveQueryVar(params.value[0])} AND ${this.solveQueryVar(params.value[1])}`;
  618. case 'in':
  619. return `${QuerySelectField.wrapKey(params.field)} ${params.not?'NOT ':''}IN (${this.solveQueryArrayData(params.value).join(',')})`;
  620. case 'is null':
  621. return `${QuerySelectField.wrapKey(params.field)} IS NULL`;
  622. case 'not null':
  623. return `${QuerySelectField.wrapKey(params.field)} IS NOT NULL`;
  624. }
  625. }
  626. return '';
  627. };
  628. private makeWhereCondition() {
  629. if(this.lastWhereCondition !== '')
  630. return this.lastWhereCondition;
  631. if(this.whereConditions.length === 0)
  632. return '';
  633. let loopForConditions = (whereConditions : QueryWhere[]) => {
  634. let index = 0;
  635. let base = '';
  636. whereConditions.forEach((condition) => {
  637. let sql : string;
  638. if(condition.valueChildren)
  639. sql = `(${loopForConditions(condition.valueChildren)}`;
  640. else
  641. sql = this.QueryWhereToSQL(condition);
  642. if(index == 0) base += ` ${sql}`;
  643. else base += ` ${condition.condition} ${sql}`;
  644. index++;
  645. });
  646. return base;
  647. }
  648. this.lastWhereCondition = 'WHERE' + loopForConditions(this.whereConditions);
  649. return this.lastWhereCondition;
  650. }
  651. /**
  652. * 清空当前查询器上的where条件
  653. */
  654. public whereClear() {
  655. this.whereConditions = [];
  656. this.lastWhereCondition = '';
  657. }
  658. /**
  659. * where条件语句
  660. * @param field 对应字段
  661. * @param valueOrOperator 可以是值;或当第三个参数填写,则改参数作为比较运算符
  662. * @param valueSecond 字段的值
  663. */
  664. public where(field : string | QueryWhereArg[] | QuerySubWhereCallback, valueOrOperator ?: QueryOperator | QueryAcceptValue, valueSecond ?: QueryAcceptValue, condition : QueryCondition = 'and', isCol = false) {
  665. if(typeof field === 'string') {
  666. this.whereConditions.push({
  667. condition: condition,
  668. operator: valueSecond ? valueOrOperator as QueryOperator : '=',
  669. value: valueSecond ? valueSecond : valueOrOperator,
  670. valueIsField: isCol,
  671. field: field,
  672. not: false,
  673. });
  674. } else if(typeof field === 'function') {
  675. let old = this.whereConditions;
  676. let valueChildren = [] as QueryWhere[];
  677. this.whereConditions.push({
  678. condition: condition,
  679. operator: '',
  680. value: null,
  681. valueIsField: isCol,
  682. valueChildren: valueChildren,
  683. field: '',
  684. not: false,
  685. });
  686. this.whereConditions = valueChildren;//换成指定子的数组
  687. field(this);//调用回调
  688. this.whereConditions = old;//换回来
  689. } else {
  690. field.forEach((v) => {
  691. let valueSecond = v.valueSecond;
  692. let valueOrOperator = v.valueOrOperator;
  693. this.whereConditions.push({
  694. condition: condition,
  695. operator: valueSecond ? valueOrOperator as QueryOperator : '=',
  696. value: valueSecond ? valueSecond : valueOrOperator,
  697. field: v.field,
  698. valueIsField: isCol,
  699. not: false,
  700. });
  701. })
  702. }
  703. this.clearWhereCondition();
  704. return this;
  705. }
  706. /**
  707. * where条件语句但是使用or与前方的where进行拼接
  708. * @param field 对应字段
  709. * @param valueOrOperator 可以是值;或当第三个参数填写,则改参数作为比较运算符
  710. * @param valueSecond 字段的值
  711. */
  712. public orWhere(field : string | QueryWhereArg[] | QuerySubWhereCallback, valueOrOperator ?: QueryOperator | QueryAcceptValue, valueSecond ?: QueryAcceptValue) {
  713. return this.where(field, valueOrOperator, valueSecond, 'or');
  714. }
  715. /**
  716. * where条件语句,对比两个字段
  717. * @param field 对应字段
  718. * @param field2OrOperator 可以是值;或当第三个参数填写,则改参数作为比较运算符
  719. * @param field2 字段的值
  720. */
  721. public whereColumn(field : string | QueryWhereArg[], field2OrOperator ?: string | QueryOperator, field2 ?: string, condition : QueryCondition = 'and') {
  722. return this.where(field, field2OrOperator, field2, condition, true);
  723. }
  724. /**
  725. * 注入原生 where 子句
  726. * @param sql SQL子句。不需要加or或者and
  727. */
  728. public whereRaw(sql : string, condition : QueryCondition = 'and') {
  729. this.whereConditions.push({
  730. condition: condition,
  731. operator: '',
  732. value: sql,
  733. valueIsRawSql: true,
  734. field: '',
  735. not: false,
  736. });
  737. this.clearWhereCondition();
  738. return this;
  739. }
  740. /**
  741. * 注入原生 where 子句(OR连接)
  742. * @param sql SQL子句。不需要加or或者and
  743. */
  744. public orWhereRaw(sql : string) {
  745. return this.whereRaw(sql, 'or');
  746. }
  747. /**
  748. * 添加某字段值是否包含在一个数组中的条件
  749. * @param field 字段
  750. * @param values 可能的值
  751. */
  752. public whereIn(field : string, values : any[], condition : QueryCondition = 'and', not = false) {
  753. if(!Array.isArray(values) || values.length === 0)
  754. return this;
  755. this.whereConditions.push({
  756. condition: condition,
  757. operator: 'in',
  758. value: values,
  759. field: field,
  760. not: not,
  761. });
  762. this.clearWhereCondition();
  763. return this;
  764. }
  765. /**
  766. * 添加某字段值是否不包含在一个数组中的条件
  767. * @param field 字段
  768. * @param values 可能的值
  769. */
  770. public whereNotIn(field : string, values : any[], condition : QueryCondition = 'and') {
  771. return this.whereIn(field, values, condition, true);
  772. }
  773. /**
  774. * 字段为空条件
  775. * @param field 字段
  776. */
  777. public whereNull(field : string, condition : QueryCondition = 'and') {
  778. this.whereConditions.push({
  779. condition: condition,
  780. operator: 'is null',
  781. value: null,
  782. field: field,
  783. not: false,
  784. });
  785. this.clearWhereCondition();
  786. return this;
  787. }
  788. /**
  789. * 字段非空条件
  790. * @param field 字段
  791. */
  792. public whereNotNull(field : string, condition : QueryCondition = 'and') {
  793. this.whereConditions.push({
  794. condition: condition,
  795. operator: 'not null',
  796. value: null,
  797. field: field,
  798. not: false,
  799. });
  800. return this;
  801. }
  802. /**
  803. * 字段值在某个区间
  804. * @param field 字段
  805. * @param valueBetween 某个区间 [x,y]
  806. */
  807. public whereBetween(field : string, valueBetween : any[], condition : QueryCondition = 'and', not = false) {
  808. this.whereConditions.push({
  809. condition: condition,
  810. operator: 'between',
  811. value: valueBetween,
  812. field: field,
  813. not: not,
  814. });
  815. this.clearWhereCondition();
  816. return this;
  817. }
  818. /**
  819. * 字段值不在某个区间
  820. * @param field 字段
  821. * @param valueBetween 某个区间
  822. */
  823. public whereNotBetween(field : string, valueBetween : any[], condition : QueryCondition = 'and') {
  824. return this.whereBetween(field, valueBetween, condition, true);
  825. }
  826. //#endregion
  827. //#region when
  828. /**
  829. * 在给定值在请求中存在的情况下才执行某些查询。
  830. * @param condition 给定值
  831. * @param trueCallback 给定值为真时执行回调
  832. * @param falseCallback 给定值为假时执行回调
  833. */
  834. public when(condition : any, trueCallback : QuerySubWhereCallback, falseCallback ?: QuerySubWhereCallback) {
  835. if(condition) trueCallback(this);
  836. else if(typeof falseCallback === 'function') falseCallback(this);
  837. }
  838. //#endregion
  839. //#region limit 语句
  840. private limitCondition = -1;
  841. private offsetCondition = -1;
  842. private lastLimitCondition = '';
  843. private clearLimitCondition() {
  844. if(this.lastLimitCondition !== '')
  845. this.lastLimitCondition = '';
  846. }
  847. private makeLimitCondition() {
  848. if(this.lastLimitCondition === '') {
  849. if(this.offsetCondition >= 0) this.lastLimitCondition = `LIMIT ${this.offsetCondition},${this.limitCondition}`;
  850. else if(this.limitCondition >= 0) this.lastLimitCondition = `LIMIT ${this.limitCondition}`;
  851. else this.lastLimitCondition = '';
  852. }
  853. return this.lastLimitCondition;
  854. }
  855. /**
  856. * limit 条件语句
  857. * @param sizeOrOffset 大小。如果第二个参数填写,则当前参数作为offset。
  858. * @param size 大小
  859. */
  860. public limit(sizeOrOffset : number, size ?: number) {
  861. if(typeof size === 'number') {
  862. this.offsetCondition = sizeOrOffset;
  863. this.limitCondition = size;
  864. } else this.limitCondition = sizeOrOffset;
  865. this.clearLimitCondition();
  866. return this;
  867. }
  868. /**
  869. * offset 条件语句
  870. * @param size 大小
  871. */
  872. public offset(size : number) {
  873. this.offsetCondition = size;
  874. this.clearLimitCondition();
  875. return this;
  876. }
  877. //#endregion
  878. //#region orderBy、groupBy 语句
  879. private orderByConditions = new Array<QueryOrderBy>();
  880. private groupByConditions = new Array<string>();
  881. private lastOrderByCondition = '';
  882. private lastGroupByCondition = '';
  883. private clearGroupByCondition() { if(this.lastGroupByCondition !== '') this.lastGroupByCondition = ''; }
  884. private makeGroupByCondition() {
  885. if(this.lastGroupByCondition !== '') return this.lastGroupByCondition;
  886. if(this.groupByConditions.length === 0) return '';
  887. let base = 'GROUP BY';
  888. for(let i = 0, c = this.orderByConditions.length; i < c; i++)
  889. base += `${i > 0?',':''}${QuerySelectField.wrapKey(this.groupByConditions[i])}`;
  890. this.lastGroupByCondition = base;
  891. return this.lastGroupByCondition;
  892. }
  893. private clearOrderByCondition() { if(this.lastOrderByCondition !== '') this.lastOrderByCondition = ''; }
  894. private makeOrderByCondition() {
  895. if(this.lastOrderByCondition !== '') return this.lastOrderByCondition;
  896. if(this.orderByConditions.length === 0) return '';
  897. let base = 'ORDER BY ';
  898. for(let i = 0, c = this.orderByConditions.length; i < c; i++) {
  899. let condition = this.orderByConditions[i];
  900. if(condition.isRand) {
  901. base += 'RAND()';
  902. break;
  903. }else {
  904. if(i > 0) base += ',';
  905. base += `${QuerySelectField.wrapKey(condition.field)} ${condition.direction.toUpperCase()}`;
  906. }
  907. }
  908. this.lastOrderByCondition = base;
  909. return this.lastOrderByCondition;
  910. }
  911. /**
  912. * orderBy语句
  913. * @param field 字段
  914. * @param direction 排序方式
  915. */
  916. public orderBy(field : string, direction : 'asc'|'desc' = 'asc') {
  917. this.orderByConditions.push({ field: field, direction: direction, isRand: false });
  918. this.clearOrderByCondition();
  919. return this;
  920. }
  921. /**
  922. * groupBy rand() 语句,用于随机排序
  923. */
  924. public inRandomOrder() {
  925. this.orderByConditions = [];
  926. this.orderByConditions.push({ field: '', direction: 'asc', isRand: true });
  927. this.clearOrderByCondition();
  928. return this;
  929. }
  930. /**
  931. * groupBy 语句
  932. * @param field 字段
  933. */
  934. public groupBy(field : string) {
  935. this.groupByConditions.push(field);
  936. this.clearGroupByCondition();
  937. return this;
  938. }
  939. //#endregion
  940. //#region join子句
  941. private leftJoinTable = '';
  942. private leftJoinConditions : Record<string, string> = {};
  943. private makeLeftJoinConditions() {
  944. if (this.leftJoinTable) {
  945. let leftJoinConstr = '';
  946. for (const key in this.leftJoinConditions) {
  947. if (Object.prototype.hasOwnProperty.call(this.leftJoinConditions, key)) {
  948. leftJoinConstr += `${QuerySelectField.wrapKey(key)} = ${QuerySelectField.wrapKey(this.leftJoinConditions[key])}`
  949. }
  950. }
  951. return `LEFT JOIN \`${this.leftJoinTable}\`` + (leftJoinConstr ? (' ON ' + leftJoinConstr) : '');
  952. }
  953. return '';
  954. }
  955. public leftJoin(tableName : string, conditions: Record<string, string>) {
  956. this.leftJoinTable = tableName;
  957. this.leftJoinConditions = conditions;
  958. return this;
  959. }
  960. //#endregion
  961. }