@reldens/storage 0.116.0 → 0.118.0
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/.claude/test-architecture.md +27 -17
- package/CLAUDE.md +67 -11
- package/README.md +204 -22
- package/bin/reldens-storage.js +43 -10
- package/index.js +53 -12
- package/lib/drizzle/drizzle-data-server.js +123 -0
- package/lib/drizzle/drizzle-driver.js +228 -0
- package/lib/drizzle/drizzle-models-generation.js +49 -0
- package/lib/drizzle/drizzle-modules-loader.js +32 -0
- package/lib/drizzle/drizzle-modules-validator.js +74 -0
- package/lib/entities-generator.js +36 -7
- package/lib/entity-templates/drizzle-model.template +28 -0
- package/lib/entity-templates/query-builder-model.template +17 -0
- package/lib/generators/base-generator.js +13 -0
- package/lib/generators/entities-generation.js +0 -13
- package/lib/generators/models-generation.js +179 -693
- package/lib/generators/relations-detection.js +171 -0
- package/lib/knex/knex-data-server.js +128 -0
- package/lib/knex/knex-driver.js +157 -0
- package/lib/knex/knex-modules-loader.js +28 -0
- package/lib/knex/knex-modules-validator.js +45 -0
- package/lib/kysely/kysely-data-server.js +128 -0
- package/lib/kysely/kysely-driver.js +176 -0
- package/lib/kysely/kysely-modules-loader.js +28 -0
- package/lib/kysely/kysely-modules-validator.js +53 -0
- package/lib/mikro-orm/mikro-orm-data-server.js +48 -29
- package/lib/mikro-orm/mikro-orm-driver.js +385 -121
- package/lib/mikro-orm/mikro-orm-models-generation.js +178 -0
- package/lib/mikro-orm/mikro-orm-modules-loader.js +50 -0
- package/lib/mikro-orm/mikro-orm-modules-validator.js +46 -0
- package/lib/mysql2-connection-config.js +38 -0
- package/lib/objection-js/objection-js-data-server.js +71 -125
- package/lib/objection-js/objection-js-driver.js +4 -1
- package/lib/objection-js/objection-js-models-generation.js +98 -0
- package/lib/objection-js/objection-modules-loader.js +28 -0
- package/lib/objection-js/objection-modules-validator.js +31 -0
- package/lib/package-resolver.js +45 -0
- package/lib/prisma/prisma-client-loader.js +11 -1
- package/lib/prisma/prisma-driver.js +16 -2
- package/lib/prisma/prisma-filter-processor.js +5 -6
- package/lib/prisma/prisma-models-generation.js +236 -0
- package/lib/prisma/prisma-schema-generator.js +5 -1
- package/lib/prisma/prisma-type-caster.js +14 -0
- package/lib/query-builder-driver.js +210 -0
- package/lib/query-builder-models-generation.js +80 -0
- package/lib/relations-loader.js +192 -0
- package/lib/type-mapper.js +38 -0
- package/package.json +7 -7
- package/tests/.env.test.example +6 -0
- package/tests/fixtures/categories-fixtures.js +8 -0
- package/tests/fixtures/expected-entities/entities/test-product-details-entity.js +72 -0
- package/tests/fixtures/expected-entities/entities-config.js +6 -4
- package/tests/fixtures/expected-entities/entities-translations.js +25 -14
- package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/registered-models-mikro-orm.js +2 -0
- package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-categories-model.js +13 -8
- package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-product-details-model.js +80 -0
- package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-products-model.js +20 -14
- package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-reviews-model.js +13 -12
- package/tests/fixtures/expected-entities-objection-js/models/objection-js/registered-models-objection-js.js +2 -0
- package/tests/fixtures/expected-entities-objection-js/models/objection-js/test-product-details-model.js +33 -0
- package/tests/fixtures/expected-entities-objection-js/models/objection-js/test-products-model.js +9 -0
- package/tests/fixtures/expected-entities-prisma/models/prisma/registered-models-prisma.js +2 -0
- package/tests/fixtures/expected-entities-prisma/models/prisma/test-product-details-model.js +43 -0
- package/tests/fixtures/expected-entities-prisma/models/prisma/test-products-model.js +2 -0
- package/tests/fixtures/product-details-fixtures.js +22 -0
- package/tests/fixtures/reviews-fixtures.js +2 -1
- package/tests/fixtures/sql/test-schema.sql +23 -1
- package/tests/fixtures/table-columns-fixtures.js +97 -0
- package/tests/integration/test-cross-driver-equivalence.js +286 -0
- package/tests/integration/test-nested-filters.js +265 -408
- package/tests/integration/test-relations.js +199 -0
- package/tests/integration/test-reldens-sample-data.js +255 -0
- package/tests/integration/test-reldens-shape-relations.js +292 -0
- package/tests/run-tests.js +112 -60
- package/tests/unit/test-drivers.js +7 -1
- package/tests/unit/test-models-generation.js +174 -0
- package/tests/utils/column-value-assert.js +139 -0
- package/tests/utils/driver-registry.js +1 -1
- package/tests/utils/entity-projection.js +111 -0
- package/tests/utils/observed-value.js +41 -0
- package/tests/utils/projection-difference.js +151 -0
- package/tests/utils/relations-shape-support.js +104 -0
- package/tests/utils/reldens-schema-loader.js +274 -0
- package/tests/utils/test-helpers.js +152 -19
- package/tests/utils/test-runner.js +26 -5
- package/tests/utils/whole-result-assert.js +243 -0
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
const { BaseDriver } = require('../base-driver');
|
|
8
8
|
const { Logger, sc } = require('@reldens/utils');
|
|
9
|
-
const { Collection } = require('@mikro-orm/core');
|
|
10
9
|
|
|
11
10
|
class MikroOrmDriver extends BaseDriver
|
|
12
11
|
{
|
|
@@ -26,6 +25,12 @@ class MikroOrmDriver extends BaseDriver
|
|
|
26
25
|
Logger.critical('Missing raw entity on Mikro ORM driver.');
|
|
27
26
|
return false;
|
|
28
27
|
}
|
|
28
|
+
this.mikroOrmModules = sc.get(props, 'mikroOrmModules', false);
|
|
29
|
+
if(!this.mikroOrmModules){
|
|
30
|
+
Logger.critical('Missing Mikro ORM modules on Mikro ORM driver.');
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
this.Collection = this.mikroOrmModules.Collection;
|
|
29
34
|
this.orm = props.orm;
|
|
30
35
|
this.server = props.server;
|
|
31
36
|
this.entitySchema = (sc.isObject(this.rawModel) && sc.hasOwn(this.rawModel, 'schema')) ? this.rawModel.schema : this.rawModel;
|
|
@@ -76,28 +81,109 @@ class MikroOrmDriver extends BaseDriver
|
|
|
76
81
|
let relations = [];
|
|
77
82
|
for(let propName of Object.keys(this.entitySchema.meta.properties)){
|
|
78
83
|
let prop = this.entitySchema.meta.properties[propName];
|
|
79
|
-
if(prop.kind
|
|
84
|
+
if('m:1' === prop.kind || '1:1' === prop.kind || '1:m' === prop.kind || 'm:n' === prop.kind){
|
|
80
85
|
relations.push(propName);
|
|
81
86
|
}
|
|
82
87
|
}
|
|
83
88
|
return relations;
|
|
84
89
|
}
|
|
85
90
|
|
|
86
|
-
|
|
91
|
+
isOwningRelation(prop)
|
|
87
92
|
{
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
93
|
+
if('m:1' === prop.kind){
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
return '1:1' === prop.kind && Boolean(prop.owner);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
isInverseRelation(prop)
|
|
100
|
+
{
|
|
101
|
+
if('1:m' === prop.kind){
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
return '1:1' === prop.kind && !prop.owner;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
inverseRelationOwnerJoinColumn(prop, relationDriver)
|
|
108
|
+
{
|
|
109
|
+
if(!this.isInverseRelation(prop)){
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
if(!prop.mappedBy){
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
if(!relationDriver.entitySchema){
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
if(!relationDriver.entitySchema.meta){
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
let mappedProp = relationDriver.entitySchema.meta.properties[prop.mappedBy];
|
|
122
|
+
if(!mappedProp){
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
if(!mappedProp.joinColumns){
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
return mappedProp.joinColumns[0] || false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
nestedFkColumn(prop, relationDriver)
|
|
132
|
+
{
|
|
133
|
+
if(!this.isOwningRelation(prop)){
|
|
134
|
+
return this.inverseRelationOwnerJoinColumn(prop, relationDriver);
|
|
135
|
+
}
|
|
136
|
+
if(!prop.joinColumns){
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
return prop.joinColumns[0] || false;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
removeRelationsFromParams(params)
|
|
143
|
+
{
|
|
144
|
+
if(!params || 'object' !== typeof params){
|
|
145
|
+
return params;
|
|
146
|
+
}
|
|
147
|
+
let columnsOnly = {...params};
|
|
148
|
+
for(let relationName of this.getAllRelations()){
|
|
149
|
+
delete columnsOnly[relationName];
|
|
150
|
+
}
|
|
151
|
+
return columnsOnly;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async createEntity(params)
|
|
155
|
+
{
|
|
156
|
+
let writeManager = this.orm.em.fork();
|
|
157
|
+
let newInstance = await writeManager.create(this.entitySchema, this.removeRelationsFromParams(params));
|
|
158
|
+
await writeManager.upsert(newInstance);
|
|
159
|
+
await writeManager.flush();
|
|
92
160
|
return newInstance;
|
|
93
161
|
}
|
|
94
162
|
|
|
95
|
-
async
|
|
163
|
+
async create(params)
|
|
96
164
|
{
|
|
97
|
-
|
|
98
|
-
|
|
165
|
+
return this.withoutUninitializedCollections(await this.createEntity(params));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
withoutUninitializedCollections(entity)
|
|
169
|
+
{
|
|
170
|
+
if(!entity || !this.Collection){
|
|
171
|
+
return entity;
|
|
99
172
|
}
|
|
100
|
-
let
|
|
173
|
+
let result = {};
|
|
174
|
+
for(let key of Object.keys(entity)){
|
|
175
|
+
if(entity[key] instanceof this.Collection && !entity[key].isInitialized()){
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
result[key] = entity[key];
|
|
179
|
+
}
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async createWithRelations(params, relations)
|
|
184
|
+
{
|
|
185
|
+
relations = this.normalizeRelations(relations);
|
|
186
|
+
let newInstance = await this.createEntity(params);
|
|
101
187
|
await this.createNested(newInstance, params, relations);
|
|
102
188
|
return await this.appendRelationsToCollection(newInstance, relations);
|
|
103
189
|
}
|
|
@@ -108,9 +194,9 @@ class MikroOrmDriver extends BaseDriver
|
|
|
108
194
|
if(0 === entities.length){
|
|
109
195
|
return false;
|
|
110
196
|
}
|
|
111
|
-
let
|
|
197
|
+
let columnsOnly = this.removeRelationsFromParams(updatePatch);
|
|
112
198
|
for(let entity of entities){
|
|
113
|
-
Object.assign(entity,
|
|
199
|
+
Object.assign(entity, columnsOnly);
|
|
114
200
|
await this.orm.em.upsert(entity);
|
|
115
201
|
await this.orm.em.flush();
|
|
116
202
|
}
|
|
@@ -139,7 +225,7 @@ class MikroOrmDriver extends BaseDriver
|
|
|
139
225
|
if(1 < entities.length){
|
|
140
226
|
Logger.warning('Multiple entities updated by ID: '+entities.map(e => e.id).join(', '));
|
|
141
227
|
}
|
|
142
|
-
return entities.shift();
|
|
228
|
+
return this.withoutUninitializedCollections(entities.shift());
|
|
143
229
|
}
|
|
144
230
|
|
|
145
231
|
async upsert(params, filters)
|
|
@@ -155,8 +241,7 @@ class MikroOrmDriver extends BaseDriver
|
|
|
155
241
|
if(filters){
|
|
156
242
|
let existent = await this.loadOne(filters);
|
|
157
243
|
if(existent){
|
|
158
|
-
|
|
159
|
-
return this.updateById(existent.id, transformed);
|
|
244
|
+
return this.updateById(existent.id, params);
|
|
160
245
|
}
|
|
161
246
|
}
|
|
162
247
|
return this.create(params);
|
|
@@ -173,18 +258,18 @@ class MikroOrmDriver extends BaseDriver
|
|
|
173
258
|
await this.orm.em.remove(entry);
|
|
174
259
|
}
|
|
175
260
|
await this.orm.em.flush();
|
|
176
|
-
return
|
|
261
|
+
return entries.length;
|
|
177
262
|
}
|
|
178
263
|
|
|
179
264
|
async deleteById(id)
|
|
180
265
|
{
|
|
181
|
-
let entity = await this.
|
|
266
|
+
let entity = await this.repository.findOne(this.processFilters({id}));
|
|
182
267
|
if(!entity){
|
|
183
268
|
return false;
|
|
184
269
|
}
|
|
185
270
|
await this.orm.em.remove(entity);
|
|
186
271
|
await this.orm.em.flush();
|
|
187
|
-
return
|
|
272
|
+
return 1;
|
|
188
273
|
}
|
|
189
274
|
|
|
190
275
|
async count(filters)
|
|
@@ -195,9 +280,7 @@ class MikroOrmDriver extends BaseDriver
|
|
|
195
280
|
|
|
196
281
|
async countWithRelations(filters, relations)
|
|
197
282
|
{
|
|
198
|
-
|
|
199
|
-
relations = this.getAllRelations();
|
|
200
|
-
}
|
|
283
|
+
relations = this.normalizeRelations(relations);
|
|
201
284
|
let processedFilters = this.processFilters(filters);
|
|
202
285
|
if(0 === relations.length || !this.entitySchema.meta || !this.entitySchema.meta.properties){
|
|
203
286
|
return this.repository.count(processedFilters);
|
|
@@ -207,22 +290,20 @@ class MikroOrmDriver extends BaseDriver
|
|
|
207
290
|
let aliasCounter = 0;
|
|
208
291
|
for(let relationName of relations){
|
|
209
292
|
let prop = properties[relationName];
|
|
210
|
-
if(!prop || !prop.kind || (prop
|
|
293
|
+
if(!prop || !prop.kind || (!this.isOwningRelation(prop) && !this.isInverseRelation(prop))){
|
|
211
294
|
continue;
|
|
212
295
|
}
|
|
213
|
-
let relationDriver = this.
|
|
296
|
+
let relationDriver = this.relationDriverFor(prop);
|
|
214
297
|
if(!relationDriver || !relationDriver.entitySchema){
|
|
215
298
|
continue;
|
|
216
299
|
}
|
|
217
300
|
let alias = 'rel_'+aliasCounter;
|
|
218
|
-
if(
|
|
301
|
+
if(this.isOwningRelation(prop) && prop.joinColumns && prop.joinColumns[0]){
|
|
219
302
|
queryBuilder.leftJoin(relationName, alias, 'e.'+prop.joinColumns[0]+' = '+alias+'.id');
|
|
220
303
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
queryBuilder.leftJoin(relationName, alias, 'e.id = '+alias+'.'+mappedProp.joinColumns[0]);
|
|
225
|
-
}
|
|
304
|
+
let ownerJoinColumn = this.inverseRelationOwnerJoinColumn(prop, relationDriver);
|
|
305
|
+
if(ownerJoinColumn){
|
|
306
|
+
queryBuilder.leftJoin(relationName, alias, 'e.id = '+alias+'.'+ownerJoinColumn);
|
|
226
307
|
}
|
|
227
308
|
aliasCounter++;
|
|
228
309
|
}
|
|
@@ -232,106 +313,180 @@ class MikroOrmDriver extends BaseDriver
|
|
|
232
313
|
return queryBuilder.getCount();
|
|
233
314
|
}
|
|
234
315
|
|
|
235
|
-
loadAll()
|
|
316
|
+
async loadAll()
|
|
236
317
|
{
|
|
237
|
-
return this.repository.findAll();
|
|
318
|
+
return this.toPlainResult(await this.repository.findAll());
|
|
238
319
|
}
|
|
239
320
|
|
|
240
|
-
|
|
321
|
+
normalizeRelations(relations)
|
|
241
322
|
{
|
|
323
|
+
if(sc.isString(relations)){
|
|
324
|
+
relations = this.parseRelationsString(relations);
|
|
325
|
+
}
|
|
242
326
|
if(!sc.isArray(relations) || 0 === relations.length){
|
|
243
|
-
|
|
327
|
+
return this.getAllRelations();
|
|
244
328
|
}
|
|
245
|
-
|
|
246
|
-
return await this.appendRelationsToCollection(entities, relations);
|
|
329
|
+
return relations;
|
|
247
330
|
}
|
|
248
331
|
|
|
249
|
-
|
|
332
|
+
toPlainResult(result, populatedPaths, ancestors)
|
|
250
333
|
{
|
|
251
|
-
|
|
252
|
-
|
|
334
|
+
if(!result || 'object' !== typeof result || result instanceof Date){
|
|
335
|
+
return result;
|
|
336
|
+
}
|
|
337
|
+
if(!ancestors){
|
|
338
|
+
ancestors = new Set();
|
|
339
|
+
}
|
|
340
|
+
if(!populatedPaths){
|
|
341
|
+
populatedPaths = {};
|
|
342
|
+
}
|
|
343
|
+
if(result instanceof this.Collection){
|
|
344
|
+
if(!result.isInitialized()){
|
|
345
|
+
return [];
|
|
346
|
+
}
|
|
347
|
+
return this.toPlainList(result.getItems(), populatedPaths, ancestors);
|
|
348
|
+
}
|
|
349
|
+
if(sc.isArray(result)){
|
|
350
|
+
return this.toPlainList(result, populatedPaths, ancestors);
|
|
351
|
+
}
|
|
352
|
+
return this.toPlainEntity(result, populatedPaths, ancestors);
|
|
253
353
|
}
|
|
254
354
|
|
|
255
|
-
|
|
355
|
+
toPlainList(result, populatedPaths, ancestors)
|
|
256
356
|
{
|
|
257
|
-
|
|
258
|
-
|
|
357
|
+
ancestors.add(result);
|
|
358
|
+
let plainList = [];
|
|
359
|
+
for(let item of result){
|
|
360
|
+
if(ancestors.has(item)){
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
plainList.push(this.toPlainResult(item, populatedPaths, ancestors));
|
|
259
364
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
return await this.appendRelationsToCollection(entitiesCollection, relations);
|
|
365
|
+
ancestors.delete(result);
|
|
366
|
+
return plainList;
|
|
263
367
|
}
|
|
264
368
|
|
|
265
|
-
|
|
369
|
+
toPlainEntity(result, populatedPaths, ancestors)
|
|
266
370
|
{
|
|
267
|
-
|
|
268
|
-
let
|
|
269
|
-
|
|
371
|
+
ancestors.add(result);
|
|
372
|
+
let relationKeys = this.getAllRelations();
|
|
373
|
+
let plainEntity = {};
|
|
374
|
+
for(let key of Object.keys(result)){
|
|
375
|
+
if(ancestors.has(result[key])){
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
if(-1 === relationKeys.indexOf(key)){
|
|
379
|
+
plainEntity[key] = result[key];
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
if(!populatedPaths[key]){
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
plainEntity[key] = this.toPlainRelation(key, result[key], populatedPaths[key], ancestors);
|
|
386
|
+
}
|
|
387
|
+
ancestors.delete(result);
|
|
388
|
+
return plainEntity;
|
|
270
389
|
}
|
|
271
390
|
|
|
272
|
-
|
|
391
|
+
toPlainRelation(propName, relationValue, populatedPaths, ancestors)
|
|
273
392
|
{
|
|
274
|
-
|
|
275
|
-
|
|
393
|
+
let relationDriver = this.relationDriverFor(this.entitySchema.meta.properties[propName]);
|
|
394
|
+
if(!relationDriver){
|
|
395
|
+
return this.toPlainResult(relationValue, populatedPaths, ancestors);
|
|
276
396
|
}
|
|
397
|
+
return relationDriver.toPlainResult(relationValue, populatedPaths, ancestors);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
async plainRelationsResult(entitiesCollection, relations)
|
|
401
|
+
{
|
|
402
|
+
return this.toPlainResult(
|
|
403
|
+
await this.appendRelationsToCollection(entitiesCollection, relations),
|
|
404
|
+
this.populatedPathsTree(relations)
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
findEntities(filters)
|
|
409
|
+
{
|
|
410
|
+
return this.repository.find(this.processFilters(filters), this.queryBuilder(true, true, true));
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
findOneEntity(filters)
|
|
414
|
+
{
|
|
415
|
+
return this.repository.findOne(this.processFilters(filters), this.queryBuilder(false, true, true));
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
async loadAllWithRelations(relations)
|
|
419
|
+
{
|
|
420
|
+
relations = this.normalizeRelations(relations);
|
|
421
|
+
let entities = await this.repository.findAll();
|
|
422
|
+
return this.plainRelationsResult(entities, relations);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
async load(filters)
|
|
426
|
+
{
|
|
427
|
+
return this.toPlainResult(await this.findEntities(filters));
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
async loadWithRelations(filters, relations)
|
|
431
|
+
{
|
|
432
|
+
relations = this.normalizeRelations(relations);
|
|
433
|
+
let entitiesCollection = await this.findEntities(filters);
|
|
434
|
+
return this.plainRelationsResult(entitiesCollection, relations);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
async loadBy(field, fieldValue, operator = null)
|
|
438
|
+
{
|
|
439
|
+
return this.toPlainResult(await this.findEntities(this.createSingleFilter(field, fieldValue, operator)));
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
async loadByWithRelations(field, fieldValue, relations, operator = null)
|
|
443
|
+
{
|
|
444
|
+
relations = this.normalizeRelations(relations);
|
|
277
445
|
let filter = this.createSingleFilter(field, fieldValue, operator);
|
|
278
|
-
let
|
|
279
|
-
|
|
280
|
-
return await this.appendRelationsToCollection(entitiesCollection, relations);
|
|
446
|
+
let entitiesCollection = await this.findEntities(filter);
|
|
447
|
+
return this.plainRelationsResult(entitiesCollection, relations);
|
|
281
448
|
}
|
|
282
449
|
|
|
283
|
-
loadById(id)
|
|
450
|
+
async loadById(id)
|
|
284
451
|
{
|
|
285
|
-
return this.
|
|
452
|
+
return this.toPlainResult(await this.findOneEntity({id}));
|
|
286
453
|
}
|
|
287
454
|
|
|
288
455
|
async loadByIdWithRelations(id, relations)
|
|
289
456
|
{
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
let entity = await this.loadOneBy('id', id);
|
|
294
|
-
return await this.appendRelationsToCollection(entity, relations);
|
|
457
|
+
relations = this.normalizeRelations(relations);
|
|
458
|
+
let entity = await this.findOneEntity({id});
|
|
459
|
+
return this.plainRelationsResult(entity, relations);
|
|
295
460
|
}
|
|
296
461
|
|
|
297
|
-
loadByIds(ids)
|
|
462
|
+
async loadByIds(ids)
|
|
298
463
|
{
|
|
299
|
-
|
|
300
|
-
return this.repository.find(processedFilters);
|
|
464
|
+
return this.toPlainResult(await this.repository.find(this.processFilters({id: {$in: ids}})));
|
|
301
465
|
}
|
|
302
466
|
|
|
303
|
-
loadOne(filters)
|
|
467
|
+
async loadOne(filters)
|
|
304
468
|
{
|
|
305
|
-
|
|
306
|
-
return this.repository.findOne(processedFilters, this.queryBuilder(false, true, true));
|
|
469
|
+
return this.toPlainResult(await this.findOneEntity(filters));
|
|
307
470
|
}
|
|
308
471
|
|
|
309
472
|
async loadOneWithRelations(filters, relations)
|
|
310
473
|
{
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
let processedFilters = this.processFilters(filters);
|
|
315
|
-
let entitiesCollection = await this.repository.findOne(processedFilters, this.queryBuilder(false, true, true));
|
|
316
|
-
return this.appendRelationsToCollection(entitiesCollection, relations);
|
|
474
|
+
relations = this.normalizeRelations(relations);
|
|
475
|
+
let entitiesCollection = await this.findOneEntity(filters);
|
|
476
|
+
return this.plainRelationsResult(entitiesCollection, relations);
|
|
317
477
|
}
|
|
318
478
|
|
|
319
|
-
loadOneBy(field, fieldValue, operator = null)
|
|
479
|
+
async loadOneBy(field, fieldValue, operator = null)
|
|
320
480
|
{
|
|
321
|
-
|
|
322
|
-
let processedFilters = this.processFilters(filter);
|
|
323
|
-
return this.repository.findOne(processedFilters, this.queryBuilder(false, true, true));
|
|
481
|
+
return this.toPlainResult(await this.findOneEntity(this.createSingleFilter(field, fieldValue, operator)));
|
|
324
482
|
}
|
|
325
483
|
|
|
326
484
|
async loadOneByWithRelations(field, fieldValue, relations, operator = null)
|
|
327
485
|
{
|
|
328
|
-
|
|
329
|
-
relations = this.getAllRelations();
|
|
330
|
-
}
|
|
486
|
+
relations = this.normalizeRelations(relations);
|
|
331
487
|
let filter = this.createSingleFilter(field, fieldValue, operator);
|
|
332
|
-
let
|
|
333
|
-
|
|
334
|
-
return this.appendRelationsToCollection(entitiesCollection, relations);
|
|
488
|
+
let entitiesCollection = await this.findOneEntity(filter);
|
|
489
|
+
return this.plainRelationsResult(entitiesCollection, relations);
|
|
335
490
|
}
|
|
336
491
|
|
|
337
492
|
processFilters(filters)
|
|
@@ -342,6 +497,9 @@ class MikroOrmDriver extends BaseDriver
|
|
|
342
497
|
let processedFilters = {};
|
|
343
498
|
for(let key of Object.keys(filters)){
|
|
344
499
|
let value = filters[key];
|
|
500
|
+
if(('AND' === key || 'OR' === key) && sc.isArray(value) && 0 === value.length){
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
345
503
|
if('AND' === key && sc.isArray(value)){
|
|
346
504
|
processedFilters.$and = value.map(condition => this.processFilters(condition));
|
|
347
505
|
continue;
|
|
@@ -418,15 +576,39 @@ class MikroOrmDriver extends BaseDriver
|
|
|
418
576
|
return filter;
|
|
419
577
|
}
|
|
420
578
|
|
|
421
|
-
|
|
579
|
+
populatedPathsTree(relations)
|
|
580
|
+
{
|
|
581
|
+
let tree = {};
|
|
582
|
+
if(!sc.isArray(relations)){
|
|
583
|
+
return tree;
|
|
584
|
+
}
|
|
585
|
+
for(let relationPath of relations){
|
|
586
|
+
let node = tree;
|
|
587
|
+
for(let pathPart of String(relationPath).split('.')){
|
|
588
|
+
if(!node[pathPart]){
|
|
589
|
+
node[pathPart] = {};
|
|
590
|
+
}
|
|
591
|
+
node = node[pathPart];
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
return tree;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
convertCollectionsToArrays(entity, populatedPaths)
|
|
422
598
|
{
|
|
423
599
|
if(!entity){
|
|
424
600
|
return entity;
|
|
425
601
|
}
|
|
426
|
-
for(let key of Object.keys(
|
|
427
|
-
if(entity[key]
|
|
428
|
-
|
|
602
|
+
for(let key of Object.keys(populatedPaths)){
|
|
603
|
+
if(!entity[key] || !(entity[key] instanceof this.Collection)){
|
|
604
|
+
continue;
|
|
429
605
|
}
|
|
606
|
+
if(!entity[key].isInitialized()){
|
|
607
|
+
Logger.warning('Relation "'+key+'" was not populated on '+this.tableName()+', it will be empty.');
|
|
608
|
+
entity[key] = [];
|
|
609
|
+
continue;
|
|
610
|
+
}
|
|
611
|
+
entity[key] = entity[key].getItems();
|
|
430
612
|
}
|
|
431
613
|
return entity;
|
|
432
614
|
}
|
|
@@ -462,13 +644,104 @@ class MikroOrmDriver extends BaseDriver
|
|
|
462
644
|
return entitiesCollection;
|
|
463
645
|
}
|
|
464
646
|
await this.orm.em.populate(entitiesCollection, relations);
|
|
647
|
+
let populatedPaths = this.populatedPathsTree(relations);
|
|
465
648
|
if(sc.isArray(entitiesCollection)){
|
|
466
649
|
for(let entity of entitiesCollection){
|
|
467
|
-
this.
|
|
650
|
+
this.restoreRelatedForeignKeys(entity, new Set(), populatedPaths);
|
|
651
|
+
await this.resolveKeyMappedRelations(entity, populatedPaths);
|
|
468
652
|
}
|
|
469
653
|
return entitiesCollection;
|
|
470
654
|
}
|
|
471
|
-
|
|
655
|
+
this.restoreRelatedForeignKeys(entitiesCollection, new Set(), populatedPaths);
|
|
656
|
+
await this.resolveKeyMappedRelations(entitiesCollection, populatedPaths);
|
|
657
|
+
return entitiesCollection;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
async resolveKeyMappedRelations(entity, populatedPaths)
|
|
661
|
+
{
|
|
662
|
+
if(!entity || !this.entitySchema.meta || !this.entitySchema.meta.properties){
|
|
663
|
+
return entity;
|
|
664
|
+
}
|
|
665
|
+
for(let propName of Object.keys(populatedPaths)){
|
|
666
|
+
let prop = this.entitySchema.meta.properties[propName];
|
|
667
|
+
if(!prop || !prop.mapToPk || !prop.targetKey || sc.isObject(entity[propName])){
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
if(null === entity[propName] || 'undefined' === typeof entity[propName]){
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
let relationDriver = this.relationDriverFor(prop);
|
|
674
|
+
if(!relationDriver){
|
|
675
|
+
continue;
|
|
676
|
+
}
|
|
677
|
+
entity[propName] = await relationDriver.loadOneBy(prop.targetKey, entity[propName]);
|
|
678
|
+
}
|
|
679
|
+
return entity;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
restoreRelatedForeignKeys(entity, visited, populatedPaths)
|
|
683
|
+
{
|
|
684
|
+
if(!entity || visited.has(entity)){
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
visited.add(entity);
|
|
688
|
+
this.convertCollectionsToArrays(entity, populatedPaths);
|
|
689
|
+
this.addFkFieldsFromRelations(entity);
|
|
690
|
+
if(!this.entitySchema.meta || !this.entitySchema.meta.properties){
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
for(let propName of Object.keys(populatedPaths)){
|
|
694
|
+
let relationDriver = this.relationDriverFor(this.entitySchema.meta.properties[propName]);
|
|
695
|
+
if(!relationDriver){
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
698
|
+
relationDriver.restoreRelatedEntities(entity[propName], visited, populatedPaths[propName]);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
restoreRelatedEntities(relatedValue, visited, populatedPaths)
|
|
703
|
+
{
|
|
704
|
+
if(!relatedValue || relatedValue instanceof this.Collection){
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
if(sc.isArray(relatedValue)){
|
|
708
|
+
for(let relatedEntity of relatedValue){
|
|
709
|
+
this.restoreRelatedForeignKeys(relatedEntity, visited, populatedPaths);
|
|
710
|
+
}
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
this.restoreRelatedForeignKeys(relatedValue, visited, populatedPaths);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
relationTargetClass(prop)
|
|
717
|
+
{
|
|
718
|
+
if(!sc.isFunction(prop?.entity)){
|
|
719
|
+
return false;
|
|
720
|
+
}
|
|
721
|
+
return prop.entity();
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
relationDriverFor(prop)
|
|
725
|
+
{
|
|
726
|
+
let targetClass = this.relationTargetClass(prop);
|
|
727
|
+
let targetClassName = (prop && prop.targetMeta) ? prop.targetMeta.className : false;
|
|
728
|
+
if(!targetClass && !targetClassName){
|
|
729
|
+
return false;
|
|
730
|
+
}
|
|
731
|
+
let entities = this.server.entityManager.entities;
|
|
732
|
+
for(let entityKey of Object.keys(entities)){
|
|
733
|
+
let candidate = entities[entityKey];
|
|
734
|
+
if(!candidate || !candidate.entitySchema || !candidate.entitySchema.meta){
|
|
735
|
+
continue;
|
|
736
|
+
}
|
|
737
|
+
if(targetClass && candidate.entitySchema.meta.class === targetClass){
|
|
738
|
+
return candidate;
|
|
739
|
+
}
|
|
740
|
+
if(targetClassName && candidate.entitySchema.meta.className === targetClassName){
|
|
741
|
+
return candidate;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
return false;
|
|
472
745
|
}
|
|
473
746
|
|
|
474
747
|
async createNested(newInstance, params, relations)
|
|
@@ -479,36 +752,42 @@ class MikroOrmDriver extends BaseDriver
|
|
|
479
752
|
let properties = this.entitySchema.meta.properties;
|
|
480
753
|
for(let relationName of Object.keys(properties)){
|
|
481
754
|
let prop = properties[relationName];
|
|
482
|
-
if(!prop.kind || (prop
|
|
755
|
+
if(!prop.kind || (!this.isOwningRelation(prop) && !this.isInverseRelation(prop))){
|
|
483
756
|
continue;
|
|
484
757
|
}
|
|
485
758
|
if(sc.isArray(relations) && 0 < relations.length && -1 === relations.indexOf(relationName)){
|
|
486
759
|
continue;
|
|
487
760
|
}
|
|
488
|
-
|
|
761
|
+
if(!sc.hasOwn(params, relationName)){
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
let relationDriver = this.relationDriverFor(prop);
|
|
489
765
|
if(!relationDriver){
|
|
490
|
-
Logger.warning('Entity not found for relation:'
|
|
766
|
+
Logger.warning('Entity not found for relation: '+relationName);
|
|
491
767
|
continue;
|
|
492
768
|
}
|
|
493
|
-
|
|
494
|
-
let isManyToOne = 'm:1' === prop.kind;
|
|
495
|
-
if(isManyToOne && sc.hasOwn(params, relationName)){
|
|
769
|
+
if(this.isOwningRelation(prop)){
|
|
496
770
|
await this.createOne(params, relationName, relationDriver, newInstance, prop);
|
|
497
771
|
continue;
|
|
498
772
|
}
|
|
499
|
-
if(
|
|
773
|
+
if(sc.isArray(params[relationName])){
|
|
500
774
|
await this.createMany(params, relationName, relationDriver, newInstance, prop);
|
|
775
|
+
continue;
|
|
776
|
+
}
|
|
777
|
+
if('1:1' === prop.kind && sc.isObject(params[relationName])){
|
|
778
|
+
await this.createOne(params, relationName, relationDriver, newInstance, prop);
|
|
501
779
|
}
|
|
502
780
|
}
|
|
503
781
|
}
|
|
504
782
|
|
|
505
783
|
async createOne(params, relationName, relationDriver, newInstance, prop)
|
|
506
784
|
{
|
|
507
|
-
let nestedData = params[relationName];
|
|
508
|
-
|
|
509
|
-
|
|
785
|
+
let nestedData = Object.assign({}, params[relationName]);
|
|
786
|
+
let fkColumn = this.nestedFkColumn(prop, relationDriver);
|
|
787
|
+
if(fkColumn){
|
|
788
|
+
nestedData[fkColumn] = newInstance.id;
|
|
510
789
|
}
|
|
511
|
-
let nestedObject = await relationDriver.
|
|
790
|
+
let nestedObject = await relationDriver.createEntity(nestedData);
|
|
512
791
|
await this.orm.em.flush();
|
|
513
792
|
newInstance[relationName] = nestedObject;
|
|
514
793
|
}
|
|
@@ -516,34 +795,19 @@ class MikroOrmDriver extends BaseDriver
|
|
|
516
795
|
async createMany(params, relationName, relationDriver, newInstance, prop)
|
|
517
796
|
{
|
|
518
797
|
let nestedArray = [];
|
|
798
|
+
let fkColumn = this.nestedFkColumn(prop, relationDriver);
|
|
519
799
|
for(let objectData of params[relationName]){
|
|
520
|
-
|
|
521
|
-
|
|
800
|
+
let nestedData = Object.assign({}, objectData);
|
|
801
|
+
if(fkColumn){
|
|
802
|
+
nestedData[fkColumn] = newInstance.id;
|
|
522
803
|
}
|
|
523
|
-
let nestedObject = await relationDriver.
|
|
804
|
+
let nestedObject = await relationDriver.createEntity(nestedData);
|
|
524
805
|
await this.orm.em.flush();
|
|
525
806
|
nestedArray.push(nestedObject);
|
|
526
807
|
}
|
|
527
808
|
newInstance[relationName] = nestedArray;
|
|
528
809
|
}
|
|
529
810
|
|
|
530
|
-
transformFkToRelations(params)
|
|
531
|
-
{
|
|
532
|
-
if(!params || 'object' !== typeof params){
|
|
533
|
-
return params;
|
|
534
|
-
}
|
|
535
|
-
let transformed = {...params};
|
|
536
|
-
for(let fkColumn of Object.keys(this.fkMappings)){
|
|
537
|
-
if(!sc.hasOwn(params, fkColumn) || null === params[fkColumn]){
|
|
538
|
-
continue;
|
|
539
|
-
}
|
|
540
|
-
let mapping = this.fkMappings[fkColumn];
|
|
541
|
-
transformed[mapping.relationKey] = this.orm.em.getReference(mapping.entityName, params[fkColumn]);
|
|
542
|
-
delete transformed[fkColumn];
|
|
543
|
-
}
|
|
544
|
-
return transformed;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
811
|
}
|
|
548
812
|
|
|
549
813
|
module.exports.MikroOrmDriver = MikroOrmDriver;
|