pure-orm 4.0.2 → 4.1.4

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 (209) hide show
  1. package/.benchmarks/bench-core-baseline.json +303 -0
  2. package/.eslintrc.json +20 -10
  3. package/README.md +0 -7
  4. package/coverage/clover.xml +1493 -1232
  5. package/coverage/coverage-final.json +103 -103
  6. package/coverage/lcov-report/dist/src/core.js.html +590 -383
  7. package/coverage/lcov-report/dist/src/driver-integrations/index.html +20 -20
  8. package/coverage/lcov-report/dist/src/driver-integrations/pgp.js.html +52 -52
  9. package/coverage/lcov-report/dist/src/index.html +28 -28
  10. package/coverage/lcov-report/dist/src/index.js.html +2 -2
  11. package/coverage/lcov-report/dist/src/orm.js.html +392 -209
  12. package/coverage/lcov-report/dist/test-utils/blog/entities.js.html +1 -1
  13. package/coverage/lcov-report/dist/test-utils/blog/index.html +1 -1
  14. package/coverage/lcov-report/dist/test-utils/blog/models/article.js.html +15 -15
  15. package/coverage/lcov-report/dist/test-utils/blog/models/article_tag.js.html +2 -2
  16. package/coverage/lcov-report/dist/test-utils/blog/models/index.html +1 -1
  17. package/coverage/lcov-report/dist/test-utils/blog/models/person.js.html +13 -13
  18. package/coverage/lcov-report/dist/test-utils/blog/models/tag.js.html +1 -1
  19. package/coverage/lcov-report/dist/test-utils/five/entities.js.html +1 -1
  20. package/coverage/lcov-report/dist/test-utils/five/index.html +1 -1
  21. package/coverage/lcov-report/dist/test-utils/five/models/index.html +1 -1
  22. package/coverage/lcov-report/dist/test-utils/five/models/line-item.js.html +4 -4
  23. package/coverage/lcov-report/dist/test-utils/five/models/order.js.html +3 -3
  24. package/coverage/lcov-report/dist/test-utils/five/models/parcel-event.js.html +6 -6
  25. package/coverage/lcov-report/dist/test-utils/five/models/parcel-line-item.js.html +7 -7
  26. package/coverage/lcov-report/dist/test-utils/five/models/parcel.js.html +2 -2
  27. package/coverage/lcov-report/dist/test-utils/fourteen/entities.js.html +1 -1
  28. package/coverage/lcov-report/dist/test-utils/fourteen/index.html +1 -1
  29. package/coverage/lcov-report/dist/test-utils/fourteen/models/customer.js.html +1 -1
  30. package/coverage/lcov-report/dist/test-utils/fourteen/models/index.html +1 -1
  31. package/coverage/lcov-report/dist/test-utils/fourteen/models/person.js.html +1 -1
  32. package/coverage/lcov-report/dist/test-utils/nine/entities.js.html +1 -1
  33. package/coverage/lcov-report/dist/test-utils/nine/index.html +1 -1
  34. package/coverage/lcov-report/dist/test-utils/nine/models/feature-switch.js.html +6 -6
  35. package/coverage/lcov-report/dist/test-utils/nine/models/index.html +1 -1
  36. package/coverage/lcov-report/dist/test-utils/order/entities.js.html +9 -9
  37. package/coverage/lcov-report/dist/test-utils/order/index.html +1 -1
  38. package/coverage/lcov-report/dist/test-utils/order/models/index.html +14 -14
  39. package/coverage/lcov-report/dist/test-utils/order/models/line-item.js.html +11 -11
  40. package/coverage/lcov-report/dist/test-utils/order/models/order.js.html +41 -41
  41. package/coverage/lcov-report/dist/test-utils/order/models/product-variant.js.html +18 -18
  42. package/coverage/lcov-report/dist/test-utils/order/models/product.js.html +17 -17
  43. package/coverage/lcov-report/dist/test-utils/order/models/utm-source.js.html +12 -12
  44. package/coverage/lcov-report/dist/test-utils/order-more/entities.js.html +1 -1
  45. package/coverage/lcov-report/dist/test-utils/order-more/index.html +1 -1
  46. package/coverage/lcov-report/dist/test-utils/order-more/models/actual-product-variant.js.html +3 -3
  47. package/coverage/lcov-report/dist/test-utils/order-more/models/color.js.html +6 -6
  48. package/coverage/lcov-report/dist/test-utils/order-more/models/customer.js.html +3 -3
  49. package/coverage/lcov-report/dist/test-utils/order-more/models/gender.js.html +4 -4
  50. package/coverage/lcov-report/dist/test-utils/order-more/models/index.html +23 -23
  51. package/coverage/lcov-report/dist/test-utils/order-more/models/inventory-level.js.html +11 -11
  52. package/coverage/lcov-report/dist/test-utils/order-more/models/line-item.js.html +15 -15
  53. package/coverage/lcov-report/dist/test-utils/order-more/models/order.js.html +39 -39
  54. package/coverage/lcov-report/dist/test-utils/order-more/models/parcel-event.js.html +6 -6
  55. package/coverage/lcov-report/dist/test-utils/order-more/models/parcel-line-item.js.html +7 -7
  56. package/coverage/lcov-report/dist/test-utils/order-more/models/parcel.js.html +2 -2
  57. package/coverage/lcov-report/dist/test-utils/order-more/models/physical-address.js.html +12 -12
  58. package/coverage/lcov-report/dist/test-utils/order-more/models/product-variant-image.js.html +7 -7
  59. package/coverage/lcov-report/dist/test-utils/order-more/models/product-variant.js.html +22 -22
  60. package/coverage/lcov-report/dist/test-utils/order-more/models/product.js.html +11 -11
  61. package/coverage/lcov-report/dist/test-utils/order-more/models/refund.js.html +11 -11
  62. package/coverage/lcov-report/dist/test-utils/order-more/models/shipment-actual-product-variant.js.html +9 -9
  63. package/coverage/lcov-report/dist/test-utils/order-more/models/shipment.js.html +4 -4
  64. package/coverage/lcov-report/dist/test-utils/order-more/models/size.js.html +4 -4
  65. package/coverage/lcov-report/dist/test-utils/order-more/models/utm-medium.js.html +15 -15
  66. package/coverage/lcov-report/dist/test-utils/order-more/models/utm-source.js.html +17 -17
  67. package/coverage/lcov-report/dist/test-utils/six/entities.js.html +1 -1
  68. package/coverage/lcov-report/dist/test-utils/six/index.html +1 -1
  69. package/coverage/lcov-report/dist/test-utils/six/models/customer.js.html +3 -3
  70. package/coverage/lcov-report/dist/test-utils/six/models/index.html +21 -21
  71. package/coverage/lcov-report/dist/test-utils/six/models/line-item.js.html +10 -10
  72. package/coverage/lcov-report/dist/test-utils/six/models/order.js.html +13 -13
  73. package/coverage/lcov-report/dist/test-utils/six/models/parcel-line-item.js.html +2 -2
  74. package/coverage/lcov-report/dist/test-utils/six/models/parcel.js.html +2 -2
  75. package/coverage/lcov-report/dist/test-utils/thirteen/entities.js.html +1 -1
  76. package/coverage/lcov-report/dist/test-utils/thirteen/index.html +1 -1
  77. package/coverage/lcov-report/dist/test-utils/thirteen/models/audience.js.html +2 -2
  78. package/coverage/lcov-report/dist/test-utils/thirteen/models/brand.js.html +2 -2
  79. package/coverage/lcov-report/dist/test-utils/thirteen/models/category.js.html +1 -1
  80. package/coverage/lcov-report/dist/test-utils/thirteen/models/index.html +14 -14
  81. package/coverage/lcov-report/dist/test-utils/thirteen/models/member.js.html +2 -2
  82. package/coverage/lcov-report/dist/test-utils/thirteen/models/passion.js.html +2 -2
  83. package/coverage/lcov-report/dist/test-utils/thirteen/models/product.js.html +10 -10
  84. package/coverage/lcov-report/dist/test-utils/thirteen/models/recommendation-audience.js.html +2 -2
  85. package/coverage/lcov-report/dist/test-utils/thirteen/models/recommendation.js.html +2 -2
  86. package/coverage/lcov-report/dist/test-utils/three/index.html +1 -1
  87. package/coverage/lcov-report/dist/test-utils/three/results.js.html +1 -1
  88. package/coverage/lcov-report/dist/test-utils/twelve/entities.js.html +1 -1
  89. package/coverage/lcov-report/dist/test-utils/twelve/index.html +1 -1
  90. package/coverage/lcov-report/dist/test-utils/twelve/models/index.html +1 -1
  91. package/coverage/lcov-report/dist/test-utils/twelve/models/member.js.html +1 -1
  92. package/coverage/lcov-report/dist/test-utils/twelve/models/prompt.js.html +2 -2
  93. package/coverage/lcov-report/dist/test-utils/two/index.html +1 -1
  94. package/coverage/lcov-report/dist/test-utils/two/results.js.html +1 -1
  95. package/coverage/lcov-report/index.html +103 -103
  96. package/coverage/lcov-report/src/core.ts.html +841 -415
  97. package/coverage/lcov-report/src/driver-integrations/index.html +20 -20
  98. package/coverage/lcov-report/src/driver-integrations/pgp.ts.html +63 -63
  99. package/coverage/lcov-report/src/index.html +28 -28
  100. package/coverage/lcov-report/src/index.ts.html +1 -1
  101. package/coverage/lcov-report/src/orm.ts.html +388 -298
  102. package/coverage/lcov-report/test-utils/blog/entities.ts.html +1 -1
  103. package/coverage/lcov-report/test-utils/blog/index.html +1 -1
  104. package/coverage/lcov-report/test-utils/blog/models/article.ts.html +15 -15
  105. package/coverage/lcov-report/test-utils/blog/models/article_tag.ts.html +2 -2
  106. package/coverage/lcov-report/test-utils/blog/models/index.html +1 -1
  107. package/coverage/lcov-report/test-utils/blog/models/person.ts.html +13 -13
  108. package/coverage/lcov-report/test-utils/blog/models/tag.ts.html +1 -1
  109. package/coverage/lcov-report/test-utils/five/entities.ts.html +1 -1
  110. package/coverage/lcov-report/test-utils/five/index.html +1 -1
  111. package/coverage/lcov-report/test-utils/five/models/index.html +1 -1
  112. package/coverage/lcov-report/test-utils/five/models/line-item.ts.html +4 -4
  113. package/coverage/lcov-report/test-utils/five/models/order.ts.html +3 -3
  114. package/coverage/lcov-report/test-utils/five/models/parcel-event.ts.html +6 -6
  115. package/coverage/lcov-report/test-utils/five/models/parcel-line-item.ts.html +7 -7
  116. package/coverage/lcov-report/test-utils/five/models/parcel.ts.html +2 -2
  117. package/coverage/lcov-report/test-utils/fourteen/entities.ts.html +1 -1
  118. package/coverage/lcov-report/test-utils/fourteen/index.html +1 -1
  119. package/coverage/lcov-report/test-utils/fourteen/models/customer.ts.html +1 -1
  120. package/coverage/lcov-report/test-utils/fourteen/models/index.html +1 -1
  121. package/coverage/lcov-report/test-utils/fourteen/models/person.ts.html +1 -1
  122. package/coverage/lcov-report/test-utils/nine/entities.ts.html +1 -1
  123. package/coverage/lcov-report/test-utils/nine/index.html +1 -1
  124. package/coverage/lcov-report/test-utils/nine/models/feature-switch.ts.html +8 -8
  125. package/coverage/lcov-report/test-utils/nine/models/index.html +1 -1
  126. package/coverage/lcov-report/test-utils/order/entities.ts.html +2 -2
  127. package/coverage/lcov-report/test-utils/order/index.html +1 -1
  128. package/coverage/lcov-report/test-utils/order/models/index.html +14 -14
  129. package/coverage/lcov-report/test-utils/order/models/line-item.ts.html +5 -5
  130. package/coverage/lcov-report/test-utils/order/models/order.ts.html +36 -36
  131. package/coverage/lcov-report/test-utils/order/models/product-variant.ts.html +13 -13
  132. package/coverage/lcov-report/test-utils/order/models/product.ts.html +13 -13
  133. package/coverage/lcov-report/test-utils/order/models/utm-source.ts.html +8 -8
  134. package/coverage/lcov-report/test-utils/order-more/entities.ts.html +1 -1
  135. package/coverage/lcov-report/test-utils/order-more/index.html +1 -1
  136. package/coverage/lcov-report/test-utils/order-more/models/actual-product-variant.ts.html +3 -3
  137. package/coverage/lcov-report/test-utils/order-more/models/color.ts.html +6 -6
  138. package/coverage/lcov-report/test-utils/order-more/models/customer.ts.html +3 -3
  139. package/coverage/lcov-report/test-utils/order-more/models/gender.ts.html +4 -4
  140. package/coverage/lcov-report/test-utils/order-more/models/index.html +23 -23
  141. package/coverage/lcov-report/test-utils/order-more/models/inventory-level.ts.html +11 -11
  142. package/coverage/lcov-report/test-utils/order-more/models/line-item.ts.html +15 -15
  143. package/coverage/lcov-report/test-utils/order-more/models/order.ts.html +45 -45
  144. package/coverage/lcov-report/test-utils/order-more/models/parcel-event.ts.html +6 -6
  145. package/coverage/lcov-report/test-utils/order-more/models/parcel-line-item.ts.html +7 -7
  146. package/coverage/lcov-report/test-utils/order-more/models/parcel.ts.html +2 -2
  147. package/coverage/lcov-report/test-utils/order-more/models/physical-address.ts.html +12 -12
  148. package/coverage/lcov-report/test-utils/order-more/models/product-variant-image.ts.html +7 -7
  149. package/coverage/lcov-report/test-utils/order-more/models/product-variant.ts.html +22 -22
  150. package/coverage/lcov-report/test-utils/order-more/models/product.ts.html +11 -11
  151. package/coverage/lcov-report/test-utils/order-more/models/refund.ts.html +11 -11
  152. package/coverage/lcov-report/test-utils/order-more/models/shipment-actual-product-variant.ts.html +9 -9
  153. package/coverage/lcov-report/test-utils/order-more/models/shipment.ts.html +4 -4
  154. package/coverage/lcov-report/test-utils/order-more/models/size.ts.html +4 -4
  155. package/coverage/lcov-report/test-utils/order-more/models/utm-medium.ts.html +15 -15
  156. package/coverage/lcov-report/test-utils/order-more/models/utm-source.ts.html +17 -17
  157. package/coverage/lcov-report/test-utils/six/entities.ts.html +1 -1
  158. package/coverage/lcov-report/test-utils/six/index.html +1 -1
  159. package/coverage/lcov-report/test-utils/six/models/customer.ts.html +3 -3
  160. package/coverage/lcov-report/test-utils/six/models/index.html +21 -21
  161. package/coverage/lcov-report/test-utils/six/models/line-item.ts.html +10 -10
  162. package/coverage/lcov-report/test-utils/six/models/order.ts.html +13 -13
  163. package/coverage/lcov-report/test-utils/six/models/parcel-line-item.ts.html +2 -2
  164. package/coverage/lcov-report/test-utils/six/models/parcel.ts.html +2 -2
  165. package/coverage/lcov-report/test-utils/thirteen/entities.ts.html +1 -1
  166. package/coverage/lcov-report/test-utils/thirteen/index.html +1 -1
  167. package/coverage/lcov-report/test-utils/thirteen/models/audience.ts.html +2 -2
  168. package/coverage/lcov-report/test-utils/thirteen/models/brand.ts.html +2 -2
  169. package/coverage/lcov-report/test-utils/thirteen/models/category.ts.html +1 -1
  170. package/coverage/lcov-report/test-utils/thirteen/models/index.html +14 -14
  171. package/coverage/lcov-report/test-utils/thirteen/models/member.ts.html +2 -2
  172. package/coverage/lcov-report/test-utils/thirteen/models/passion.ts.html +2 -2
  173. package/coverage/lcov-report/test-utils/thirteen/models/product.ts.html +10 -10
  174. package/coverage/lcov-report/test-utils/thirteen/models/recommendation-audience.ts.html +2 -2
  175. package/coverage/lcov-report/test-utils/thirteen/models/recommendation.ts.html +2 -2
  176. package/coverage/lcov-report/test-utils/three/index.html +1 -1
  177. package/coverage/lcov-report/test-utils/three/results.js.html +1 -1
  178. package/coverage/lcov-report/test-utils/twelve/entities.ts.html +1 -1
  179. package/coverage/lcov-report/test-utils/twelve/index.html +1 -1
  180. package/coverage/lcov-report/test-utils/twelve/models/index.html +1 -1
  181. package/coverage/lcov-report/test-utils/twelve/models/member.ts.html +1 -1
  182. package/coverage/lcov-report/test-utils/twelve/models/prompt.ts.html +2 -2
  183. package/coverage/lcov-report/test-utils/two/index.html +1 -1
  184. package/coverage/lcov-report/test-utils/two/results.js.html +1 -1
  185. package/coverage/lcov.info +2136 -1991
  186. package/dist/example/data-access/person.d.ts +1 -1
  187. package/dist/src/core.d.ts +13 -7
  188. package/dist/src/core.js +258 -189
  189. package/dist/src/core.spec.js +413 -0
  190. package/dist/src/driver-integrations/index.d.ts +5 -5
  191. package/dist/src/driver-integrations/pgp.spec.d.ts +1 -0
  192. package/dist/src/driver-integrations/pgp.spec.js +376 -0
  193. package/dist/src/orm.d.ts +9 -9
  194. package/dist/src/orm.js +137 -76
  195. package/dist/src/orm.spec.js +535 -85
  196. package/dist/test-utils/nine/models/feature-switch.d.ts +2 -2
  197. package/dist/test-utils/nine/models/feature-switch.ts +2 -2
  198. package/example/data-access/person.ts +1 -1
  199. package/package.json +9 -6
  200. package/scripts/bench-core.js +636 -0
  201. package/scripts/check-bench-scenarios.js +47 -0
  202. package/src/core.spec.ts +485 -2
  203. package/src/core.ts +369 -227
  204. package/src/driver-integrations/index.ts +5 -5
  205. package/src/driver-integrations/pgp.spec.ts +444 -0
  206. package/src/driver-integrations/pgp.ts +5 -5
  207. package/src/orm.spec.ts +592 -88
  208. package/src/orm.ts +173 -143
  209. package/test-utils/nine/models/feature-switch.ts +2 -2
