@rws-framework/db 4.1.0 → 4.1.1

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.
@@ -422,8 +422,15 @@ class RWSModel {
422
422
  // Check if it's already a full object with data or just an ID reference
423
423
  if (relationData.id || Object.keys(relationData).length > 1) {
424
424
  // Create new instance and hydrate ONLY basic fields, NO RELATIONS
425
+ // Respect ignored_keys from child model
426
+ const childIgnoredKeys = ModelClass._CUT_KEYS || [];
425
427
  const relatedInstance = new ModelClass();
426
- await relatedInstance._asyncFill(relationData, false, false, true);
428
+ // Filter relationData to exclude ignored keys
429
+ const filteredData = { ...relationData };
430
+ for (const ignoredKey of childIgnoredKeys) {
431
+ delete filteredData[ignoredKey];
432
+ }
433
+ await relatedInstance._asyncFill(filteredData, false, false, true);
427
434
  this[key] = relatedInstance;
428
435
  }
429
436
  }
@@ -441,6 +448,7 @@ class RWSModel {
441
448
  // Handle singular inverse relation as a single object
442
449
  if (typeof relationData === 'object' && relationData !== null &&
443
450
  (relationData.id || Object.keys(relationData).length > 1)) {
451
+ const childIgnoredKeys = ModelClass._CUT_KEYS || [];
444
452
  const relatedInstance = new ModelClass();
445
453
  // Check relation metadata to identify foreign key fields that need to be preserved
446
454
  const tempInstance = new ModelClass();
@@ -466,7 +474,12 @@ class RWSModel {
466
474
  relatedInstance[foreignKeyField] = relationData[foreignKeyField];
467
475
  }
468
476
  }
469
- await relatedInstance._asyncFill(relationData, false, false, true);
477
+ // Filter relationData to exclude ignored keys
478
+ const filteredData = { ...relationData };
479
+ for (const ignoredKey of childIgnoredKeys) {
480
+ delete filteredData[ignoredKey];
481
+ }
482
+ await relatedInstance._asyncFill(filteredData, false, false, true);
470
483
  this[key] = relatedInstance;
471
484
  }
472
485
  }
@@ -475,6 +488,7 @@ class RWSModel {
475
488
  const relatedInstances = [];
476
489
  for (const itemData of relationData) {
477
490
  if (typeof itemData === 'object' && itemData !== null) {
491
+ const childIgnoredKeys = ModelClass._CUT_KEYS || [];
478
492
  const relatedInstance = new ModelClass();
479
493
  // Check relation metadata to identify foreign key fields that need to be preserved
480
494
  const tempInstance = new ModelClass();
@@ -500,7 +514,12 @@ class RWSModel {
500
514
  relatedInstance[foreignKeyField] = itemData[foreignKeyField];
501
515
  }
502
516
  }
503
- await relatedInstance._asyncFill(itemData, false, false, true);
517
+ // Filter itemData to exclude ignored keys
518
+ const filteredData = { ...itemData };
519
+ for (const ignoredKey of childIgnoredKeys) {
520
+ delete filteredData[ignoredKey];
521
+ }
522
+ await relatedInstance._asyncFill(filteredData, false, false, true);
504
523
  relatedInstances.push(relatedInstance);
505
524
  }
506
525
  }
