@entity-access/entity-access 1.0.252 → 1.0.253

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.
Files changed (96) hide show
  1. package/.vscode/launch.json +2 -1
  2. package/dist/common/symbols/symbols.d.ts +1 -0
  3. package/dist/common/symbols/symbols.d.ts.map +1 -1
  4. package/dist/common/symbols/symbols.js +1 -0
  5. package/dist/common/symbols/symbols.js.map +1 -1
  6. package/dist/decorators/ForeignKey.d.ts +8 -7
  7. package/dist/decorators/ForeignKey.d.ts.map +1 -1
  8. package/dist/decorators/ForeignKey.js +43 -8
  9. package/dist/decorators/ForeignKey.js.map +1 -1
  10. package/dist/decorators/IColumn.d.ts +6 -3
  11. package/dist/decorators/IColumn.d.ts.map +1 -1
  12. package/dist/decorators/Relate.d.ts.map +1 -1
  13. package/dist/decorators/Relate.js +8 -6
  14. package/dist/decorators/Relate.js.map +1 -1
  15. package/dist/entity-query/EntityType.d.ts +5 -1
  16. package/dist/entity-query/EntityType.d.ts.map +1 -1
  17. package/dist/entity-query/EntityType.js +57 -25
  18. package/dist/entity-query/EntityType.js.map +1 -1
  19. package/dist/migrations/postgres/PostgresAutomaticMigrations.js +1 -1
  20. package/dist/migrations/postgres/PostgresAutomaticMigrations.js.map +1 -1
  21. package/dist/migrations/sql-server/SqlServerAutomaticMigrations.js +1 -1
  22. package/dist/migrations/sql-server/SqlServerAutomaticMigrations.js.map +1 -1
  23. package/dist/model/EntityModel.js +2 -2
  24. package/dist/model/EntityModel.js.map +1 -1
  25. package/dist/model/EntitySource.d.ts +6 -1
  26. package/dist/model/EntitySource.d.ts.map +1 -1
  27. package/dist/model/EntitySource.js.map +1 -1
  28. package/dist/model/SourceExpression.d.ts +1 -22
  29. package/dist/model/SourceExpression.d.ts.map +1 -1
  30. package/dist/model/SourceExpression.js +116 -98
  31. package/dist/model/SourceExpression.js.map +1 -1
  32. package/dist/model/changes/ChangeEntry.d.ts.map +1 -1
  33. package/dist/model/changes/ChangeEntry.js +62 -25
  34. package/dist/model/changes/ChangeEntry.js.map +1 -1
  35. package/dist/model/changes/ChangeSet.d.ts +2 -1
  36. package/dist/model/changes/ChangeSet.d.ts.map +1 -1
  37. package/dist/model/changes/ChangeSet.js +4 -3
  38. package/dist/model/changes/ChangeSet.js.map +1 -1
  39. package/dist/model/identity/IdentityMap.d.ts +23 -0
  40. package/dist/model/identity/IdentityMap.d.ts.map +1 -0
  41. package/dist/model/identity/IdentityMap.js +113 -0
  42. package/dist/model/identity/IdentityMap.js.map +1 -0
  43. package/dist/model/identity/RelationMapper.d.ts +2 -3
  44. package/dist/model/identity/RelationMapper.d.ts.map +1 -1
  45. package/dist/model/identity/RelationMapper.js +60 -27
  46. package/dist/model/identity/RelationMapper.js.map +1 -1
  47. package/dist/model/identity/SearchIndex.d.ts +17 -0
  48. package/dist/model/identity/SearchIndex.d.ts.map +1 -0
  49. package/dist/model/identity/SearchIndex.js +109 -0
  50. package/dist/model/identity/SearchIndex.js.map +1 -0
  51. package/dist/model/verification/VerificationSession.d.ts +1 -1
  52. package/dist/model/verification/VerificationSession.d.ts.map +1 -1
  53. package/dist/model/verification/VerificationSession.js +18 -16
  54. package/dist/model/verification/VerificationSession.js.map +1 -1
  55. package/dist/query/ast/ExpressionToSql.d.ts.map +1 -1
  56. package/dist/query/ast/ExpressionToSql.js +74 -52
  57. package/dist/query/ast/ExpressionToSql.js.map +1 -1
  58. package/dist/query/expander/QueryExpander.d.ts.map +1 -1
  59. package/dist/query/expander/QueryExpander.js +41 -10
  60. package/dist/query/expander/QueryExpander.js.map +1 -1
  61. package/dist/tests/db-tests/tests/multi-fk-tests.d.ts +3 -0
  62. package/dist/tests/db-tests/tests/multi-fk-tests.d.ts.map +1 -0
  63. package/dist/tests/db-tests/tests/multi-fk-tests.js +38 -0
  64. package/dist/tests/db-tests/tests/multi-fk-tests.js.map +1 -0
  65. package/dist/tests/expressions/left-joins/child-joins.js +7 -7
  66. package/dist/tests/model/ShoppingContext.d.ts +9 -0
  67. package/dist/tests/model/ShoppingContext.d.ts.map +1 -1
  68. package/dist/tests/model/ShoppingContext.js +34 -0
  69. package/dist/tests/model/ShoppingContext.js.map +1 -1
  70. package/dist/tests/security/tests/include-items.d.ts.map +1 -1
  71. package/dist/tests/security/tests/include-items.js +1 -0
  72. package/dist/tests/security/tests/include-items.js.map +1 -1
  73. package/dist/tsconfig.tsbuildinfo +1 -1
  74. package/package.json +1 -1
  75. package/src/common/symbols/symbols.ts +2 -1
  76. package/src/decorators/ForeignKey.ts +66 -28
  77. package/src/decorators/IColumn.ts +4 -3
  78. package/src/decorators/Relate.ts +8 -6
  79. package/src/entity-query/EntityType.ts +60 -26
  80. package/src/migrations/postgres/PostgresAutomaticMigrations.ts +1 -1
  81. package/src/migrations/sql-server/SqlServerAutomaticMigrations.ts +1 -1
  82. package/src/model/EntityModel.ts +2 -2
  83. package/src/model/EntitySource.ts +6 -1
  84. package/src/model/SourceExpression.ts +132 -132
  85. package/src/model/changes/ChangeEntry.ts +68 -25
  86. package/src/model/changes/ChangeSet.ts +4 -3
  87. package/src/model/identity/IdentityMap.ts +126 -0
  88. package/src/model/identity/RelationMapper.ts +71 -27
  89. package/src/model/identity/SearchIndex.ts +120 -0
  90. package/src/model/verification/VerificationSession.ts +19 -16
  91. package/src/query/ast/ExpressionToSql.ts +77 -61
  92. package/src/query/expander/QueryExpander.ts +52 -28
  93. package/src/tests/db-tests/tests/multi-fk-tests.ts +46 -0
  94. package/src/tests/expressions/left-joins/child-joins.ts +7 -7
  95. package/src/tests/model/ShoppingContext.ts +32 -0
  96. package/src/tests/security/tests/include-items.ts +1 -0