@@ -1,2 +1,2 @@
1
1
  import { Person } from '../models/person';
2
- export declare const getPerson: (id: number) => Person;
2
+ export declare const getPerson: (id: number) => Promise<Person>;
@@ -4,20 +4,20 @@ export interface IColumnData {
4
4
  references?: IModelClass;
5
5
  primaryKey?: boolean;
6
6
  }
7
- export declare type IColumn = IColumnData | string;
8
- export declare type IColumns = Array<IColumn> | (() => Array<IColumn>);
7
+ export type IColumn = IColumnData | string;
8
+ export type IColumns = Array<IColumn> | (() => Array<IColumn>);
9
9
  export interface IColumnInternalData {
10
10
  column: string;
11
11
  property: string;
12
12
  references?: IModelClass;
13
13
  primaryKey: boolean;
14
14
  }
15
- export declare type IColumnInternal = IColumnInternalData;
16
- export declare type IColumnsInternal = Array<IColumnInternal>;
15
+ export type IColumnInternal = IColumnInternalData;
16
+ export type IColumnsInternal = Array<IColumnInternal>;
17
17
  export interface IModel {
18
18
  [key: string]: any;
19
19
  }
20
- export declare type IModelClass = new (props: any) => IModel;
20
+ export type IModelClass = new (props: any) => IModel;
21
21
  export interface ICollection<T extends IModel> {
22
22
  models: Array<T>;
23
23
  }
