@dreamtree-org/korm-js 1.0.52 → 1.0.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -332,7 +332,48 @@ POST /api/Users/crud
332
332
  42 // Number of matching records
333
333
  ```
334
334
 
335
- ### 7. Replace Operation
335
+ ### 7. Sum Operation
336
+
337
+ Use either `data.sumColumn` (single column) or `data.sumFormula` (expression with `{columnName}` placeholders). Optional `where` filters the rows before summing.
338
+
339
+ ```javascript
340
+ // Sum a single column
341
+ POST /api/Employee/crud
342
+ {
343
+ "action": "sum",
344
+ "data": {
345
+ "sumColumn": "salary"
346
+ },
347
+ "where": {
348
+ "is_active": true,
349
+ "created_at": ">=2024-01-01"
350
+ }
351
+ }
352
+
353
+ // Sum an expression (e.g. salary + bonus)
354
+ POST /api/Employee/crud
355
+ {
356
+ "action": "sum",
357
+ "data": {
358
+ "sumFormula": "{salary}+{bonus}"
359
+ },
360
+ "where": {
361
+ "is_active": true
362
+ }
363
+ }
364
+
365
+ // Response
366
+ 125000 // Sum value (number)
367
+ ```
368
+
369
+ **Notes:**
370
+ - Use **either** `sumColumn` or `sumFormula`, not both. If both are provided, `sumColumn` is used.
371
+ - In `sumFormula`, column names are written as `{columnName}` and are safely quoted; only letters, numbers, and underscore are allowed in names.
372
+ - **Expression guards:** `sumFormula` allows only BODMAS-safe content: numbers (including decimals), operators `+` `-` `*` `/`, parentheses `(` `)`, and `{columnName}` placeholders. Any other character (e.g. commas, quotes, SQL) is rejected. Parentheses must be balanced and correctly ordered.
373
+ - Examples: `{salary}+{bonus}`, `({salary}+{bonus})*0.9`, `{amount}*1.1`, `{a}/{b}*100`.
374
+ - `where` supports the same operators as list/count (e.g. `>=`, `><`, `%...%`, etc.).
375
+
376
+ ### 8. Replace Operation
336
377
 
337
378
  ```javascript
338
379
  // Replace record (MySQL specific - replaces entire row)
@@ -356,7 +397,7 @@ POST /api/Users/crud
356
397
  }
357
398
  ```
358
399
 
359
- ### 8. Upsert Operation (Insert or Update)
400
+ ### 9. Upsert Operation (Insert or Update)
360
401
 
361
402
  ```javascript
362
403
  // Upsert record (insert if not exists, update if exists)
@@ -380,7 +421,7 @@ POST /api/Users/crud
380
421
  }
381
422
  ```
382
423
 
383
- ### 9. Sync Operation (Upsert + Delete)
424
+ ### 10. Sync Operation (Upsert + Delete)
384
425
 
385
426
  ```javascript
386
427
  // Sync operation: upsert records and delete others matching where clause
@@ -1613,6 +1654,7 @@ const modelInstance = korm.getModelInstance(modelDef);
1613
1654
  | `update` | Update record(s) | `where`, `data` |
1614
1655
  | `delete` | Delete record(s) | `where` |
1615
1656
  | `count` | Count records | None (optional: `where`) |
1657
+ | `sum` | Sum column or expression | `data.sumColumn` or `data.sumFormula` (optional: `where`) |
1616
1658
  | `replace` | Replace record (MySQL) | `data` |
1617
1659
  | `upsert` | Insert or update | `data`, `conflict` |
1618
1660
  | `sync` | Upsert + delete | `data`, `conflict`, `where` |