@@ -168,14 +168,16 @@ export default class ChangeEntry<T = any> implements IChanges {
168
168
  // this is tricky as we need to build inverse query...
169
169
  const { relatedRelation } = relation;
170
170
  const filter = [];
171
- for (const iterator of this.type.keys) {
172
- filter.push(`x.${relatedRelation.name}.${iterator.name} === p.${iterator.name}`);
171
+ for (const { fkColumn, relatedKeyColumn } of relatedRelation.fkMap) {
172
+ filter.push(`x.${relatedRelation.name}.${fkColumn.name} === p.${relatedKeyColumn.name}`);
173
173
  }
174
174
 
175
175
  await context.model.register(relatedEntity.typeClass)
176
176
  .where(this.entity, `(p) => (x) => ${filter.join(" && ")}` as any)
177
177
  .toArray();
178
178
 
179
+ return;
180
+
179
181
  }
180
182
 
181
183
  if (this.entity[relation.name]) {
@@ -183,9 +185,13 @@ export default class ChangeEntry<T = any> implements IChanges {
183
185
  }
184
186
 
185
187
 
186
- const key = relatedEntity.keys[0];
188
+ // need to setup inverse key check
189
+ // const key = relatedEntity.keys[0];
187
190
  const keys = {} as any;
188
- keys[key.name] = this.entity[relation.fkColumn.name];
191
+ // keys[key.name] = this.entity[relation.fkColumn.name];
192
+ for (const { fkColumn, relatedKeyColumn } of relation.fkMap) {
193
+ keys[relatedKeyColumn.name] = this.entity[fkColumn.name];
194
+ }
189
195
  this.entity[relation.name] = await context.model.register(relatedEntity.typeClass).loadByKeys(keys);
190
196
  }
191
197
 
@@ -281,7 +287,7 @@ export default class ChangeEntry<T = any> implements IChanges {
281
287
  }
282
288
 
283
289
  // if related has key defined.. set it...
284
- const rKey = iterator.relatedEntity.keys[0];
290
+ // const rKey = iterator.relatedEntity.keys[0];
285
291
 
286
292
  // lets set the prototype...
287
293
  const prototype = iterator.relatedTypeClass.prototype;
@@ -290,38 +296,75 @@ export default class ChangeEntry<T = any> implements IChanges {
290
296
  }
291
297
  const relatedChanges = this.changeSet.getEntry(related);
292
298
 
293
- const keyValue = related[rKey.name];
294
- if (keyValue === void 0) {
299
+ for (const { fkColumn, relatedKeyColumn } of iterator.fkMap) {
300
+ const keyValue = related[relatedKeyColumn.name];
301
+ if (keyValue === void 0) {
295
302
 
296
- if(relatedChanges.dependents.has(this)) {
297
- continue;
298
- }
299
- relatedChanges.dependents.add(this);
303
+ if(relatedChanges.dependents.has(this)) {
304
+ continue;
305
+ }
306
+ relatedChanges.dependents.add(this);
300
307
 
301
- this.order++;
308
+ this.order++;
302
309
 
303
- for (const d of this.dependents) {
304
- d.order++;
305
- }
310
+ for (const d of this.dependents) {
311
+ d.order++;
312
+ }
306
313
 
307
- const fk = iterator;
308
- if (!fk.fkColumn.columnName) {
309
- throw new EntityAccessError(`Configuration error, fk not set for ${fk.name}`);
310
- }
311
- relatedChanges.pending.push(() => {
312
- this.entity[fk.fkColumn.name] = related[rKey.name];
313
- });
314
- if (this.status !== "inserted") {
315
- this.modified.set(iterator, { column: iterator.fkColumn, oldValue: void 0, newValue: void 0});
314
+ if (!fkColumn.columnName) {
315
+ throw new EntityAccessError(`Configuration error, fk not set for ${fkColumn.name}`);
316
+ }
317
+ relatedChanges.pending.push(() => {
318
+ this.entity[fkColumn.name] = related[relatedKeyColumn.name];
319
+ });
320
+ if (this.status !== "inserted") {
321
+ this.modified.set(iterator, { column: fkColumn, oldValue: void 0, newValue: void 0});
322
+ }
316
323
  }
317
324
  continue;
318
325
  }
319
326
 
327
+ // const keyValue = related[rKey.name];
328
+ // if (keyValue === void 0) {
329
+
330
+ // if(relatedChanges.dependents.has(this)) {
331
+ // continue;
332
+ // }
333
+ // relatedChanges.dependents.add(this);
334
+
335
+ // this.order++;
336
+
337
+ // for (const d of this.dependents) {
338
+ // d.order++;
339
+ // }
340
+
341
+ // const fk = iterator;
342
+ // if (!fk.fkMap?.length) {
343
+ // throw new EntityAccessError(`Configuration error, fk not set for ${fk.name}`);
344
+ // }
345
+ // relatedChanges.pending.push(() => {
346
+ // for (const { fkColumn, relatedKeyColumn } of fk.fkMap) {
347
+ // this.entity[fkColumn.name] = related[relatedKeyColumn.name];
348
+ // }
349
+ // // this.entity[fk.fkColumn.name] = related[rKey.name];
350
+ // });
351
+ // if (this.status !== "inserted") {
352
+ // for (const { fkColumn } of fk.fkMap) {
353
+ // this.modified.set(iterator, { column: fkColumn, oldValue: void 0, newValue: void 0});
354
+ // }
355
+ // // this.modified.set(iterator, { column: iterator.fkColumn, oldValue: void 0, newValue: void 0});
356
+ // }
357
+ // continue;
358
+ // }
359
+
320
360
  if(!relatedChanges.dependents.has(this)) {
321
361
  relatedChanges.dependents.add(this);
322
362
  this.order += relatedChanges.order;
323
363
  }
324
- this.entity[iterator.fkColumn.name] = related[rKey.name];
364
+ for (const { fkColumn, relatedKeyColumn } of iterator.fkMap) {
365
+ this.entity[fkColumn.name] = related[relatedKeyColumn.name];
366
+ }
367
+ // this.entity[iterator.fkColumn.name] = related[rKey.name];
325
368
  }
326
369
  }
327
370
 
@@ -3,6 +3,7 @@ import EntityAccessError from "../../common/EntityAccessError.js";
3
3
  import EventSet from "../../common/EventSet.js";
4
4
  import SchemaRegistry from "../../decorators/SchemaRegistry.js";
5
5
  import EntityContext from "../EntityContext.js";
6
+ import IdentityMap from "../identity/IdentityMap.js";
6
7
  import IdentityService, { identityMapSymbol } from "../identity/IdentityService.js";
7
8
  import ChangeEntry, { privateUpdateEntry, getContext } from "./ChangeEntry.js";
8
9
 
@@ -26,7 +27,7 @@ export default class ChangeSet {
26
27
  /**
27
28
  * This will provide new entity for same key
28
29
  */
29
- private identityMap: Map<string,any> = new Map();
30
+ private identityMap = new IdentityMap();
30
31
 
31
32
  private nextId = 1;
32
33
 
@@ -70,7 +71,7 @@ export default class ChangeSet {
70
71
  this.entryMap.delete(entry.entity);
71
72
  return;
72
73
  }
73
- this.identityMap.set(jsonKey, entry.entity);
74
+ this.identityMap.set(jsonKey, entry.entity, entry.type);
74
75
  }
75
76
  }
