@dreamtree-org/korm-js 1.0.42 → 1.0.44

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.
@@ -1 +1 @@
1
- const Files=require("./helpers/files");class BaseHelperUtility{file=new Files;capitalize(e){return e.charAt(0).toUpperCase()+e.slice(1)}snakeCase(e){return e.replace(/([A-Z])/g,"_$1").toLowerCase()}camelCase(e){return e.replace(/([-_][a-z])/gi,e=>e.toUpperCase().replace("-","").replace("_",""))}kebabCase(e){return e.replace(/([A-Z])/g,"-$1").toLowerCase()}pascalCase(e){return e.replace(/([-_][a-z])/gi,e=>e.toUpperCase().replace("-","").replace("_",""))}pluralize(e){if(e.match(/[^aeiou]y$/i))return e.slice(0,-1)+"ies";if(e.match(/(s|x|z|ch|sh)$/i))return e+"es";if(e.match(/(f|fe)$/i)){if(e.endsWith("fe"))return e.slice(0,-2)+"ves";if(e.endsWith("f"))return e.slice(0,-1)+"ves"}return e.match(/(o)$/i)?e.length>1&&"aeiou".includes(e[e.length-2].toLowerCase())?e+"s":e+"es":e.match(/us$/i)?e.slice(0,-2)+"i":e.match(/is$/i)?e.slice(0,-2)+"es":e.match(/on$/i)||e.match(/um$/i)?e.slice(0,-2)+"a":e.match(/a$/i)?e+"s":e.match(/eau$/i)?e+"x":e.match(/ix$/i)||e.match(/ex$/i)?e.slice(0,-2)+"ices":e.match(/s$/i)?e:e+"s"}singularize(e){return String(e||"").trim().replace(/([b-df-hj-np-tv-z])ies$/i,"$1y").replace(/eaux$/i,"eau").replace(/oes$/i,"o").replace(/(xes|zes|ches|shes|sses)$/i,e=>e.slice(0,-2)).replace(/(us)es$/i,"$1").replace(/ves$/i,"f").replace(/([^s])s$/i,"$1")}modelName(e){let r=this.singularize(e);return r=this.camelCase(r),r=this.capitalize(r),r}dotParse(e,r,t=null){return this.dotWalk(e,{source:r,defaultValue:t})}dotWalk(e,r={}){const{source:t={},defaultValue:s=null,resolver:a=({current:e,part:r,source:t})=>e[r]}=r,i=e.split(".");let l=t;for(const e of i){const r=e.match(/^(\w+)\[(.*?)\]$/);if(r){const e=r[1],i=r[2]||0;if(l=l[e]||[],l=a({current:l,part:i,source:t}),void 0===l)return s}else{if(void 0===l[e])return s;if(l=a({current:l,part:e,source:t}),void 0===l)return s}}return l}dotWalkTree(e,r={}){const{resolver:t=({current:e,part:r,source:t})=>{}}=r,s={};for(const r of e){const e=r.split(".");let a=e.length,i=s,l=0;for(const u of e){let e=l===a-1;i[u]=i[u]||t({current:i,part:u,source:s,path:r,isLastPart:e}),i=i[u],l++}}return s}parseValue(e){return isNaN(e)||""===e?"true"===e.toLowerCase()||"false"!==e.toLowerCase()&&("null"===e.toLowerCase()?null:e):Number(e)}parseWhereValue(e){if("string"!=typeof e)return{operator:"=",value:e};if(e.startsWith("!")){const r=e.substring(1);return{operator:"!=",value:this.parseValue(r)}}if(e.includes("%"))return{operator:"like",value:e};const r=e.match(/^(><|<>)(.+)$/);if(r){const[,e,t]=r,s=t.split(",").map(e=>this.parseValue(e.trim()));if("><"===e){if(2===s.length)return{operator:"between",value:s}}else if("<>"===e&&2===s.length)return{operator:"notBetween",value:s}}const t=e.match(/^(>=|<=|<|>)(.+)$/);if(t){const[,e,r]=t;return{operator:e,value:this.parseValue(r)}}const s=e.match(/^(!?)\[\]?(.+)$/);if(s){const[,e,r]=s;return{operator:e?"notIn":"in",value:r.split(",").map(e=>this.parseValue(e.trim()))}}return{operator:"=",value:this.parseValue(e)}}objectFilter(e,r){return Object.keys(e).filter(t=>r(t,e[t])).reduce((r,t)=>(r[t]=e[t],r),{})}pluckDotWalkKey(e,r=1,t="."){let s=e.split(t);return[...s].splice(-1*s.length+r).join(t)}getDotWalkQuery(e,r=""){let t=this.dotWalkTree(Object.keys(e).filter(e=>e.includes(r)),{resolver:({current:r,part:t,source:s,path:a,isLastPart:i})=>i?e[a]:{}});return r&&(t=t[r]),t}setNested(e,r,t){let s={name:r,value:t},a=e,i=a,l="",u=s.name.split(".");return u.length>1?(u.forEach(e=>{a=i,i[e]=i[e]||{},i=i[e],l=e}),a[l]=s.value):(l=s.name,a=e[l],e[l]=s.value),e}}module.exports=BaseHelperUtility;
1
+ const Files=require("./helpers/files");class BaseHelperUtility{file=new Files;capitalize(e){return e.charAt(0).toUpperCase()+e.slice(1)}snakeCase(e){return e.replace(/([A-Z])/g,"_$1").toLowerCase()}camelCase(e){return e.replace(/([-_][a-z])/gi,e=>e.toUpperCase().replace("-","").replace("_",""))}kebabCase(e){return e.replace(/([A-Z])/g,"-$1").toLowerCase()}pascalCase(e){return e.replace(/([-_][a-z])/gi,e=>e.toUpperCase().replace("-","").replace("_",""))}pluralize(e){if(e.match(/[^aeiou]y$/i))return e.slice(0,-1)+"ies";if(e.match(/(s|x|z|ch|sh)$/i))return e+"es";if(e.match(/(f|fe)$/i)){if(e.endsWith("fe"))return e.slice(0,-2)+"ves";if(e.endsWith("f"))return e.slice(0,-1)+"ves"}return e.match(/(o)$/i)?e.length>1&&"aeiou".includes(e[e.length-2].toLowerCase())?e+"s":e+"es":e.match(/us$/i)?e.slice(0,-2)+"i":e.match(/is$/i)?e.slice(0,-2)+"es":e.match(/on$/i)||e.match(/um$/i)?e.slice(0,-2)+"a":e.match(/a$/i)?e+"s":e.match(/eau$/i)?e+"x":e.match(/ix$/i)||e.match(/ex$/i)?e.slice(0,-2)+"ices":e.match(/s$/i)?e:e+"s"}singularize(e){return String(e||"").trim().replace(/([b-df-hj-np-tv-z])ies$/i,"$1y").replace(/eaux$/i,"eau").replace(/oes$/i,"o").replace(/(xes|zes|ches|shes|sses)$/i,e=>e.slice(0,-2)).replace(/(us)es$/i,"$1").replace(/ves$/i,"f").replace(/([^s])s$/i,"$1")}modelName(e){let r=this.singularize(e);return r=this.camelCase(r),r=this.capitalize(r),r}dotParse(e,r,t=null){return this.dotWalk(e,{source:r,defaultValue:t})}dotWalk(e,r={}){const{source:t={},defaultValue:s=null,resolver:a=({current:e,part:r,source:t})=>e[r]}=r,i=e.split(".");let l=t;for(const e of i){const r=e.match(/^(\w+)\[(.*?)\]$/);if(r){const e=r[1],i=r[2]||0;if(l=l[e]||[],l=a({current:l,part:i,source:t}),void 0===l)return s}else{if(void 0===l[e])return s;if(l=a({current:l,part:e,source:t}),void 0===l)return s}}return l}dotWalkTree(e,r={}){const{resolver:t=({current:e,part:r,source:t})=>{}}=r,s={};for(const r of e){const e=r.split(".");let a=e.length,i=s,l=0;for(const u of e){let e=l===a-1;i[u]=i[u]||t({current:i,part:u,source:s,path:r,isLastPart:e}),i=i[u],l++}}return s}parseValue(e){return isNaN(e)||""===e?"true"===e.toLowerCase()||"false"!==e.toLowerCase()&&("null"===e.toLowerCase()?null:e):Number(e)}parseWhereValue(e){if("string"!=typeof e)return{operator:"=",value:e};if(e.startsWith("!")){const r=e.substring(1);return{operator:"!=",value:this.parseValue(r)}}if(e.includes("%"))return{operator:"like",value:e};const r=e.match(/^(><|<>)(.+)$/);if(r){const[,e,t]=r,s=t.split(",").map(e=>this.parseValue(e.trim()));if("><"===e){if(2===s.length)return{operator:"between",value:s}}else if("<>"===e&&2===s.length)return{operator:"notBetween",value:s}}const t=e.match(/^(>=|<=|<|>)(.+)$/);if(t){const[,e,r]=t;return{operator:e,value:this.parseValue(r)}}const s=e.match(/^(!?)\[\]?(.+)$/);if(s){const[,e,r]=s;return{operator:e?"notIn":"in",value:r.split(",").map(e=>this.parseValue(e.trim()))}}return{operator:"=",value:this.parseValue(e)}}parseWhereColumn(e){let r={joinType:"AND",column:e};return e.startsWith("Or:")&&(r.joinType="OR",r.column=e.substring(3)),r}objectFilter(e,r){return Object.keys(e).filter(t=>r(t,e[t])).reduce((r,t)=>(r[t]=e[t],r),{})}pluckDotWalkKey(e,r=1,t="."){let s=e.split(t);return[...s].splice(-1*s.length+r).join(t)}getDotWalkQuery(e,r=""){let t=this.dotWalkTree(Object.keys(e).filter(e=>e.includes(r)),{resolver:({current:r,part:t,source:s,path:a,isLastPart:i})=>i?e[a]:{}});return r&&(t=t[r]),t}setNested(e,r,t){let s={name:r,value:t},a=e,i=a,l="",u=s.name.split(".");return u.length>1?(u.forEach(e=>{a=i,i[e]=i[e]||{},i=i[e],l=e}),a[l]=s.value):(l=s.name,a=e[l],e[l]=s.value),e}}module.exports=BaseHelperUtility;
@@ -1 +1 @@
1
- const mysqlWrapper=require("./clients/mysql"),sqliteWrapper=require("./clients/sqlite"),pgWrapper=require("./clients/pg"),BaseHelperUtility=require("./BaseHelperUtility"),InstanceMapper={mysql2:mysqlWrapper,sqlite:sqliteWrapper,pg:pgWrapper},dbClientMapper={mysql2:"mysql2",mysql:"mysql2",pg:"pg",postgresql:"pg",sqlite:"sqlite",sqlite3:"sqlite"};class ControllerWrapper{static db=null;static dbClient=null;static dbClientClass=null;static schema=null;static resolverPath=null;static dbInstance=null;requestInstance=null;constructor(){this.requestInstance={}}static initializeKORM({db:t,dbClient:e,schema:s,resolverPath:a=null}){this.db=t,this.dbClient=e,this.schema=s,this.resolverPath=a;const n=dbClientMapper[e];if(!n)throw new Error(`Database client ${e} not found`);const r=InstanceMapper[n];if(!r)throw new Error(`Database client ${e} not found`);return this.dbClientClass=r,this.dbInstance=new r(this),this}static setSchema(t){this.schema=t;const e=this.dbClientClass;if(!e)throw new Error(`Database client ${dbClient} not found`);return this.dbInstance=new e(this),this}static async processRequest(t,e=null,s=null){return await this.dbInstance.processRequest(t,e,s)}static async syncDatabase(){return await this.dbInstance.syncDatabase()}static async generateSchema(){return await this.dbInstance.generateSchema()}static loadModelClass(t){return this.dbInstance.hookService.loadModelClass(t)}static getModelInstance(t){return this.dbInstance.hookService.getModelInstance(t)}}module.exports=ControllerWrapper;
1
+ const mysqlWrapper=require("./clients/mysql"),sqliteWrapper=require("./clients/sqlite"),pgWrapper=require("./clients/pg"),BaseHelperUtility=require("./BaseHelperUtility"),InstanceMapper={mysql2:mysqlWrapper,sqlite:sqliteWrapper,pg:pgWrapper},dbClientMapper={mysql2:"mysql2",mysql:"mysql2",pg:"pg",postgresql:"pg",sqlite:"sqlite",sqlite3:"sqlite"};class ControllerWrapper{static db=null;static dbClient=null;static dbClientClass=null;static schema=null;static resolverPath=null;static dbInstance=null;requestInstance=null;constructor(){this.requestInstance={}}static initializeKORM({db:t,dbClient:e,schema:s,resolverPath:n=null}){this.db=t,this.dbClient=e,this.schema=s,this.resolverPath=n;const a=dbClientMapper[e];if(!a)throw new Error(`Database client ${e} not found`);const r=InstanceMapper[a];if(!r)throw new Error(`Database client ${e} not found`);return this.dbClientClass=r,this.dbInstance=new r(this),this}static setSchema(t){this.schema=t;const e=this.dbClientClass;if(!e)throw new Error(`Database client ${dbClient} not found`);return this.dbInstance=new e(this),this}static async processRequest(t,e=null,s=null){return await this.dbInstance.processRequest(t,e,s)}static async processRequestWithOthers(t,e=null,s=null){return await this.dbInstance.processRequest(t,e,s)}static async syncDatabase(){return await this.dbInstance.syncDatabase()}static async generateSchema(){return await this.dbInstance.generateSchema()}static loadModelClass(t){return this.dbInstance.hookService.loadModelClass(t)}static getModelInstance(t){return this.dbInstance.hookService.getModelInstance(t)}}module.exports=ControllerWrapper;
package/README.md CHANGED
@@ -246,8 +246,7 @@ POST /api/Users/crud
246
246
  "where": { "id": 1 },