@@ -29,7 +29,7 @@ export interface IEntity<T extends IModel> {
29
29
  Model: new (props: any) => T;
30
30
  Collection: new ({ models }: any) => ICollection<T>;
31
31
  }
32
- export declare type IEntities<T extends IModel> = Array<IEntity<T>>;
32
+ export type IEntities<T extends IModel> = Array<IEntity<T>>;
33
33
  export interface IEntityInternal<T extends IModel> {
34
34
  tableName: string;
35
35
  displayName: string;
@@ -44,8 +44,14 @@ export interface IEntityInternal<T extends IModel> {
44
44
  references: object;
45
45
  selectColumnsClause: string;
46
46
  getPkId: (model: IModel) => string;
47
+ columnToPropertyMap: Map<string, string>;
48
+ propertyToColumnMap: Map<string, string>;
49
+ referencesEntries: Array<{
50
+ property: string;
51
+ ModelClass: IModelClass;
52
+ }>;
47
53
  }
48
- export declare type IEntitiesInternal<T extends IModel> = Array<IEntityInternal<T>>;
54
+ export type IEntitiesInternal<T extends IModel> = Array<IEntityInternal<T>>;
49
55
  export interface ICreateCoreOptions {
50
56
  entities: IEntities<IModel>;
51
57
  }
package/dist/src/core.js CHANGED
@@ -28,17 +28,33 @@ const createCore = ({ entities: externalEntities }) => {
28
28
  const pkColumnsData = columns.filter((x) => x.primaryKey);
29
29
  const _primaryKeys = pkColumnsData.map((x) => x.column);
30
30
  const primaryKeys = _primaryKeys.length > 0 ? _primaryKeys : ['id'];
31
- // Returns unique identifier of model (the values of the primary keys)
32
31
  const getPkId = (model) => {
33
- return primaryKeys
34
- .map((key) => model[key])
35
- .join('');
32
+ let id = '';
33
+ for (let i = 0; i < primaryKeys.length; i++) {
34
+ const part = model[primaryKeys[i]];
35
+ if (part !== void 0 && part !== null) {
36
+ id += String(part);
37
+ }
38
+ }
39
+ return id;
36
40
  };
37
- const references = columns
38
- .filter((x) => x.references)
39
- .reduce((accum, item) => Object.assign({}, accum, {
40
- [item.property]: item.references
41
- }), {});
41
+ const references = {};
42
+ const referencesEntries = [];
43
+ for (const col of columns) {
44
+ if (col.references) {
45
+ references[col.property] = col.references;
46
+ referencesEntries.push({
47
+ property: col.property,
48
+ ModelClass: col.references
49
+ });
50
+ }
51
+ }
52
+ const columnToPropertyMap = new Map();
53
+ const propertyToColumnMap = new Map();
54
+ for (let i = 0; i < columnNames.length; i++) {
55
+ columnToPropertyMap.set(columnNames[i], propertyNames[i]);
56
+ propertyToColumnMap.set(propertyNames[i], columnNames[i]);
57
+ }
42
58
  const selectColumnsClause = prefixedColumnNames
43
59
  .map((prefixed, index) => `"${tableName}".${columnNames[index]} as "${prefixed}"`)
44
60
  .join(', ');
@@ -55,7 +71,10 @@ const createCore = ({ entities: externalEntities }) => {
55
71
  primaryKeys,
56
72
  references,
57
73
  selectColumnsClause,
58
- getPkId
74
+ getPkId,
75
+ columnToPropertyMap,
76
+ propertyToColumnMap,
77
+ referencesEntries
59
78
  };
60
79
  });