@@ -1 +1 @@
1
- const QueryService=require("./QueryService"),HookService=require("./HookService");class CurdTable{constructor(e,r,t=null){if(this.db=e,this.utils=r,this.controllerWrapper=t,this.hookService=new HookService(e,r,t),this.queryService=new QueryService(e,r,t),!this.queryService)throw new Error("CurdTable requires queryService (execute*Query / getQuery).")}async processRequest(e,r=null,t={}){const o=this.controllerWrapper;let s=this.utils.getModel(this.controllerWrapper,r);const c=e?.action||"list";let i=null;const a={model:s,action:c,request:e,ctx:t,controller:o};switch(this.hookService?.executeValidatorHook&&await this.hookService.executeValidatorHook({...a}),this.hookService?.executeBeforeHook&&(e.beforeActionData=await this.hookService.executeBeforeHook({...a})),c){case"count":i=await this.queryService.executeCountQuery(s,e);break;case"list":i=await this.hookService.executeHasSoftDeleteHook(s)?await this.queryService.getSoftDeleteQuery(s,e):await this.queryService.getQuery(s,e);break;case"show":i=await this.queryService.executeShowQuery(s,e);break;case"create":i=await this.queryService.executeCreateQuery(s,e);break;case"update":{const r=await this.queryService.executeUpdateQuery(s,e);if(!r)throw new Error(`Record not found or not updated: ${s.table} returned ${r}`);i={message:"Record updated successfully",data:r,success:!0};break}case"replace":if(i=await this.queryService.executeReplaceQuery(s,e),!i)throw new Error(`Record not found or not replaced: ${r} returned ${i}`);i={message:"Record replaced successfully",data:i,success:!0};break;case"upsert":if(i=await this.queryService.executeUpsertQuery(s,e),!i)throw new Error(`Record not found or not upserted: ${r} returned ${i}`);i={message:"Record upserted successfully",data:i,success:!0};break;case"sync":if(i=await this.queryService.executeSyncQuery(s,e),!i)throw new Error(`Record not found or not synced: ${r} returned ${i}`);i={message:"Record synced successfully",data:i,success:!0};break;case"delete":{let t=null;if(t=await this.hookService.executeHasSoftDeleteHook(s)?await this.queryService.executeSoftDeleteQuery(s,e):await this.queryService.executeDeleteQuery(s,e),!t)throw new Error(`Record not found or not deleted: ${r} returned ${t}`);i={message:"Record deleted successfully",data:t,success:!0};break}default:if(!this.hookService?.executeCustomAction)throw new Error(`Unknown action "${c}" and no custom action hook provided.`);i=await this.hookService.executeCustomAction({...a})}if(this.hookService?.executeAfterHook&&(i=await this.hookService.executeAfterHook({...a,data:i})),e?.other_requests&&"object"==typeof e.other_requests){const r={},o=Object.entries(e.other_requests);for(const[e,s]of o)Array.isArray(s)?r[e]=await Promise.all(s.map(r=>this.processRequest(r,e,t))):r[e]=await this.processRequest(s,e,t);i.other_responses=r}return i}}module.exports=CurdTable;
1
+ const QueryService=require("./QueryService"),HookService=require("./HookService");class CurdTable{constructor(e,r,t=null){if(this.db=e,this.utils=r,this.controllerWrapper=t,this.hookService=new HookService(e,r,t),this.queryService=new QueryService(e,r,t),!this.queryService)throw new Error("CurdTable requires queryService (execute*Query / getQuery).")}async processRequest(e,r=null,t={}){const o=this.controllerWrapper;let s=this.utils.getModel(this.controllerWrapper,r);const c=e?.action||"list";let a=null;const i={model:s,action:c,request:e,ctx:t,controller:o};switch(this.hookService?.executeValidatorHook&&await this.hookService.executeValidatorHook({...i}),this.hookService?.executeBeforeHook&&(e.beforeActionData=await this.hookService.executeBeforeHook({...i})),c){case"count":a=await this.queryService.executeCountQuery(s,e);break;case"sum":a=await this.queryService.executeSumQuery(s,e);break;case"list":a=await this.hookService.executeHasSoftDeleteHook(s)?await this.queryService.getSoftDeleteQuery(s,e):await this.queryService.getQuery(s,e);break;case"show":a=await this.queryService.executeShowQuery(s,e);break;case"create":a=await this.queryService.executeCreateQuery(s,e);break;case"update":{const r=await this.queryService.executeUpdateQuery(s,e);if(!r)throw new Error(`Record not found or not updated: ${s.table} returned ${r}`);a={message:"Record updated successfully",data:r,success:!0};break}case"replace":if(a=await this.queryService.executeReplaceQuery(s,e),!a)throw new Error(`Record not found or not replaced: ${r} returned ${a}`);a={message:"Record replaced successfully",data:a,success:!0};break;case"upsert":if(a=await this.queryService.executeUpsertQuery(s,e),!a)throw new Error(`Record not found or not upserted: ${r} returned ${a}`);a={message:"Record upserted successfully",data:a,success:!0};break;case"sync":if(a=await this.queryService.executeSyncQuery(s,e),!a)throw new Error(`Record not found or not synced: ${r} returned ${a}`);a={message:"Record synced successfully",data:a,success:!0};break;case"delete":{let t=null;if(t=await this.hookService.executeHasSoftDeleteHook(s)?await this.queryService.executeSoftDeleteQuery(s,e):await this.queryService.executeDeleteQuery(s,e),!t)throw new Error(`Record not found or not deleted: ${r} returned ${t}`);a={message:"Record deleted successfully",data:t,success:!0};break}default:if(!this.hookService?.executeCustomAction)throw new Error(`Unknown action "${c}" and no custom action hook provided.`);a=await this.hookService.executeCustomAction({...i})}if(this.hookService?.executeAfterHook&&(a=await this.hookService.executeAfterHook({...i,data:a})),e?.other_requests&&"object"==typeof e.other_requests){const r={},o=Object.entries(e.other_requests);for(const[e,s]of o)Array.isArray(s)?r[e]=await Promise.all(s.map(r=>this.processRequest(r,e,t))):r[e]=await this.processRequest(s,e,t);a.other_responses=r}return a}}module.exports=CurdTable;
@@ -1 +1 @@
1
- const HelperUtility=require("./HelperUtility"),logger=require("../../Logger");class QueryBuilder{constructor(e,t,r=null){this.db=e,this.utils=t,this.controllerWrapper=r,this.helperUtility=new HelperUtility}getHookService(){return this.controllerWrapper.hookService}getQueryBuilder(e,t=null){let r=this.db(e.table);return t&&(r=t),r._getMyModel=()=>e,r}parseValue(e){return this.helperUtility.parseValue(e)}parseWhereValue(e){return this.helperUtility.parseWhereValue(e)}parseWhereColumn(e){return this.helperUtility.parseWhereColumn(e)}_applyOrWhereCondition(e,t,r,o){if(null!==o)if(Array.isArray(o))e.orWhereIn(t,o);else switch(r){case"between":e.orWhereBetween(t,o);break;case"notBetween":e.orWhereNotBetween(t,o);break;case"in":e.orWhereIn(t,o);break;case"notIn":e.orWhereNotIn(t,o);break;case"like":e.orWhereRaw(`\`${t}\` LIKE ?`,[o]);break;default:e.orWhere(t,r,o)}else e.orWhereNull(t)}_applyAndWhereCondition(e,t,r,o){if(null!==o)if(Array.isArray(o))e.whereIn(t,o);else switch(r){case"between":e.whereBetween(t,o);break;case"notBetween":e.whereNotBetween(t,o);break;case"in":e.whereIn(t,o);break;case"notIn":e.whereNotIn(t,o);break;case"like":e.whereRaw(`\`${t}\` LIKE ?`,[o]);break;default:e.where(t,r,o)}else e.whereNull(t)}buildWithTree(e,t=null){return this.helperUtility.dotWalkTree(e,{resolver:({current:e,part:r,source:o})=>t?t({current:e,part:r,source:o}):{}})}_getWithWhereForRelation(e,t){if(!e||"object"!=typeof e)return{direct:{},nested:{}};const r=`${t}.`,o={},i={};for(const[t,s]of Object.entries(e))if(t.startsWith(r)){const e=t.slice(r.length);e.includes(".")?i[e]=s:o[e]=s}return{direct:o,nested:i}}_applyWithWhereConditions(e,t){for(const[r,o]of Object.entries(t)){const{joinType:t="AND",column:i}=this.parseWhereColumn(r),{operator:s,value:l}=this.parseWhereValue(o);"AND"===t?this._applyAndWhereCondition(e,i,s,l):this._applyOrWhereCondition(e,i,s,l)}}async fetchRelatedRows(e,t,r={}){if(e.through){let o=await this.db(e.through).whereIn(e.throughLocalKey,t),i=this.db(e.table).whereIn(e.foreignKey,o.map(t=>t[e.throughForeignKey]));return Object.keys(r).length>0&&this._applyWithWhereConditions(i,r),i}{let o=this.db(e.table).whereIn(e.foreignKey,t);return Object.keys(r).length>0&&this._applyWithWhereConditions(o,r),o}}async fetchAndAttachRelated(e){const{parentRows:t,relName:r,model:o,withTree:i,relation:s,withWhere:l={}}=e;if(!s){const e=this.getHookService().getModelInstance(o),s=`get${r.charAt(0).toUpperCase()+r.slice(1)}Relation`;if("function"==typeof e[s]){const{direct:n,nested:a}=this._getWithWhereForRelation(l,r),h={rows:t,relName:r,model:o,withTree:i,controller:this.controllerWrapper,relation:o.hasRelations[r],qb:this,db:this.db,withWhere:n,nestedWithWhere:a};await e[s](h)}else logger.warn(`Method ${s} not found in model ${o.name}`);return t}let n=t.map(e=>e[s.localKey]),a=[];const h="one"===s?.type,{direct:c,nested:p}=this._getWithWhereForRelation(l,r);a=await this.fetchRelatedRows(s,n,c);let y=new Map;for(const e of a){let t=e[s.foreignKey];y.has(t)||y.set(t,[]),y.get(t).push(e)}for(const e of t){let t=e[s.localKey];h&&1==y.get(t)?.length?e[r]=y.get(t)[0]:e[r]=y.get(t)||[]}let u=Object.keys(i);for(const e of u){let o=i[e],s=t.filter(e=>h?e[r]:e[r].length>0).map(e=>e[r]).reduce((e,t)=>e.concat(t),[]),l=this.utils.getModel(this.controllerWrapper,r);await this.fetchAndAttachRelated({parentRows:s,relName:e,model:l,withTree:o,relation:l.hasRelations[e],withWhere:p})}return t}filterWhere(e,t=""){return t?Object.keys(e).filter(e=>e.includes(t)).reduce((r,o)=>(r[o.replace(t,"")]=e[o],r),{}):Object.keys(e).filter(e=>!e.includes(".")).reduce((t,r)=>(t[r]=e[r],t),{})}async getQuery(e,t){try{const{where:r={},with:o,withWhere:i,select:s,orderBy:l={column:"id",direction:"asc"},limit:n=10,offset:a=0,page:h,groupBy:c,having:p,distinct:y,join:u,leftJoin:d,rightJoin:f,innerJoin:g,count:W=!1}=t;let b=this.getQueryBuilder(e);s&&(Array.isArray(s)||"string"==typeof s)?b.select(s):b.select("*"),y&&(Array.isArray(y)||"string"==typeof y?b.distinct(y):b.distinct()),u&&this._applyJoins(b,u,"join"),d&&this._applyJoins(b,d,"leftJoin"),f&&this._applyJoins(b,f,"rightJoin"),g&&this._applyJoins(b,g,"innerJoin"),this._applyWhereClause(b,r,o),c&&(Array.isArray(c),b.groupBy(c)),p&&this._applyHavingClause(b,p),l&&this._applyOrderBy(b,l);let w=!1,_=n,m=a,A=1,j=0;h&&n>0&&(A=Math.max(1,parseInt(h)),m=(A-1)*n),n>0&&(b.limit(_),m>0&&b.offset(m));const k=[],C=b.toSQL();k.push(C.sql);const R=await b;if(o&&o.length>0){const t=this.buildWithTree(o);for(const r of Object.keys(t))await this.fetchAndAttachRelated({parentRows:R,relName:r,model:e,withTree:t[r],relation:e.hasRelations[r],withWhere:i||{}})}let O=null;if(n>0)try{let t=this.getQueryBuilder(e);r&&Object.keys(r).length>0&&this._applyWhereClause(t,r),u&&this._applyJoins(t,u,"join"),d&&this._applyJoins(t,d,"leftJoin"),f&&this._applyJoins(t,f,"rightJoin"),g&&this._applyJoins(t,g,"innerJoin");const o=t.count("* as cnt");k.push(o.toSQL().sql);O=(await o.first()).cnt}catch(e){logger.warn("Failed to get total count:",e.message),O=R.length}n>0&&null!==O&&(j=Math.ceil(O/n),w=A<j);const B=w?A+1:null,N=A>1?A-1:null;return{data:R,totalCount:O,...this.controllerWrapper.debug?{sqlDebug:k}:{},...n>0?{pagination:{page:A,limit:_,offset:m,totalPages:j,hasNext:w,hasPrev:A>1,nextPage:B,prevPage:N}}:{}}}catch(t){throw logger.error("QueryService.getQuery error:",t),new Error(`Failed to execute query: ${t.message} on model ${e.name}`)}}_applyWhereWithArray(e,t,r){for(const o of t)this._applyWhereClause(e,o,r)}_applyWhereClause(e,t,r=[]){if(Array.isArray(t))this._applyWhereWithArray(e,t,r);else{if(t&&Object.keys(t).length>0){let r=this.helperUtility.getDotWalkQuery(t);logger.debug({filteredWhere:r}),r=this.helperUtility.objectFilter(r,(e,t)=>!e.startsWith("!")&&("__exists__"!==e&&("object"!=typeof t||null===t)));for(const[t,o]of Object.entries(r)){const{joinType:r="AND",column:i}=this.parseWhereColumn(t),{operator:s,value:l}=this.parseWhereValue(o);"AND"===r?this._applyAndWhereCondition(e,i,s,l):this._applyOrWhereCondition(e,i,s,l)}}this._applyNestedWhere(e,t,r)}}_getNestedWhereConditions(e,t){if(!e||"object"!=typeof e)return{};const r=`${t}.`,o={};for(const[i,s]of Object.entries(e))if(i.startsWith(r)){o[i.slice(r.length)]=s}else i===t&&!0===s&&(o.__exists__=!0);return o}_getTopLevelRelationsFromWhere(e){if(!e||"object"!=typeof e)return[];const t=new Set;for(const r of Object.keys(e))if(r.includes(".")){const e=r.split(".")[0];t.add(e)}else r.startsWith("!")&&t.add(r);return[...t]}_applyNestedWhere(e,t,r){let o=this,i=e._getMyModel();const s=this._getTopLevelRelationsFromWhere(t);if(0!==s.length)for(let r of s){let s=r.startsWith("!"),l=s?r.slice(1):r,n=this._getNestedWhereConditions(t,r);if(0===Object.keys(n).length)continue;let a,h=i.hasRelations?.[l];if(!h){logger.warn(`Relation ${l} not found in model ${i.modelName}`);continue}try{a=this.utils.getModel(this.controllerWrapper,h.table||l)}catch(e){try{a=this.utils.getModel(this.controllerWrapper,l)}catch(e){logger.warn(`Model for relation ${l} (table: ${h.table}) not found`);continue}}e[s?"whereNotExists":"whereExists"](function(){let e=o.getQueryBuilder(a,this.select("*").from(h.table));e.whereRaw(`\`${h.table}\`.\`${h.foreignKey}\` = \`${i.table}\`.\`${h.localKey}\``);if(!(!0===n.__exists__&&1===Object.keys(n).length)){const t={...n};delete t.__exists__,o._applyWhereClause(e,t,[])}})}}_applyJoins(e,t,r){const o=Array.isArray(t)?t:[t];for(const t of o)"string"==typeof t?e[r](t):"object"==typeof t&&(t.table&&t.on?e[r](t.table,t.on):t.table&&t.first&&t.operator&&t.second&&e[r](t.table,t.first,t.operator,t.second))}_applyWithWhere(e,t){try{if(Array.isArray(t))for(const r of t)"string"==typeof r?e.withWhere(r):"object"==typeof r&&e.withWhere(r.column,r.operator,r.value);else if("object"==typeof t)for(const[r,o]of Object.entries(t))e.withWhere(r,o)}catch(e){logger.warn("Failed to apply withWhere:",e.message)}}_applyHavingClause(e,t){for(const[r,o]of Object.entries(t))"object"==typeof o&&o.operator?e.having(r,o.operator,o.value):e.having(r,o)}_applyOrderBy(e,t){if(Array.isArray(t))for(const r of t)"string"==typeof r?e.orderBy(r):"object"==typeof r&&e.orderBy(r.column,r.direction||"asc");else"string"==typeof t?e.orderBy(t):"object"==typeof t&&e.orderBy(t.column,t.direction||"asc")}}module.exports=QueryBuilder;
1
+ const HelperUtility=require("./HelperUtility"),logger=require("../../Logger");class QueryBuilder{constructor(e,t,r=null){this.db=e,this.utils=t,this.controllerWrapper=r,this.helperUtility=new HelperUtility}getHookService(){return this.controllerWrapper.hookService}getQueryBuilder(e,t=null){let r=this.db(e.table);return t&&(r=t),r._getMyModel=()=>e,r}parseValue(e){return this.helperUtility.parseValue(e)}parseWhereValue(e){return this.helperUtility.parseWhereValue(e)}parseWhereColumn(e){return this.helperUtility.parseWhereColumn(e)}_applyOrWhereCondition(e,t,r,o){if(null!==o)if(Array.isArray(o))e.orWhereIn(t,o);else switch(r){case"between":e.orWhereBetween(t,o);break;case"notBetween":e.orWhereNotBetween(t,o);break;case"in":e.orWhereIn(t,o);break;case"notIn":e.orWhereNotIn(t,o);break;case"like":e.orWhereRaw(`\`${t}\` LIKE ?`,[o]);break;default:e.orWhere(t,r,o)}else e.orWhereNull(t)}_applyAndWhereCondition(e,t,r,o){if(null!==o)if(Array.isArray(o))e.whereIn(t,o);else switch(r){case"between":e.whereBetween(t,o);break;case"notBetween":e.whereNotBetween(t,o);break;case"in":e.whereIn(t,o);break;case"notIn":e.whereNotIn(t,o);break;case"like":e.whereRaw(`\`${t}\` LIKE ?`,[o]);break;default:e.where(t,r,o)}else e.whereNull(t)}buildWithTree(e,t=null){return this.helperUtility.dotWalkTree(e,{resolver:({current:e,part:r,source:o})=>t?t({current:e,part:r,source:o}):{}})}_getWithWhereForRelation(e,t){if(!e||"object"!=typeof e)return{direct:{},nested:{}};const r=`${t}.`,o={},i={};for(const[t,s]of Object.entries(e))if(t.startsWith(r)){const e=t.slice(r.length);e.includes(".")?i[e]=s:o[e]=s}return{direct:o,nested:i}}_applyWithWhereConditions(e,t){for(const[r,o]of Object.entries(t)){const{joinType:t="AND",column:i}=this.parseWhereColumn(r),{operator:s,value:n}=this.parseWhereValue(o);"AND"===t?this._applyAndWhereCondition(e,i,s,n):this._applyOrWhereCondition(e,i,s,n)}}async fetchRelatedRows(e,t,r={}){if(e.through){let o=await this.db(e.through).whereIn(e.throughLocalKey,t),i=this.db(e.table).whereIn(e.foreignKey,o.map(t=>t[e.throughForeignKey]));return Object.keys(r).length>0&&this._applyWithWhereConditions(i,r),i}{let o=this.db(e.table).whereIn(e.foreignKey,t);return Object.keys(r).length>0&&this._applyWithWhereConditions(o,r),o}}async fetchAndAttachRelated(e){const{parentRows:t,relName:r,model:o,withTree:i,relation:s,withWhere:n={}}=e;if(!s){const e=this.getHookService().getModelInstance(o),s=`get${r.charAt(0).toUpperCase()+r.slice(1)}Relation`;if("function"==typeof e[s]){const{direct:l,nested:a}=this._getWithWhereForRelation(n,r),h={rows:t,relName:r,model:o,withTree:i,controller:this.controllerWrapper,relation:o.hasRelations[r],qb:this,db:this.db,withWhere:l,nestedWithWhere:a};await e[s](h)}else logger.warn(`Method ${s} not found in model ${o.name}`);return t}let l=t.map(e=>e[s.localKey]),a=[];const h="one"===s?.type,{direct:c,nested:p}=this._getWithWhereForRelation(n,r);let u={...c};try{const e=this.utils.getModel(this.controllerWrapper,r);if(this.controllerWrapper?.hookService){await this.controllerWrapper.hookService.executeHasSoftDeleteHook(e)&&(u={...u,deleted_at:null})}}catch(e){}a=await this.fetchRelatedRows(s,l,u);let y=new Map;for(const e of a){let t=e[s.foreignKey];y.has(t)||y.set(t,[]),y.get(t).push(e)}for(const e of t){let t=e[s.localKey];h&&1==y.get(t)?.length?e[r]=y.get(t)[0]:e[r]=y.get(t)||[]}let d=Object.keys(i);for(const e of d){let o=i[e],s=t.filter(e=>h?e[r]:e[r].length>0).map(e=>e[r]).reduce((e,t)=>e.concat(t),[]),n=this.utils.getModel(this.controllerWrapper,r);await this.fetchAndAttachRelated({parentRows:s,relName:e,model:n,withTree:o,relation:n.hasRelations[e],withWhere:p})}return t}filterWhere(e,t=""){return t?Object.keys(e).filter(e=>e.includes(t)).reduce((r,o)=>(r[o.replace(t,"")]=e[o],r),{}):Object.keys(e).filter(e=>!e.includes(".")).reduce((t,r)=>(t[r]=e[r],t),{})}async getQuery(e,t){try{const{where:r={},with:o,withWhere:i,select:s,orderBy:n={column:"id",direction:"asc"},limit:l=10,offset:a=0,page:h,groupBy:c,having:p,distinct:u,join:y,leftJoin:d,rightJoin:f,innerJoin:g,count:W=!1}=t;let w=this.getQueryBuilder(e);s&&(Array.isArray(s)||"string"==typeof s)?w.select(s):w.select("*"),u&&(Array.isArray(u)||"string"==typeof u?w.distinct(u):w.distinct()),y&&this._applyJoins(w,y,"join"),d&&this._applyJoins(w,d,"leftJoin"),f&&this._applyJoins(w,f,"rightJoin"),g&&this._applyJoins(w,g,"innerJoin"),this._applyWhereClause(w,r,o),c&&(Array.isArray(c),w.groupBy(c)),p&&this._applyHavingClause(w,p),n&&this._applyOrderBy(w,n);let b=!1,m=l,_=a,A=1,j=0;h&&l>0&&(A=Math.max(1,parseInt(h)),_=(A-1)*l),l>0&&(w.limit(m),_>0&&w.offset(_));const k=[],C=w.toSQL();k.push(C.sql);const J=await w;if(o&&o.length>0){const t=this.buildWithTree(o);for(const r of Object.keys(t))await this.fetchAndAttachRelated({parentRows:J,relName:r,model:e,withTree:t[r],relation:e.hasRelations[r],withWhere:i||{}})}let R=null;if(l>0)try{let t=this.getQueryBuilder(e);r&&Object.keys(r).length>0&&this._applyWhereClause(t,r),y&&this._applyJoins(t,y,"join"),d&&this._applyJoins(t,d,"leftJoin"),f&&this._applyJoins(t,f,"rightJoin"),g&&this._applyJoins(t,g,"innerJoin");const o=t.count("* as cnt");k.push(o.toSQL().sql);R=(await o.first()).cnt}catch(e){logger.warn("Failed to get total count:",e.message),R=J.length}l>0&&null!==R&&(j=Math.ceil(R/l),b=A<j);const B=b?A+1:null,N=A>1?A-1:null;return{data:J,totalCount:R,...this.controllerWrapper.debug?{sqlDebug:k}:{},...l>0?{pagination:{page:A,limit:m,offset:_,totalPages:j,hasNext:b,hasPrev:A>1,nextPage:B,prevPage:N}}:{}}}catch(t){throw logger.error("QueryService.getQuery error:",t),new Error(`Failed to execute query: ${t.message} on model ${e.name}`)}}getSumQuery(e,t){const{where:r={},join:o,leftJoin:i,rightJoin:s,innerJoin:n}=t,l=t.data||t,a=l.sumColumn,h=l.sumFormula;if(!a&&!h)throw new Error("Sum action requires either data.sumColumn or data.sumFormula");const c=e=>"`"+String(e).replace(/[`\\]/g,"")+"`";let p;if(a){const e=String(a).trim().replace(/[^a-zA-Z0-9_]/g,"");if(!e)throw new Error("data.sumColumn must be a valid column name");p="SUM("+c(e)+")"}else{const e=String(h).trim();if(!/\{[a-zA-Z0-9_]+\}/.test(e))throw new Error("data.sumFormula must contain at least one {columnName}");let t=0;for(const r of e)if("("===r)t++;else if(")"===r&&(t--,t<0))break;if(0!==t)throw new Error("data.sumFormula has unbalanced or misordered parentheses ( and )");const r=e.replace(/\{[a-zA-Z0-9_]+\}/g,"@");if(!/^[\s0-9.+*\/\-()@]+$/.test(r))throw new Error("data.sumFormula may only use BODMAS: numbers, + - * /, parentheses, and {columnName}");p="SUM("+e.replace(/\{([a-zA-Z0-9_]+)\}/g,(e,t)=>c(t))+")"}let u=this.getQueryBuilder(e);return o&&this._applyJoins(u,o,"join"),i&&this._applyJoins(u,i,"leftJoin"),s&&this._applyJoins(u,s,"rightJoin"),n&&this._applyJoins(u,n,"innerJoin"),this._applyWhereClause(u,r,[]),u.select(this.db.raw(p+" as sum")),u}_applyWhereWithArray(e,t,r){for(const o of t)this._applyWhereClause(e,o,r)}_applyWhereClause(e,t,r=[]){if(Array.isArray(t))this._applyWhereWithArray(e,t,r);else{if(t&&Object.keys(t).length>0){let r=this.helperUtility.getDotWalkQuery(t);logger.debug({filteredWhere:r}),r=this.helperUtility.objectFilter(r,(e,t)=>!e.startsWith("!")&&("__exists__"!==e&&("object"!=typeof t||null===t)));for(const[t,o]of Object.entries(r)){const{joinType:r="AND",column:i}=this.parseWhereColumn(t),{operator:s,value:n}=this.parseWhereValue(o);"AND"===r?this._applyAndWhereCondition(e,i,s,n):this._applyOrWhereCondition(e,i,s,n)}}this._applyNestedWhere(e,t,r)}}_getNestedWhereConditions(e,t){if(!e||"object"!=typeof e)return{};const r=`${t}.`,o={};for(const[i,s]of Object.entries(e))if(i.startsWith(r)){o[i.slice(r.length)]=s}else i===t&&!0===s&&(o.__exists__=!0);return o}_getTopLevelRelationsFromWhere(e){if(!e||"object"!=typeof e)return[];const t=new Set;for(const r of Object.keys(e))if(r.includes(".")){const e=r.split(".")[0];t.add(e)}else r.startsWith("!")&&t.add(r);return[...t]}_applyNestedWhere(e,t,r){let o=this,i=e._getMyModel();const s=this._getTopLevelRelationsFromWhere(t);if(0!==s.length)for(let r of s){let s=r.startsWith("!"),n=s?r.slice(1):r,l=this._getNestedWhereConditions(t,r);if(0===Object.keys(l).length)continue;let a,h=i.hasRelations?.[n];if(!h){logger.warn(`Relation ${n} not found in model ${i.modelName}`);continue}try{a=this.utils.getModel(this.controllerWrapper,h.table||n)}catch(e){try{a=this.utils.getModel(this.controllerWrapper,n)}catch(e){logger.warn(`Model for relation ${n} (table: ${h.table}) not found`);continue}}e[s?"whereNotExists":"whereExists"](function(){let e=o.getQueryBuilder(a,this.select("*").from(h.table));e.whereRaw(`\`${h.table}\`.\`${h.foreignKey}\` = \`${i.table}\`.\`${h.localKey}\``);if(!(!0===l.__exists__&&1===Object.keys(l).length)){const t={...l};delete t.__exists__,o._applyWhereClause(e,t,[])}})}}_applyJoins(e,t,r){const o=Array.isArray(t)?t:[t];for(const t of o)"string"==typeof t?e[r](t):"object"==typeof t&&(t.table&&t.on?e[r](t.table,t.on):t.table&&t.first&&t.operator&&t.second&&e[r](t.table,t.first,t.operator,t.second))}_applyWithWhere(e,t){try{if(Array.isArray(t))for(const r of t)"string"==typeof r?e.withWhere(r):"object"==typeof r&&e.withWhere(r.column,r.operator,r.value);else if("object"==typeof t)for(const[r,o]of Object.entries(t))e.withWhere(r,o)}catch(e){logger.warn("Failed to apply withWhere:",e.message)}}_applyHavingClause(e,t){for(const[r,o]of Object.entries(t))"object"==typeof o&&o.operator?e.having(r,o.operator,o.value):e.having(r,o)}_applyOrderBy(e,t){if(Array.isArray(t))for(const r of t)"string"==typeof r?e.orderBy(r):"object"==typeof r&&e.orderBy(r.column,r.direction||"asc");else"string"==typeof t?e.orderBy(t):"object"==typeof t&&e.orderBy(t.column,t.direction||"asc")}}module.exports=QueryBuilder;
@@ -1 +1 @@
1
- const QueryBuilder=require("./QueryBuilder");class QueryService{constructor(e,t,r=null){this.db=e,this.utils=t,this.controllerWrapper=r,this.queryBuilder=new QueryBuilder(e,t,r)}async getQuery(e,t){return await this.queryBuilder.getQuery(e,t)}async getSoftDeleteQuery(e,t){return await this.queryBuilder.getQuery(e,{...t,where:{...t.where||{},deleted_at:null}})}async executeShowQuery(e,t){let r=await this.getQuery(e,{...t,limit:1,offset:0});return r.data.length>0?r.data[0]:null}async executeCountQuery(e,t){let r=await this.db(e.table).count();return Object.values(r[0])[0]}async executeCreateQuery(e,t){return await this.db(e.table).insert(t.data).returning("*")}async executeUpdateQuery(e,t){return await this.db.transaction(async r=>(await r(e.table).where(t.where).update(t.data),await r(e.table).where(t.where).first()))}async executeDeleteQuery(e,t){return await this.db(e.table).where(t.where).delete()}async executeSoftDeleteQuery(e,t){return await this.db(e.table).where(t.where).update({deleted_at:new Date}).returning("*")}async executeUpsertQuery(e,t){return await this.db(e.table).insert(t.data).onConflict(t.conflict).update(t.data)}async executeReplaceQuery(e,t){return await this.db(e.table).replace(t.data)}async executeSyncQuery(e,t){return{insertOrUpdateQuery:await this.db(e.table).insert(t.data).onConflict(t.conflict).update(t.data),deleteQuery:await this.db(e.table).where(t.where).delete()}}}module.exports=QueryService;
1
+ const QueryBuilder=require("./QueryBuilder");class QueryService{constructor(e,t,r=null){this.db=e,this.utils=t,this.controllerWrapper=r,this.queryBuilder=new QueryBuilder(e,t,r)}async getQuery(e,t){return await this.queryBuilder.getQuery(e,t)}async getSoftDeleteQuery(e,t){return await this.queryBuilder.getQuery(e,{...t,where:{...t.where||{},deleted_at:null}})}async executeShowQuery(e,t){let r=await this.getQuery(e,{...t,limit:1,offset:0});return r.data.length>0?r.data[0]:null}async executeCountQuery(e,t){let r=await this.db(e.table).count();return Object.values(r[0])[0]}async executeSumQuery(e,t){const r=await this.queryBuilder.getSumQuery(e,t).first(),a=r&&null!=r.sum?r.sum:0;return Number(a)}async executeCreateQuery(e,t){const r=await this.db(e.table).insert(t.data),a=Array.isArray(r)?r[0]:r;if(null==a)return[];const u=e.columns&&e.columns.find(e=>e.primary),i=u&&u.name?u.name:"id",n=await this.db(e.table).where(i,a).select("*");return Array.isArray(n)?n:[n]}async executeUpdateQuery(e,t){return await this.db.transaction(async r=>(await r(e.table).where(t.where).update(t.data),await r(e.table).where(t.where).first()))}async executeDeleteQuery(e,t){return await this.db(e.table).where(t.where).delete()}async executeSoftDeleteQuery(e,t){await this.db(e.table).where(t.where).update({deleted_at:new Date});const r=await this.db(e.table).where(t.where).select("*");return Array.isArray(r)?r:[r]}async executeUpsertQuery(e,t){return await this.db(e.table).insert(t.data).onConflict(t.conflict).update(t.data)}async executeReplaceQuery(e,t){return await this.db(e.table).replace(t.data)}async executeSyncQuery(e,t){return{insertOrUpdateQuery:await this.db(e.table).insert(t.data).onConflict(t.conflict).update(t.data),deleteQuery:await this.db(e.table).where(t.where).delete()}}}module.exports=QueryService;
@@ -1 +1 @@
1
- const QueryService=require("./QueryService"),HookService=require("./HookService");class CurdTable{constructor(e,r,t=null){if(this.db=e,this.utils=r,this.controllerWrapper=t,this.hookService=new HookService(e,r,t),this.queryService=new QueryService(e,r,t),!this.queryService)throw new Error("CurdTable requires queryService (execute*Query / getQuery).")}async processRequest(e,r=null,t=null){const o=this.controllerWrapper;let s=this.utils.getModel(this.controllerWrapper,r);const c=e?.action||"list";let i=null;const a={model:s,action:c,request:e,ctx:t,controller:o};switch(this.hookService?.executeValidatorHook&&await this.hookService.executeValidatorHook({...a}),this.hookService?.executeBeforeHook&&(e.beforeActionData=await this.hookService.executeBeforeHook({...a})),c){case"count":i=await this.queryService.executeCountQuery(s,e);break;case"list":i=await this.hookService.executeHasSoftDeleteHook(s)?await this.queryService.getSoftDeleteQuery(s,e):await this.queryService.getQuery(s,e);break;case"show":i=await this.queryService.executeShowQuery(s,e);break;case"create":i=await this.queryService.executeCreateQuery(s,e);break;case"update":{const r=await this.queryService.executeUpdateQuery(s,e);if(!r)throw new Error(`Record not found or not updated: ${s.table} returned ${r}`);i={message:"Record updated successfully",data:r,success:!0};break}case"replace":if(i=await this.queryService.executeReplaceQuery(s,e),!i)throw new Error(`Record not found or not replaced: ${r} returned ${i}`);i={message:"Record replaced successfully",data:i,success:!0};break;case"upsert":if(i=await this.queryService.executeUpsertQuery(s,e),!i)throw new Error(`Record not found or not upserted: ${r} returned ${i}`);i={message:"Record upserted successfully",data:i,success:!0};break;case"sync":if(i=await this.queryService.executeSyncQuery(s,e),!i)throw new Error(`Record not found or not synced: ${r} returned ${i}`);i={message:"Record synced successfully",data:i,success:!0};break;case"delete":{let t=null;if(t=await this.hookService.executeHasSoftDeleteHook(s)?await this.queryService.executeSoftDeleteQuery(s,e):await this.queryService.executeDeleteQuery(s,e),!t)throw new Error(`Record not found or not deleted: ${r} returned ${t}`);i={message:"Record deleted successfully",data:t,success:!0};break}default:if(!this.hookService?.executeCustomAction)throw new Error(`Unknown action "${c}" and no custom action hook provided.`);i=await this.hookService.executeCustomAction({...a})}if(this.hookService?.executeAfterHook&&(i=await this.hookService.executeAfterHook({...a,data:i})),e?.other_requests&&"object"==typeof e.other_requests){const r={},o=Object.entries(e.other_requests);for(const[e,s]of o)Array.isArray(s)?r[e]=await Promise.all(s.map(r=>this.processRequest(r,e,t))):r[e]=await this.processRequest(s,e,t);i.other_responses=r}return i}}module.exports=CurdTable;
1
+ const QueryService=require("./QueryService"),HookService=require("./HookService");class CurdTable{constructor(e,r,t=null){if(this.db=e,this.utils=r,this.controllerWrapper=t,this.hookService=new HookService(e,r,t),this.queryService=new QueryService(e,r,t),!this.queryService)throw new Error("CurdTable requires queryService (execute*Query / getQuery).")}async processRequest(e,r=null,t=null){const o=this.controllerWrapper;let s=this.utils.getModel(this.controllerWrapper,r);const c=e?.action||"list";let u=null;const a={model:s,action:c,request:e,ctx:t,controller:o};switch(this.hookService?.executeValidatorHook&&await this.hookService.executeValidatorHook({...a}),this.hookService?.executeBeforeHook&&(e.beforeActionData=await this.hookService.executeBeforeHook({...a})),c){case"count":u=await this.queryService.executeCountQuery(s,e);break;case"sum":u=await this.queryService.executeSumQuery(s,e);break;case"list":u=await this.hookService.executeHasSoftDeleteHook(s)?await this.queryService.getSoftDeleteQuery(s,e):await this.queryService.getQuery(s,e);break;case"show":u=await this.queryService.executeShowQuery(s,e);break;case"create":u=await this.queryService.executeCreateQuery(s,e);break;case"update":{const r=await this.queryService.executeUpdateQuery(s,e);if(!r)throw new Error(`Record not found or not updated: ${s.table} returned ${r}`);u={message:"Record updated successfully",data:r,success:!0};break}case"replace":if(u=await this.queryService.executeReplaceQuery(s,e),!u)throw new Error(`Record not found or not replaced: ${r} returned ${u}`);u={message:"Record replaced successfully",data:u,success:!0};break;case"upsert":if(u=await this.queryService.executeUpsertQuery(s,e),!u)throw new Error(`Record not found or not upserted: ${r} returned ${u}`);u={message:"Record upserted successfully",data:u,success:!0};break;case"sync":if(u=await this.queryService.executeSyncQuery(s,e),!u)throw new Error(`Record not found or not synced: ${r} returned ${u}`);u={message:"Record synced successfully",data:u,success:!0};break;case"delete":{let t=null;if(t=await this.hookService.executeHasSoftDeleteHook(s)?await this.queryService.executeSoftDeleteQuery(s,e):await this.queryService.executeDeleteQuery(s,e),!t)throw new Error(`Record not found or not deleted: ${r} returned ${t}`);u={message:"Record deleted successfully",data:t,success:!0};break}default:if(!this.hookService?.executeCustomAction)throw new Error(`Unknown action "${c}" and no custom action hook provided.`);u=await this.hookService.executeCustomAction({...a})}if(this.hookService?.executeAfterHook&&(u=await this.hookService.executeAfterHook({...a,data:u})),e?.other_requests&&"object"==typeof e.other_requests){const r={},o=Object.entries(e.other_requests);for(const[e,s]of o)Array.isArray(s)?r[e]=await Promise.all(s.map(r=>this.processRequest(r,e,t))):r[e]=await this.processRequest(s,e,t);u.other_responses=r}return u}}module.exports=CurdTable;
@@ -1 +1 @@
1
- const HelperUtility=require("./HelperUtility"),logger=require("../../Logger");class QueryBuilder{constructor(e,t,r=null){this.db=e,this.utils=t,this.controllerWrapper=r,this.helperUtility=new HelperUtility}getHookService(){return this.controllerWrapper.hookService}getQueryBuilder(e,t=null){let r=this.db(e.table);return t&&(r=t),r._getMyModel=()=>e,r}parseValue(e){return this.helperUtility.parseValue(e)}parseWhereValue(e){return this.helperUtility.parseWhereValue(e)}parseWhereColumn(e){return this.helperUtility.parseWhereColumn(e)}_applyOrWhereCondition(e,t,r,o){if(null!==o)if(Array.isArray(o))e.orWhereIn(t,o);else switch(r){case"between":e.orWhereBetween(t,o);break;case"notBetween":e.orWhereNotBetween(t,o);break;case"in":e.orWhereIn(t,o);break;case"notIn":e.orWhereNotIn(t,o);break;case"like":e.orWhere(t,"like",o);break;default:e.orWhere(t,r,o)}else e.orWhereNull(t)}_applyAndWhereCondition(e,t,r,o){if(null!==o)if(Array.isArray(o))e.whereIn(t,o);else switch(r){case"between":e.whereBetween(t,o);break;case"notBetween":e.whereNotBetween(t,o);break;case"in":e.whereIn(t,o);break;case"notIn":e.whereNotIn(t,o);break;case"like":e.where(t,"like",o);break;default:e.where(t,r,o)}else e.whereNull(t)}buildWithTree(e,t=null){return this.helperUtility.dotWalkTree(e,{resolver:({current:e,part:r,source:o})=>t?t({current:e,part:r,source:o}):{}})}_getWithWhereForRelation(e,t){if(!e||"object"!=typeof e)return{direct:{},nested:{}};const r=`${t}.`,o={},i={};for(const[t,s]of Object.entries(e))if(t.startsWith(r)){const e=t.slice(r.length);e.includes(".")?i[e]=s:o[e]=s}return{direct:o,nested:i}}_applyWithWhereConditions(e,t){for(const[r,o]of Object.entries(t)){const{joinType:t="AND",column:i}=this.parseWhereColumn(r),{operator:s,value:l}=this.parseWhereValue(o);"AND"===t?this._applyAndWhereCondition(e,i,s,l):this._applyOrWhereCondition(e,i,s,l)}}async fetchRelatedRows(e,t,r={}){if(e.through){let o=await this.db(e.through).whereIn(e.throughLocalKey,t),i=this.db(e.table).whereIn(e.foreignKey,o.map(t=>t[e.throughForeignKey]));return Object.keys(r).length>0&&this._applyWithWhereConditions(i,r),i}{let o=this.db(e.table).whereIn(e.foreignKey,t);return Object.keys(r).length>0&&this._applyWithWhereConditions(o,r),o}}async fetchAndAttachRelated(e){const{parentRows:t,relName:r,model:o,withTree:i,relation:s,withWhere:l={}}=e;if(!s){const e=this.getHookService().getModelInstance(o),s=`get${r.charAt(0).toUpperCase()+r.slice(1)}Relation`;if("function"==typeof e[s]){const{direct:n,nested:a}=this._getWithWhereForRelation(l,r),h={rows:t,relName:r,model:o,withTree:i,controller:this.controllerWrapper,relation:o.hasRelations[r],qb:this,db:this.db,withWhere:n,nestedWithWhere:a};await e[s](h)}else logger.warn(`Method ${s} not found in model ${o.name}`);return t}let n=t.map(e=>e[s.localKey]);const a="one"===s?.type;let h=[];const{direct:c,nested:p}=this._getWithWhereForRelation(l,r);h=await this.fetchRelatedRows(s,n,c);let y=new Map;for(const e of h){let t=e[s.foreignKey];y.has(t)||y.set(t,[]),y.get(t).push(e)}for(const e of t){let t=e[s.localKey];a&&1==y.get(t)?.length?e[r]=y.get(t)[0]:e[r]=y.get(t)||[]}let u=Object.keys(i);for(const e of u){let o=i[e],s=t.filter(e=>a?e[r]:e[r].length>0).map(e=>e[r]).reduce((e,t)=>e.concat(t),[]),l=this.utils.getModel(this.controllerWrapper,r);await this.fetchAndAttachRelated({parentRows:s,relName:e,model:l,withTree:o,relation:l.hasRelations[e],withWhere:p})}return t}filterWhere(e,t=""){return t?Object.keys(e).filter(e=>e.includes(t)).reduce((r,o)=>(r[o.replace(t,"")]=e[o],r),{}):Object.keys(e).filter(e=>!e.includes(".")).reduce((t,r)=>(t[r]=e[r],t),{})}async getQuery(e,t){try{const{where:r={},with:o,withWhere:i,select:s,orderBy:l={column:"id",direction:"asc"},limit:n=10,offset:a=0,page:h,groupBy:c,having:p,distinct:y,join:u,leftJoin:d,rightJoin:f,innerJoin:g,count:W=!1}=t;let b=this.getQueryBuilder(e);s&&(Array.isArray(s)||"string"==typeof s)?b.select(s):b.select("*"),y&&(Array.isArray(y)||"string"==typeof y?b.distinct(y):b.distinct()),u&&this._applyJoins(b,u,"join"),d&&this._applyJoins(b,d,"leftJoin"),f&&this._applyJoins(b,f,"rightJoin"),g&&this._applyJoins(b,g,"innerJoin"),this._applyWhereClause(b,r,o),c&&(Array.isArray(c),b.groupBy(c)),p&&this._applyHavingClause(b,p),l&&this._applyOrderBy(b,l);let w=!1,_=n,m=a,A=1,j=0;h&&n>0&&(A=Math.max(1,parseInt(h)),m=(A-1)*n),n>0&&(b.limit(_),m>0&&b.offset(m));const k=[],C=b.toSQL();k.push(C.sql);const O=await b;if(o&&o.length>0){const t=this.buildWithTree(o);for(const r of Object.keys(t))await this.fetchAndAttachRelated({parentRows:O,relName:r,model:e,withTree:t[r],relation:e.hasRelations[r],withWhere:i||{}})}let R=null;if(n>0)try{let t=this.getQueryBuilder(e);r&&Object.keys(r).length>0&&this._applyWhereClause(t,r),u&&this._applyJoins(t,u,"join"),d&&this._applyJoins(t,d,"leftJoin"),f&&this._applyJoins(t,f,"rightJoin"),g&&this._applyJoins(t,g,"innerJoin");const o=t.count("* as cnt");k.push(o.toSQL().sql);R=(await o.first()).cnt}catch(e){logger.warn("Failed to get total count:",e.message),R=O.length}n>0&&null!==R&&(j=Math.ceil(R/n),w=A<j);const B=w?A+1:null,N=A>1?A-1:null;return{data:O,totalCount:R,...this.controllerWrapper.debug?{sqlDebug:k}:{},...n>0?{pagination:{page:A,limit:_,offset:m,totalPages:j,hasNext:w,hasPrev:A>1,nextPage:B,prevPage:N}}:{}}}catch(t){throw logger.error("QueryService.getQuery error:",t),new Error(`Failed to execute query: ${t.message} on model ${e.name}`)}}_applyWhereWithArray(e,t,r){for(const o of t)this._applyWhereClause(e,o,r)}_applyWhereClause(e,t,r=[]){if(Array.isArray(t))this._applyWhereWithArray(e,t,r);else{if(t&&Object.keys(t).length>0){let r=this.helperUtility.getDotWalkQuery(t);logger.debug({filteredWhere:r}),r=this.helperUtility.objectFilter(r,(e,t)=>!e.startsWith("!")&&("__exists__"!==e&&("object"!=typeof t||null===t)));for(const[t,o]of Object.entries(r)){const{joinType:r="AND",column:i}=this.parseWhereColumn(t),{operator:s,value:l}=this.parseWhereValue(o);"AND"===r?this._applyAndWhereCondition(e,i,s,l):this._applyOrWhereCondition(e,i,s,l)}}this._applyNestedWhere(e,t,r)}}_getNestedWhereConditions(e,t){if(!e||"object"!=typeof e)return{};const r=`${t}.`,o={};for(const[i,s]of Object.entries(e))if(i.startsWith(r)){o[i.slice(r.length)]=s}else i===t&&!0===s&&(o.__exists__=!0);return o}_getTopLevelRelationsFromWhere(e){if(!e||"object"!=typeof e)return[];const t=new Set;for(const r of Object.keys(e))if(r.includes(".")){const e=r.split(".")[0];t.add(e)}else r.startsWith("!")&&t.add(r);return[...t]}_applyNestedWhere(e,t,r){let o=this,i=e._getMyModel();const s=this._getTopLevelRelationsFromWhere(t);if(0!==s.length)for(let r of s){let s=r.startsWith("!"),l=s?r.slice(1):r,n=this._getNestedWhereConditions(t,r);if(0===Object.keys(n).length)continue;let a,h=i.hasRelations?.[l];if(!h){logger.warn(`Relation ${l} not found in model ${i.modelName}`);continue}try{a=this.utils.getModel(this.controllerWrapper,h.table||l)}catch(e){try{a=this.utils.getModel(this.controllerWrapper,l)}catch(e){logger.warn(`Model for relation ${l} (table: ${h.table}) not found`);continue}}e[s?"whereNotExists":"whereExists"](function(){let e=o.getQueryBuilder(a,this.select("*").from(h.table));e.whereRaw(`"${h.table}"."${h.foreignKey}" = "${i.table}"."${h.localKey}"`);if(!(!0===n.__exists__&&1===Object.keys(n).length)){const t={...n};delete t.__exists__,o._applyWhereClause(e,t,[])}})}}_applyJoins(e,t,r){const o=Array.isArray(t)?t:[t];for(const t of o)"string"==typeof t?e[r](t):"object"==typeof t&&(t.table&&t.on?e[r](t.table,t.on):t.table&&t.first&&t.operator&&t.second&&e[r](t.table,t.first,t.operator,t.second))}_applyWithWhere(e,t){try{if(Array.isArray(t))for(const r of t)"string"==typeof r?e.withWhere(r):"object"==typeof r&&e.withWhere(r.column,r.operator,r.value);else if("object"==typeof t)for(const[r,o]of Object.entries(t))e.withWhere(r,o)}catch(e){logger.warn("Failed to apply withWhere:",e.message)}}_applyHavingClause(e,t){for(const[r,o]of Object.entries(t))"object"==typeof o&&o.operator?e.having(r,o.operator,o.value):e.having(r,o)}_applyOrderBy(e,t){if(Array.isArray(t))for(const r of t)"string"==typeof r?e.orderBy(r):"object"==typeof r&&e.orderBy(r.column,r.direction||"asc");else"string"==typeof t?e.orderBy(t):"object"==typeof t&&e.orderBy(t.column,t.direction||"asc")}}module.exports=QueryBuilder;
1
+ const HelperUtility=require("./HelperUtility"),logger=require("../../Logger");class QueryBuilder{constructor(e,t,r=null){this.db=e,this.utils=t,this.controllerWrapper=r,this.helperUtility=new HelperUtility}getHookService(){return this.controllerWrapper.hookService}getQueryBuilder(e,t=null){let r=this.db(e.table);return t&&(r=t),r._getMyModel=()=>e,r}parseValue(e){return this.helperUtility.parseValue(e)}parseWhereValue(e){return this.helperUtility.parseWhereValue(e)}parseWhereColumn(e){return this.helperUtility.parseWhereColumn(e)}_applyOrWhereCondition(e,t,r,o){if(null!==o)if(Array.isArray(o))e.orWhereIn(t,o);else switch(r){case"between":e.orWhereBetween(t,o);break;case"notBetween":e.orWhereNotBetween(t,o);break;case"in":e.orWhereIn(t,o);break;case"notIn":e.orWhereNotIn(t,o);break;case"like":e.orWhere(t,"like",o);break;default:e.orWhere(t,r,o)}else e.orWhereNull(t)}_applyAndWhereCondition(e,t,r,o){if(null!==o)if(Array.isArray(o))e.whereIn(t,o);else switch(r){case"between":e.whereBetween(t,o);break;case"notBetween":e.whereNotBetween(t,o);break;case"in":e.whereIn(t,o);break;case"notIn":e.whereNotIn(t,o);break;case"like":e.where(t,"like",o);break;default:e.where(t,r,o)}else e.whereNull(t)}buildWithTree(e,t=null){return this.helperUtility.dotWalkTree(e,{resolver:({current:e,part:r,source:o})=>t?t({current:e,part:r,source:o}):{}})}_getWithWhereForRelation(e,t){if(!e||"object"!=typeof e)return{direct:{},nested:{}};const r=`${t}.`,o={},i={};for(const[t,s]of Object.entries(e))if(t.startsWith(r)){const e=t.slice(r.length);e.includes(".")?i[e]=s:o[e]=s}return{direct:o,nested:i}}_applyWithWhereConditions(e,t){for(const[r,o]of Object.entries(t)){const{joinType:t="AND",column:i}=this.parseWhereColumn(r),{operator:s,value:n}=this.parseWhereValue(o);"AND"===t?this._applyAndWhereCondition(e,i,s,n):this._applyOrWhereCondition(e,i,s,n)}}async fetchRelatedRows(e,t,r={}){if(e.through){let o=await this.db(e.through).whereIn(e.throughLocalKey,t),i=this.db(e.table).whereIn(e.foreignKey,o.map(t=>t[e.throughForeignKey]));return Object.keys(r).length>0&&this._applyWithWhereConditions(i,r),i}{let o=this.db(e.table).whereIn(e.foreignKey,t);return Object.keys(r).length>0&&this._applyWithWhereConditions(o,r),o}}async fetchAndAttachRelated(e){const{parentRows:t,relName:r,model:o,withTree:i,relation:s,withWhere:n={}}=e;if(!s){const e=this.getHookService().getModelInstance(o),s=`get${r.charAt(0).toUpperCase()+r.slice(1)}Relation`;if("function"==typeof e[s]){const{direct:l,nested:a}=this._getWithWhereForRelation(n,r),h={rows:t,relName:r,model:o,withTree:i,controller:this.controllerWrapper,relation:o.hasRelations[r],qb:this,db:this.db,withWhere:l,nestedWithWhere:a};await e[s](h)}else logger.warn(`Method ${s} not found in model ${o.name}`);return t}let l=t.map(e=>e[s.localKey]);const a="one"===s?.type;let h=[];const{direct:c,nested:p}=this._getWithWhereForRelation(n,r);let u={...c};try{const e=this.utils.getModel(this.controllerWrapper,r);if(this.controllerWrapper?.hookService){await this.controllerWrapper.hookService.executeHasSoftDeleteHook(e)&&(u={...u,deleted_at:null})}}catch(e){}h=await this.fetchRelatedRows(s,l,u);let y=new Map;for(const e of h){let t=e[s.foreignKey];y.has(t)||y.set(t,[]),y.get(t).push(e)}for(const e of t){let t=e[s.localKey];a&&1==y.get(t)?.length?e[r]=y.get(t)[0]:e[r]=y.get(t)||[]}let d=Object.keys(i);for(const e of d){let o=i[e],s=t.filter(e=>a?e[r]:e[r].length>0).map(e=>e[r]).reduce((e,t)=>e.concat(t),[]),n=this.utils.getModel(this.controllerWrapper,r);await this.fetchAndAttachRelated({parentRows:s,relName:e,model:n,withTree:o,relation:n.hasRelations[e],withWhere:p})}return t}filterWhere(e,t=""){return t?Object.keys(e).filter(e=>e.includes(t)).reduce((r,o)=>(r[o.replace(t,"")]=e[o],r),{}):Object.keys(e).filter(e=>!e.includes(".")).reduce((t,r)=>(t[r]=e[r],t),{})}async getQuery(e,t){try{const{where:r={},with:o,withWhere:i,select:s,orderBy:n={column:"id",direction:"asc"},limit:l=10,offset:a=0,page:h,groupBy:c,having:p,distinct:u,join:y,leftJoin:d,rightJoin:f,innerJoin:g,count:W=!1}=t;let w=this.getQueryBuilder(e);s&&(Array.isArray(s)||"string"==typeof s)?w.select(s):w.select("*"),u&&(Array.isArray(u)||"string"==typeof u?w.distinct(u):w.distinct()),y&&this._applyJoins(w,y,"join"),d&&this._applyJoins(w,d,"leftJoin"),f&&this._applyJoins(w,f,"rightJoin"),g&&this._applyJoins(w,g,"innerJoin"),this._applyWhereClause(w,r,o),c&&(Array.isArray(c),w.groupBy(c)),p&&this._applyHavingClause(w,p),n&&this._applyOrderBy(w,n);let b=!1,m=l,_=a,A=1,j=0;h&&l>0&&(A=Math.max(1,parseInt(h)),_=(A-1)*l),l>0&&(w.limit(m),_>0&&w.offset(_));const k=[],C=w.toSQL();k.push(C.sql);const J=await w;if(o&&o.length>0){const t=this.buildWithTree(o);for(const r of Object.keys(t))await this.fetchAndAttachRelated({parentRows:J,relName:r,model:e,withTree:t[r],relation:e.hasRelations[r],withWhere:i||{}})}let B=null;if(l>0)try{let t=this.getQueryBuilder(e);r&&Object.keys(r).length>0&&this._applyWhereClause(t,r),y&&this._applyJoins(t,y,"join"),d&&this._applyJoins(t,d,"leftJoin"),f&&this._applyJoins(t,f,"rightJoin"),g&&this._applyJoins(t,g,"innerJoin");const o=t.count("* as cnt");k.push(o.toSQL().sql);B=(await o.first()).cnt}catch(e){logger.warn("Failed to get total count:",e.message),B=J.length}l>0&&null!==B&&(j=Math.ceil(B/l),b=A<j);const N=b?A+1:null,O=A>1?A-1:null;return{data:J,totalCount:B,...this.controllerWrapper.debug?{sqlDebug:k}:{},...l>0?{pagination:{page:A,limit:m,offset:_,totalPages:j,hasNext:b,hasPrev:A>1,nextPage:N,prevPage:O}}:{}}}catch(t){throw logger.error("QueryService.getQuery error:",t),new Error(`Failed to execute query: ${t.message} on model ${e.name}`)}}getSumQuery(e,t){const{where:r={},join:o,leftJoin:i,rightJoin:s,innerJoin:n}=t,l=t.data||t,a=l.sumColumn,h=l.sumFormula;if(!a&&!h)throw new Error("Sum action requires either data.sumColumn or data.sumFormula");const c=e=>'"'+String(e).replace(/["\\]/g,"")+'"';let p;if(a){const e=String(a).trim().replace(/[^a-zA-Z0-9_]/g,"");if(!e)throw new Error("data.sumColumn must be a valid column name");p="SUM("+c(e)+")"}else{const e=String(h).trim();if(!/\{[a-zA-Z0-9_]+\}/.test(e))throw new Error("data.sumFormula must contain at least one {columnName}");let t=0;for(const r of e)if("("===r)t++;else if(")"===r&&(t--,t<0))break;if(0!==t)throw new Error("data.sumFormula has unbalanced or misordered parentheses ( and )");const r=e.replace(/\{[a-zA-Z0-9_]+\}/g,"@");if(!/^[\s0-9.+*\/\-()@]+$/.test(r))throw new Error("data.sumFormula may only use BODMAS: numbers, + - * /, parentheses, and {columnName}");p="SUM("+e.replace(/\{([a-zA-Z0-9_]+)\}/g,(e,t)=>c(t))+")"}let u=this.getQueryBuilder(e);return o&&this._applyJoins(u,o,"join"),i&&this._applyJoins(u,i,"leftJoin"),s&&this._applyJoins(u,s,"rightJoin"),n&&this._applyJoins(u,n,"innerJoin"),this._applyWhereClause(u,r,[]),u.select(this.db.raw(p+" as sum")),u}_applyWhereWithArray(e,t,r){for(const o of t)this._applyWhereClause(e,o,r)}_applyWhereClause(e,t,r=[]){if(Array.isArray(t))this._applyWhereWithArray(e,t,r);else{if(t&&Object.keys(t).length>0){let r=this.helperUtility.getDotWalkQuery(t);logger.debug({filteredWhere:r}),r=this.helperUtility.objectFilter(r,(e,t)=>!e.startsWith("!")&&("__exists__"!==e&&("object"!=typeof t||null===t)));for(const[t,o]of Object.entries(r)){const{joinType:r="AND",column:i}=this.parseWhereColumn(t),{operator:s,value:n}=this.parseWhereValue(o);"AND"===r?this._applyAndWhereCondition(e,i,s,n):this._applyOrWhereCondition(e,i,s,n)}}this._applyNestedWhere(e,t,r)}}_getNestedWhereConditions(e,t){if(!e||"object"!=typeof e)return{};const r=`${t}.`,o={};for(const[i,s]of Object.entries(e))if(i.startsWith(r)){o[i.slice(r.length)]=s}else i===t&&!0===s&&(o.__exists__=!0);return o}_getTopLevelRelationsFromWhere(e){if(!e||"object"!=typeof e)return[];const t=new Set;for(const r of Object.keys(e))if(r.includes(".")){const e=r.split(".")[0];t.add(e)}else r.startsWith("!")&&t.add(r);return[...t]}_applyNestedWhere(e,t,r){let o=this,i=e._getMyModel();const s=this._getTopLevelRelationsFromWhere(t);if(0!==s.length)for(let r of s){let s=r.startsWith("!"),n=s?r.slice(1):r,l=this._getNestedWhereConditions(t,r);if(0===Object.keys(l).length)continue;let a,h=i.hasRelations?.[n];if(!h){logger.warn(`Relation ${n} not found in model ${i.modelName}`);continue}try{a=this.utils.getModel(this.controllerWrapper,h.table||n)}catch(e){try{a=this.utils.getModel(this.controllerWrapper,n)}catch(e){logger.warn(`Model for relation ${n} (table: ${h.table}) not found`);continue}}e[s?"whereNotExists":"whereExists"](function(){let e=o.getQueryBuilder(a,this.select("*").from(h.table));e.whereRaw(`"${h.table}"."${h.foreignKey}" = "${i.table}"."${h.localKey}"`);if(!(!0===l.__exists__&&1===Object.keys(l).length)){const t={...l};delete t.__exists__,o._applyWhereClause(e,t,[])}})}}_applyJoins(e,t,r){const o=Array.isArray(t)?t:[t];for(const t of o)"string"==typeof t?e[r](t):"object"==typeof t&&(t.table&&t.on?e[r](t.table,t.on):t.table&&t.first&&t.operator&&t.second&&e[r](t.table,t.first,t.operator,t.second))}_applyWithWhere(e,t){try{if(Array.isArray(t))for(const r of t)"string"==typeof r?e.withWhere(r):"object"==typeof r&&e.withWhere(r.column,r.operator,r.value);else if("object"==typeof t)for(const[r,o]of Object.entries(t))e.withWhere(r,o)}catch(e){logger.warn("Failed to apply withWhere:",e.message)}}_applyHavingClause(e,t){for(const[r,o]of Object.entries(t))"object"==typeof o&&o.operator?e.having(r,o.operator,o.value):e.having(r,o)}_applyOrderBy(e,t){if(Array.isArray(t))for(const r of t)"string"==typeof r?e.orderBy(r):"object"==typeof r&&e.orderBy(r.column,r.direction||"asc");else"string"==typeof t?e.orderBy(t):"object"==typeof t&&e.orderBy(t.column,t.direction||"asc")}}module.exports=QueryBuilder;
@@ -1 +1 @@
1
- const QueryBuilder=require("./QueryBuilder");class QueryService{constructor(e,t,r=null){this.db=e,this.utils=t,this.controllerWrapper=r,this.queryBuilder=new QueryBuilder(e,t,r)}async getQuery(e,t){return await this.queryBuilder.getQuery(e,t)}async getSoftDeleteQuery(e,t){return await this.queryBuilder.getQuery(e,{...t,where:{...t.where||{},deleted_at:null}})}async executeShowQuery(e,t){let r=await this.getQuery(e,{...t,limit:1,offset:0});return r.data.length>0?r.data[0]:null}async executeCountQuery(e,t){let r=await this.db(e.table).count();return Object.values(r[0])[0]}async executeCreateQuery(e,t){return await this.db(e.table).insert(t.data).returning("*")}async executeUpdateQuery(e,t){return await this.db(e.table).where(t.where).update(t.data).returning("*")}async executeDeleteQuery(e,t){return await this.db(e.table).where(t.where).delete()}async executeSoftDeleteQuery(e,t){return await this.db(e.table).where(t.where).update({deleted_at:new Date}).returning("*")}async executeUpsertQuery(e,t){return await this.db(e.table).insert(t.data).onConflict(t.conflict).update(t.data)}async executeReplaceQuery(e,t){return await this.db(e.table).replace(t.data)}async executeSyncQuery(e,t){return{insertOrUpdateQuery:await this.db(e.table).insert(t.data).onConflict(t.conflict).update(t.data),deleteQuery:await this.db(e.table).where(t.where).delete()}}}module.exports=QueryService;
1
+ const QueryBuilder=require("./QueryBuilder");class QueryService{constructor(e,t,r=null){this.db=e,this.utils=t,this.controllerWrapper=r,this.queryBuilder=new QueryBuilder(e,t,r)}async getQuery(e,t){return await this.queryBuilder.getQuery(e,t)}async getSoftDeleteQuery(e,t){return await this.queryBuilder.getQuery(e,{...t,where:{...t.where||{},deleted_at:null}})}async executeShowQuery(e,t){let r=await this.getQuery(e,{...t,limit:1,offset:0});return r.data.length>0?r.data[0]:null}async executeCountQuery(e,t){let r=await this.db(e.table).count();return Object.values(r[0])[0]}async executeSumQuery(e,t){const r=await this.queryBuilder.getSumQuery(e,t).first(),a=r&&null!=r.sum?r.sum:0;return Number(a)}async executeCreateQuery(e,t){return await this.db(e.table).insert(t.data).returning("*")}async executeUpdateQuery(e,t){return await this.db(e.table).where(t.where).update(t.data).returning("*")}async executeDeleteQuery(e,t){return await this.db(e.table).where(t.where).delete()}async executeSoftDeleteQuery(e,t){return await this.db(e.table).where(t.where).update({deleted_at:new Date}).returning("*")}async executeUpsertQuery(e,t){return await this.db(e.table).insert(t.data).onConflict(t.conflict).update(t.data)}async executeReplaceQuery(e,t){return await this.db(e.table).replace(t.data)}async executeSyncQuery(e,t){return{insertOrUpdateQuery:await this.db(e.table).insert(t.data).onConflict(t.conflict).update(t.data),deleteQuery:await this.db(e.table).where(t.where).delete()}}}module.exports=QueryService;
@@ -1 +1 @@
1
- const QueryService=require("./QueryService"),HookService=require("./HookService");class CurdTable{constructor(e,r,t=null){if(this.db=e,this.utils=r,this.controllerWrapper=t,this.hookService=new HookService(e,r,t),this.queryService=new QueryService(e,r,t),!this.queryService)throw new Error("CurdTable requires queryService (execute*Query / getQuery).")}async processRequest(e,r=null,t={}){const o=this.controllerWrapper;let s=this.utils.getModel(this.controllerWrapper,r);const c=e?.action||"list";let i=null;const a={model:s,action:c,request:e,ctx:t,controller:o};switch(this.hookService?.executeValidatorHook&&await this.hookService.executeValidatorHook({...a}),this.hookService?.executeBeforeHook&&(e.beforeActionData=await this.hookService.executeBeforeHook({...a})),c){case"count":i=await this.queryService.executeCountQuery(s,e);break;case"list":i=await this.hookService.executeHasSoftDeleteHook(s)?await this.queryService.getSoftDeleteQuery(s,e):await this.queryService.getQuery(s,e);break;case"show":i=await this.queryService.executeShowQuery(s,e);break;case"create":i=await this.queryService.executeCreateQuery(s,e);break;case"update":{const r=await this.queryService.executeUpdateQuery(s,e);if(!r)throw new Error(`Record not found or not updated: ${s.table} returned ${r}`);i={message:"Record updated successfully",data:r,success:!0};break}case"replace":if(i=await this.queryService.executeReplaceQuery(s,e),!i)throw new Error(`Record not found or not replaced: ${r} returned ${i}`);i={message:"Record replaced successfully",data:i,success:!0};break;case"upsert":if(i=await this.queryService.executeUpsertQuery(s,e),!i)throw new Error(`Record not found or not upserted: ${r} returned ${i}`);i={message:"Record upserted successfully",data:i,success:!0};break;case"sync":if(i=await this.queryService.executeSyncQuery(s,e),!i)throw new Error(`Record not found or not synced: ${r} returned ${i}`);i={message:"Record synced successfully",data:i,success:!0};break;case"delete":{let t=null;if(t=await this.hookService.executeHasSoftDeleteHook(s)?await this.queryService.executeSoftDeleteQuery(s,e):await this.queryService.executeDeleteQuery(s,e),!t)throw new Error(`Record not found or not deleted: ${r} returned ${t}`);i={message:"Record deleted successfully",data:t,success:!0};break}default:if(!this.hookService?.executeCustomAction)throw new Error(`Unknown action "${c}" and no custom action hook provided.`);i=await this.hookService.executeCustomAction({...a})}if(this.hookService?.executeAfterHook&&(i=await this.hookService.executeAfterHook({...a,data:i})),e?.other_requests&&"object"==typeof e.other_requests){const r={},o=Object.entries(e.other_requests);for(const[e,s]of o)Array.isArray(s)?r[e]=await Promise.all(s.map(r=>this.processRequest(r,e,t))):r[e]=await this.processRequest(s,e,t);i.other_responses=r}return i}}module.exports=CurdTable;
1
+ const QueryService=require("./QueryService"),HookService=require("./HookService");class CurdTable{constructor(e,r,t=null){if(this.db=e,this.utils=r,this.controllerWrapper=t,this.hookService=new HookService(e,r,t),this.queryService=new QueryService(e,r,t),!this.queryService)throw new Error("CurdTable requires queryService (execute*Query / getQuery).")}async processRequest(e,r=null,t={}){const o=this.controllerWrapper;let s=this.utils.getModel(this.controllerWrapper,r);const c=e?.action||"list";let a=null;const i={model:s,action:c,request:e,ctx:t,controller:o};switch(this.hookService?.executeValidatorHook&&await this.hookService.executeValidatorHook({...i}),this.hookService?.executeBeforeHook&&(e.beforeActionData=await this.hookService.executeBeforeHook({...i})),c){case"count":a=await this.queryService.executeCountQuery(s,e);break;case"sum":a=await this.queryService.executeSumQuery(s,e);break;case"list":a=await this.hookService.executeHasSoftDeleteHook(s)?await this.queryService.getSoftDeleteQuery(s,e):await this.queryService.getQuery(s,e);break;case"show":a=await this.queryService.executeShowQuery(s,e);break;case"create":a=await this.queryService.executeCreateQuery(s,e);break;case"update":{const r=await this.queryService.executeUpdateQuery(s,e);if(!r)throw new Error(`Record not found or not updated: ${s.table} returned ${r}`);a={message:"Record updated successfully",data:r,success:!0};break}case"replace":if(a=await this.queryService.executeReplaceQuery(s,e),!a)throw new Error(`Record not found or not replaced: ${r} returned ${a}`);a={message:"Record replaced successfully",data:a,success:!0};break;case"upsert":if(a=await this.queryService.executeUpsertQuery(s,e),!a)throw new Error(`Record not found or not upserted: ${r} returned ${a}`);a={message:"Record upserted successfully",data:a,success:!0};break;case"sync":if(a=await this.queryService.executeSyncQuery(s,e),!a)throw new Error(`Record not found or not synced: ${r} returned ${a}`);a={message:"Record synced successfully",data:a,success:!0};break;case"delete":{let t=null;if(t=await this.hookService.executeHasSoftDeleteHook(s)?await this.queryService.executeSoftDeleteQuery(s,e):await this.queryService.executeDeleteQuery(s,e),!t)throw new Error(`Record not found or not deleted: ${r} returned ${t}`);a={message:"Record deleted successfully",data:t,success:!0};break}default:if(!this.hookService?.executeCustomAction)throw new Error(`Unknown action "${c}" and no custom action hook provided.`);a=await this.hookService.executeCustomAction({...i})}if(this.hookService?.executeAfterHook&&(a=await this.hookService.executeAfterHook({...i,data:a})),e?.other_requests&&"object"==typeof e.other_requests){const r={},o=Object.entries(e.other_requests);for(const[e,s]of o)Array.isArray(s)?r[e]=await Promise.all(s.map(r=>this.processRequest(r,e,t))):r[e]=await this.processRequest(s,e,t);a.other_responses=r}return a}}module.exports=CurdTable;
@@ -1 +1 @@
1
- const HelperUtility=require("./HelperUtility"),logger=require("../../Logger");class QueryBuilder{constructor(e,t,r=null){this.db=e,this.utils=t,this.controllerWrapper=r,this.helperUtility=new HelperUtility}getHookService(){return this.controllerWrapper.hookService}getQueryBuilder(e,t=null){let r=this.db(e.table);return t&&(r=t),r._getMyModel=()=>e,r}parseValue(e){return this.helperUtility.parseValue(e)}parseWhereValue(e){return this.helperUtility.parseWhereValue(e)}parseWhereColumn(e){return this.helperUtility.parseWhereColumn(e)}_applyOrWhereCondition(e,t,r,o){if(null!==o)if(Array.isArray(o))e.orWhereIn(t,o);else switch(r){case"between":e.orWhereBetween(t,o);break;case"notBetween":e.orWhereNotBetween(t,o);break;case"in":e.orWhereIn(t,o);break;case"notIn":e.orWhereNotIn(t,o);break;case"like":e.orWhere(t,"like",o);break;default:e.orWhere(t,r,o)}else e.orWhereNull(t)}_applyAndWhereCondition(e,t,r,o){if(null!==o)if(Array.isArray(o))e.whereIn(t,o);else switch(r){case"between":e.whereBetween(t,o);break;case"notBetween":e.whereNotBetween(t,o);break;case"in":e.whereIn(t,o);break;case"notIn":e.whereNotIn(t,o);break;case"like":e.where(t,"like",o);break;default:e.where(t,r,o)}else e.whereNull(t)}buildWithTree(e,t=null){return this.helperUtility.dotWalkTree(e,{resolver:({current:e,part:r,source:o})=>t?t({current:e,part:r,source:o}):{}})}_getWithWhereForRelation(e,t){if(!e||"object"!=typeof e)return{direct:{},nested:{}};const r=`${t}.`,o={},i={};for(const[t,s]of Object.entries(e))if(t.startsWith(r)){const e=t.slice(r.length);e.includes(".")?i[e]=s:o[e]=s}return{direct:o,nested:i}}_applyWithWhereConditions(e,t){for(const[r,o]of Object.entries(t)){const{joinType:t="AND",column:i}=this.parseWhereColumn(r),{operator:s,value:l}=this.parseWhereValue(o);"AND"===t?this._applyAndWhereCondition(e,i,s,l):this._applyOrWhereCondition(e,i,s,l)}}async fetchRelatedRows(e,t,r={}){if(e.through){let o=await this.db(e.through).whereIn(e.throughLocalKey,t),i=this.db(e.table).whereIn(e.foreignKey,o.map(t=>t[e.throughForeignKey]));return Object.keys(r).length>0&&this._applyWithWhereConditions(i,r),i}{let o=this.db(e.table).whereIn(e.foreignKey,t);return Object.keys(r).length>0&&this._applyWithWhereConditions(o,r),o}}async fetchAndAttachRelated(e){const{parentRows:t,relName:r,model:o,withTree:i,relation:s,withWhere:l={}}=e;if(!s){const e=this.getHookService().getModelInstance(o),s=`get${r.charAt(0).toUpperCase()+r.slice(1)}Relation`;if("function"==typeof e[s]){const{direct:n,nested:a}=this._getWithWhereForRelation(l,r),h={rows:t,relName:r,model:o,withTree:i,controller:this.controllerWrapper,relation:o.hasRelations[r],qb:this,db:this.db,withWhere:n,nestedWithWhere:a};await e[s](h)}else logger.warn(`Method ${s} not found in model ${o.name}`);return t}let n=t.map(e=>e[s.localKey]);const a="one"===s?.type;let h=[];const{direct:c,nested:p}=this._getWithWhereForRelation(l,r);h=await this.fetchRelatedRows(s,n,c);let y=new Map;for(const e of h){let t=e[s.foreignKey];y.has(t)||y.set(t,[]),y.get(t).push(e)}for(const e of t){let t=e[s.localKey];a&&1==y.get(t)?.length?e[r]=y.get(t)[0]:e[r]=y.get(t)||[]}let u=Object.keys(i);for(const e of u){let o=i[e],s=t.filter(e=>a?e[r]:e[r].length>0).map(e=>e[r]).reduce((e,t)=>e.concat(t),[]),l=this.utils.getModel(this.controllerWrapper,r);await this.fetchAndAttachRelated({parentRows:s,relName:e,model:l,withTree:o,relation:l.hasRelations[e],withWhere:p})}return t}filterWhere(e,t=""){return t?Object.keys(e).filter(e=>e.includes(t)).reduce((r,o)=>(r[o.replace(t,"")]=e[o],r),{}):Object.keys(e).filter(e=>!e.includes(".")).reduce((t,r)=>(t[r]=e[r],t),{})}async getQuery(e,t){try{const{where:r={},with:o,withWhere:i,select:s,orderBy:l={column:"id",direction:"asc"},limit:n=10,offset:a=0,page:h,groupBy:c,having:p,distinct:y,join:u,leftJoin:f,rightJoin:d,innerJoin:g,count:W=!1}=t;let b=this.getQueryBuilder(e);s&&(Array.isArray(s)||"string"==typeof s)?b.select(s):b.select("*"),y&&(Array.isArray(y)||"string"==typeof y?b.distinct(y):b.distinct()),u&&this._applyJoins(b,u,"join"),f&&this._applyJoins(b,f,"leftJoin"),d&&this._applyJoins(b,d,"rightJoin"),g&&this._applyJoins(b,g,"innerJoin"),this._applyWhereClause(b,r,o),c&&(Array.isArray(c),b.groupBy(c)),p&&this._applyHavingClause(b,p),l&&this._applyOrderBy(b,l);let w=!1,_=n,m=a,A=1,j=0;h&&n>0&&(A=Math.max(1,parseInt(h)),m=(A-1)*n),n>0&&(b.limit(_),m>0&&b.offset(m));const k=[],C=b.toSQL();k.push(C.sql);const O=await b;if(o&&o.length>0){const t=this.buildWithTree(o);for(const r of Object.keys(t))await this.fetchAndAttachRelated({parentRows:O,relName:r,model:e,withTree:t[r],relation:e.hasRelations[r],withWhere:i||{}})}let R=null;if(n>0)try{let t=this.getQueryBuilder(e);r&&Object.keys(r).length>0&&this._applyWhereClause(t,r),u&&this._applyJoins(t,u,"join"),f&&this._applyJoins(t,f,"leftJoin"),d&&this._applyJoins(t,d,"rightJoin"),g&&this._applyJoins(t,g,"innerJoin");const o=t.count("* as cnt");k.push(o.toSQL().sql);R=(await o.first()).cnt}catch(e){logger.warn("Failed to get total count:",e.message),R=O.length}n>0&&null!==R&&(j=Math.ceil(R/n),w=A<j);const B=w?A+1:null,N=A>1?A-1:null;return{data:O,totalCount:R,...this.controllerWrapper.debug?{sqlDebug:k}:{},...n>0?{pagination:{page:A,limit:_,offset:m,totalPages:j,hasNext:w,hasPrev:A>1,nextPage:B,prevPage:N}}:{}}}catch(t){throw logger.error("QueryService.getQuery error:",t),new Error(`Failed to execute query: ${t.message} on model ${e.name}`)}}_applyWhereWithArray(e,t,r){for(const o of t)this._applyWhereClause(e,o,r)}_applyWhereClause(e,t,r=[]){if(Array.isArray(t))this._applyWhereWithArray(e,t,r);else{if(t&&Object.keys(t).length>0){let r=this.helperUtility.getDotWalkQuery(t);r=this.helperUtility.objectFilter(r,(e,t)=>!e.startsWith("!")&&("__exists__"!==e&&("object"!=typeof t||null===t)));for(const[t,o]of Object.entries(r)){const{joinType:r="AND",column:i}=this.parseWhereColumn(t),{operator:s,value:l}=this.parseWhereValue(o);"AND"===r?this._applyAndWhereCondition(e,i,s,l):this._applyOrWhereCondition(e,i,s,l)}}this._applyNestedWhere(e,t,r)}}_getNestedWhereConditions(e,t){if(!e||"object"!=typeof e)return{};const r=`${t}.`,o={};for(const[i,s]of Object.entries(e))if(i.startsWith(r)){o[i.slice(r.length)]=s}else i===t&&!0===s&&(o.__exists__=!0);return o}_getTopLevelRelationsFromWhere(e){if(!e||"object"!=typeof e)return[];const t=new Set;for(const r of Object.keys(e))if(r.includes(".")){const e=r.split(".")[0];t.add(e)}else r.startsWith("!")&&t.add(r);return[...t]}_applyNestedWhere(e,t,r){let o=this,i=e._getMyModel();const s=this._getTopLevelRelationsFromWhere(t);if(0!==s.length)for(let r of s){let s=r.startsWith("!"),l=s?r.slice(1):r,n=this._getNestedWhereConditions(t,r);if(0===Object.keys(n).length)continue;let a,h=i.hasRelations?.[l];if(!h){logger.warn(`Relation ${l} not found in model ${i.modelName}`);continue}try{a=this.utils.getModel(this.controllerWrapper,h.table||l)}catch(e){try{a=this.utils.getModel(this.controllerWrapper,l)}catch(e){logger.warn(`Model for relation ${l} (table: ${h.table}) not found`);continue}}e[s?"whereNotExists":"whereExists"](function(){let e=o.getQueryBuilder(a,this.select("*").from(h.table));e.whereRaw(`"${h.table}"."${h.foreignKey}" = "${i.table}"."${h.localKey}"`);if(!(!0===n.__exists__&&1===Object.keys(n).length)){const t={...n};delete t.__exists__,o._applyWhereClause(e,t,[])}})}}_applyJoins(e,t,r){const o=Array.isArray(t)?t:[t];for(const t of o)"string"==typeof t?e[r](t):"object"==typeof t&&(t.table&&t.on?e[r](t.table,t.on):t.table&&t.first&&t.operator&&t.second&&e[r](t.table,t.first,t.operator,t.second))}_applyWithWhere(e,t){try{if(Array.isArray(t))for(const r of t)"string"==typeof r?e.withWhere(r):"object"==typeof r&&e.withWhere(r.column,r.operator,r.value);else if("object"==typeof t)for(const[r,o]of Object.entries(t))e.withWhere(r,o)}catch(e){logger.warn("Failed to apply withWhere:",e.message)}}_applyHavingClause(e,t){for(const[r,o]of Object.entries(t))"object"==typeof o&&o.operator?e.having(r,o.operator,o.value):e.having(r,o)}_applyOrderBy(e,t){if(Array.isArray(t))for(const r of t)"string"==typeof r?e.orderBy(r):"object"==typeof r&&e.orderBy(r.column,r.direction||"asc");else"string"==typeof t?e.orderBy(t):"object"==typeof t&&e.orderBy(t.column,t.direction||"asc")}}module.exports=QueryBuilder;
1
+ const HelperUtility=require("./HelperUtility"),logger=require("../../Logger");class QueryBuilder{constructor(e,t,r=null){this.db=e,this.utils=t,this.controllerWrapper=r,this.helperUtility=new HelperUtility}getHookService(){return this.controllerWrapper.hookService}getQueryBuilder(e,t=null){let r=this.db(e.table);return t&&(r=t),r._getMyModel=()=>e,r}parseValue(e){return this.helperUtility.parseValue(e)}parseWhereValue(e){return this.helperUtility.parseWhereValue(e)}parseWhereColumn(e){return this.helperUtility.parseWhereColumn(e)}_applyOrWhereCondition(e,t,r,o){if(null!==o)if(Array.isArray(o))e.orWhereIn(t,o);else switch(r){case"between":e.orWhereBetween(t,o);break;case"notBetween":e.orWhereNotBetween(t,o);break;case"in":e.orWhereIn(t,o);break;case"notIn":e.orWhereNotIn(t,o);break;case"like":e.orWhere(t,"like",o);break;default:e.orWhere(t,r,o)}else e.orWhereNull(t)}_applyAndWhereCondition(e,t,r,o){if(null!==o)if(Array.isArray(o))e.whereIn(t,o);else switch(r){case"between":e.whereBetween(t,o);break;case"notBetween":e.whereNotBetween(t,o);break;case"in":e.whereIn(t,o);break;case"notIn":e.whereNotIn(t,o);break;case"like":e.where(t,"like",o);break;default:e.where(t,r,o)}else e.whereNull(t)}buildWithTree(e,t=null){return this.helperUtility.dotWalkTree(e,{resolver:({current:e,part:r,source:o})=>t?t({current:e,part:r,source:o}):{}})}_getWithWhereForRelation(e,t){if(!e||"object"!=typeof e)return{direct:{},nested:{}};const r=`${t}.`,o={},i={};for(const[t,s]of Object.entries(e))if(t.startsWith(r)){const e=t.slice(r.length);e.includes(".")?i[e]=s:o[e]=s}return{direct:o,nested:i}}_applyWithWhereConditions(e,t){for(const[r,o]of Object.entries(t)){const{joinType:t="AND",column:i}=this.parseWhereColumn(r),{operator:s,value:n}=this.parseWhereValue(o);"AND"===t?this._applyAndWhereCondition(e,i,s,n):this._applyOrWhereCondition(e,i,s,n)}}async fetchRelatedRows(e,t,r={}){if(e.through){let o=await this.db(e.through).whereIn(e.throughLocalKey,t),i=this.db(e.table).whereIn(e.foreignKey,o.map(t=>t[e.throughForeignKey]));return Object.keys(r).length>0&&this._applyWithWhereConditions(i,r),i}{let o=this.db(e.table).whereIn(e.foreignKey,t);return Object.keys(r).length>0&&this._applyWithWhereConditions(o,r),o}}async fetchAndAttachRelated(e){const{parentRows:t,relName:r,model:o,withTree:i,relation:s,withWhere:n={}}=e;if(!s){const e=this.getHookService().getModelInstance(o),s=`get${r.charAt(0).toUpperCase()+r.slice(1)}Relation`;if("function"==typeof e[s]){const{direct:l,nested:a}=this._getWithWhereForRelation(n,r),h={rows:t,relName:r,model:o,withTree:i,controller:this.controllerWrapper,relation:o.hasRelations[r],qb:this,db:this.db,withWhere:l,nestedWithWhere:a};await e[s](h)}else logger.warn(`Method ${s} not found in model ${o.name}`);return t}let l=t.map(e=>e[s.localKey]);const a="one"===s?.type;let h=[];const{direct:c,nested:p}=this._getWithWhereForRelation(n,r);let u={...c};try{const e=this.utils.getModel(this.controllerWrapper,r);if(this.controllerWrapper?.hookService){await this.controllerWrapper.hookService.executeHasSoftDeleteHook(e)&&(u={...u,deleted_at:null})}}catch(e){}h=await this.fetchRelatedRows(s,l,u);let y=new Map;for(const e of h){let t=e[s.foreignKey];y.has(t)||y.set(t,[]),y.get(t).push(e)}for(const e of t){let t=e[s.localKey];a&&1==y.get(t)?.length?e[r]=y.get(t)[0]:e[r]=y.get(t)||[]}let d=Object.keys(i);for(const e of d){let o=i[e],s=t.filter(e=>a?e[r]:e[r].length>0).map(e=>e[r]).reduce((e,t)=>e.concat(t),[]),n=this.utils.getModel(this.controllerWrapper,r);await this.fetchAndAttachRelated({parentRows:s,relName:e,model:n,withTree:o,relation:n.hasRelations[e],withWhere:p})}return t}filterWhere(e,t=""){return t?Object.keys(e).filter(e=>e.includes(t)).reduce((r,o)=>(r[o.replace(t,"")]=e[o],r),{}):Object.keys(e).filter(e=>!e.includes(".")).reduce((t,r)=>(t[r]=e[r],t),{})}async getQuery(e,t){try{const{where:r={},with:o,withWhere:i,select:s,orderBy:n={column:"id",direction:"asc"},limit:l=10,offset:a=0,page:h,groupBy:c,having:p,distinct:u,join:y,leftJoin:d,rightJoin:f,innerJoin:g,count:W=!1}=t;let w=this.getQueryBuilder(e);s&&(Array.isArray(s)||"string"==typeof s)?w.select(s):w.select("*"),u&&(Array.isArray(u)||"string"==typeof u?w.distinct(u):w.distinct()),y&&this._applyJoins(w,y,"join"),d&&this._applyJoins(w,d,"leftJoin"),f&&this._applyJoins(w,f,"rightJoin"),g&&this._applyJoins(w,g,"innerJoin"),this._applyWhereClause(w,r,o),c&&(Array.isArray(c),w.groupBy(c)),p&&this._applyHavingClause(w,p),n&&this._applyOrderBy(w,n);let m=!1,b=l,_=a,A=1,j=0;h&&l>0&&(A=Math.max(1,parseInt(h)),_=(A-1)*l),l>0&&(w.limit(b),_>0&&w.offset(_));const k=[],C=w.toSQL();k.push(C.sql);const J=await w;if(o&&o.length>0){const t=this.buildWithTree(o);for(const r of Object.keys(t))await this.fetchAndAttachRelated({parentRows:J,relName:r,model:e,withTree:t[r],relation:e.hasRelations[r],withWhere:i||{}})}let B=null;if(l>0)try{let t=this.getQueryBuilder(e);r&&Object.keys(r).length>0&&this._applyWhereClause(t,r),y&&this._applyJoins(t,y,"join"),d&&this._applyJoins(t,d,"leftJoin"),f&&this._applyJoins(t,f,"rightJoin"),g&&this._applyJoins(t,g,"innerJoin");const o=t.count("* as cnt");k.push(o.toSQL().sql);B=(await o.first()).cnt}catch(e){logger.warn("Failed to get total count:",e.message),B=J.length}l>0&&null!==B&&(j=Math.ceil(B/l),m=A<j);const N=m?A+1:null,O=A>1?A-1:null;return{data:J,totalCount:B,...this.controllerWrapper.debug?{sqlDebug:k}:{},...l>0?{pagination:{page:A,limit:b,offset:_,totalPages:j,hasNext:m,hasPrev:A>1,nextPage:N,prevPage:O}}:{}}}catch(t){throw logger.error("QueryService.getQuery error:",t),new Error(`Failed to execute query: ${t.message} on model ${e.name}`)}}getSumQuery(e,t){const{where:r={},join:o,leftJoin:i,rightJoin:s,innerJoin:n}=t,l=t.data||t,a=l.sumColumn,h=l.sumFormula;if(!a&&!h)throw new Error("Sum action requires either data.sumColumn or data.sumFormula");const c=e=>'"'+String(e).replace(/["\\]/g,"")+'"';let p;if(a){const e=String(a).trim().replace(/[^a-zA-Z0-9_]/g,"");if(!e)throw new Error("data.sumColumn must be a valid column name");p="SUM("+c(e)+")"}else{const e=String(h).trim();if(!/\{[a-zA-Z0-9_]+\}/.test(e))throw new Error("data.sumFormula must contain at least one {columnName}");let t=0;for(const r of e)if("("===r)t++;else if(")"===r&&(t--,t<0))break;if(0!==t)throw new Error("data.sumFormula has unbalanced or misordered parentheses ( and )");const r=e.replace(/\{[a-zA-Z0-9_]+\}/g,"@");if(!/^[\s0-9.+*\/\-()@]+$/.test(r))throw new Error("data.sumFormula may only use BODMAS: numbers, + - * /, parentheses, and {columnName}");p="SUM("+e.replace(/\{([a-zA-Z0-9_]+)\}/g,(e,t)=>c(t))+")"}let u=this.getQueryBuilder(e);return o&&this._applyJoins(u,o,"join"),i&&this._applyJoins(u,i,"leftJoin"),s&&this._applyJoins(u,s,"rightJoin"),n&&this._applyJoins(u,n,"innerJoin"),this._applyWhereClause(u,r,[]),u.select(this.db.raw(p+" as sum")),u}_applyWhereWithArray(e,t,r){for(const o of t)this._applyWhereClause(e,o,r)}_applyWhereClause(e,t,r=[]){if(Array.isArray(t))this._applyWhereWithArray(e,t,r);else{if(t&&Object.keys(t).length>0){let r=this.helperUtility.getDotWalkQuery(t);r=this.helperUtility.objectFilter(r,(e,t)=>!e.startsWith("!")&&("__exists__"!==e&&("object"!=typeof t||null===t)));for(const[t,o]of Object.entries(r)){const{joinType:r="AND",column:i}=this.parseWhereColumn(t),{operator:s,value:n}=this.parseWhereValue(o);"AND"===r?this._applyAndWhereCondition(e,i,s,n):this._applyOrWhereCondition(e,i,s,n)}}this._applyNestedWhere(e,t,r)}}_getNestedWhereConditions(e,t){if(!e||"object"!=typeof e)return{};const r=`${t}.`,o={};for(const[i,s]of Object.entries(e))if(i.startsWith(r)){o[i.slice(r.length)]=s}else i===t&&!0===s&&(o.__exists__=!0);return o}_getTopLevelRelationsFromWhere(e){if(!e||"object"!=typeof e)return[];const t=new Set;for(const r of Object.keys(e))if(r.includes(".")){const e=r.split(".")[0];t.add(e)}else r.startsWith("!")&&t.add(r);return[...t]}_applyNestedWhere(e,t,r){let o=this,i=e._getMyModel();const s=this._getTopLevelRelationsFromWhere(t);if(0!==s.length)for(let r of s){let s=r.startsWith("!"),n=s?r.slice(1):r,l=this._getNestedWhereConditions(t,r);if(0===Object.keys(l).length)continue;let a,h=i.hasRelations?.[n];if(!h){logger.warn(`Relation ${n} not found in model ${i.modelName}`);continue}try{a=this.utils.getModel(this.controllerWrapper,h.table||n)}catch(e){try{a=this.utils.getModel(this.controllerWrapper,n)}catch(e){logger.warn(`Model for relation ${n} (table: ${h.table}) not found`);continue}}e[s?"whereNotExists":"whereExists"](function(){let e=o.getQueryBuilder(a,this.select("*").from(h.table));e.whereRaw(`"${h.table}"."${h.foreignKey}" = "${i.table}"."${h.localKey}"`);if(!(!0===l.__exists__&&1===Object.keys(l).length)){const t={...l};delete t.__exists__,o._applyWhereClause(e,t,[])}})}}_applyJoins(e,t,r){const o=Array.isArray(t)?t:[t];for(const t of o)"string"==typeof t?e[r](t):"object"==typeof t&&(t.table&&t.on?e[r](t.table,t.on):t.table&&t.first&&t.operator&&t.second&&e[r](t.table,t.first,t.operator,t.second))}_applyWithWhere(e,t){try{if(Array.isArray(t))for(const r of t)"string"==typeof r?e.withWhere(r):"object"==typeof r&&e.withWhere(r.column,r.operator,r.value);else if("object"==typeof t)for(const[r,o]of Object.entries(t))e.withWhere(r,o)}catch(e){logger.warn("Failed to apply withWhere:",e.message)}}_applyHavingClause(e,t){for(const[r,o]of Object.entries(t))"object"==typeof o&&o.operator?e.having(r,o.operator,o.value):e.having(r,o)}_applyOrderBy(e,t){if(Array.isArray(t))for(const r of t)"string"==typeof r?e.orderBy(r):"object"==typeof r&&e.orderBy(r.column,r.direction||"asc");else"string"==typeof t?e.orderBy(t):"object"==typeof t&&e.orderBy(t.column,t.direction||"asc")}}module.exports=QueryBuilder;
@@ -1 +1 @@
1
- const QueryBuilder=require("./QueryBuilder");class QueryService{constructor(e,t,r=null){this.db=e,this.utils=t,this.controllerWrapper=r,this.queryBuilder=new QueryBuilder(e,t,r)}async getQuery(e,t){return await this.queryBuilder.getQuery(e,t)}async getSoftDeleteQuery(e,t){return await this.queryBuilder.getQuery(e,{...t,where:{...t.where||{},deleted_at:null}})}async executeShowQuery(e,t){let r=await this.getQuery(e,{...t,limit:1,offset:0});return r.data.length>0?r.data[0]:null}async executeCountQuery(e,t){let r=await this.db(e.table).count();return Object.values(r[0])[0]}async executeCreateQuery(e,t){return await this.db(e.table).insert(t.data).returning("*")}async executeUpdateQuery(e,t){return await this.db(e.table).where(t.where).update(t.data).returning("*")}async executeDeleteQuery(e,t){return await this.db(e.table).where(t.where).delete()}async executeSoftDeleteQuery(e,t){return await this.db(e.table).where(t.where).update({deleted_at:new Date}).returning("*")}async executeUpsertQuery(e,t){return await this.db(e.table).insert(t.data).onConflict(t.conflict).update(t.data)}async executeReplaceQuery(e,t){return await this.db(e.table).replace(t.data)}async executeSyncQuery(e,t){return{insertOrUpdateQuery:await this.db(e.table).insert(t.data).onConflict(t.conflict).update(t.data),deleteQuery:await this.db(e.table).where(t.where).delete()}}}module.exports=QueryService;
1
+ const QueryBuilder=require("./QueryBuilder");class QueryService{constructor(e,t,r=null){this.db=e,this.utils=t,this.controllerWrapper=r,this.queryBuilder=new QueryBuilder(e,t,r)}async getQuery(e,t){return await this.queryBuilder.getQuery(e,t)}async getSoftDeleteQuery(e,t){return await this.queryBuilder.getQuery(e,{...t,where:{...t.where||{},deleted_at:null}})}async executeShowQuery(e,t){let r=await this.getQuery(e,{...t,limit:1,offset:0});return r.data.length>0?r.data[0]:null}async executeCountQuery(e,t){let r=await this.db(e.table).count();return Object.values(r[0])[0]}async executeSumQuery(e,t){const r=await this.queryBuilder.getSumQuery(e,t).first(),a=r&&null!=r.sum?r.sum:0;return Number(a)}async executeCreateQuery(e,t){return await this.db(e.table).insert(t.data).returning("*")}async executeUpdateQuery(e,t){return await this.db(e.table).where(t.where).update(t.data).returning("*")}async executeDeleteQuery(e,t){return await this.db(e.table).where(t.where).delete()}async executeSoftDeleteQuery(e,t){return await this.db(e.table).where(t.where).update({deleted_at:new Date}).returning("*")}async executeUpsertQuery(e,t){return await this.db(e.table).insert(t.data).onConflict(t.conflict).update(t.data)}async executeReplaceQuery(e,t){return await this.db(e.table).replace(t.data)}async executeSyncQuery(e,t){return{insertOrUpdateQuery:await this.db(e.table).insert(t.data).onConflict(t.conflict).update(t.data),deleteQuery:await this.db(e.table).where(t.where).delete()}}}module.exports=QueryService;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dreamtree-org/korm-js",
3
- "version": "1.0.52",
3
+ "version": "1.0.53",
4
4
  "description": "Knowledge Object-Relational Mapping - A powerful, modular ORM system for Node.js with dynamic database operations, complex queries, relationships, and nested requests",
5
5
  "author": {
6
6
  "name": "Partha Preetham Krishna",
@@ -8,6 +8,7 @@
8
8
  "url": "https://www.linkedin.com/in/partha-preetham-krishna-68ba00197/"
9
9
  },
10
10
  "main": "index.js",
11
+ "types": "index.d.ts",
11
12
  "scripts": {
12
13
  "test": "jest",
13
14
  "test:watch": "jest --watch",