247
247
  "data": {
248
248
  "first_name": "Jane",
249
- "last_name": "Smith",
250
- "updated_at": new Date().toISOString()
249
+ "last_name": "Smith"
251
250
  }
252
251
  }
253
252
 
@@ -257,8 +256,7 @@ POST /api/Users/crud
257
256
  "action": "update",
258
257
  "where": { "is_active": false },
259
258
  "data": {
260
- "is_active": true,
261
- "updated_at": new Date().toISOString()
259
+ "is_active": true
262
260
  }
263
261
  }
264
262
 
@@ -421,28 +419,152 @@ POST /api/Users/crud
421
419
  }
422
420
  }
423
421
 
424
- // OR conditions (using arrays)
422
+ // NULL checks
425
423
  POST /api/Users/crud
426
424
  {
427
425
  "action": "list",
428
426
  "where": {
429
- "or": [
430
- { "first_name": "John" },
431
- { "last_name": "Doe" }
432
- ]
427
+ "deleted_at": null
433
428
  }
434
429
  }
430
+ ```
435
431
 
436
- // NULL checks
432
+ ### OR Conditions with `Or:` Prefix
433
+
434
+ Use the `Or:` prefix on column names to create OR conditions in your queries:
435
+
436
+ ```javascript
437
+ // OR condition on a single column
437
438
  POST /api/Users/crud
438
439
  {
439
440
  "action": "list",
440
441
  "where": {
441
- "deleted_at": null
442
+ "status": "active",
443
+ "Or:name": "like:%john%"
444
+ }
445
+ }
446
+ // Generated SQL: WHERE status = 'active' OR name LIKE '%john%'
447
+
448
+ // Multiple OR conditions
449
+ POST /api/Users/crud
450
+ {
451
+ "action": "list",
452
+ "where": {
453
+ "is_active": true,
454
+ "Or:first_name": "John",
455
+ "Or:last_name": "Doe"
456
+ }
457
+ }
458
+ // Generated SQL: WHERE is_active = 1 OR first_name = 'John' OR last_name = 'Doe'
459
+
460
+ // Combining AND and OR conditions
461
+ POST /api/Users/crud
462
+ {
463
+ "action": "list",
464
+ "where": {
465
+ "status": "active",
466
+ "age": { ">=": 18 },
467
+ "Or:role": "admin",
468
+ "Or:role": "super_admin"
469
+ }
470
+ }
471
+ // Generated SQL: WHERE status = 'active' AND age >= 18 OR role = 'admin' OR role = 'super_admin'
472
+
473
+ // OR with operators
474
+ POST /api/Users/crud
475
+ {
476
+ "action": "list",
477
+ "where": {
478
+ "department": "sales",
479
+ "Or:email": { "like": "%@company.com" },
480
+ "Or:created_at": { ">=": "2024-01-01" }
442
481
  }
443
482
  }