61
80
  const tableNameToEntityMap = entities.reduce((map, entity) => {
@@ -83,200 +102,250 @@ const createCore = ({ entities: externalEntities }) => {
83
102
  const getEntityByModel = (model) => {
84
103
  return getEntityByModelClass(model.constructor);
85
104
  };
86
- /*
87
- * In:
88
- * [
89
- * [Article {id: 32}, ArticleTag {id: 54}]
90
- * [Article {id: 32}, ArticleTag {id: 55}]
91
- * ]
92
- * Out:
93
- * Article {id: 32, ArticleTags articleTags: [ArticleTag {id: 54}, ArticleTag {id: 55}]
94
- */
95
- const nestClump = (clump) => {
96
- clump = clump.map((x) => Object.values(x));
97
- const root = clump[0][0];
98
- clump = clump.map((row) => row.filter((item, index) => index !== 0));
99
- const built = { [getEntityByModel(root).displayName]: root };
100
- let nodes = [root];
101
- // Wowzer is this both CPU and Memory inefficient
102
- clump.forEach((array) => {
103
- array.forEach((_model) => {
104
- const nodeAlreadySeen = nodes.find((x) => x.constructor.name === _model.constructor.name &&
105
- getEntityByModel(x).getPkId(x) ===
106
- getEntityByModel(_model).getPkId(_model));
107
- const model = nodeAlreadySeen || _model;
108
- const isNodeAlreadySeen = !!nodeAlreadySeen;
109
- const nodePointingToIt = nodes.find((node) => {
110
- const indexes = Object.values(getEntityByModel(node).references)
111
- .map((x, i) => x === model.constructor ? i : null)
112
- .filter((x, i) => x != null);
113
- if (!indexes.length) {
114
- return false;
115
- }
116
- for (const index of indexes) {
117
- const property = Object.keys(getEntityByModel(node).references)[index];
118
- if (node[property] === model.id) {
119
- return true;
120
- }
121
- }
122
- return false;
123
- });
124
- // For first obj type which is has an instance in nodes array,
125
- // get its index in nodes array
126
- const indexOfOldestParent = array.reduce((answer, obj) => {
127
- if (answer != null) {
128
- return answer;
129
- }
130
- const index = nodes.findIndex((n) => n.constructor === obj.constructor);
131
- if (index !== -1) {
132
- return index;
133
- }
134
- return null;
135
- }, null) || 0;
136
- const parentHeirarchy = [
137
- root,
138
- ...nodes.slice(0, indexOfOldestParent + 1).reverse()
139
- ];
140
- const nodeItPointsTo = parentHeirarchy.find((parent) => {
141
- const indexes = Object.values(getEntityByModel(model).references)
142
- .map((x, i) => x === parent.constructor ? i : null)
143
- .filter((x, i) => x != null);
144
- if (!indexes.length) {
145
- return false;
146
- }
147
- for (const index of indexes) {
148
- const property = Object.keys(getEntityByModel(model).references)[index];
149
- if (model[property] === parent.id) {
150
- return true;
151
- }
152
- }
153
- return false;
154
- });
155
- if (isNodeAlreadySeen) {
156
- if (nodeItPointsTo && !nodePointingToIt) {
157
- nodes = [model, ...nodes];
158
- return;
159
- }
160
- // If the nodePointingToIt (eg, parcel_event) is part of an
161
- // existing collection on this node (eg, parcel) which is a
162
- // nodeAlreadySeen, early return so we don't create it (parcel) on
163
- // the nodePointingToIt (parcel_event), since it (parcel) has been
164
- // shown to be the parent (of parcel_events).
165
- if (nodePointingToIt) {
166
- const ec = model[getEntityByModel(nodePointingToIt)
167
- .collectionDisplayName];
168
- if (ec && ec.models.find((m) => m === nodePointingToIt)) {
169
- nodes = [model, ...nodes];
170
- return;
171
- }
172
- }
105
+ const entityReferencePlans = new Map();
106
+ for (let i = 0; i < entities.length; i++) {
107
+ const entity = entities[i];
108
+ const plans = new Array(entity.referencesEntries.length);
109
+ for (let j = 0; j < entity.referencesEntries.length; j++) {
110
+ const ref = entity.referencesEntries[j];
111
+ plans[j] = {
112
+ property: ref.property,
113
+ targetEntity: getEntityByModelClass(ref.ModelClass)
114
+ };
115
+ }
116
+ entityReferencePlans.set(entity, plans);
117
+ }
118
+ const getPkIdFromRow = (row, primaryKeyRowKeys) => {
119
+ let id = '';
120
+ for (let i = 0; i < primaryKeyRowKeys.length; i++) {
121
+ const part = row[primaryKeyRowKeys[i]];
122
+ if (part !== void 0 && part !== null) {
123
+ id += String(part);
124
+ }
125
+ }
126
+ return id;
127
+ };
128
+ const buildEntityRowPlans = (sampleRow) => {
129
+ const plansByTable = new Map();
130
+ const tableOrder = [];
131
+ for (const text in sampleRow) {
132
+ if (!Object.prototype.hasOwnProperty.call(sampleRow, text)) {
133
+ continue;
134
+ }
135
+ const hashIndex = text.indexOf('#');
136
+ if (hashIndex === -1) {
137
+ throw new Error('Column names must be namespaced to table');
138
+ }
139
+ const tableName = text.substring(0, hashIndex);
140
+ const column = text.substring(hashIndex + 1);
141
+ let plan = plansByTable.get(tableName);
142
+ if (!plan) {
143
+ const entity = getEntityByTableName(tableName);
144
+ const primaryKeyRowKeys = entity.primaryKeys.map((pk) => `${tableName}#${pk}`);
145
+ plan = {
146
+ entity,
147
+ columnPlans: [],
148
+ primaryKeyRowKeys
149
+ };
150
+ plansByTable.set(tableName, plan);
151
+ tableOrder.push(tableName);
152
+ }
153
+ let propertyName = plan.entity.columnToPropertyMap.get(column);
154
+ if (!propertyName) {
155
+ if (column.startsWith('meta_')) {
156
+ propertyName = (0, camelcase_1.default)(column);
173
157
  }
174
- if (nodePointingToIt) {
175
- nodePointingToIt[getEntityByModel(model).displayName] = model;
158
+ else {
159
+ throw Error(`No property name for "${column}" in business object "${plan.entity.displayName}". Non-spec'd columns must begin with "meta_".`);
176
160
  }
177
- else if (nodeItPointsTo) {
178
- let collection = nodeItPointsTo[getEntityByModel(model).collectionDisplayName];
179
- if (collection) {
180
- collection.models.push(model);
181
- }
182
- else {
183
- const Collection = getEntityByModel(model).Collection;
184
- nodeItPointsTo[getEntityByModel(model).collectionDisplayName] =
185
- new Collection({
186
- models: [model]
187
- });
188
- }
161
+ }
162
+ plan.columnPlans.push({
163
+ rowKey: text,
164
+ propertyName
165
+ });
166
+ }
167
+ const orderedPlans = new Array(tableOrder.length);
168
+ for (let i = 0; i < tableOrder.length; i++) {
169
+ orderedPlans[i] = plansByTable.get(tableOrder[i]);
170
+ }
171
+ return orderedPlans;
172
+ };
173
+ const materializeModelsFromRow = (row, entityRowPlans, rootScopedModelsByEntity, rowModels, rowModelPkIds, rowCreatedWithPkIndexes) => {
174
+ rowCreatedWithPkIndexes.length = 0;
175
+ for (let i = 0; i < entityRowPlans.length; i++) {
176
+ const plan = entityRowPlans[i];
177
+ const pkId = getPkIdFromRow(row, plan.primaryKeyRowKeys);
178
+ rowModelPkIds[i] = pkId;
179
+ if (pkId) {
180
+ let modelsForEntity = rootScopedModelsByEntity.get(plan.entity);
181
+ if (!modelsForEntity) {
182
+ modelsForEntity = new Map();
183
+ rootScopedModelsByEntity.set(plan.entity, modelsForEntity);
189
184
  }
190
185
  else {
191
- if (!getEntityByModel(model).getPkId(model)) {
192
- // If the join is fruitless; todo: add a test for this path
193
- return;
186
+ const existing = modelsForEntity.get(pkId);
187
+ if (existing) {
188
+ rowModels[i] = existing;
189
+ continue;
194
190
  }
195
- throw Error(`Could not find how this BO fits: ${JSON.stringify(model)} ${getEntityByModel(model).tableName}`);
196
191
  }
197
- nodes = [model, ...nodes];
198
- });
199
- });
200
- return built;
192
+ }
193
+ else if (i !== 0) {
194
+ // No primary key means this is typically an outer-joined null row.
195
+ // Skip model construction for non-root entities since it cannot link.
196
+ rowModels[i] = void 0;
197
+ continue;
198
+ }
199
+ const props = {};
200
+ for (let j = 0; j < plan.columnPlans.length; j++) {
201
+ const columnPlan = plan.columnPlans[j];
202
+ props[columnPlan.propertyName] = row[columnPlan.rowKey];
203
+ }
204
+ const model = new plan.entity.Model(props);
205
+ if (pkId) {
206
+ // modelsForEntity is guaranteed to be initialized above for pk rows.
207
+ rootScopedModelsByEntity.get(plan.entity).set(pkId, model);
208
+ rowCreatedWithPkIndexes.push(i);
209
+ }
210
+ rowModels[i] = model;
211
+ }
212
+ return rowModels[0];
213
+ };
214
+ const getRootScopeKey = (row, rootEntity, rootPrimaryKeys) => {
215
+ let rootScopeKey = '';
216
+ for (let i = 0; i < rootPrimaryKeys.length; i++) {
217
+ if (i > 0) {
218
+ rootScopeKey += '@';
219
+ }
220
+ const value = row[`${rootEntity.tableName}#${rootPrimaryKeys[i]}`];
221
+ rootScopeKey += value === void 0 || value === null ? '' : String(value);
222
+ }
223
+ return rootScopeKey;
224
+ };
225
+ const ensureRootScopeState = (rootScopeKey, rootScopeStateByKey) => {
226
+ let state = rootScopeStateByKey.get(rootScopeKey);
227
+ if (!state) {
228
+ state = {
229
+ modelsByEntity: new Map()
230
+ };
231
+ rootScopeStateByKey.set(rootScopeKey, state);
232
+ }
233
+ return state;
234
+ };
235
+ const ensureCollectionMembership = (rootScopeState) => {
236
+ if (!rootScopeState.collectionMembership) {
237
+ rootScopeState.collectionMembership = new WeakMap();
238
+ }
239
+ return rootScopeState.collectionMembership;
240
+ };
241
+ const linkSourceToTarget = ({ sourceEntity, sourceModel, sourceModelPkId, targetEntity, targetModel, collectionMembership }) => {
242
+ sourceModel[targetEntity.displayName] =
243
+ targetModel;
244
+ let collection = targetModel[sourceEntity.collectionDisplayName];
245
+ if (!collection) {
246
+ const Collection = sourceEntity.Collection;
247
+ collection = new Collection({ models: [] });
248
+ targetModel[sourceEntity.collectionDisplayName] = collection;
249
+ }
250
+ let byCollection = collectionMembership.get(targetModel);
251
+ if (!byCollection) {
252
+ byCollection = new Map();
253
+ collectionMembership.set(targetModel, byCollection);
254
+ }
255
+ let memberIds = byCollection.get(sourceEntity);
256
+ if (!memberIds) {
257
+ memberIds = new Set();
258
+ byCollection.set(sourceEntity, memberIds);
259
+ }
260
+ if (!memberIds.has(sourceModelPkId)) {
261
+ collection.models.push(sourceModel);
262
+ memberIds.add(sourceModelPkId);
263
+ }
201
264
  };
202
265
  /*
203
- * Clump array of flat objects into groups based on id of root
204
- * In:
205
- * [
206
- * [Article {id: 32}, ArticleTag {id: 54}]
207
- * [Article {id: 32}, ArticleTag {id: 55}]
208
- * [Article {id: 33}, ArticleTag {id: 56}]
209
- * ]
210
- * Out:
211
- * [
212
- * [
213
- * [Article {id: 32}, ArticleTag {id: 54}]
214
- * [Article {id: 32}, ArticleTag {id: 55}]
215
- * ]
216
- * [
217
- * [Article {id: 33}, ArticleTag {id: 56}]
218
- * ]
219
- * ]
266
+ * createFromDatabase architecture:
267
+ * 1) Compile row plans once (column -> property mapping per entity/table).
268
+ * 2) Materialize models per row with scoped de-duplication by root scope key.
269
+ * 3) Index models by root scope + entity + entity primary key.
270
+ * 4) Link refs incrementally as new models appear.
271
+ * 5) Return root models in first-seen root scope order.
220
272
  */
221
- const clumpIntoGroups = (processed) => {
222
- const root = processed[0][0];
223
- const rootBo = root.constructor;
224
- const clumps = processed.reduce((accum, item) => {
225
- const id = getEntityByModel(root)
226
- .primaryKeys.map((key) => { var _a; return (_a = item.find((x) => x.constructor === rootBo)) === null || _a === void 0 ? void 0 : _a[key]; })
227
- .join('@');
228
- if (accum.has(id)) {
229
- accum.set(id, [...accum.get(id), item]);
273
+ const createFromDatabase = (rows) => {
274
+ var _a;
275
+ const result = Array.isArray(rows) ? rows : [rows];
276
+ const len = result.length;
277
+ const entityRowPlans = buildEntityRowPlans(result[0]);
278
+ const selectedEntities = new Set();
279
+ for (let i = 0; i < entityRowPlans.length; i++) {
280
+ selectedEntities.add(entityRowPlans[i].entity);
281
+ }
282
+ const applicableRefPlans = new Map();
283
+ for (let i = 0; i < entityRowPlans.length; i++) {
284
+ const entity = entityRowPlans[i].entity;
285
+ const refs = entityReferencePlans.get(entity) || [];
286
+ const filteredRefs = refs.filter((ref) => selectedEntities.has(ref.targetEntity));
287
+ applicableRefPlans.set(entity, filteredRefs);
288
+ }
289
+ const rootEntity = entityRowPlans[0].entity;
290
+ const rootPrimaryKeys = rootEntity.primaryKeys;
291
+ const rootScopeOrder = [];
292
+ const rootModelsByScopeKey = new Map();
293
+ const rootScopeStateByKey = new Map();
294
+ let currentRootScopeKey = void 0;
295
+ let currentRootScopeState = void 0;
296
+ const rowModels = new Array(entityRowPlans.length);
297
+ const rowModelPkIds = new Array(entityRowPlans.length);
298
+ const rowCreatedWithPkIndexes = [];
299
+ // Phase 1: materialize and index model instances by root scope + entity.
300
+ for (let i = 0; i < len; i++) {
301
+ const row = result[i];
302
+ const rootScopeKey = getRootScopeKey(row, rootEntity, rootPrimaryKeys);
303
+ let rootScopeState = currentRootScopeState;
304
+ if (!rootScopeState || rootScopeKey !== currentRootScopeKey) {
305
+ rootScopeState = ensureRootScopeState(rootScopeKey, rootScopeStateByKey);
306
+ currentRootScopeKey = rootScopeKey;
307
+ currentRootScopeState = rootScopeState;
230
308
  }
231
- else {
232
- accum.set(id, [item]);
309
+ const rootModel = materializeModelsFromRow(row, entityRowPlans, rootScopeState.modelsByEntity, rowModels, rowModelPkIds, rowCreatedWithPkIndexes);
310
+ if (!rootModelsByScopeKey.has(rootScopeKey)) {
311
+ rootScopeOrder.push(rootScopeKey);
312
+ rootModelsByScopeKey.set(rootScopeKey, rootModel);
233
313
  }
234
- return accum;
235
- }, new Map());
236
- return [...clumps.values()];
237
- };
238
- const mapToBos = (objectified) => {
239
- return Object.keys(objectified).map((tableName) => {
240
- const entity = getEntityByTableName(tableName);
241
- const propified = Object.keys(objectified[tableName]).reduce((obj, column) => {
242
- let propertyName = entity.propertyNames[entity.columnNames.indexOf(column)];
243
- if (!propertyName) {
244
- if (column.startsWith('meta_')) {
245
- propertyName = (0, camelcase_1.default)(column);
314
+ for (let c = 0; c < rowCreatedWithPkIndexes.length; c++) {
315
+ const j = rowCreatedWithPkIndexes[c];
316
+ const sourceModel = rowModels[j];
317
+ const sourceModelPkId = rowModelPkIds[j];
318
+ const sourceEntity = entityRowPlans[j].entity;
319
+ const refs = applicableRefPlans.get(sourceEntity);
320
+ if (!refs || refs.length === 0) {
321
+ continue;
322
+ }
323
+ for (let r = 0; r < refs.length; r++) {
324
+ const ref = refs[r];
325
+ const refId = sourceModel[ref.property];
326
+ if (refId == null) {
327
+ continue;
246
328
  }
247
- else {
248
- throw Error(`No property name for "${column}" in business object "${entity.displayName}". Non-spec'd columns must begin with "meta_".`);
329
+ const targetPkId = String(refId);
330
+ const target = (_a = rootScopeState.modelsByEntity
331
+ .get(ref.targetEntity)) === null || _a === void 0 ? void 0 : _a.get(targetPkId);
332
+ if (target) {
333
+ linkSourceToTarget({
334
+ sourceEntity,
335
+ sourceModel,
336
+ sourceModelPkId,
337
+ targetEntity: ref.targetEntity,
338
+ targetModel: target,
339
+ collectionMembership: ensureCollectionMembership(rootScopeState)
340
+ });
249
341
  }
250
342
  }
251
- obj[propertyName] = objectified[tableName][column];
252
- return obj;
253
- }, {});
254
- return new entity.Model(propified);
255
- });
256
- };
257
- /*
258
- * Make objects (based on special table#column names) from flat database
259
- * return value.
260
- */
261
- const objectifyDatabaseResult = (result) => {
262
- return Object.keys(result).reduce((obj, text) => {
263
- const tableName = text.split('#')[0];
264
- const column = text.split('#')[1];
265
- if (!tableName || !column) {
266
- throw new Error('Column names must be namespaced to table');
267
343
  }
268
- obj[tableName] = obj[tableName] || {};
269
- obj[tableName][column] = result[text];
270
- return obj;
271
- }, {});
272
- };
273
- const createFromDatabase = (rows) => {
274
- const result = Array.isArray(rows) ? rows : [rows];
275
- const objectified = result.map(objectifyDatabaseResult);
276
- const boified = objectified.map(mapToBos);
277
- const clumps = clumpIntoGroups(boified);
278
- const nested = clumps.map(nestClump);
279
- const models = nested.map((n) => Object.values(n)[0]);
344
+ }
345
+ const models = new Array(rootScopeOrder.length);
346
+ for (let i = 0; i < rootScopeOrder.length; i++) {
347
+ models[i] = rootModelsByScopeKey.get(rootScopeOrder[i]);
348
+ }
280
349
  const Collection = getEntityByModel(models[0]).Collection;
281
350
  return new Collection({ models });
282
351
  };