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.
- package/.benchmarks/bench-core-baseline.json +303 -0
- package/.eslintrc.json +20 -10
- package/README.md +0 -7
- package/coverage/clover.xml +1493 -1232
- package/coverage/coverage-final.json +103 -103
- package/coverage/lcov-report/dist/src/core.js.html +590 -383
- package/coverage/lcov-report/dist/src/driver-integrations/index.html +20 -20
- package/coverage/lcov-report/dist/src/driver-integrations/pgp.js.html +52 -52
- package/coverage/lcov-report/dist/src/index.html +28 -28
- package/coverage/lcov-report/dist/src/index.js.html +2 -2
- package/coverage/lcov-report/dist/src/orm.js.html +392 -209
- package/coverage/lcov-report/dist/test-utils/blog/entities.js.html +1 -1
- package/coverage/lcov-report/dist/test-utils/blog/index.html +1 -1
- package/coverage/lcov-report/dist/test-utils/blog/models/article.js.html +15 -15
- package/coverage/lcov-report/dist/test-utils/blog/models/article_tag.js.html +2 -2
- package/coverage/lcov-report/dist/test-utils/blog/models/index.html +1 -1
- package/coverage/lcov-report/dist/test-utils/blog/models/person.js.html +13 -13
- package/coverage/lcov-report/dist/test-utils/blog/models/tag.js.html +1 -1
- package/coverage/lcov-report/dist/test-utils/five/entities.js.html +1 -1
- package/coverage/lcov-report/dist/test-utils/five/index.html +1 -1
- package/coverage/lcov-report/dist/test-utils/five/models/index.html +1 -1
- package/coverage/lcov-report/dist/test-utils/five/models/line-item.js.html +4 -4
- package/coverage/lcov-report/dist/test-utils/five/models/order.js.html +3 -3
- package/coverage/lcov-report/dist/test-utils/five/models/parcel-event.js.html +6 -6
- package/coverage/lcov-report/dist/test-utils/five/models/parcel-line-item.js.html +7 -7
- package/coverage/lcov-report/dist/test-utils/five/models/parcel.js.html +2 -2
- package/coverage/lcov-report/dist/test-utils/fourteen/entities.js.html +1 -1
- package/coverage/lcov-report/dist/test-utils/fourteen/index.html +1 -1
- package/coverage/lcov-report/dist/test-utils/fourteen/models/customer.js.html +1 -1
- package/coverage/lcov-report/dist/test-utils/fourteen/models/index.html +1 -1
- package/coverage/lcov-report/dist/test-utils/fourteen/models/person.js.html +1 -1
- package/coverage/lcov-report/dist/test-utils/nine/entities.js.html +1 -1
- package/coverage/lcov-report/dist/test-utils/nine/index.html +1 -1
- package/coverage/lcov-report/dist/test-utils/nine/models/feature-switch.js.html +6 -6
- package/coverage/lcov-report/dist/test-utils/nine/models/index.html +1 -1
- package/coverage/lcov-report/dist/test-utils/order/entities.js.html +9 -9
- package/coverage/lcov-report/dist/test-utils/order/index.html +1 -1
- package/coverage/lcov-report/dist/test-utils/order/models/index.html +14 -14
- package/coverage/lcov-report/dist/test-utils/order/models/line-item.js.html +11 -11
- package/coverage/lcov-report/dist/test-utils/order/models/order.js.html +41 -41
- package/coverage/lcov-report/dist/test-utils/order/models/product-variant.js.html +18 -18
- package/coverage/lcov-report/dist/test-utils/order/models/product.js.html +17 -17
- package/coverage/lcov-report/dist/test-utils/order/models/utm-source.js.html +12 -12
- package/coverage/lcov-report/dist/test-utils/order-more/entities.js.html +1 -1
- package/coverage/lcov-report/dist/test-utils/order-more/index.html +1 -1
- package/coverage/lcov-report/dist/test-utils/order-more/models/actual-product-variant.js.html +3 -3
- package/coverage/lcov-report/dist/test-utils/order-more/models/color.js.html +6 -6
- package/coverage/lcov-report/dist/test-utils/order-more/models/customer.js.html +3 -3
- package/coverage/lcov-report/dist/test-utils/order-more/models/gender.js.html +4 -4
- package/coverage/lcov-report/dist/test-utils/order-more/models/index.html +23 -23
- package/coverage/lcov-report/dist/test-utils/order-more/models/inventory-level.js.html +11 -11
- package/coverage/lcov-report/dist/test-utils/order-more/models/line-item.js.html +15 -15
- package/coverage/lcov-report/dist/test-utils/order-more/models/order.js.html +39 -39
- package/coverage/lcov-report/dist/test-utils/order-more/models/parcel-event.js.html +6 -6
- package/coverage/lcov-report/dist/test-utils/order-more/models/parcel-line-item.js.html +7 -7
- package/coverage/lcov-report/dist/test-utils/order-more/models/parcel.js.html +2 -2
- package/coverage/lcov-report/dist/test-utils/order-more/models/physical-address.js.html +12 -12
- package/coverage/lcov-report/dist/test-utils/order-more/models/product-variant-image.js.html +7 -7
- package/coverage/lcov-report/dist/test-utils/order-more/models/product-variant.js.html +22 -22
- package/coverage/lcov-report/dist/test-utils/order-more/models/product.js.html +11 -11
- package/coverage/lcov-report/dist/test-utils/order-more/models/refund.js.html +11 -11
- package/coverage/lcov-report/dist/test-utils/order-more/models/shipment-actual-product-variant.js.html +9 -9
- package/coverage/lcov-report/dist/test-utils/order-more/models/shipment.js.html +4 -4
- package/coverage/lcov-report/dist/test-utils/order-more/models/size.js.html +4 -4
- package/coverage/lcov-report/dist/test-utils/order-more/models/utm-medium.js.html +15 -15
- package/coverage/lcov-report/dist/test-utils/order-more/models/utm-source.js.html +17 -17
- package/coverage/lcov-report/dist/test-utils/six/entities.js.html +1 -1
- package/coverage/lcov-report/dist/test-utils/six/index.html +1 -1
- package/coverage/lcov-report/dist/test-utils/six/models/customer.js.html +3 -3
- package/coverage/lcov-report/dist/test-utils/six/models/index.html +21 -21
- package/coverage/lcov-report/dist/test-utils/six/models/line-item.js.html +10 -10
- package/coverage/lcov-report/dist/test-utils/six/models/order.js.html +13 -13
- package/coverage/lcov-report/dist/test-utils/six/models/parcel-line-item.js.html +2 -2
- package/coverage/lcov-report/dist/test-utils/six/models/parcel.js.html +2 -2
- package/coverage/lcov-report/dist/test-utils/thirteen/entities.js.html +1 -1
- package/coverage/lcov-report/dist/test-utils/thirteen/index.html +1 -1
- package/coverage/lcov-report/dist/test-utils/thirteen/models/audience.js.html +2 -2
- package/coverage/lcov-report/dist/test-utils/thirteen/models/brand.js.html +2 -2
- package/coverage/lcov-report/dist/test-utils/thirteen/models/category.js.html +1 -1
- package/coverage/lcov-report/dist/test-utils/thirteen/models/index.html +14 -14
- package/coverage/lcov-report/dist/test-utils/thirteen/models/member.js.html +2 -2
- package/coverage/lcov-report/dist/test-utils/thirteen/models/passion.js.html +2 -2
- package/coverage/lcov-report/dist/test-utils/thirteen/models/product.js.html +10 -10
- package/coverage/lcov-report/dist/test-utils/thirteen/models/recommendation-audience.js.html +2 -2
- package/coverage/lcov-report/dist/test-utils/thirteen/models/recommendation.js.html +2 -2
- package/coverage/lcov-report/dist/test-utils/three/index.html +1 -1
- package/coverage/lcov-report/dist/test-utils/three/results.js.html +1 -1
- package/coverage/lcov-report/dist/test-utils/twelve/entities.js.html +1 -1
- package/coverage/lcov-report/dist/test-utils/twelve/index.html +1 -1
- package/coverage/lcov-report/dist/test-utils/twelve/models/index.html +1 -1
- package/coverage/lcov-report/dist/test-utils/twelve/models/member.js.html +1 -1
- package/coverage/lcov-report/dist/test-utils/twelve/models/prompt.js.html +2 -2
- package/coverage/lcov-report/dist/test-utils/two/index.html +1 -1
- package/coverage/lcov-report/dist/test-utils/two/results.js.html +1 -1
- package/coverage/lcov-report/index.html +103 -103
- package/coverage/lcov-report/src/core.ts.html +841 -415
- package/coverage/lcov-report/src/driver-integrations/index.html +20 -20
- package/coverage/lcov-report/src/driver-integrations/pgp.ts.html +63 -63
- package/coverage/lcov-report/src/index.html +28 -28
- package/coverage/lcov-report/src/index.ts.html +1 -1
- package/coverage/lcov-report/src/orm.ts.html +388 -298
- package/coverage/lcov-report/test-utils/blog/entities.ts.html +1 -1
- package/coverage/lcov-report/test-utils/blog/index.html +1 -1
- package/coverage/lcov-report/test-utils/blog/models/article.ts.html +15 -15
- package/coverage/lcov-report/test-utils/blog/models/article_tag.ts.html +2 -2
- package/coverage/lcov-report/test-utils/blog/models/index.html +1 -1
- package/coverage/lcov-report/test-utils/blog/models/person.ts.html +13 -13
- package/coverage/lcov-report/test-utils/blog/models/tag.ts.html +1 -1
- package/coverage/lcov-report/test-utils/five/entities.ts.html +1 -1
- package/coverage/lcov-report/test-utils/five/index.html +1 -1
- package/coverage/lcov-report/test-utils/five/models/index.html +1 -1
- package/coverage/lcov-report/test-utils/five/models/line-item.ts.html +4 -4
- package/coverage/lcov-report/test-utils/five/models/order.ts.html +3 -3
- package/coverage/lcov-report/test-utils/five/models/parcel-event.ts.html +6 -6
- package/coverage/lcov-report/test-utils/five/models/parcel-line-item.ts.html +7 -7
- package/coverage/lcov-report/test-utils/five/models/parcel.ts.html +2 -2
- package/coverage/lcov-report/test-utils/fourteen/entities.ts.html +1 -1
- package/coverage/lcov-report/test-utils/fourteen/index.html +1 -1
- package/coverage/lcov-report/test-utils/fourteen/models/customer.ts.html +1 -1
- package/coverage/lcov-report/test-utils/fourteen/models/index.html +1 -1
- package/coverage/lcov-report/test-utils/fourteen/models/person.ts.html +1 -1
- package/coverage/lcov-report/test-utils/nine/entities.ts.html +1 -1
- package/coverage/lcov-report/test-utils/nine/index.html +1 -1
- package/coverage/lcov-report/test-utils/nine/models/feature-switch.ts.html +8 -8
- package/coverage/lcov-report/test-utils/nine/models/index.html +1 -1
- package/coverage/lcov-report/test-utils/order/entities.ts.html +2 -2
- package/coverage/lcov-report/test-utils/order/index.html +1 -1
- package/coverage/lcov-report/test-utils/order/models/index.html +14 -14
- package/coverage/lcov-report/test-utils/order/models/line-item.ts.html +5 -5
- package/coverage/lcov-report/test-utils/order/models/order.ts.html +36 -36
- package/coverage/lcov-report/test-utils/order/models/product-variant.ts.html +13 -13
- package/coverage/lcov-report/test-utils/order/models/product.ts.html +13 -13
- package/coverage/lcov-report/test-utils/order/models/utm-source.ts.html +8 -8
- package/coverage/lcov-report/test-utils/order-more/entities.ts.html +1 -1
- package/coverage/lcov-report/test-utils/order-more/index.html +1 -1
- package/coverage/lcov-report/test-utils/order-more/models/actual-product-variant.ts.html +3 -3
- package/coverage/lcov-report/test-utils/order-more/models/color.ts.html +6 -6
- package/coverage/lcov-report/test-utils/order-more/models/customer.ts.html +3 -3
- package/coverage/lcov-report/test-utils/order-more/models/gender.ts.html +4 -4
- package/coverage/lcov-report/test-utils/order-more/models/index.html +23 -23
- package/coverage/lcov-report/test-utils/order-more/models/inventory-level.ts.html +11 -11
- package/coverage/lcov-report/test-utils/order-more/models/line-item.ts.html +15 -15
- package/coverage/lcov-report/test-utils/order-more/models/order.ts.html +45 -45
- package/coverage/lcov-report/test-utils/order-more/models/parcel-event.ts.html +6 -6
- package/coverage/lcov-report/test-utils/order-more/models/parcel-line-item.ts.html +7 -7
- package/coverage/lcov-report/test-utils/order-more/models/parcel.ts.html +2 -2
- package/coverage/lcov-report/test-utils/order-more/models/physical-address.ts.html +12 -12
- package/coverage/lcov-report/test-utils/order-more/models/product-variant-image.ts.html +7 -7
- package/coverage/lcov-report/test-utils/order-more/models/product-variant.ts.html +22 -22
- package/coverage/lcov-report/test-utils/order-more/models/product.ts.html +11 -11
- package/coverage/lcov-report/test-utils/order-more/models/refund.ts.html +11 -11
- package/coverage/lcov-report/test-utils/order-more/models/shipment-actual-product-variant.ts.html +9 -9
- package/coverage/lcov-report/test-utils/order-more/models/shipment.ts.html +4 -4
- package/coverage/lcov-report/test-utils/order-more/models/size.ts.html +4 -4
- package/coverage/lcov-report/test-utils/order-more/models/utm-medium.ts.html +15 -15
- package/coverage/lcov-report/test-utils/order-more/models/utm-source.ts.html +17 -17
- package/coverage/lcov-report/test-utils/six/entities.ts.html +1 -1
- package/coverage/lcov-report/test-utils/six/index.html +1 -1
- package/coverage/lcov-report/test-utils/six/models/customer.ts.html +3 -3
- package/coverage/lcov-report/test-utils/six/models/index.html +21 -21
- package/coverage/lcov-report/test-utils/six/models/line-item.ts.html +10 -10
- package/coverage/lcov-report/test-utils/six/models/order.ts.html +13 -13
- package/coverage/lcov-report/test-utils/six/models/parcel-line-item.ts.html +2 -2
- package/coverage/lcov-report/test-utils/six/models/parcel.ts.html +2 -2
- package/coverage/lcov-report/test-utils/thirteen/entities.ts.html +1 -1
- package/coverage/lcov-report/test-utils/thirteen/index.html +1 -1
- package/coverage/lcov-report/test-utils/thirteen/models/audience.ts.html +2 -2
- package/coverage/lcov-report/test-utils/thirteen/models/brand.ts.html +2 -2
- package/coverage/lcov-report/test-utils/thirteen/models/category.ts.html +1 -1
- package/coverage/lcov-report/test-utils/thirteen/models/index.html +14 -14
- package/coverage/lcov-report/test-utils/thirteen/models/member.ts.html +2 -2
- package/coverage/lcov-report/test-utils/thirteen/models/passion.ts.html +2 -2
- package/coverage/lcov-report/test-utils/thirteen/models/product.ts.html +10 -10
- package/coverage/lcov-report/test-utils/thirteen/models/recommendation-audience.ts.html +2 -2
- package/coverage/lcov-report/test-utils/thirteen/models/recommendation.ts.html +2 -2
- package/coverage/lcov-report/test-utils/three/index.html +1 -1
- package/coverage/lcov-report/test-utils/three/results.js.html +1 -1
- package/coverage/lcov-report/test-utils/twelve/entities.ts.html +1 -1
- package/coverage/lcov-report/test-utils/twelve/index.html +1 -1
- package/coverage/lcov-report/test-utils/twelve/models/index.html +1 -1
- package/coverage/lcov-report/test-utils/twelve/models/member.ts.html +1 -1
- package/coverage/lcov-report/test-utils/twelve/models/prompt.ts.html +2 -2
- package/coverage/lcov-report/test-utils/two/index.html +1 -1
- package/coverage/lcov-report/test-utils/two/results.js.html +1 -1
- package/coverage/lcov.info +2136 -1991
- package/dist/example/data-access/person.d.ts +1 -1
- package/dist/src/core.d.ts +13 -7
- package/dist/src/core.js +258 -189
- package/dist/src/core.spec.js +413 -0
- package/dist/src/driver-integrations/index.d.ts +5 -5
- package/dist/src/driver-integrations/pgp.spec.d.ts +1 -0
- package/dist/src/driver-integrations/pgp.spec.js +376 -0
- package/dist/src/orm.d.ts +9 -9
- package/dist/src/orm.js +137 -76
- package/dist/src/orm.spec.js +535 -85
- package/dist/test-utils/nine/models/feature-switch.d.ts +2 -2
- package/dist/test-utils/nine/models/feature-switch.ts +2 -2
- package/example/data-access/person.ts +1 -1
- package/package.json +9 -6
- package/scripts/bench-core.js +636 -0
- package/scripts/check-bench-scenarios.js +47 -0
- package/src/core.spec.ts +485 -2
- package/src/core.ts +369 -227
- package/src/driver-integrations/index.ts +5 -5
- package/src/driver-integrations/pgp.spec.ts +444 -0
- package/src/driver-integrations/pgp.ts +5 -5
- package/src/orm.spec.ts +592 -88
- package/src/orm.ts +173 -143
- package/test-utils/nine/models/feature-switch.ts +2 -2
package/src/core.ts
CHANGED
|
@@ -51,6 +51,9 @@ export interface IEntityInternal<T extends IModel> {
|
|
|
51
51
|
references: object;
|
|
52
52
|
selectColumnsClause: string;
|
|
53
53
|
getPkId: (model: IModel) => string;
|
|
54
|
+
columnToPropertyMap: Map<string, string>;
|
|
55
|
+
propertyToColumnMap: Map<string, string>;
|
|
56
|
+
referencesEntries: Array<{ property: string; ModelClass: IModelClass }>;
|
|
54
57
|
}
|
|
55
58
|
export type IEntitiesInternal<T extends IModel> = Array<IEntityInternal<T>>;
|
|
56
59
|
|
|
@@ -137,22 +140,38 @@ export const createCore = ({
|
|
|
137
140
|
const _primaryKeys = pkColumnsData.map((x: IColumnInternal) => x.column);
|
|
138
141
|
const primaryKeys = _primaryKeys.length > 0 ? _primaryKeys : ['id'];
|
|
139
142
|
|
|
140
|
-
// Returns unique identifier of model (the values of the primary keys)
|
|
141
143
|
const getPkId = (model: IModel): string => {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
144
|
+
let id = '';
|
|
145
|
+
for (let i = 0; i < primaryKeys.length; i++) {
|
|
146
|
+
const part = model[primaryKeys[i] as keyof typeof model];
|
|
147
|
+
if (part !== void 0 && part !== null) {
|
|
148
|
+
id += String(part);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return id;
|
|
145
152
|
};
|
|
146
153
|
|
|
147
|
-
const references =
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
const references: any = {};
|
|
155
|
+
const referencesEntries: Array<{
|
|
156
|
+
property: string;
|
|
157
|
+
ModelClass: IModelClass;
|
|
158
|
+
}> = [];
|
|
159
|
+
for (const col of columns) {
|
|
160
|
+
if (col.references) {
|
|
161
|
+
references[col.property] = col.references;
|
|
162
|
+
referencesEntries.push({
|
|
163
|
+
property: col.property,
|
|
164
|
+
ModelClass: col.references
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const columnToPropertyMap = new Map<string, string>();
|
|
170
|
+
const propertyToColumnMap = new Map<string, string>();
|
|
171
|
+
for (let i = 0; i < columnNames.length; i++) {
|
|
172
|
+
columnToPropertyMap.set(columnNames[i], propertyNames[i]);
|
|
173
|
+
propertyToColumnMap.set(propertyNames[i], columnNames[i]);
|
|
174
|
+
}
|
|
156
175
|
|
|
157
176
|
const selectColumnsClause = prefixedColumnNames
|
|
158
177
|
.map(
|
|
@@ -174,7 +193,10 @@ export const createCore = ({
|
|
|
174
193
|
primaryKeys,
|
|
175
194
|
references,
|
|
176
195
|
selectColumnsClause,
|
|
177
|
-
getPkId
|
|
196
|
+
getPkId,
|
|
197
|
+
columnToPropertyMap,
|
|
198
|
+
propertyToColumnMap,
|
|
199
|
+
referencesEntries
|
|
178
200
|
};
|
|
179
201
|
}
|
|
180
202
|
);
|
|
@@ -223,237 +245,357 @@ export const createCore = ({
|
|
|
223
245
|
return getEntityByModelClass(model.constructor as IModelClass);
|
|
224
246
|
};
|
|
225
247
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
248
|
+
const entityReferencePlans = new Map<
|
|
249
|
+
IEntityInternal<IModel>,
|
|
250
|
+
Array<{ property: string; targetEntity: IEntityInternal<IModel> }>
|
|
251
|
+
>();
|
|
252
|
+
for (let i = 0; i < entities.length; i++) {
|
|
253
|
+
const entity = entities[i];
|
|
254
|
+
const plans = new Array<{
|
|
255
|
+
property: string;
|
|
256
|
+
targetEntity: IEntityInternal<IModel>;
|
|
257
|
+
}>(entity.referencesEntries.length);
|
|
258
|
+
for (let j = 0; j < entity.referencesEntries.length; j++) {
|
|
259
|
+
const ref = entity.referencesEntries[j];
|
|
260
|
+
plans[j] = {
|
|
261
|
+
property: ref.property,
|
|
262
|
+
targetEntity: getEntityByModelClass(ref.ModelClass)
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
entityReferencePlans.set(entity, plans);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
interface IRowColumnPlan {
|
|
269
|
+
rowKey: string;
|
|
270
|
+
propertyName: string;
|
|
271
|
+
}
|
|
272
|
+
interface IEntityRowPlan {
|
|
273
|
+
entity: IEntityInternal<IModel>;
|
|
274
|
+
columnPlans: Array<IRowColumnPlan>;
|
|
275
|
+
primaryKeyRowKeys: Array<string>;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const getPkIdFromRow = (
|
|
279
|
+
row: any,
|
|
280
|
+
primaryKeyRowKeys: Array<string>
|
|
281
|
+
): string => {
|
|
282
|
+
let id = '';
|
|
283
|
+
for (let i = 0; i < primaryKeyRowKeys.length; i++) {
|
|
284
|
+
const part = row[primaryKeyRowKeys[i]];
|
|
285
|
+
if (part !== void 0 && part !== null) {
|
|
286
|
+
id += String(part);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return id;
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const buildEntityRowPlans = (sampleRow: any): Array<IEntityRowPlan> => {
|
|
293
|
+
const plansByTable = new Map<string, IEntityRowPlan>();
|
|
294
|
+
const tableOrder: Array<string> = [];
|
|
295
|
+
for (const text in sampleRow) {
|
|
296
|
+
if (!Object.prototype.hasOwnProperty.call(sampleRow, text)) {
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
const hashIndex = text.indexOf('#');
|
|
300
|
+
if (hashIndex === -1) {
|
|
301
|
+
throw new Error('Column names must be namespaced to table');
|
|
302
|
+
}
|
|
303
|
+
const tableName = text.substring(0, hashIndex);
|
|
304
|
+
const column = text.substring(hashIndex + 1);
|
|
305
|
+
|
|
306
|
+
let plan = plansByTable.get(tableName);
|
|
307
|
+
if (!plan) {
|
|
308
|
+
const entity = getEntityByTableName(tableName);
|
|
309
|
+
const primaryKeyRowKeys = entity.primaryKeys.map(
|
|
310
|
+
(pk: string) => `${tableName}#${pk}`
|
|
253
311
|
);
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
index
|
|
268
|
-
];
|
|
269
|
-
if (node[property] === model.id) {
|
|
270
|
-
return true;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
return false;
|
|
274
|
-
});
|
|
275
|
-
// For first obj type which is has an instance in nodes array,
|
|
276
|
-
// get its index in nodes array
|
|
277
|
-
const indexOfOldestParent =
|
|
278
|
-
array.reduce((answer: number | null, obj: IModel) => {
|
|
279
|
-
if (answer != null) {
|
|
280
|
-
return answer;
|
|
281
|
-
}
|
|
282
|
-
const index = nodes.findIndex(
|
|
283
|
-
(n) => n.constructor === obj.constructor
|
|
284
|
-
);
|
|
285
|
-
if (index !== -1) {
|
|
286
|
-
return index;
|
|
287
|
-
}
|
|
288
|
-
return null;
|
|
289
|
-
}, null) || 0;
|
|
290
|
-
const parentHeirarchy = [
|
|
291
|
-
root,
|
|
292
|
-
...nodes.slice(0, indexOfOldestParent + 1).reverse()
|
|
293
|
-
];
|
|
294
|
-
const nodeItPointsTo = parentHeirarchy.find((parent) => {
|
|
295
|
-
const indexes = Object.values(getEntityByModel(model).references)
|
|
296
|
-
.map((x: IModelClass, i: number) =>
|
|
297
|
-
x === parent.constructor ? i : null
|
|
298
|
-
)
|
|
299
|
-
.filter((x: number | null, i) => x != null) as Array<number>;
|
|
300
|
-
if (!indexes.length) {
|
|
301
|
-
return false;
|
|
302
|
-
}
|
|
303
|
-
for (const index of indexes) {
|
|
304
|
-
const property = Object.keys(getEntityByModel(model).references)[
|
|
305
|
-
index
|
|
306
|
-
];
|
|
307
|
-
if (model[property] === parent.id) {
|
|
308
|
-
return true;
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
return false;
|
|
312
|
-
});
|
|
313
|
-
if (isNodeAlreadySeen) {
|
|
314
|
-
if (nodeItPointsTo && !nodePointingToIt) {
|
|
315
|
-
nodes = [model, ...nodes];
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
318
|
-
// If the nodePointingToIt (eg, parcel_event) is part of an
|
|
319
|
-
// existing collection on this node (eg, parcel) which is a
|
|
320
|
-
// nodeAlreadySeen, early return so we don't create it (parcel) on
|
|
321
|
-
// the nodePointingToIt (parcel_event), since it (parcel) has been
|
|
322
|
-
// shown to be the parent (of parcel_events).
|
|
323
|
-
if (nodePointingToIt) {
|
|
324
|
-
const ec =
|
|
325
|
-
model[
|
|
326
|
-
getEntityByModel(nodePointingToIt)
|
|
327
|
-
.collectionDisplayName as keyof typeof model
|
|
328
|
-
];
|
|
329
|
-
if (ec && ec.models.find((m: IModel) => m === nodePointingToIt)) {
|
|
330
|
-
nodes = [model, ...nodes];
|
|
331
|
-
return;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
if (nodePointingToIt) {
|
|
336
|
-
nodePointingToIt[getEntityByModel(model).displayName] = model;
|
|
337
|
-
} else if (nodeItPointsTo) {
|
|
338
|
-
let collection =
|
|
339
|
-
nodeItPointsTo[getEntityByModel(model).collectionDisplayName];
|
|
340
|
-
if (collection) {
|
|
341
|
-
collection.models.push(model);
|
|
342
|
-
} else {
|
|
343
|
-
const Collection = getEntityByModel(model).Collection;
|
|
344
|
-
nodeItPointsTo[getEntityByModel(model).collectionDisplayName] =
|
|
345
|
-
new Collection({
|
|
346
|
-
models: [model]
|
|
347
|
-
});
|
|
348
|
-
}
|
|
312
|
+
plan = {
|
|
313
|
+
entity,
|
|
314
|
+
columnPlans: [],
|
|
315
|
+
primaryKeyRowKeys
|
|
316
|
+
};
|
|
317
|
+
plansByTable.set(tableName, plan);
|
|
318
|
+
tableOrder.push(tableName);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
let propertyName = plan.entity.columnToPropertyMap.get(column);
|
|
322
|
+
if (!propertyName) {
|
|
323
|
+
if (column.startsWith('meta_')) {
|
|
324
|
+
propertyName = camelCase(column);
|
|
349
325
|
} else {
|
|
350
|
-
if (!getEntityByModel(model).getPkId(model)) {
|
|
351
|
-
// If the join is fruitless; todo: add a test for this path
|
|
352
|
-
return;
|
|
353
|
-
}
|
|
354
326
|
throw Error(
|
|
355
|
-
`
|
|
356
|
-
getEntityByModel(model).tableName
|
|
357
|
-
}`
|
|
327
|
+
`No property name for "${column}" in business object "${plan.entity.displayName}". Non-spec'd columns must begin with "meta_".`
|
|
358
328
|
);
|
|
359
329
|
}
|
|
360
|
-
|
|
330
|
+
}
|
|
331
|
+
plan.columnPlans.push({
|
|
332
|
+
rowKey: text,
|
|
333
|
+
propertyName
|
|
361
334
|
});
|
|
362
|
-
}
|
|
335
|
+
}
|
|
363
336
|
|
|
364
|
-
|
|
337
|
+
const orderedPlans = new Array<IEntityRowPlan>(tableOrder.length);
|
|
338
|
+
for (let i = 0; i < tableOrder.length; i++) {
|
|
339
|
+
orderedPlans[i] = plansByTable.get(tableOrder[i]) as IEntityRowPlan;
|
|
340
|
+
}
|
|
341
|
+
return orderedPlans;
|
|
365
342
|
};
|
|
366
343
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
.
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
.join('@');
|
|
398
|
-
if (accum.has(id)) {
|
|
399
|
-
accum.set(id, [...accum.get(id), item]);
|
|
400
|
-
} else {
|
|
401
|
-
accum.set(id, [item]);
|
|
344
|
+
const materializeModelsFromRow = (
|
|
345
|
+
row: any,
|
|
346
|
+
entityRowPlans: Array<IEntityRowPlan>,
|
|
347
|
+
rootScopedModelsByEntity: IModelsByEntity,
|
|
348
|
+
rowModels: Array<IModel | void>,
|
|
349
|
+
rowModelPkIds: Array<string>,
|
|
350
|
+
rowCreatedWithPkIndexes: Array<number>
|
|
351
|
+
): IModel => {
|
|
352
|
+
rowCreatedWithPkIndexes.length = 0;
|
|
353
|
+
for (let i = 0; i < entityRowPlans.length; i++) {
|
|
354
|
+
const plan = entityRowPlans[i];
|
|
355
|
+
const pkId = getPkIdFromRow(row, plan.primaryKeyRowKeys);
|
|
356
|
+
rowModelPkIds[i] = pkId;
|
|
357
|
+
if (pkId) {
|
|
358
|
+
let modelsForEntity = rootScopedModelsByEntity.get(plan.entity);
|
|
359
|
+
if (!modelsForEntity) {
|
|
360
|
+
modelsForEntity = new Map<string, IModel>();
|
|
361
|
+
rootScopedModelsByEntity.set(plan.entity, modelsForEntity);
|
|
362
|
+
} else {
|
|
363
|
+
const existing = modelsForEntity.get(pkId);
|
|
364
|
+
if (existing) {
|
|
365
|
+
rowModels[i] = existing;
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
} else if (i !== 0) {
|
|
370
|
+
// No primary key means this is typically an outer-joined null row.
|
|
371
|
+
// Skip model construction for non-root entities since it cannot link.
|
|
372
|
+
rowModels[i] = void 0;
|
|
373
|
+
continue;
|
|
402
374
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
375
|
+
const props: any = {};
|
|
376
|
+
for (let j = 0; j < plan.columnPlans.length; j++) {
|
|
377
|
+
const columnPlan = plan.columnPlans[j];
|
|
378
|
+
props[columnPlan.propertyName] = row[columnPlan.rowKey];
|
|
379
|
+
}
|
|
380
|
+
const model = new plan.entity.Model(props);
|
|
381
|
+
if (pkId) {
|
|
382
|
+
// modelsForEntity is guaranteed to be initialized above for pk rows.
|
|
383
|
+
(rootScopedModelsByEntity.get(plan.entity) as Map<string, IModel>).set(
|
|
384
|
+
pkId,
|
|
385
|
+
model
|
|
386
|
+
);
|
|
387
|
+
rowCreatedWithPkIndexes.push(i);
|
|
388
|
+
}
|
|
389
|
+
rowModels[i] = model;
|
|
390
|
+
}
|
|
391
|
+
return rowModels[0] as IModel;
|
|
406
392
|
};
|
|
407
393
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
}
|
|
394
|
+
type IModelsByEntity = Map<IEntityInternal<IModel>, Map<string, IModel>>;
|
|
395
|
+
interface IRootScopeState {
|
|
396
|
+
modelsByEntity: IModelsByEntity;
|
|
397
|
+
collectionMembership?: WeakMap<
|
|
398
|
+
IModel,
|
|
399
|
+
Map<IEntityInternal<IModel>, Set<string>>
|
|
400
|
+
>;
|
|
401
|
+
}
|
|
402
|
+
type IRootScopeStateByKey = Map<string, IRootScopeState>;
|
|
403
|
+
|
|
404
|
+
const getRootScopeKey = (
|
|
405
|
+
row: any,
|
|
406
|
+
rootEntity: IEntityInternal<IModel>,
|
|
407
|
+
rootPrimaryKeys: Array<string>
|
|
408
|
+
): string => {
|
|
409
|
+
let rootScopeKey = '';
|
|
410
|
+
for (let i = 0; i < rootPrimaryKeys.length; i++) {
|
|
411
|
+
if (i > 0) {
|
|
412
|
+
rootScopeKey += '@';
|
|
413
|
+
}
|
|
414
|
+
const value = row[`${rootEntity.tableName}#${rootPrimaryKeys[i]}`];
|
|
415
|
+
rootScopeKey += value === void 0 || value === null ? '' : String(value);
|
|
416
|
+
}
|
|
417
|
+
return rootScopeKey;
|
|
431
418
|
};
|
|
432
419
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
420
|
+
const ensureRootScopeState = (
|
|
421
|
+
rootScopeKey: string,
|
|
422
|
+
rootScopeStateByKey: IRootScopeStateByKey
|
|
423
|
+
): IRootScopeState => {
|
|
424
|
+
let state = rootScopeStateByKey.get(rootScopeKey);
|
|
425
|
+
if (!state) {
|
|
426
|
+
state = {
|
|
427
|
+
modelsByEntity: new Map<IEntityInternal<IModel>, Map<string, IModel>>()
|
|
428
|
+
};
|
|
429
|
+
rootScopeStateByKey.set(rootScopeKey, state);
|
|
430
|
+
}
|
|
431
|
+
return state;
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
const ensureCollectionMembership = (
|
|
435
|
+
rootScopeState: IRootScopeState
|
|
436
|
+
): WeakMap<IModel, Map<IEntityInternal<IModel>, Set<string>>> => {
|
|
437
|
+
if (!rootScopeState.collectionMembership) {
|
|
438
|
+
rootScopeState.collectionMembership = new WeakMap<
|
|
439
|
+
IModel,
|
|
440
|
+
Map<IEntityInternal<IModel>, Set<string>>
|
|
441
|
+
>();
|
|
442
|
+
}
|
|
443
|
+
return rootScopeState.collectionMembership;
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
const linkSourceToTarget = ({
|
|
447
|
+
sourceEntity,
|
|
448
|
+
sourceModel,
|
|
449
|
+
sourceModelPkId,
|
|
450
|
+
targetEntity,
|
|
451
|
+
targetModel,
|
|
452
|
+
collectionMembership
|
|
453
|
+
}: {
|
|
454
|
+
sourceEntity: IEntityInternal<IModel>;
|
|
455
|
+
sourceModel: IModel;
|
|
456
|
+
sourceModelPkId: string;
|
|
457
|
+
targetEntity: IEntityInternal<IModel>;
|
|
458
|
+
targetModel: IModel;
|
|
459
|
+
collectionMembership: WeakMap<
|
|
460
|
+
IModel,
|
|
461
|
+
Map<IEntityInternal<IModel>, Set<string>>
|
|
462
|
+
>;
|
|
463
|
+
}) => {
|
|
464
|
+
sourceModel[targetEntity.displayName as keyof typeof sourceModel] =
|
|
465
|
+
targetModel;
|
|
466
|
+
|
|
467
|
+
let collection =
|
|
468
|
+
targetModel[
|
|
469
|
+
sourceEntity.collectionDisplayName as keyof typeof targetModel
|
|
470
|
+
];
|
|
471
|
+
if (!collection) {
|
|
472
|
+
const Collection = sourceEntity.Collection;
|
|
473
|
+
collection = new Collection({ models: [] });
|
|
474
|
+
targetModel[
|
|
475
|
+
sourceEntity.collectionDisplayName as keyof typeof targetModel
|
|
476
|
+
] = collection;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
let byCollection = collectionMembership.get(targetModel);
|
|
480
|
+
if (!byCollection) {
|
|
481
|
+
byCollection = new Map<IEntityInternal<IModel>, Set<string>>();
|
|
482
|
+
collectionMembership.set(targetModel, byCollection);
|
|
483
|
+
}
|
|
484
|
+
let memberIds = byCollection.get(sourceEntity);
|
|
485
|
+
if (!memberIds) {
|
|
486
|
+
memberIds = new Set<string>();
|
|
487
|
+
byCollection.set(sourceEntity, memberIds);
|
|
488
|
+
}
|
|
489
|
+
if (!memberIds.has(sourceModelPkId)) {
|
|
490
|
+
collection.models.push(sourceModel);
|
|
491
|
+
memberIds.add(sourceModelPkId);
|
|
492
|
+
}
|
|
448
493
|
};
|
|
449
494
|
|
|
495
|
+
/*
|
|
496
|
+
* createFromDatabase architecture:
|
|
497
|
+
* 1) Compile row plans once (column -> property mapping per entity/table).
|
|
498
|
+
* 2) Materialize models per row with scoped de-duplication by root scope key.
|
|
499
|
+
* 3) Index models by root scope + entity + entity primary key.
|
|
500
|
+
* 4) Link refs incrementally as new models appear.
|
|
501
|
+
* 5) Return root models in first-seen root scope order.
|
|
502
|
+
*/
|
|
450
503
|
const createFromDatabase = <T extends ICollection<IModel>>(rows: any): T => {
|
|
451
504
|
const result = Array.isArray(rows) ? rows : [rows];
|
|
452
|
-
const
|
|
453
|
-
const
|
|
454
|
-
const
|
|
455
|
-
|
|
456
|
-
|
|
505
|
+
const len = result.length;
|
|
506
|
+
const entityRowPlans = buildEntityRowPlans(result[0]);
|
|
507
|
+
const selectedEntities = new Set<IEntityInternal<IModel>>();
|
|
508
|
+
for (let i = 0; i < entityRowPlans.length; i++) {
|
|
509
|
+
selectedEntities.add(entityRowPlans[i].entity);
|
|
510
|
+
}
|
|
511
|
+
const applicableRefPlans = new Map<
|
|
512
|
+
IEntityInternal<IModel>,
|
|
513
|
+
Array<{ property: string; targetEntity: IEntityInternal<IModel> }>
|
|
514
|
+
>();
|
|
515
|
+
for (let i = 0; i < entityRowPlans.length; i++) {
|
|
516
|
+
const entity = entityRowPlans[i].entity;
|
|
517
|
+
const refs = entityReferencePlans.get(entity) || [];
|
|
518
|
+
const filteredRefs = refs.filter((ref) =>
|
|
519
|
+
selectedEntities.has(ref.targetEntity)
|
|
520
|
+
);
|
|
521
|
+
applicableRefPlans.set(entity, filteredRefs);
|
|
522
|
+
}
|
|
523
|
+
const rootEntity = entityRowPlans[0].entity;
|
|
524
|
+
const rootPrimaryKeys = rootEntity.primaryKeys;
|
|
525
|
+
const rootScopeOrder: Array<string> = [];
|
|
526
|
+
const rootModelsByScopeKey = new Map<string, IModel>();
|
|
527
|
+
const rootScopeStateByKey: IRootScopeStateByKey = new Map();
|
|
528
|
+
let currentRootScopeKey: string | void = void 0;
|
|
529
|
+
let currentRootScopeState: IRootScopeState | void = void 0;
|
|
530
|
+
const rowModels = new Array<IModel | void>(entityRowPlans.length);
|
|
531
|
+
const rowModelPkIds = new Array<string>(entityRowPlans.length);
|
|
532
|
+
const rowCreatedWithPkIndexes: Array<number> = [];
|
|
533
|
+
|
|
534
|
+
// Phase 1: materialize and index model instances by root scope + entity.
|
|
535
|
+
for (let i = 0; i < len; i++) {
|
|
536
|
+
const row = result[i];
|
|
537
|
+
const rootScopeKey = getRootScopeKey(row, rootEntity, rootPrimaryKeys);
|
|
538
|
+
let rootScopeState: IRootScopeState | void = currentRootScopeState;
|
|
539
|
+
if (!rootScopeState || rootScopeKey !== currentRootScopeKey) {
|
|
540
|
+
rootScopeState = ensureRootScopeState(
|
|
541
|
+
rootScopeKey,
|
|
542
|
+
rootScopeStateByKey
|
|
543
|
+
);
|
|
544
|
+
currentRootScopeKey = rootScopeKey;
|
|
545
|
+
currentRootScopeState = rootScopeState;
|
|
546
|
+
}
|
|
547
|
+
const rootModel = materializeModelsFromRow(
|
|
548
|
+
row,
|
|
549
|
+
entityRowPlans,
|
|
550
|
+
rootScopeState.modelsByEntity,
|
|
551
|
+
rowModels,
|
|
552
|
+
rowModelPkIds,
|
|
553
|
+
rowCreatedWithPkIndexes
|
|
554
|
+
);
|
|
555
|
+
if (!rootModelsByScopeKey.has(rootScopeKey)) {
|
|
556
|
+
rootScopeOrder.push(rootScopeKey);
|
|
557
|
+
rootModelsByScopeKey.set(rootScopeKey, rootModel);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
for (let c = 0; c < rowCreatedWithPkIndexes.length; c++) {
|
|
561
|
+
const j = rowCreatedWithPkIndexes[c];
|
|
562
|
+
const sourceModel = rowModels[j] as IModel;
|
|
563
|
+
const sourceModelPkId = rowModelPkIds[j];
|
|
564
|
+
const sourceEntity = entityRowPlans[j].entity;
|
|
565
|
+
|
|
566
|
+
const refs = applicableRefPlans.get(sourceEntity);
|
|
567
|
+
if (!refs || refs.length === 0) {
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
for (let r = 0; r < refs.length; r++) {
|
|
572
|
+
const ref = refs[r];
|
|
573
|
+
const refId = sourceModel[ref.property as keyof typeof sourceModel];
|
|
574
|
+
if (refId == null) {
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
const targetPkId = String(refId);
|
|
578
|
+
const target = rootScopeState.modelsByEntity
|
|
579
|
+
.get(ref.targetEntity)
|
|
580
|
+
?.get(targetPkId);
|
|
581
|
+
if (target) {
|
|
582
|
+
linkSourceToTarget({
|
|
583
|
+
sourceEntity,
|
|
584
|
+
sourceModel,
|
|
585
|
+
sourceModelPkId,
|
|
586
|
+
targetEntity: ref.targetEntity,
|
|
587
|
+
targetModel: target,
|
|
588
|
+
collectionMembership: ensureCollectionMembership(rootScopeState)
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
const models = new Array<IModel>(rootScopeOrder.length);
|
|
596
|
+
for (let i = 0; i < rootScopeOrder.length; i++) {
|
|
597
|
+
models[i] = rootModelsByScopeKey.get(rootScopeOrder[i]) as IModel;
|
|
598
|
+
}
|
|
457
599
|
const Collection = getEntityByModel(models[0]).Collection;
|
|
458
600
|
return <T>new Collection({ models });
|
|
459
601
|
};
|