76
77
 
@@ -101,7 +102,7 @@ export default class ChangeSet {
101
102
  return entry.updateValues(original);
102
103
  }
103
104
  } else {
104
- this.identityMap.set(jsonKey, entity);
105
+ this.identityMap.set(jsonKey, entity, type);
105
106
  }
106
107
  }
107
108
  entry = new ChangeEntry({
@@ -0,0 +1,126 @@
1
+ import { entityTypeSymbol } from "../../common/symbols/symbols.js";
2
+ import { IColumn } from "../../decorators/IColumn.js";
3
+ import type EntityType from "../../entity-query/EntityType.js";
4
+
5
+ /**
6
+ * Locally cache uniquely identifiable entities
7
+ */
8
+ export default class IdentityMap {
9
+
10
+ public get indexedColumns() {
11
+ return this.keys.keys();
12
+ }
13
+
14
+ private map = new Map<string, any>();
15
+
16
+ private keys = new Map<IColumn, Map<any, any[]>>();
17
+
18
+ public delete(jsonKey) {
19
+ const item = this.map.get(jsonKey);
20
+ this.map.delete(jsonKey);
21
+ if (item) {
22
+ const type = item[entityTypeSymbol] as EntityType;
23
+ for (const column of type.columns) {
24
+ const values = this.keys.get(column);
25
+ if (!values) {
26
+ continue;
27
+ }
28
+ const value = item[column.name];
29
+ if (value === void 0 || value === null) {
30
+ continue;
31
+ }
32
+ const entries = values.get(value);
33
+ if (!entries) {
34
+ continue;
35
+ }
36
+ const index = entries.findIndex(item);
37
+ entries.splice(index, 1);
38
+ }
39
+ }
40
+ }
41
+
42
+ public get(jsonKeys) {
43
+ return this.map.get(jsonKeys);
44
+ }
45
+
46
+ public set(jsonKey, entity, type: EntityType) {
47
+ entity[entityTypeSymbol] = type;
48
+ this.map.set(jsonKey, entity);
49
+ this.updateSearchIndex(type, entity);
50
+ }
51
+
52
+ public clear() {
53
+ this.map.clear();
54
+ this.keys.clear();
55
+ }
56
+
57
+ public build(key: IColumn) {
58
+ return this.getKeyEntry(key, true);
59
+ }
60
+
61
+ searchByKeys(pairs: { key: IColumn, value}[], create = true) {
62
+ let results: any[];
63
+ for (const { key, value } of pairs) {
64
+ const items = this.getAll(key, value, create);
65
+ if (!items?.length) {
66
+ return;
67
+ }
68
+ if (!results) {
69
+ results = [].concat(items);
70
+ continue;
71
+ }
72
+ const old = results;
73
+ results = [];
74
+ for (const item of items) {
75
+ if (old.includes(item)) {
76
+ results.push(item);
77
+ }
78
+ }
79
+ }
80
+ return results[0];
81
+ }
82
+
83
+ private getAll(key: IColumn, value: any, create = true) {
84
+ const keyEntry = this.getKeyEntry(key, create);
85
+ return keyEntry.get(value);
86
+ }
87
+
88
+ private getKeyEntry(key: IColumn, create = false) {
89
+ let keyEntry = this.keys.get(key);
90
+ if (keyEntry) {
91
+ return keyEntry;
92
+ }
93
+ if (!create) {
94
+ return;
95
+ }
96
+ keyEntry = new Map<any, any[]>();
97
+ this.keys.set(key, keyEntry);
98
+ for (const entry of this.map.values()) {
99
+ this.updateSearchIndex(entry[entityTypeSymbol], entry);
100
+ }
101
+ return keyEntry;
102
+ }
103
+
104
+ private updateSearchIndex(type: EntityType, entity: any) {
105
+ for (const key of this.keys.keys()) {
106
+ if (type.getField(key.name) !== key) {
107
+ continue;
108
+ }
109
+ const keyEntry = this.getKeyEntry(key, true);
110
+ const value = entity[key.name];
111
+ if (value === void 0 || value === null) {
112
+ continue;
113
+ }
114
+ let values = keyEntry.get(value);
115
+ if (!values) {
116
+ values = [];
117
+ keyEntry.set(value, values);
118
+ }
119
+ if (values.includes(entity)) {
120
+ continue;
121
+ }
122
+ values.push(entity);
123
+ }
124
+ }
125
+
126
+ }
@@ -1,26 +1,33 @@
1
+ import EventEmitter from "events";
1
2
  import type ChangeEntry from "../changes/ChangeEntry.js";
2
3
  import type ChangeSet from "../changes/ChangeSet.js";
3
4
  import IdentityService, { identityMapSymbol } from "./IdentityService.js";
5
+ import { IColumn } from "../../decorators/IColumn.js";
6
+
7
+
8
+
4
9
 
5
10
  export default class RelationMapper {
6
11
 
7
- private map: Map<string, ChangeEntry[]> = new Map();
12
+ // private map: Map<string, ChangeEntry[]> = new Map();
13
+
14
+ private events: EventEmitter = new EventEmitter();
8
15
 
9
16
  constructor(
10
17
  private changeSet: ChangeSet,
11
- private identityMap: Map<string, ChangeEntry> = changeSet[identityMapSymbol]
18
+ private identityMap = changeSet[identityMapSymbol]
12
19
  ) {
13
20
 
14
21
  }
15
22
 
16
- push(id: string, waiter: ChangeEntry) {
17
- let queue = this.map.get(id);
18
- if (!queue) {
19
- queue = [];
20
- this.map.set(id, queue);
21
- }
22
- queue.push(waiter);
23
- }
23
+ // push(id: string, waiter: ChangeEntry) {
24
+ // let queue = this.map.get(id);
25
+ // if (!queue) {
26
+ // queue = [];
27
+ // this.map.set(id, queue);
28
+ // }
29
+ // queue.push(waiter);
30
+ // }
24
31
 
25
32
  fix(entry: ChangeEntry, nest = true) {
26
33
 
@@ -30,23 +37,48 @@ export default class RelationMapper {
30
37
  if (iterator.isInverseRelation) {
31
38
  continue;
32
39
  }
33
- const fkColumn = iterator.fkColumn.name;
34
- const fkValue = entity[fkColumn];
35
- if (fkValue === void 0) {
36
- continue;
37
- }
40
+ // const fkColumn = iterator.fkColumn.name;
41
+ // const fkValue = entity[fkColumn];
42
+ // if (fkValue === void 0) {
43
+ // continue;
44
+ // }
45
+
38
46
  // get from identity...
39
- const id = IdentityService.buildIdentity(iterator.relatedEntity, fkValue);
40
- const parent = this.identityMap.get(id);
47
+ // const id = IdentityService.buildIdentity(iterator.relatedEntity, fkValue);
48
+ // const parent = this.identityMap.get(id);
49
+ // if (!parent) {
50
+ // let waiters = this.map.get(id);
51
+ // if (!waiters) {
52
+ // waiters = [];
53
+ // this.map.set(id, waiters);
54
+ // }
55
+ // waiters.push(entry);
56
+ // continue;
57
+ // }
58
+
59
+ const pairs = [] as { key: IColumn, value: any}[];
60
+
61
+ for (const { fkColumn, relatedKeyColumn } of iterator.fkMap) {
62
+ this.identityMap.build(relatedKeyColumn);
63
+ const fkValue = entity[fkColumn.name];
64
+ if (fkValue === void 0) {
65
+ continue;
66
+ }
67
+ pairs.push({ key: relatedKeyColumn, value: fkValue});
68
+ }
69
+
70
+ const parent = this.identityMap.searchByKeys(pairs, true);
41
71
  if (!parent) {
42
- let waiters = this.map.get(id);
43
- if (!waiters) {
44
- waiters = [];
45
- this.map.set(id, waiters);
72
+ if (nest) {
73
+ for (const { key, value } of pairs) {
74
+ this.events.once(`${key.entityType.name}-${key.name}-${value}`, (k) => {
75
+ this.fix(entry, false);
76
+ });
77
+ }
46
78
  }
47
- waiters.push(entry);
48
79
  continue;
49
80
  }
81
+
50
82
  entity[iterator.name] = parent;
51
83
 
52
84
  if (iterator.relatedRelation.isCollection) {
@@ -64,12 +96,24 @@ export default class RelationMapper {
64
96
  }
65
97
 
66
98
  // see if anyone is waiting for us or not...
67
- const identity = IdentityService.getIdentity(entry.type, entry.entity);
68
- const pending = this.map.get(identity);
69
- if (pending && pending.length) {
70
- for (const iterator of pending) {
71
- this.fix(iterator, false);
99
+ // const identity = IdentityService.getIdentity(entry.type, entry.entity);
100
+ // const pending = this.map.get(identity);
101
+ // if (pending && pending.length) {
102
+ // for (const iterator of pending) {
103
+ // this.fix(iterator, false);
104
+ // }
105
+ // }
106
+
107
+ for (const iterator of this.identityMap.indexedColumns) {
108
+ if (iterator.entityType !== entry.type) {
109
+ continue;
110
+ }
111
+ const value = entry.entity[iterator.name];
112
+ if (value === void 0 || value === null) {
113
+ continue;
72
114
  }
115
+ const key = `${iterator.entityType.name}-${iterator.name}-${value}`;
116
+ this.events.emit(key, key);
73
117
  }
74
118
  }
75
119
  }
@@ -0,0 +1,120 @@
1
+ import type { IColumn } from "../../decorators/IColumn.js";
2
+ import type ChangeEntry from "../changes/ChangeEntry.js";
3
+
4
+ export default class SearchIndex {
5
+
6
+ private keys = new Map<string, Map<any, any[]>>();
7
+
8
+ constructor(private entries: ChangeEntry[]) {
9
+
10
+ }
11
+
12
+ getByKeys(pairs: { key, value}[], create = true) {
13
+ let results: any[];
14
+ for (const { key, value } of pairs) {
15
+ const items = this.getAll(key, value, create);
16
+ if (!items?.length) {
17
+ return;
18
+ }
19
+ if (!results) {
20
+ results = [].concat(items);
21
+ continue;
22
+ }
23
+ const old = results;
24
+ results = [];
25
+ for (const item of items) {
26
+ if (old.includes(item)) {
27
+ results.push(item);
28
+ }
29
+ }
30
+ }
31
+ return results;
32
+ }
33
+
34
+ getAll(key: string, value: any, create = true) {
35
+ const keyEntry = this.getKeyEntry(key, create);
36
+ return keyEntry.get(value);
37
+ }
38
+
39
+ delete(entry) {
40
+ for (const [key,values] of this.keys) {
41
+ const value = entry[key];
42
+ if (value === void 0 || value === null) {
43
+ continue;
44
+ }
45
+ const entries = values.get(value);
46
+ if (!entries) {
47
+ continue;
48
+ }
49
+ const index = entries.findIndex(entry);
50
+ entries.splice(index, 1);
51
+ }
52
+ }
53
+
54
+ get(keys: IColumn[], entry) {
55
+ let results: any[];
56
+ for (const { name } of keys) {
57
+ const items = this.getAll(name, entry[name]);
58
+ if (!items?.length) {
59
+ return;
60
+ }
61
+ if (!results) {
62
+ results = [].concat(items);
63
+ continue;
64
+ }
65
+ const old = results;
66
+ results = [];
67
+ for (const item of items) {
68
+ if (old.includes(item)) {
69
+ results.push(item);
70
+ }
71
+ }
72
+ }
73
+ return results[0];
74
+ }
75
+
76
+ update(keys: IColumn[], entry) {
77
+ for (const key of keys) {
78
+ const keyEntry = this.getKeyEntry(key.name, true);
79
+ const value = entry[key.name];
80
+ if (value === void 0 || value === null) {
81
+ continue;
82
+ }
83
+ let values = keyEntry.get(value);
84
+ if (!values) {
85
+ values = [];
86
+ keyEntry.set(value, values);
87
+ }
88
+ if (values.includes(entry)) {
89
+ continue;
90
+ }
91
+ values.push(entry);
92
+ }
93
+ }
94
+
95
+ private getKeyEntry(key: string, create = false) {
96
+ let keyEntry = this.keys.get(key);
97
+ if (keyEntry) {
98
+ return keyEntry;
99
+ }
100
+ if (!create) {
101
+ return;
102
+ }
103
+ keyEntry = new Map<any, any[]>();
104
+ this.keys.set(key, keyEntry);
105
+ for (const entry of this.entries) {
106
+ const value = entry[key];
107
+ if (value === void 0 || value === null) {
108
+ continue;
109
+ }
110
+ let values = keyEntry.get(value);
111
+ if (!values) {
112
+ values = [];
113
+ keyEntry.set(value, values);
114
+ }
115
+ values.push(value);
116
+ }
117
+ return keyEntry;
118
+ }
119
+
120
+ }
@@ -63,30 +63,33 @@ export default class VerificationSession {
63
63
  continue;
64
64
  }
65
65
 
66
- const fk = relation.fkColumn;
66
+ const fk = relation.fkMap;
67
67
  if (!fk) {
68
68
  continue;
69
69
  }
70
70
 
71
- const fkValue = entity[fk.name];
72
- if (fkValue === void 0) {
73
- // not set... ignore..
74
- continue;
75
- }
76
- if (isKeyEmpty(fkValue, relation.fkColumn)) {
77
- continue;
78
- }
71
+ for (const { fkColumn , relatedKeyColumn } of fk) {
79
72
 
80
- // only if it is modified...
81
- if (change.status !== "inserted") {
82
- if (!change.isModified(fk.name)) {
73
+ const fkValue = entity[fkColumn.name];
74
+ if (fkValue === void 0) {
75
+ // not set... ignore..
83
76
  continue;
84
77
  }
78
+ if (isKeyEmpty(fkValue, fkColumn)) {
79
+ continue;
80
+ }
81
+
82
+ // only if it is modified...
83
+ if (change.status !== "inserted") {
84
+ if (!change.isModified(fkColumn.name)) {
85
+ continue;
86
+ }
87
+ }
88
+ this.queueEntityForeignKey(change, relation, fkColumn, relatedKeyColumn, fkValue);
85
89
  }
86
- this.queueEntityForeignKey(change, relation, fkValue);
87
90
  }
88
91
  }
89
- queueEntityForeignKey(change: ChangeEntry, relation: IEntityRelation, value) {
92
+ queueEntityForeignKey(change: ChangeEntry, relation: IEntityRelation, fkColumn, relatedKeyColumn, value) {
90
93
  const relatedModel = relation.relatedEntity;
91
94
  const type = relation.relatedEntity.typeClass;
92
95
  const events = this.context.eventsFor(change.type.typeClass);
@@ -97,7 +100,7 @@ export default class VerificationSession {
97
100
  events: relatedEvents,
98
101
  type: relatedModel,
99
102
  name: relation.name,
100
- fkName: relation.fkColumn.name,
103
+ fkName: fkColumn.name,
101
104
  entity: change.entity
102
105
  });
103
106
  let query = events.onForeignKeyFilter(fk);
@@ -110,7 +113,7 @@ export default class VerificationSession {
110
113
 
111
114
  const eq = query as EntityQuery;
112
115
  const compare = Expression.equal(
113
- Expression.member(eq.selectStatement.sourceParameter, relatedModel.keys[0].columnName),
116
+ Expression.member(eq.selectStatement.sourceParameter, relatedKeyColumn.columnName),
114
117
  Expression.constant(value)
115
118
  );
116
119
  const typeName = TypeInfo.nameOfType(type);