@@ -524,8 +543,32 @@ class RWSModel {
524
543
  else {
525
544
  where[pk] = this[pk];
526
545
  }
527
- // Find the fresh data from database
528
- const freshData = await FindUtils_1.FindUtils.findOneBy(this.constructor, { conditions: where });
546
+ // Get ignored keys from model's @RWSCollection decorator
547
+ const ignoredKeys = (this.constructor._CUT_KEYS || []);
548
+ let fields = undefined;
549
+ // Build fields list excluding ignored ones if there are ignored keys
550
+ if (ignoredKeys.length > 0) {
551
+ // Get proper database fields from model annotations
552
+ const annotations = await ModelUtils_1.ModelUtils.getModelAnnotations(this.constructor);
553
+ // Get scalar fields (TrackType decorated fields)
554
+ const scalarFields = ModelUtils_1.ModelUtils.getModelScalarFields(this);
555
+ // Get relation fields from annotations
556
+ const relationFields = Object.keys(annotations).filter(key => annotations[key].annotationType === 'Relation' ||
557
+ annotations[key].annotationType === 'InverseRelation');
558
+ // Combine all database fields
559
+ const allDbFields = [...scalarFields, ...relationFields];
560
+ // Filter out ignored keys
561
+ fields = allDbFields.filter(field => !ignoredKeys.includes(field));
562
+ // Always include id if not ignored
563
+ if (!fields.includes('id') && !ignoredKeys.includes('id')) {
564
+ fields.push('id');
565
+ }
566
+ }
567
+ // Find the fresh data from database with field filtering
568
+ const freshData = await FindUtils_1.FindUtils.findOneBy(this.constructor, {
569
+ conditions: where,
570
+ fields: fields
571
+ });
529
572
  if (!freshData) {
530
573
  return null;
531
574
  }
@@ -10,4 +10,8 @@ export declare class HydrateUtils {
10
10
  static hydrateRelations(model: RWSModel<any>, relManyData: RelManyMetaType<IRWSModel>, relOneData: RelOneMetaType<IRWSModel>, seriesHydrationfields: string[], fullDataMode: boolean, data: {
11
11
  [key: string]: any;
12
12
  }, postLoadExecute?: boolean): Promise<void>;
13
+ /**
14
+ * Get all database fields for a model excluding ignored ones
15
+ */
16
+ private static getFieldsExcludingIgnored;
13
17
  }
@@ -19,9 +19,11 @@ class HydrateUtils {
19
19
  foreignKeyFields.add(relationMeta.hydrationField);
20
20
  }
21
21
  }