483
+ // Generated SQL: WHERE department = 'sales' OR email LIKE '%@company.com' OR created_at >= '2024-01-01'
484
+
485
+ // OR with NULL checks
486
+ POST /api/Users/crud
487
+ {
488
+ "action": "list",
489
+ "where": {
490
+ "is_verified": true,
491
+ "Or:verified_at": null
492
+ }
493
+ }
494
+ // Generated SQL: WHERE is_verified = 1 OR verified_at IS NULL
495
+
496
+ // OR with IN operator
497
+ POST /api/Users/crud
498
+ {
499
+ "action": "list",
500
+ "where": {
501
+ "status": "pending",
502
+ "Or:role": ["admin", "moderator", "editor"]
503
+ }
504
+ }
505
+ // Generated SQL: WHERE status = 'pending' OR role IN ('admin', 'moderator', 'editor')
444
506
  ```
445
507
 
508
+ ### Array-Based Where Conditions
509
+
510
+ You can also pass `where` as an array of condition objects for more complex queries:
511
+
512
+ ```javascript
513
+ // Array of conditions - each object is applied sequentially
514
+ POST /api/Users/crud
515
+ {
516
+ "action": "list",
517
+ "where": [
518
+ { "status": "active" },
519
+ { "Or:role": "admin" },
520
+ { "Or:role": "moderator" }
521
+ ]
522
+ }
523
+ // Generated SQL: WHERE status = 'active' OR role = 'admin' OR role = 'moderator'
524
+
525
+ // Combining multiple AND and OR groups
526
+ POST /api/Users/crud
527
+ {
528
+ "action": "list",
529
+ "where": [
530
+ { "is_active": true, "age": { ">=": 18 } },
531
+ { "Or:status": "verified" },
532
+ { "Or:role": "admin" }
533
+ ]
534
+ }
535
+ // Generated SQL: WHERE is_active = 1 AND age >= 18 OR status = 'verified' OR role = 'admin'
536
+
537
+ // Complex filtering with array conditions
538
+ POST /api/Users/crud
539
+ {
540
+ "action": "list",
541
+ "where": [
542
+ { "department": "engineering" },
543
+ { "Or:department": "product" },
544
+ { "created_at": { ">=": "2024-01-01" } }
545
+ ]
546
+ }
547
+ // Generated SQL: WHERE department = 'engineering' OR department = 'product' AND created_at >= '2024-01-01'
548
+ ```
549
+
550
+ ### Where Operators Reference
551
+
552
+ | Operator | Description | Example |
553
+ |----------|-------------|---------|
554
+ | `=` (default) | Equals | `"status": "active"` |
555
+ | `>=` | Greater than or equal | `"age": { ">=": 18 }` |
556
+ | `<=` | Less than or equal | `"age": { "<=": 65 }` |
557
+ | `>` | Greater than | `"price": { ">": 100 }` |
558
+ | `<` | Less than | `"price": { "<": 500 }` |
559
+ | `!=` | Not equal | `"status": { "!=": "deleted" }` |
560
+ | `like` | Pattern matching | `"name": { "like": "%john%" }` |
561
+ | `in` | Value in list | `"status": ["active", "pending"]` |
562
+ | `notIn` | Value not in list | `"role": { "notIn": ["banned", "suspended"] }` |
563
+ | `between` | Range (inclusive) | `"age": { "between": [18, 65] }` |
564
+ | `notBetween` | Outside range | `"score": { "notBetween": [0, 50] }` |
565
+ | `null` | IS NULL check | `"deleted_at": null` |
566
+ | `Or:column` | OR condition prefix | `"Or:name": "John"` |
567
+
446
568
  ### Sorting
447
569
 
448
570
  ```javascript
@@ -504,7 +626,7 @@ POST /api/Users/crud
504
626
 
505
627
  ### Understanding hasRelations Structure
506
628
 
507
- The `hasRelations` object in your schema defines relationships between models. Based on your actual schema file (`../borebell.in/schema/sync.json`), relationships use the following structure:
629
+ The `hasRelations` object in your schema defines relationships between models. Relationships in your schema use the following structure:
508
630
 
509
631
  #### One-to-One or Belongs-To Relationship (`type: "one"`)
510
632
 
@@ -633,7 +755,7 @@ POST /api/Post/crud
633
755
 
634
756
  ### Example: Complete Relationship Structure
635
757
 
636
- Based on your actual schema (`../borebell.in/schema/sync.json`):
758
+ Example complete relationship structure:
637
759
 