22
+ // Get ignored keys from model's @RWSCollection decorator
23
+ const ignoredKeys = (model).constructor._CUT_KEYS || [];
22
24
  for (const key in data) {
23
25
  if (data.hasOwnProperty(key)) {
24
- if (!fullDataMode && (model).constructor._CUT_KEYS.includes(key)) {
26
+ if (!fullDataMode && ignoredKeys.includes(key)) {
25
27
  continue;
26
28
  }
27
29
  // Skip relation property names
@@ -52,20 +54,25 @@ class HydrateUtils {
52
54
  }
53
55
  }
54
56
  static async hydrateRelations(model, relManyData, relOneData, seriesHydrationfields, fullDataMode, data, postLoadExecute = false) {
57
+ const ignoredKeys = (model).constructor._CUT_KEYS || [];
55
58
  // Handle many-to-many relations
56
59
  for (const key in relManyData) {
57
- if (!fullDataMode && model.constructor._CUT_KEYS.includes(key)) {
60
+ if (!fullDataMode && ignoredKeys.includes(key)) {
58
61
  continue;
59
62
  }
60
63
  const relMeta = relManyData[key];
61
64
  const relationEnabled = !RelationUtils_1.RelationUtils.checkRelDisabled(model, relMeta.key);
62
65
  if (relationEnabled) {
63
66
  const pk = ModelUtils_1.ModelUtils.findPrimaryKeyFields(model.constructor);
67
+ // Get child model ignored keys to pass to find operations
68
+ const childIgnoredKeys = relMeta.inversionModel._CUT_KEYS || [];
69
+ const childFields = childIgnoredKeys.length > 0 ? await this.getFieldsExcludingIgnored(relMeta.inversionModel, childIgnoredKeys) : undefined;
64
70
  if (relMeta.singular) {
65
71
  model[relMeta.key] = await relMeta.inversionModel.findOneBy({
66
72
  conditions: {
67
73
  [relMeta.foreignKey]: data[pk]
68
74
  },
75
+ fields: childFields,
69
76
  allowRelations: false,
70
77
  cancelPostLoad: !postLoadExecute
71
78
  });
@@ -75,6 +82,7 @@ class HydrateUtils {
75
82
  conditions: {
76
83
  [relMeta.foreignKey]: data[pk]
77
84
  },
85
+ fields: childFields,
78
86
  allowRelations: false,
79
87
  cancelPostLoad: !postLoadExecute
80
88
  });
@@ -83,7 +91,7 @@ class HydrateUtils {
83
91
  }
84
92
  // Handle one-to-one relations
85
93
  for (const key in relOneData) {
86
- if (!fullDataMode && model.constructor._CUT_KEYS.includes(key)) {
94
+ if (!fullDataMode && ignoredKeys.includes(key)) {
87
95
  continue;
88
96
  }
89
97
  const relMeta = relOneData[key];
@@ -101,13 +109,19 @@ class HydrateUtils {
101
109
  else {
102
110
  where[pk] = data[relMeta.hydrationField];
103
111
  }
104
- model[relMeta.key] = await relMeta.model.findOneBy({ conditions: where }, { allowRelations: false });
112
+ // Get child model ignored keys to pass to find operation
113
+ const childIgnoredKeys = relMeta.model._CUT_KEYS || [];
114
+ const childFields = childIgnoredKeys.length > 0 ? await this.getFieldsExcludingIgnored(relMeta.model, childIgnoredKeys) : undefined;
115
+ model[relMeta.key] = await relMeta.model.findOneBy({
116
+ conditions: where,
117
+ fields: childFields
118
+ }, { allowRelations: false });
105
119
  }
106
120
  else if (relationEnabled && !data[relMeta.hydrationField] && data[relMeta.key]) {
107
121
  const newRelModel = await relMeta.model.create(data[relMeta.key]);
108
122
  model[relMeta.key] = await newRelModel.save();
109
123
  }
110
- const cutKeys = model.constructor._CUT_KEYS;
124
+ const cutKeys = ignoredKeys;
111
125
  const trackedField = Object.keys((await ModelUtils_1.ModelUtils.getModelAnnotations(model.constructor))).includes(relMeta.hydrationField);
112
126
  if (!cutKeys.includes(relMeta.hydrationField) && !trackedField) {
113
127
  cutKeys.push(relMeta.hydrationField);
@@ -115,5 +129,30 @@ class HydrateUtils {
115
129
  // seriesHydrationfields.push(relMeta.hydrationField);
116
130
  }
117
131
  }
132
+ /**
133
+ * Get all database fields for a model excluding ignored ones
134
+ */
135
+ static async getFieldsExcludingIgnored(modelClass, ignoredKeys) {
136
+ if (!ignoredKeys || ignoredKeys.length === 0) {
137
+ return undefined;
138
+ }
139
+ // Get proper database fields from model annotations
140
+ const tempInstance = new modelClass();
141
+ const annotations = await ModelUtils_1.ModelUtils.getModelAnnotations(modelClass);
142
+ // Get scalar fields (TrackType decorated fields)
143
+ const scalarFields = ModelUtils_1.ModelUtils.getModelScalarFields(tempInstance);
144
+ // Get relation fields from annotations
145
+ const relationFields = Object.keys(annotations).filter(key => annotations[key].annotationType === 'Relation' ||
146
+ annotations[key].annotationType === 'InverseRelation');
147
+ // Combine all database fields
148
+ const allDbFields = [...scalarFields, ...relationFields];
149
+ // Filter out ignored keys
150
+ const filteredFields = allDbFields.filter(field => !ignoredKeys.includes(field));
151
+ // Always include id if not ignored
152
+ if (!filteredFields.includes('id') && !ignoredKeys.includes('id')) {
153
+ filteredFields.push('id');
154
+ }
155
+ return filteredFields.length > 0 ? filteredFields : undefined;
156
+ }
118
157
  }
119
158
  exports.HydrateUtils = HydrateUtils;
@@ -55,7 +55,7 @@ class ModelUtils {
55
55
  return baseClass.prototype.isPrototypeOf(constructor.prototype);
56
56
  }
57
57
  static getModelScalarFields(model) {
58
- return FieldsHelper_1.FieldsHelper.getAllClassFields(model)
58
+ return FieldsHelper_1.FieldsHelper.getAllClassFields(model.constructor)
59
59
  .filter((item) => item.indexOf('TrackType') === 0)
60
60
  .map((item) => item.split(':').at(-1));
61
61
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rws-framework/db",
3
3
  "private": false,
4
- "version": "4.1.0",
4
+ "version": "4.1.1",
5
5
  "description": "",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -547,8 +547,17 @@ class RWSModel<T> implements IModel {
547
547
  // Check if it's already a full object with data or just an ID reference
548
548
  if (relationData.id || Object.keys(relationData).length > 1) {
549
549
  // Create new instance and hydrate ONLY basic fields, NO RELATIONS
550
+ // Respect ignored_keys from child model
551
+ const childIgnoredKeys = (ModelClass as OpModelType<any>)._CUT_KEYS || [];
550
552
  const relatedInstance = new ModelClass();
551
- await relatedInstance._asyncFill(relationData, false, false, true);
553
+
554
+ // Filter relationData to exclude ignored keys
555
+ const filteredData = { ...relationData };
556
+ for (const ignoredKey of childIgnoredKeys) {
557
+ delete filteredData[ignoredKey];
558
+ }
559
+
560
+ await relatedInstance._asyncFill(filteredData, false, false, true);
552
561
  this[key] = relatedInstance;
553
562
  }
554
563
  }
@@ -568,6 +577,7 @@ class RWSModel<T> implements IModel {
568
577
  // Handle singular inverse relation as a single object
569
578
  if (typeof relationData === 'object' && relationData !== null &&
570
579
  (relationData.id || Object.keys(relationData).length > 1)) {
580
+ const childIgnoredKeys = (ModelClass as OpModelType<any>)._CUT_KEYS || [];
571
581
  const relatedInstance = new ModelClass();
572
582
 
573
583
  // Check relation metadata to identify foreign key fields that need to be preserved
@@ -597,7 +607,13 @@ class RWSModel<T> implements IModel {
597
607
  }
598
608
  }
599
609
 
600
- await relatedInstance._asyncFill(relationData, false, false, true);
610
+ // Filter relationData to exclude ignored keys
611
+ const filteredData = { ...relationData };
612
+ for (const ignoredKey of childIgnoredKeys) {
613
+ delete filteredData[ignoredKey];
614
+ }
615
+
616
+ await relatedInstance._asyncFill(filteredData, false, false, true);
601
617
  this[key] = relatedInstance;
602
618
  }
603
619
  } else if (Array.isArray(relationData) && relationData.length > 0) {
@@ -605,6 +621,7 @@ class RWSModel<T> implements IModel {
605
621
  const relatedInstances = [];
606
622
  for (const itemData of relationData) {
607
623
  if (typeof itemData === 'object' && itemData !== null) {
624
+ const childIgnoredKeys = (ModelClass as OpModelType<any>)._CUT_KEYS || [];
608
625
  const relatedInstance = new ModelClass();
609
626
 
610
627
  // Check relation metadata to identify foreign key fields that need to be preserved
@@ -634,7 +651,13 @@ class RWSModel<T> implements IModel {
634
651
  }
635
652
  }
636
653
 
637
- await relatedInstance._asyncFill(itemData, false, false, true);
654
+ // Filter itemData to exclude ignored keys
655
+ const filteredData = { ...itemData };
656
+ for (const ignoredKey of childIgnoredKeys) {
657
+ delete filteredData[ignoredKey];
658
+ }
659
+
660
+ await relatedInstance._asyncFill(filteredData, false, false, true);
638
661
  relatedInstances.push(relatedInstance);
639
662
  }
640
663
  }
@@ -663,8 +686,41 @@ class RWSModel<T> implements IModel {
663
686
  where[pk as string] = this[pk as string]
664
687
  }
665
688
 
666
- // Find the fresh data from database
667
- const freshData = await FindUtils.findOneBy(this.constructor as OpModelType<any>, { conditions: where });
689
+ // Get ignored keys from model's @RWSCollection decorator
690
+ const ignoredKeys = ((this.constructor as OpModelType<any>)._CUT_KEYS || []);
691
+ let fields: string[] | undefined = undefined;
692
+
693
+ // Build fields list excluding ignored ones if there are ignored keys
694
+ if (ignoredKeys.length > 0) {
695
+ // Get proper database fields from model annotations
696
+ const annotations = await ModelUtils.getModelAnnotations(this.constructor as OpModelType<any>);
697
+
698
+ // Get scalar fields (TrackType decorated fields)
699
+ const scalarFields = ModelUtils.getModelScalarFields(this);
700
+
701
+ // Get relation fields from annotations
702
+ const relationFields = Object.keys(annotations).filter(key =>
703
+ annotations[key].annotationType === 'Relation' ||
704
+ annotations[key].annotationType === 'InverseRelation'
705
+ );
706
+
707
+ // Combine all database fields
708
+ const allDbFields = [...scalarFields, ...relationFields];
709
+
710
+ // Filter out ignored keys
711
+ fields = allDbFields.filter(field => !ignoredKeys.includes(field));
712
+
713
+ // Always include id if not ignored
714
+ if (!fields.includes('id') && !ignoredKeys.includes('id')) {
715
+ fields.push('id');
716
+ }
717
+ }
718
+
719
+ // Find the fresh data from database with field filtering
720
+ const freshData = await FindUtils.findOneBy(this.constructor as OpModelType<any>, {
721
+ conditions: where,
722
+ fields: fields
723
+ });
668
724
 
669
725
  if (!freshData) {
670
726
  return null;
@@ -5,6 +5,7 @@ import { TimeSeriesUtils } from "./TimeSeriesUtils";
5
5
  import { RelationUtils } from "./RelationUtils";
6
6
  import { OpModelType } from "..";
7
7
  import { ModelUtils } from "./ModelUtils";
8
+ import { FieldsHelper } from "../../helper/FieldsHelper";
8
9
  import chalk from 'chalk';
9
10
 
10
11
  export class HydrateUtils {
@@ -20,9 +21,12 @@ export class HydrateUtils {
20
21
  }
21
22
  }
22
23
 
24
+ // Get ignored keys from model's @RWSCollection decorator
25
+ const ignoredKeys = ((model).constructor as OpModelType<any>)._CUT_KEYS || [];
26
+
23
27
  for (const key in data) {
24
28
  if (data.hasOwnProperty(key)) {
25
- if (!fullDataMode && ((model).constructor as OpModelType<any>)._CUT_KEYS.includes(key)) {
29
+ if (!fullDataMode && ignoredKeys.includes(key)) {
26
30
  continue;
27
31
  }
28
32
 
@@ -62,9 +66,11 @@ export class HydrateUtils {
62
66
  }
63
67
 
64
68
  static async hydrateRelations(model: RWSModel<any>, relManyData: RelManyMetaType<IRWSModel>, relOneData: RelOneMetaType<IRWSModel>, seriesHydrationfields: string[], fullDataMode: boolean, data: { [key: string]: any }, postLoadExecute = false) {
69
+ const ignoredKeys = ((model).constructor as OpModelType<any>)._CUT_KEYS || [];
70
+
65
71
  // Handle many-to-many relations
66
72
  for (const key in relManyData) {
67
- if (!fullDataMode && (model as any).constructor._CUT_KEYS.includes(key)) {
73
+ if (!fullDataMode && ignoredKeys.includes(key)) {
68
74
  continue;
69
75
  }
70
76
 
@@ -72,16 +78,19 @@ export class HydrateUtils {
72
78
 
73
79
  const relationEnabled = !RelationUtils.checkRelDisabled(model, relMeta.key);
74
80
 
75
-
76
-
77
81
  if (relationEnabled) {
78
82
  const pk = ModelUtils.findPrimaryKeyFields(model.constructor as OpModelType<any>) as string;
79
83
 
84
+ // Get child model ignored keys to pass to find operations
85
+ const childIgnoredKeys = (relMeta.inversionModel as OpModelType<any>)._CUT_KEYS || [];
86
+ const childFields = childIgnoredKeys.length > 0 ? await this.getFieldsExcludingIgnored(relMeta.inversionModel, childIgnoredKeys) : undefined;
87
+
80
88
  if (relMeta.singular) {
81
89
  model[relMeta.key] = await relMeta.inversionModel.findOneBy({
82
90
  conditions: {
83
91
  [relMeta.foreignKey]: data[pk]
84
92
  },
93
+ fields: childFields,
85
94
  allowRelations: false,
86
95
  cancelPostLoad: !postLoadExecute
87
96
  });
@@ -90,6 +99,7 @@ export class HydrateUtils {
90
99
  conditions: {
91
100
  [relMeta.foreignKey]: data[pk]
92
101
  },
102
+ fields: childFields,
93
103
  allowRelations: false,
94
104
  cancelPostLoad: !postLoadExecute
95
105
  });
@@ -99,7 +109,7 @@ export class HydrateUtils {
99
109
 
100
110
  // Handle one-to-one relations
101
111
  for (const key in relOneData) {
102
- if (!fullDataMode && ((model as any).constructor as OpModelType<any>)._CUT_KEYS.includes(key)) {
112
+ if (!fullDataMode && ignoredKeys.includes(key)) {
103
113
  continue;
104
114
  }
105
115
 
@@ -122,14 +132,21 @@ export class HydrateUtils {
122
132
  where[pk as string] = data[relMeta.hydrationField]
123
133
  }
124
134
 
125
- model[relMeta.key] = await relMeta.model.findOneBy({ conditions: where }, { allowRelations: false });
135
+ // Get child model ignored keys to pass to find operation
136
+ const childIgnoredKeys = (relMeta.model as OpModelType<any>)._CUT_KEYS || [];
137
+ const childFields = childIgnoredKeys.length > 0 ? await this.getFieldsExcludingIgnored(relMeta.model, childIgnoredKeys) : undefined;
138
+
139
+ model[relMeta.key] = await relMeta.model.findOneBy({
140
+ conditions: where,
141
+ fields: childFields
142
+ }, { allowRelations: false });
126
143
  }
127
144
  else if (relationEnabled && !data[relMeta.hydrationField] && data[relMeta.key]) {
128
145
  const newRelModel: RWSModel<any> = await relMeta.model.create(data[relMeta.key]);
129
146
  model[relMeta.key] = await newRelModel.save();
130
147
  }
131
148
 
132
- const cutKeys = ((model.constructor as OpModelType<any>)._CUT_KEYS as string[]);
149
+ const cutKeys = ignoredKeys;
133
150
 
134
151
  const trackedField = Object.keys((await ModelUtils.getModelAnnotations(model.constructor as OpModelType<any>))).includes(relMeta.hydrationField);
135
152
 
@@ -140,4 +157,39 @@ export class HydrateUtils {
140
157
  // seriesHydrationfields.push(relMeta.hydrationField);
141
158
  }
142
159
  }
160
+
161
+ /**
162
+ * Get all database fields for a model excluding ignored ones
163
+ */
164
+ private static async getFieldsExcludingIgnored(modelClass: OpModelType<any>, ignoredKeys: string[]): Promise<string[] | undefined> {
165
+ if (!ignoredKeys || ignoredKeys.length === 0) {
166
+ return undefined;
167
+ }
168
+
169
+ // Get proper database fields from model annotations
170
+ const tempInstance = new modelClass();
171
+ const annotations = await ModelUtils.getModelAnnotations(modelClass);
172
+
173
+ // Get scalar fields (TrackType decorated fields)
174
+ const scalarFields = ModelUtils.getModelScalarFields(tempInstance);
175
+
176
+ // Get relation fields from annotations
177
+ const relationFields = Object.keys(annotations).filter(key =>
178
+ annotations[key].annotationType === 'Relation' ||
179
+ annotations[key].annotationType === 'InverseRelation'
180
+ );
181
+
182
+ // Combine all database fields
183
+ const allDbFields = [...scalarFields, ...relationFields];
184
+
185
+ // Filter out ignored keys
186
+ const filteredFields = allDbFields.filter(field => !ignoredKeys.includes(field));
187
+
188
+ // Always include id if not ignored
189
+ if (!filteredFields.includes('id') && !ignoredKeys.includes('id')) {
190
+ filteredFields.push('id');
191
+ }
192
+
193
+ return filteredFields.length > 0 ? filteredFields : undefined;
194
+ }
143
195
  }
@@ -70,7 +70,7 @@ export class ModelUtils {
70
70
  }
71
71
 
72
72
  static getModelScalarFields(model: RWSModel<any>): string[] {
73
- return FieldsHelper.getAllClassFields(model)
73
+ return FieldsHelper.getAllClassFields(model.constructor)
74
74
  .filter((item: string) => item.indexOf('TrackType') === 0)
75
75
  .map((item: string) => item.split(':').at(-1));
76
76
  }