638
760
  ```json
639
761
  {
@@ -694,8 +816,6 @@ Based on your actual schema (`../borebell.in/schema/sync.json`):
694
816
 
695
817
  ### Important Notes
696
818
 
697
- > **Always refer to your actual schema file** (`../borebell.in/schema/sync.json`) for the exact relationship definitions in your project. The structure shown above matches the format used in your schema.
698
-
699
819
  - `type: "one"` = One-to-One or Belongs-To relationship
700
820
  - `type: "many"` = One-to-Many relationship
701
821
  - `through` = Required for Many-to-Many relationships (specifies the join table)
@@ -896,7 +1016,6 @@ const advancedRules = {
896
1016
  email: 'required|type:string|regex:email',
897
1017
  phone: 'regex:phone',
898
1018
  password: 'required|type:string|minLen:8',
899
- confirm_password: 'required|type:string|match:password',
900
1019
  status: 'in:active,inactive,pending',
901
1020
  user_id: 'exists:users,id'
902
1021
  };
@@ -1054,8 +1173,9 @@ const schema = await ControllerWrapper.generateSchema();
1054
1173
  | `max:n` | Maximum value | `age: 'max:150'` |
1055
1174
  | `in:val1,val2` | Value must be in list | `status: 'in:active,inactive'` |
1056
1175
  | `regex:name` | Custom regex pattern | `email: 'regex:email'` |
1057
- | `match:field` | Must match another field | `confirm_password: 'match:password'` |
1176
+ | `call:name` | Custom callback function | `field: 'call:myValidator'` |
1058
1177
  | `exists:table,column` | Value must exist in table | `user_id: 'exists:users,id'` |
1178
+ | `default:value` | Default value if not provided | `status: 'default:active'` |
1059
1179
 
1060
1180
  ## Database Support
1061
1181
 
@@ -1 +1 @@
1
- const QueryService=require("./QueryService"),HookService=require("./HookService");class CurdTable{constructor(e,t,r=null){if(this.db=e,this.utils=t,this.controllerWrapper=r,this.hookService=new HookService(e,t,r),this.queryService=new QueryService(e,t,r),!this.queryService)throw new Error("CurdTable requires queryService (execute*Query / getQuery).")}async processRequest(e,t=null,r={}){const o=this.controllerWrapper;let s=this.utils.getModel(this.controllerWrapper,t);const c=e?.action||"list";let i=null;const a={model:s,action:c,request:e,ctx:r,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 t=await this.queryService.executeUpdateQuery(s,e);if(!t)throw new Error(`Record not found or not updated: ${s.table} returned ${t}`);i={message:"Record updated successfully",data:t,success:!0};break}case"replace":if(i=await this.queryService.executeReplaceQuery(s,e),!i)throw new Error(`Record not found or not replaced: ${t} 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: ${t} 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: ${t} returned ${i}`);i={message:"Record synced successfully",data:i,success:!0};break;case"delete":{let r=null;if(r=await this.hookService.executeHasSoftDeleteHook(s)?await this.queryService.executeSoftDeleteQuery(s,e):await this.queryService.executeDeleteQuery(s,e),!r)throw new Error(`Record not found or not deleted: ${t} returned ${r}`);i={message:"Record deleted successfully",data:r,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})}return this.hookService?.executeAfterHook&&(i=await this.hookService.executeAfterHook({...a,data:i})),i}async processRequestWithOthers(e,t=null,r={}){const o=await this.processRequest(e,t,r);if(e?.other_requests&&"object"==typeof e.other_requests){const t={},s=Object.entries(e.other_requests);for(const[e,o]of s)Array.isArray(o)?t[e]=await Promise.all(o.map(t=>this.processRequestWithOthers(t,e,r))):t[e]=await this.processRequestWithOthers(o,e,r);o.other_responses=t}return o}}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 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 +1 @@
1
- const HelperUtility=require("./HelperUtility");class QueryBuilder{constructor(e,t,r=null){this.db=e,this.utils=t,this.controllerWrapper=r,this.helperUtility=new HelperUtility}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)}_applyWhereCondition(e,t,r,i){switch(r){case"between":e.whereBetween(t,i);break;case"notBetween":e.whereNotBetween(t,i);break;case"in":e.whereIn(t,i);break;case"notIn":e.whereNotIn(t,i);break;case"like":e.whereRaw(`\`${t}\` LIKE ?`,[i]);break;default:e.where(t,r,i)}}buildWithTree(e,t=null){return this.helperUtility.dotWalkTree(e,{resolver:({current:e,part:r,source:i})=>t?t({current:e,part:r,source:i}):{}})}async fetchRelatedRows(e,t){if(e.through){let r=await this.db(e.through).whereIn(e.throughLocalKey,t);return await this.db(e.table).whereIn(e.foreignKey,r.map(t=>t[e.throughForeignKey]))}return this.db(e.table).whereIn(e.foreignKey,t)}async fetchAndAttachRelated(e){const{parentRows:t,relName:r,model:i,withTree:l,relation:o}=e;let a=t.map(e=>e[o.localKey]),s=[];s=await this.fetchRelatedRows(o,a);let n=new Map;for(const e of s){let t=e[o.foreignKey];n.has(t)||n.set(t,[]),n.get(t).push(e)}for(const e of t){let t=e[o.localKey];"one"===o?.type&&1==n.get(t)?.length?e[r]=n.get(t)[0]:e[r]=n.get(t)||[]}let h=Object.keys(l);for(const e of h){let i=l[e],o=t.filter(e=>e[r].length>0).map(e=>e[r]).reduce((e,t)=>e.concat(t),[]),a=this.utils.getModel(this.controllerWrapper,r);await this.fetchAndAttachRelated({parentRows:o,relName:e,model:a,withTree:i,relation:a.hasRelations[e]})}return t}filterWhere(e,t=""){return t?Object.keys(e).filter(e=>e.includes(t)).reduce((r,i)=>(r[i.replace(t,"")]=e[i],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:i,withWhere:l,select:o,orderBy:a={column:"id",direction:"asc"},limit:s=10,offset:n=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);o&&(Array.isArray(o)||"string"==typeof o)?b.select(o):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,i),c&&(Array.isArray(c),b.groupBy(c)),p&&this._applyHavingClause(b,p),a&&this._applyOrderBy(b,a);let W=!1,_=s,m=n,j=1,A=0;h&&s>0&&(j=Math.max(1,parseInt(h)),m=(j-1)*s),s>0&&(b.limit(_),m>0&&b.offset(m));const k=await b;if(i&&i.length>0){const t=this.buildWithTree(i);for(const r of Object.keys(t))await this.fetchAndAttachRelated({parentRows:k,relName:r,model:e,withTree:t[r],relation:e.hasRelations[r]})}let B=null;if(s>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");B=(await t.count("* as cnt").first()).cnt}catch(e){console.warn("Failed to get total count:",e.message),B=k.length}s>0&&null!==B&&(A=Math.ceil(B/s),W=j<A);return{data:k,totalCount:B,...s>0?{pagination:{page:j,limit:_,offset:m,totalPages:A,hasNext:W,hasPrev:j>1,nextPage:W?j+1:null,prevPage:j>1?j-1:null}}:{}}}catch(e){throw console.error("QueryService.getQuery error:",e),new Error(`Failed to execute query: ${e.message}`)}}_applyWhereClause(e,t,r=[]){if(t&&Object.keys(t).length>0){let r=this.helperUtility.getDotWalkQuery(t);console.log({filteredWhere:r}),r=this.helperUtility.objectFilter(r,(e,t)=>"object"!=typeof t||null===t);for(const[t,i]of Object.entries(r))if(null===i)e.whereNull(t);else if(Array.isArray(i))e.whereIn(t,i);else{const{operator:r,value:l}=this.parseWhereValue(i);this._applyWhereCondition(e,t,r,l)}}r&&r.length>0&&this._applyNestedWhere(e,t,r)}_applyNestedWhere(e,t,r){let i=this,l=e._getMyModel();if(r&&r.length>0)for(const o of r){let a=this.helperUtility.getDotWalkQuery(t,o);if(a&&Object.keys(a).length>0){let t=this.utils.getModel(this.controllerWrapper,o),s=l.hasRelations[o],n=r.map(e=>this.helperUtility.pluckDotWalkKey(e,1));e.whereExists(function(){let e=i.getQueryBuilder(t,this.select("*").from(t.table));e.whereRaw(`${s.foreignKey} = ${l.table}.${s.localKey}`),i._applyWhereClause(e,a,n)})}}}_applyJoins(e,t,r){const i=Array.isArray(t)?t:[t];for(const t of i)"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,i]of Object.entries(t))e.withWhere(r,i)}catch(e){console.warn("Failed to apply withWhere:",e.message)}}_applyHavingClause(e,t){for(const[r,i]of Object.entries(t))"object"==typeof i&&i.operator?e.having(r,i.operator,i.value):e.having(r,i)}_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");class QueryBuilder{constructor(e,t,r=null){this.db=e,this.utils=t,this.controllerWrapper=r,this.helperUtility=new HelperUtility}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,i){if(null!==i)if(Array.isArray(i))e.orWhereIn(t,i);else switch(r){case"between":e.orWhereBetween(t,i);break;case"notBetween":e.orWhereNotBetween(t,i);break;case"in":e.orWhereIn(t,i);break;case"notIn":e.orWhereNotIn(t,i);break;case"like":e.orWhereRaw(`\`${t}\` LIKE ?`,[i]);break;default:e.orWhere(t,r,i)}else e.orWhereNull(t)}_applyAndWhereCondition(e,t,r,i){if(null!==i)if(Array.isArray(i))e.whereIn(t,i);else switch(r){case"between":e.whereBetween(t,i);break;case"notBetween":e.whereNotBetween(t,i);break;case"in":e.whereIn(t,i);break;case"notIn":e.whereNotIn(t,i);break;case"like":e.whereRaw(`\`${t}\` LIKE ?`,[i]);break;default:e.where(t,r,i)}else e.whereNull(t)}buildWithTree(e,t=null){return this.helperUtility.dotWalkTree(e,{resolver:({current:e,part:r,source:i})=>t?t({current:e,part:r,source:i}):{}})}async fetchRelatedRows(e,t){if(e.through){let r=await this.db(e.through).whereIn(e.throughLocalKey,t);return await this.db(e.table).whereIn(e.foreignKey,r.map(t=>t[e.throughForeignKey]))}return this.db(e.table).whereIn(e.foreignKey,t)}async fetchAndAttachRelated(e){const{parentRows:t,relName:r,model:i,withTree:l,relation:o}=e;let a=t.map(e=>e[o.localKey]),s=[];s=await this.fetchRelatedRows(o,a);let n=new Map;for(const e of s){let t=e[o.foreignKey];n.has(t)||n.set(t,[]),n.get(t).push(e)}for(const e of t){let t=e[o.localKey];"one"===o?.type&&1==n.get(t)?.length?e[r]=n.get(t)[0]:e[r]=n.get(t)||[]}let h=Object.keys(l);for(const e of h){let i=l[e],o=t.filter(e=>e[r].length>0).map(e=>e[r]).reduce((e,t)=>e.concat(t),[]),a=this.utils.getModel(this.controllerWrapper,r);await this.fetchAndAttachRelated({parentRows:o,relName:e,model:a,withTree:i,relation:a.hasRelations[e]})}return t}filterWhere(e,t=""){return t?Object.keys(e).filter(e=>e.includes(t)).reduce((r,i)=>(r[i.replace(t,"")]=e[i],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:i,withWhere:l,select:o,orderBy:a={column:"id",direction:"asc"},limit:s=10,offset:n=0,page:h,groupBy:p,having:c,distinct:y,join:u,leftJoin:f,rightJoin:d,innerJoin:g,count:w=!1}=t;let W=this.getQueryBuilder(e);o&&(Array.isArray(o)||"string"==typeof o)?W.select(o):W.select("*"),y&&(Array.isArray(y)||"string"==typeof y?W.distinct(y):W.distinct()),u&&this._applyJoins(W,u,"join"),f&&this._applyJoins(W,f,"leftJoin"),d&&this._applyJoins(W,d,"rightJoin"),g&&this._applyJoins(W,g,"innerJoin"),this._applyWhereClause(W,r,i),p&&(Array.isArray(p),W.groupBy(p)),c&&this._applyHavingClause(W,c),a&&this._applyOrderBy(W,a);let b=!1,A=s,_=n,m=1,k=0;h&&s>0&&(m=Math.max(1,parseInt(h)),_=(m-1)*s),s>0&&(W.limit(A),_>0&&W.offset(_));const j=await W;if(i&&i.length>0){const t=this.buildWithTree(i);for(const r of Object.keys(t))await this.fetchAndAttachRelated({parentRows:j,relName:r,model:e,withTree:t[r],relation:e.hasRelations[r]})}let B=null;if(s>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");B=(await t.count("* as cnt").first()).cnt}catch(e){console.warn("Failed to get total count:",e.message),B=j.length}s>0&&null!==B&&(k=Math.ceil(B/s),b=m<k);return{data:j,totalCount:B,...s>0?{pagination:{page:m,limit:A,offset:_,totalPages:k,hasNext:b,hasPrev:m>1,nextPage:b?m+1:null,prevPage:m>1?m-1:null}}:{}}}catch(e){throw console.error("QueryService.getQuery error:",e),new Error(`Failed to execute query: ${e.message}`)}}_applyWhereWithArray(e,t,r){for(const i of t)this._applyWhereClause(e,i,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);console.log({filteredWhere:r}),r=this.helperUtility.objectFilter(r,(e,t)=>"object"!=typeof t||null===t);for(const[t,i]of Object.entries(r)){const{joinType:r="AND",column:l}=this.parseWhereColumn(t),{operator:o,value:a}=this.parseWhereValue(i);"AND"===r?this._applyAndWhereCondition(e,l,o,a):this._applyOrWhereCondition(e,l,o,a)}}r&&r.length>0&&this._applyNestedWhere(e,t,r)}}_applyNestedWhere(e,t,r){let i=this,l=e._getMyModel();if(r&&r.length>0)for(const o of r){let a=this.helperUtility.getDotWalkQuery(t,o);if(a&&Object.keys(a).length>0){let t=this.utils.getModel(this.controllerWrapper,o),s=l.hasRelations[o],n=r.map(e=>this.helperUtility.pluckDotWalkKey(e,1));e.whereExists(function(){let e=i.getQueryBuilder(t,this.select("*").from(t.table));e.whereRaw(`${s.foreignKey} = ${l.table}.${s.localKey}`),i._applyWhereClause(e,a,n)})}}}_applyJoins(e,t,r){const i=Array.isArray(t)?t:[t];for(const t of i)"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,i]of Object.entries(t))e.withWhere(r,i)}catch(e){console.warn("Failed to apply withWhere:",e.message)}}_applyHavingClause(e,t){for(const[r,i]of Object.entries(t))"object"==typeof i&&i.operator?e.having(r,i.operator,i.value):e.having(r,i)}_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 QueryService=require("./QueryService"),HookService=require("./HookService");class CurdTable{constructor(e,t,r=null){if(this.db=e,this.utils=t,this.controllerWrapper=r,this.hookService=new HookService(e,t,r),this.queryService=new QueryService(e,t,r),!this.queryService)throw new Error("CurdTable requires queryService (execute*Query / getQuery).")}async processRequest(e,t=null,r=null){const o=this.controllerWrapper;let s=this.utils.getModel(this.controllerWrapper,t);const c=e?.action||"list";let i=null;const u={model:s,action:c,request:e,ctx:r,controller:o};switch(this.hookService?.executeValidatorHook&&await this.hookService.executeValidatorHook({...u}),this.hookService?.executeBeforeHook&&(e.beforeActionData=await this.hookService.executeBeforeHook({...u})),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 t=await this.queryService.executeUpdateQuery(s,e);if(!t)throw new Error(`Record not found or not updated: ${s.table} returned ${t}`);i={message:"Record updated successfully",data:t,success:!0};break}case"replace":if(i=await this.queryService.executeReplaceQuery(s,e),!i)throw new Error(`Record not found or not replaced: ${t} 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: ${t} 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: ${t} returned ${i}`);i={message:"Record synced successfully",data:i,success:!0};break;case"delete":{let r=null;if(r=await this.hookService.executeHasSoftDeleteHook(s)?await this.queryService.executeSoftDeleteQuery(s,e):await this.queryService.executeDeleteQuery(s,e),!r)throw new Error(`Record not found or not deleted: ${t} returned ${r}`);i={message:"Record deleted successfully",data:r,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({...u})}return this.hookService?.executeAfterHook&&(i=await this.hookService.executeAfterHook({...u,data:i})),i}async processRequestWithOthers(e,t=null,r={}){const o=await this.processRequest(e,t,r);if(e?.other_requests&&"object"==typeof e.other_requests){const t={},s=Object.entries(e.other_requests);for(const[e,o]of s)Array.isArray(o)?t[e]=await Promise.all(o.map(t=>this.processRequestWithOthers(t,e,r))):t[e]=await this.processRequestWithOthers(o,e,r);o.other_responses=t}return o}}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 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 +1 @@
1
- const HelperUtility=require("./HelperUtility");class QueryBuilder{constructor(e,t,r=null){this.db=e,this.utils=t,this.controllerWrapper=r,this.helperUtility=new HelperUtility}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)}_applyWhereCondition(e,t,r,i){switch(r){case"between":e.whereBetween(t,i);break;case"notBetween":e.whereNotBetween(t,i);break;case"in":e.whereIn(t,i);break;case"notIn":e.whereNotIn(t,i);break;case"like":e.whereLike(t,i);break;default:e.where(t,r,i)}}buildWithTree(e,t=null){return this.helperUtility.dotWalkTree(e,{resolver:({current:e,part:r,source:i})=>t?t({current:e,part:r,source:i}):{}})}async fetchRelatedRows(e,t){if(e.through){let r=await this.db(e.through).whereIn(e.throughLocalKey,t);return await this.db(e.table).whereIn(e.foreignKey,r.map(t=>t[e.throughForeignKey]))}return this.db(e.table).whereIn(e.foreignKey,t)}async fetchAndAttachRelated(e){const{parentRows:t,relName:r,model:i,withTree:l,relation:o}=e;let a=t.map(e=>e[o.localKey]),s=[];s=await this.fetchRelatedRows(o,a);let n=new Map;for(const e of s){let t=e[o.foreignKey];n.has(t)||n.set(t,[]),n.get(t).push(e)}for(const e of t){let t=e[o.localKey];"one"===o?.type&&1==n.get(t)?.length?e[r]=n.get(t)[0]:e[r]=n.get(t)||[]}let h=Object.keys(l);for(const e of h){let i=l[e],o=t.filter(e=>e[r].length>0).map(e=>e[r]).reduce((e,t)=>e.concat(t),[]),a=this.utils.getModel(this.controllerWrapper,r);await this.fetchAndAttachRelated({parentRows:o,relName:e,model:a,withTree:i,relation:a.hasRelations[e]})}return t}filterWhere(e,t=""){return t?Object.keys(e).filter(e=>e.includes(t)).reduce((r,i)=>(r[i.replace(t,"")]=e[i],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:i,withWhere:l,select:o,orderBy:a={column:"id",direction:"asc"},limit:s=10,offset:n=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);o&&(Array.isArray(o)||"string"==typeof o)?b.select(o):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,i),c&&(Array.isArray(c),b.groupBy(c)),p&&this._applyHavingClause(b,p),a&&this._applyOrderBy(b,a);let W=!1,_=s,m=n,j=1,A=0;h&&s>0&&(j=Math.max(1,parseInt(h)),m=(j-1)*s),s>0&&(b.limit(_),m>0&&b.offset(m));const k=await b;if(i&&i.length>0){const t=this.buildWithTree(i);for(const r of Object.keys(t))await this.fetchAndAttachRelated({parentRows:k,relName:r,model:e,withTree:t[r],relation:e.hasRelations[r]})}let B=null;if(s>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");B=(await t.count("* as cnt").first()).cnt}catch(e){console.warn("Failed to get total count:",e.message),B=k.length}s>0&&null!==B&&(A=Math.ceil(B/s),W=j<A);return{data:k,totalCount:B,...s>0?{pagination:{page:j,limit:_,offset:m,totalPages:A,hasNext:W,hasPrev:j>1,nextPage:W?j+1:null,prevPage:j>1?j-1:null}}:{}}}catch(e){throw console.error("QueryService.getQuery error:",e),new Error(`Failed to execute query: ${e.message}`)}}_applyWhereClause(e,t,r=[]){if(t&&Object.keys(t).length>0){let r=this.helperUtility.getDotWalkQuery(t);console.log({filteredWhere:r}),r=this.helperUtility.objectFilter(r,(e,t)=>"object"!=typeof t||null===t);for(const[t,i]of Object.entries(r))if(null===i)e.whereNull(t);else if(Array.isArray(i))e.whereIn(t,i);else{const{operator:r,value:l}=this.parseWhereValue(i);this._applyWhereCondition(e,t,r,l)}}r&&r.length>0&&this._applyNestedWhere(e,t,r)}_applyNestedWhere(e,t,r){let i=this,l=e._getMyModel();if(r&&r.length>0)for(const o of r){let a=this.helperUtility.getDotWalkQuery(t,o);if(a&&Object.keys(a).length>0){let t=this.utils.getModel(this.controllerWrapper,o),s=l.hasRelations[o],n=r.map(e=>this.helperUtility.pluckDotWalkKey(e,1));e.whereExists(function(){let e=i.getQueryBuilder(t,this.select("*").from(t.table));e.whereRaw(`${s.foreignKey} = ${l.table}.${s.localKey}`),i._applyWhereClause(e,a,n)})}}}_applyJoins(e,t,r){const i=Array.isArray(t)?t:[t];for(const t of i)"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,i]of Object.entries(t))e.withWhere(r,i)}catch(e){console.warn("Failed to apply withWhere:",e.message)}}_applyHavingClause(e,t){for(const[r,i]of Object.entries(t))"object"==typeof i&&i.operator?e.having(r,i.operator,i.value):e.having(r,i)}_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");class QueryBuilder{constructor(e,t,r=null){this.db=e,this.utils=t,this.controllerWrapper=r,this.helperUtility=new HelperUtility}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,i){if(null!==i)if(Array.isArray(i))e.orWhereIn(t,i);else switch(r){case"between":e.orWhereBetween(t,i);break;case"notBetween":e.orWhereNotBetween(t,i);break;case"in":e.orWhereIn(t,i);break;case"notIn":e.orWhereNotIn(t,i);break;case"like":e.orWhere(t,"like",i);break;default:e.orWhere(t,r,i)}else e.orWhereNull(t)}_applyAndWhereCondition(e,t,r,i){if(null!==i)if(Array.isArray(i))e.whereIn(t,i);else switch(r){case"between":e.whereBetween(t,i);break;case"notBetween":e.whereNotBetween(t,i);break;case"in":e.whereIn(t,i);break;case"notIn":e.whereNotIn(t,i);break;case"like":e.where(t,"like",i);break;default:e.where(t,r,i)}else e.whereNull(t)}buildWithTree(e,t=null){return this.helperUtility.dotWalkTree(e,{resolver:({current:e,part:r,source:i})=>t?t({current:e,part:r,source:i}):{}})}async fetchRelatedRows(e,t){if(e.through){let r=await this.db(e.through).whereIn(e.throughLocalKey,t);return await this.db(e.table).whereIn(e.foreignKey,r.map(t=>t[e.throughForeignKey]))}return this.db(e.table).whereIn(e.foreignKey,t)}async fetchAndAttachRelated(e){const{parentRows:t,relName:r,model:i,withTree:l,relation:o}=e;let a=t.map(e=>e[o.localKey]),s=[];s=await this.fetchRelatedRows(o,a);let n=new Map;for(const e of s){let t=e[o.foreignKey];n.has(t)||n.set(t,[]),n.get(t).push(e)}for(const e of t){let t=e[o.localKey];"one"===o?.type&&1==n.get(t)?.length?e[r]=n.get(t)[0]:e[r]=n.get(t)||[]}let h=Object.keys(l);for(const e of h){let i=l[e],o=t.filter(e=>e[r].length>0).map(e=>e[r]).reduce((e,t)=>e.concat(t),[]),a=this.utils.getModel(this.controllerWrapper,r);await this.fetchAndAttachRelated({parentRows:o,relName:e,model:a,withTree:i,relation:a.hasRelations[e]})}return t}filterWhere(e,t=""){return t?Object.keys(e).filter(e=>e.includes(t)).reduce((r,i)=>(r[i.replace(t,"")]=e[i],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:i,withWhere:l,select:o,orderBy:a={column:"id",direction:"asc"},limit:s=10,offset:n=0,page:h,groupBy:p,having:c,distinct:y,join:u,leftJoin:f,rightJoin:d,innerJoin:g,count:w=!1}=t;let W=this.getQueryBuilder(e);o&&(Array.isArray(o)||"string"==typeof o)?W.select(o):W.select("*"),y&&(Array.isArray(y)||"string"==typeof y?W.distinct(y):W.distinct()),u&&this._applyJoins(W,u,"join"),f&&this._applyJoins(W,f,"leftJoin"),d&&this._applyJoins(W,d,"rightJoin"),g&&this._applyJoins(W,g,"innerJoin"),this._applyWhereClause(W,r,i),p&&(Array.isArray(p),W.groupBy(p)),c&&this._applyHavingClause(W,c),a&&this._applyOrderBy(W,a);let b=!1,A=s,_=n,m=1,k=0;h&&s>0&&(m=Math.max(1,parseInt(h)),_=(m-1)*s),s>0&&(W.limit(A),_>0&&W.offset(_));const j=await W;if(i&&i.length>0){const t=this.buildWithTree(i);for(const r of Object.keys(t))await this.fetchAndAttachRelated({parentRows:j,relName:r,model:e,withTree:t[r],relation:e.hasRelations[r]})}let B=null;if(s>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");B=(await t.count("* as cnt").first()).cnt}catch(e){console.warn("Failed to get total count:",e.message),B=j.length}s>0&&null!==B&&(k=Math.ceil(B/s),b=m<k);return{data:j,totalCount:B,...s>0?{pagination:{page:m,limit:A,offset:_,totalPages:k,hasNext:b,hasPrev:m>1,nextPage:b?m+1:null,prevPage:m>1?m-1:null}}:{}}}catch(e){throw console.error("QueryService.getQuery error:",e),new Error(`Failed to execute query: ${e.message}`)}}_applyWhereWithArray(e,t,r){for(const i of t)this._applyWhereClause(e,i,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);console.log({filteredWhere:r}),r=this.helperUtility.objectFilter(r,(e,t)=>"object"!=typeof t||null===t);for(const[t,i]of Object.entries(r)){const{joinType:r="AND",column:l}=this.parseWhereColumn(t),{operator:o,value:a}=this.parseWhereValue(i);"AND"===r?this._applyAndWhereCondition(e,l,o,a):this._applyOrWhereCondition(e,l,o,a)}}r&&r.length>0&&this._applyNestedWhere(e,t,r)}}_applyNestedWhere(e,t,r){let i=this,l=e._getMyModel();if(r&&r.length>0)for(const o of r){let a=this.helperUtility.getDotWalkQuery(t,o);if(a&&Object.keys(a).length>0){let t=this.utils.getModel(this.controllerWrapper,o),s=l.hasRelations[o],n=r.map(e=>this.helperUtility.pluckDotWalkKey(e,1));e.whereExists(function(){let e=i.getQueryBuilder(t,this.select("*").from(t.table));e.whereRaw(`${s.foreignKey} = ${l.table}.${s.localKey}`),i._applyWhereClause(e,a,n)})}}}_applyJoins(e,t,r){const i=Array.isArray(t)?t:[t];for(const t of i)"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,i]of Object.entries(t))e.withWhere(r,i)}catch(e){console.warn("Failed to apply withWhere:",e.message)}}_applyHavingClause(e,t){for(const[r,i]of Object.entries(t))"object"==typeof i&&i.operator?e.having(r,i.operator,i.value):e.having(r,i)}_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 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={}){let o=this.utils.getModel(this.controllerWrapper,r);const s=e?.action||"list";let c=null;switch(this.hookService?.executeValidatorHook&&await this.hookService.executeValidatorHook(o,s,e,t),this.hookService?.executeBeforeHook&&(e.beforeActionData=await this.hookService.executeBeforeHook(o,s,e,t)),s){case"count":c=await this.queryService.executeCountQuery(o,e);break;case"list":c=await this.hookService.executeHasSoftDeleteHook(o)?await this.queryService.getSoftDeleteQuery(o,e):await this.queryService.getQuery(o,e);break;case"show":c=await this.queryService.executeShowQuery(o,e);break;case"create":c=await this.queryService.executeCreateQuery(o,e);break;case"update":{const r=await this.queryService.executeUpdateQuery(o,e);if(!r)throw new Error(`Record not found or not updated: ${o.table} returned ${r}`);c={message:"Record updated successfully",data:r,success:!0};break}case"replace":if(c=await this.queryService.executeReplaceQuery(o,e),!c)throw new Error(`Record not found or not replaced: ${r} returned ${c}`);c={message:"Record replaced successfully",data:c,success:!0};break;case"upsert":if(c=await this.queryService.executeUpsertQuery(o,e),!c)throw new Error(`Record not found or not upserted: ${r} returned ${c}`);c={message:"Record upserted successfully",data:c,success:!0};break;case"sync":if(c=await this.queryService.executeSyncQuery(o,e),!c)throw new Error(`Record not found or not synced: ${r} returned ${c}`);c={message:"Record synced successfully",data:c,success:!0};break;case"delete":{let t=null;if(t=await this.hookService.executeHasSoftDeleteHook(o)?await this.queryService.executeSoftDeleteQuery(o,e):await this.queryService.executeDeleteQuery(o,e),!t)throw new Error(`Record not found or not deleted: ${r} returned ${t}`);c={message:"Record deleted successfully",data:t,success:!0};break}default:if(!this.hookService?.executeCustomAction)throw new Error(`Unknown action "${s}" and no custom action hook provided.`);c=await this.hookService.executeCustomAction(r,s,e,t)}return this.hookService?.executeAfterHook&&(c=await this.hookService.executeAfterHook(r,s,c,e,t)),c}async processRequestWithOthers(e,r=null,t={}){const o=await this.processRequest(e,r,t);if(e?.other_requests&&"object"==typeof e.other_requests){const r={},s=Object.entries(e.other_requests);for(const[e,o]of s)Array.isArray(o)?r[e]=await Promise.all(o.map(r=>this.processRequestWithOthers(r,e,t))):r[e]=await this.processRequestWithOthers(o,e,t);o.other_responses=r}return o}}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={}){let o=this.utils.getModel(this.controllerWrapper,r);const s=e?.action||"list";let c=null;switch(this.hookService?.executeValidatorHook&&await this.hookService.executeValidatorHook(o,s,e,t),this.hookService?.executeBeforeHook&&(e.beforeActionData=await this.hookService.executeBeforeHook(o,s,e,t)),s){case"count":c=await this.queryService.executeCountQuery(o,e);break;case"list":c=await this.hookService.executeHasSoftDeleteHook(o)?await this.queryService.getSoftDeleteQuery(o,e):await this.queryService.getQuery(o,e);break;case"show":c=await this.queryService.executeShowQuery(o,e);break;case"create":c=await this.queryService.executeCreateQuery(o,e);break;case"update":{const r=await this.queryService.executeUpdateQuery(o,e);if(!r)throw new Error(`Record not found or not updated: ${o.table} returned ${r}`);c={message:"Record updated successfully",data:r,success:!0};break}case"replace":if(c=await this.queryService.executeReplaceQuery(o,e),!c)throw new Error(`Record not found or not replaced: ${r} returned ${c}`);c={message:"Record replaced successfully",data:c,success:!0};break;case"upsert":if(c=await this.queryService.executeUpsertQuery(o,e),!c)throw new Error(`Record not found or not upserted: ${r} returned ${c}`);c={message:"Record upserted successfully",data:c,success:!0};break;case"sync":if(c=await this.queryService.executeSyncQuery(o,e),!c)throw new Error(`Record not found or not synced: ${r} returned ${c}`);c={message:"Record synced successfully",data:c,success:!0};break;case"delete":{let t=null;if(t=await this.hookService.executeHasSoftDeleteHook(o)?await this.queryService.executeSoftDeleteQuery(o,e):await this.queryService.executeDeleteQuery(o,e),!t)throw new Error(`Record not found or not deleted: ${r} returned ${t}`);c={message:"Record deleted successfully",data:t,success:!0};break}default:if(!this.hookService?.executeCustomAction)throw new Error(`Unknown action "${s}" and no custom action hook provided.`);c=await this.hookService.executeCustomAction(r,s,e,t)}if(this.hookService?.executeAfterHook&&(c=await this.hookService.executeAfterHook(r,s,c,e,t)),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);c.other_responses=r}return c}}module.exports=CurdTable;
@@ -1 +1 @@
1
- const HelperUtility=require("./HelperUtility");class QueryBuilder{constructor(e,t,r=null){this.db=e,this.utils=t,this.controllerWrapper=r,this.helperUtility=new HelperUtility}getQueryBuilder(e){return this.db(e.table)}parseValue(e){return this.helperUtility.parseValue(e)}parseWhereValue(e){return this.helperUtility.parseWhereValue(e)}_applyWhereCondition(e,t,r,i){switch(r){case"between":e.whereBetween(t,i);break;case"notBetween":e.whereNotBetween(t,i);break;case"in":e.whereIn(t,i);break;case"notIn":e.whereNotIn(t,i);break;case"like":e.whereLike(t,i);break;default:e.where(t,r,i)}}buildWithTree(e,t=null){return this.helperUtility.dotWalkTree(e,{resolver:({current:e,part:r,source:i})=>t?t({current:e,part:r,source:i}):{}})}async fetchRelatedRows(e,t){if(e.through){let r=await this.db(e.through).whereIn(e.throughLocalKey,t);return await this.db(e.table).whereIn(e.foreignKey,r.map(t=>t[e.throughForeignKey]))}return this.db(e.table).whereIn(e.foreignKey,t)}async fetchAndAttachRelated(e){const{parentRows:t,relName:r,model:i,withTree:l,relation:o}=e;let a=t.map(e=>e[o.localKey]),s=[];s=await this.fetchRelatedRows(o,a);let n=new Map;for(const e of s){let t=e[o.foreignKey];n.has(t)||n.set(t,[]),n.get(t).push(e)}for(const e of t){let t=e[o.localKey];"one"===o?.type&&1==n.get(t)?.length?e[r]=n.get(t)[0]:e[r]=n.get(t)||[]}let h=Object.keys(l);for(const e of h){let i=l[e],o=t.filter(e=>e[r].length>0).map(e=>e[r]).reduce((e,t)=>e.concat(t),[]),a=this.utils.getModel(this.controllerWrapper,r);await this.fetchAndAttachRelated({parentRows:o,relName:e,model:a,withTree:i,relation:a.hasRelations[e]})}return t}filterWhere(e,t=""){return t?Object.keys(e).filter(e=>e.includes(t)).reduce((r,i)=>(r[i.replace(t,"")]=e[i],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:i,withWhere:l,select:o,orderBy:a={column:"id",direction:"asc"},limit:s=10,offset:n=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);o&&(Array.isArray(o)||"string"==typeof o)?b.select(o):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,i),c&&(Array.isArray(c),b.groupBy(c)),p&&this._applyHavingClause(b,p),a&&this._applyOrderBy(b,a);let W=!1,m=s,_=n,j=1,A=0;h&&s>0&&(j=Math.max(1,parseInt(h)),_=(j-1)*s),s>0&&(b.limit(m),_>0&&b.offset(_));const k=await b;if(i&&i.length>0){const t=this.buildWithTree(i);for(const r of Object.keys(t))await this.fetchAndAttachRelated({parentRows:k,relName:r,model:e,withTree:t[r],relation:e.hasRelations[r]})}let B=null;if(s>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");B=(await t.count("* as cnt").first()).cnt}catch(e){console.warn("Failed to get total count:",e.message),B=k.length}s>0&&null!==B&&(A=Math.ceil(B/s),W=j<A);return{data:k,totalCount:B,...s>0?{pagination:{page:j,limit:m,offset:_,totalPages:A,hasNext:W,hasPrev:j>1,nextPage:W?j+1:null,prevPage:j>1?j-1:null}}:{}}}catch(e){throw console.error("QueryService.getQuery error:",e),new Error(`Failed to execute query: ${e.message}`)}}_applyWhereClause(e,t,r=[]){if(t&&Object.keys(t).length>0){let r=this.helperUtility.getDotWalkQuery(t);r=this.helperUtility.objectFilter(r,(e,t)=>"object"!=typeof t||null===t);for(const[t,i]of Object.entries(r))if(null===i)e.whereNull(t);else if(Array.isArray(i))e.whereIn(t,i);else{const{operator:r,value:i}=this.parseWhereValue(i);this._applyWhereCondition(e,t,r,i)}}r&&r.length>0&&this._applyNestedWhere(e,t,r)}_applyNestedWhere(e,t,r){if(r&&r.length>0)for(const i of r){let l=this.helperUtility.getDotWalkQuery(t,i);if(l&&Object.keys(l).length>0){let t=this.getQueryBuilder(this.utils.getModel(this.controllerWrapper,i)),o=this.helperUtility.map(r,e=>this.helperUtility.pluckDotWalkKey(e,1));e.whereExists(this._applyWhereClause(t,l,o))}}}_applyJoins(e,t,r){const i=Array.isArray(t)?t:[t];for(const t of i)"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,i]of Object.entries(t))e.withWhere(r,i)}catch(e){console.warn("Failed to apply withWhere:",e.message)}}_applyHavingClause(e,t){for(const[r,i]of Object.entries(t))"object"==typeof i&&i.operator?e.having(r,i.operator,i.value):e.having(r,i)}_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");class QueryBuilder{constructor(e,t,r=null){this.db=e,this.utils=t,this.controllerWrapper=r,this.helperUtility=new HelperUtility}getQueryBuilder(e){return this.db(e.table)}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,i){if(null!==i)if(Array.isArray(i))e.orWhereIn(t,i);else switch(r){case"between":e.orWhereBetween(t,i);break;case"notBetween":e.orWhereNotBetween(t,i);break;case"in":e.orWhereIn(t,i);break;case"notIn":e.orWhereNotIn(t,i);break;case"like":e.orWhere(t,"like",i);break;default:e.orWhere(t,r,i)}else e.orWhereNull(t)}_applyAndWhereCondition(e,t,r,i){if(null!==i)if(Array.isArray(i))e.whereIn(t,i);else switch(r){case"between":e.whereBetween(t,i);break;case"notBetween":e.whereNotBetween(t,i);break;case"in":e.whereIn(t,i);break;case"notIn":e.whereNotIn(t,i);break;case"like":e.where(t,"like",i);break;default:e.where(t,r,i)}else e.whereNull(t)}buildWithTree(e,t=null){return this.helperUtility.dotWalkTree(e,{resolver:({current:e,part:r,source:i})=>t?t({current:e,part:r,source:i}):{}})}async fetchRelatedRows(e,t){if(e.through){let r=await this.db(e.through).whereIn(e.throughLocalKey,t);return await this.db(e.table).whereIn(e.foreignKey,r.map(t=>t[e.throughForeignKey]))}return this.db(e.table).whereIn(e.foreignKey,t)}async fetchAndAttachRelated(e){const{parentRows:t,relName:r,model:i,withTree:l,relation:o}=e;let a=t.map(e=>e[o.localKey]),s=[];s=await this.fetchRelatedRows(o,a);let n=new Map;for(const e of s){let t=e[o.foreignKey];n.has(t)||n.set(t,[]),n.get(t).push(e)}for(const e of t){let t=e[o.localKey];"one"===o?.type&&1==n.get(t)?.length?e[r]=n.get(t)[0]:e[r]=n.get(t)||[]}let h=Object.keys(l);for(const e of h){let i=l[e],o=t.filter(e=>e[r].length>0).map(e=>e[r]).reduce((e,t)=>e.concat(t),[]),a=this.utils.getModel(this.controllerWrapper,r);await this.fetchAndAttachRelated({parentRows:o,relName:e,model:a,withTree:i,relation:a.hasRelations[e]})}return t}filterWhere(e,t=""){return t?Object.keys(e).filter(e=>e.includes(t)).reduce((r,i)=>(r[i.replace(t,"")]=e[i],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:i,withWhere:l,select:o,orderBy:a={column:"id",direction:"asc"},limit:s=10,offset:n=0,page:h,groupBy:p,having:c,distinct:y,join:u,leftJoin:f,rightJoin:d,innerJoin:g,count:w=!1}=t;let W=this.getQueryBuilder(e);o&&(Array.isArray(o)||"string"==typeof o)?W.select(o):W.select("*"),y&&(Array.isArray(y)||"string"==typeof y?W.distinct(y):W.distinct()),u&&this._applyJoins(W,u,"join"),f&&this._applyJoins(W,f,"leftJoin"),d&&this._applyJoins(W,d,"rightJoin"),g&&this._applyJoins(W,g,"innerJoin"),this._applyWhereClause(W,r,i),p&&(Array.isArray(p),W.groupBy(p)),c&&this._applyHavingClause(W,c),a&&this._applyOrderBy(W,a);let b=!1,A=s,_=n,k=1,m=0;h&&s>0&&(k=Math.max(1,parseInt(h)),_=(k-1)*s),s>0&&(W.limit(A),_>0&&W.offset(_));const j=await W;if(i&&i.length>0){const t=this.buildWithTree(i);for(const r of Object.keys(t))await this.fetchAndAttachRelated({parentRows:j,relName:r,model:e,withTree:t[r],relation:e.hasRelations[r]})}let B=null;if(s>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");B=(await t.count("* as cnt").first()).cnt}catch(e){console.warn("Failed to get total count:",e.message),B=j.length}s>0&&null!==B&&(m=Math.ceil(B/s),b=k<m);return{data:j,totalCount:B,...s>0?{pagination:{page:k,limit:A,offset:_,totalPages:m,hasNext:b,hasPrev:k>1,nextPage:b?k+1:null,prevPage:k>1?k-1:null}}:{}}}catch(e){throw console.error("QueryService.getQuery error:",e),new Error(`Failed to execute query: ${e.message}`)}}_applyWhereWithArray(e,t,r){for(const i of t)this._applyWhereClause(e,i,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)=>"object"!=typeof t||null===t);for(const[t,i]of Object.entries(r)){const{joinType:r="AND",column:l}=this.parseWhereColumn(t),{operator:o,value:a}=this.parseWhereValue(i);"AND"===r?this._applyAndWhereCondition(e,l,o,a):this._applyOrWhereCondition(e,l,o,a)}}r&&r.length>0&&this._applyNestedWhere(e,t,r)}}_applyNestedWhere(e,t,r){if(r&&r.length>0)for(const i of r){let l=this.helperUtility.getDotWalkQuery(t,i);if(l&&Object.keys(l).length>0){let t=this.getQueryBuilder(this.utils.getModel(this.controllerWrapper,i)),o=this.helperUtility.map(r,e=>this.helperUtility.pluckDotWalkKey(e,1));e.whereExists(this._applyWhereClause(t,l,o))}}}_applyJoins(e,t,r){const i=Array.isArray(t)?t:[t];for(const t of i)"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,i]of Object.entries(t))e.withWhere(r,i)}catch(e){console.warn("Failed to apply withWhere:",e.message)}}_applyHavingClause(e,t){for(const[r,i]of Object.entries(t))"object"==typeof i&&i.operator?e.having(r,i.operator,i.value):e.having(r,i)}_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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dreamtree-org/korm-js",
3
- "version": "1.0.42",
3
+ "version": "1.0.44",
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",