@reldens/storage 0.117.0 → 0.119.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.
Files changed (78) hide show
  1. package/.claude/test-architecture.md +27 -17
  2. package/CLAUDE.md +67 -11
  3. package/README.md +204 -22
  4. package/bin/reldens-storage.js +43 -10
  5. package/index.js +53 -12
  6. package/lib/drizzle/drizzle-data-server.js +123 -0
  7. package/lib/drizzle/drizzle-driver.js +228 -0
  8. package/lib/drizzle/drizzle-models-generation.js +49 -0
  9. package/lib/drizzle/drizzle-modules-loader.js +32 -0
  10. package/lib/drizzle/drizzle-modules-validator.js +74 -0
  11. package/lib/entities-generator.js +36 -7
  12. package/lib/entity-templates/drizzle-model.template +28 -0
  13. package/lib/entity-templates/query-builder-model.template +17 -0
  14. package/lib/generators/base-generator.js +13 -0
  15. package/lib/generators/entities-generation.js +0 -13
  16. package/lib/generators/models-generation.js +179 -680
  17. package/lib/generators/relations-detection.js +171 -0
  18. package/lib/knex/knex-data-server.js +128 -0
  19. package/lib/knex/knex-driver.js +157 -0
  20. package/lib/knex/knex-modules-loader.js +28 -0
  21. package/lib/knex/knex-modules-validator.js +45 -0
  22. package/lib/kysely/kysely-data-server.js +128 -0
  23. package/lib/kysely/kysely-driver.js +176 -0
  24. package/lib/kysely/kysely-modules-loader.js +28 -0
  25. package/lib/kysely/kysely-modules-validator.js +53 -0
  26. package/lib/mikro-orm/mikro-orm-data-server.js +48 -29
  27. package/lib/mikro-orm/mikro-orm-driver.js +317 -108
  28. package/lib/mikro-orm/mikro-orm-models-generation.js +178 -0
  29. package/lib/mikro-orm/mikro-orm-modules-loader.js +50 -0
  30. package/lib/mikro-orm/mikro-orm-modules-validator.js +46 -0
  31. package/lib/mysql2-connection-config.js +38 -0
  32. package/lib/objection-js/objection-js-data-server.js +71 -125
  33. package/lib/objection-js/objection-js-driver.js +4 -1
  34. package/lib/objection-js/objection-js-models-generation.js +98 -0
  35. package/lib/objection-js/objection-modules-loader.js +28 -0
  36. package/lib/objection-js/objection-modules-validator.js +31 -0
  37. package/lib/package-resolver.js +45 -0
  38. package/lib/prisma/prisma-client-loader.js +11 -1
  39. package/lib/prisma/prisma-driver.js +16 -2
  40. package/lib/prisma/prisma-filter-processor.js +5 -6
  41. package/lib/prisma/prisma-models-generation.js +236 -0
  42. package/lib/prisma/prisma-schema-generator.js +5 -1
  43. package/lib/prisma/prisma-type-caster.js +14 -0
  44. package/lib/query-builder-driver.js +210 -0
  45. package/lib/query-builder-models-generation.js +80 -0
  46. package/lib/relations-loader.js +192 -0
  47. package/lib/type-mapper.js +38 -0
  48. package/package.json +8 -8
  49. package/tests/.env.test.example +6 -0
  50. package/tests/fixtures/categories-fixtures.js +8 -0
  51. package/tests/fixtures/expected-entities/entities/test-product-details-entity.js +10 -1
  52. package/tests/fixtures/expected-entities/entities-translations.js +2 -0
  53. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-categories-model.js +13 -8
  54. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-product-details-model.js +30 -10
  55. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-products-model.js +15 -14
  56. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-reviews-model.js +13 -12
  57. package/tests/fixtures/expected-entities-prisma/models/prisma/test-product-details-model.js +3 -1
  58. package/tests/fixtures/product-details-fixtures.js +4 -1
  59. package/tests/fixtures/reviews-fixtures.js +2 -1
  60. package/tests/fixtures/sql/test-schema.sql +11 -2
  61. package/tests/fixtures/table-columns-fixtures.js +97 -0
  62. package/tests/integration/test-cross-driver-equivalence.js +286 -0
  63. package/tests/integration/test-nested-filters.js +265 -408
  64. package/tests/integration/test-relations.js +135 -0
  65. package/tests/integration/test-reldens-sample-data.js +255 -0
  66. package/tests/integration/test-reldens-shape-relations.js +292 -0
  67. package/tests/run-tests.js +111 -65
  68. package/tests/unit/test-drivers.js +7 -1
  69. package/tests/unit/test-models-generation.js +28 -18
  70. package/tests/utils/column-value-assert.js +139 -0
  71. package/tests/utils/entity-projection.js +111 -0
  72. package/tests/utils/observed-value.js +41 -0
  73. package/tests/utils/projection-difference.js +151 -0
  74. package/tests/utils/relations-shape-support.js +104 -0
  75. package/tests/utils/reldens-schema-loader.js +274 -0
  76. package/tests/utils/test-helpers.js +143 -13
  77. package/tests/utils/test-runner.js +26 -5
  78. package/tests/utils/whole-result-assert.js +243 -0
@@ -0,0 +1,31 @@
1
+ /**
2
+ *
3
+ * Reldens - ObjectionModulesValidator
4
+ *
5
+ */
6
+
7
+ const { Logger, sc } = require('@reldens/utils');
8
+
9
+ class ObjectionModulesValidator
10
+ {
11
+
12
+ static validate(objectionModules)
13
+ {
14
+ if(!sc.isObject(objectionModules)){
15
+ Logger.critical('Missing "objectionModules" object.');
16
+ return false;
17
+ }
18
+ if(!sc.isFunction(objectionModules.Model)){
19
+ Logger.critical('Invalid "objectionModules.Model".');
20
+ return false;
21
+ }
22
+ if(!sc.isFunction(objectionModules.Model.knex)){
23
+ Logger.critical('Invalid "objectionModules.Model", "knex" not found.');
24
+ return false;
25
+ }
26
+ return true;
27
+ }
28
+
29
+ }
30
+
31
+ module.exports.ObjectionModulesValidator = ObjectionModulesValidator;
@@ -0,0 +1,45 @@
1
+ /**
2
+ *
3
+ * Reldens - PackageResolver
4
+ *
5
+ */
6
+
7
+ const { Logger } = require('@reldens/utils');
8
+
9
+ class PackageResolver
10
+ {
11
+
12
+ static loadPackage(packageName, projectPath)
13
+ {
14
+ try {
15
+ return require(PackageResolver.resolvePath(packageName, projectPath));
16
+ } catch(error) {
17
+ Logger.critical(
18
+ 'Package "'+packageName+'" not found, install it in the project: npm install '+packageName,
19
+ error.message
20
+ );
21
+ return false;
22
+ }
23
+ }
24
+
25
+ static optionalPackage(packageName, projectPath)
26
+ {
27
+ try {
28
+ return require(PackageResolver.resolvePath(packageName, projectPath));
29
+ } catch(error) {
30
+ Logger.debug('Optional package "'+packageName+'" is not available.', error.message);
31
+ return false;
32
+ }
33
+ }
34
+
35
+ static resolvePath(packageName, projectPath)
36
+ {
37
+ if(!projectPath){
38
+ return packageName;
39
+ }
40
+ return require.resolve(packageName, {paths: [projectPath]});
41
+ }
42
+
43
+ }
44
+
45
+ module.exports.PackageResolver = PackageResolver;
@@ -40,7 +40,8 @@ class PrismaClientLoader
40
40
  +(connectionData.password ? ':'+connectionData.password : '')
41
41
  +'@'+connectionData.host
42
42
  +':'+connectionData.port
43
- +'/'+connectionData.database;
43
+ +'/'+connectionData.database
44
+ +PrismaClientLoader.connectionLimitParameter(connectionData);
44
45
  Logger.info('Creating PrismaClient with connection to: '+connectionData.database);
45
46
  return PrismaClientLoader.createWithAdapter(completedModules, connectionString);
46
47
  }
@@ -50,6 +51,15 @@ class PrismaClientLoader
50
51
  * @param {string} connectionUrl
51
52
  * @returns {Object|null}
52
53
  */
54
+ static connectionLimitParameter(connectionData)
55
+ {
56
+ let connectionLimit = sc.get(connectionData, 'connectionLimit', false);
57
+ if(!connectionLimit){
58
+ return '';
59
+ }
60
+ return '?connectionLimit='+connectionLimit;
61
+ }
62
+
53
63
  static createWithAdapter(prismaModules, connectionUrl)
54
64
  {
55
65
  if(!PrismaModulesValidator.validate(prismaModules)){
@@ -237,6 +237,9 @@ class PrismaDriver extends BaseDriver
237
237
 
238
238
  async createWithRelations(params, relations)
239
239
  {
240
+ if(sc.isString(relations)){
241
+ relations = this.parseRelationsString(relations);
242
+ }
240
243
  if(!sc.isArray(relations) || 0 === relations.length){
241
244
  relations = this.getAllRelations();
242
245
  }
@@ -343,11 +346,15 @@ class PrismaDriver extends BaseDriver
343
346
  async delete(filters = {})
344
347
  {
345
348
  let processedFilters = this.filterProcessor.processFilters(filters);
346
- return this.executeWithNormalization(
349
+ let deleteResult = await this.executeWithNormalization(
347
350
  () => this.model.deleteMany({where: processedFilters}),
348
351
  'Delete error: ',
349
352
  false
350
353
  );
354
+ if(!deleteResult){
355
+ return deleteResult;
356
+ }
357
+ return sc.get(deleteResult, 'count', 0);
351
358
  }
352
359
 
353
360
  async deleteById(id)
@@ -357,11 +364,15 @@ class PrismaDriver extends BaseDriver
357
364
  if(!found){
358
365
  return false;
359
366
  }
360
- return this.executeWithNormalization(
367
+ let deleteResult = await this.executeWithNormalization(
361
368
  () => this.model.delete({where: {[this.idFieldName]: castedId}}),
362
369
  'Delete by ID error: ',
363
370
  false
364
371
  );
372
+ if(!deleteResult){
373
+ return deleteResult;
374
+ }
375
+ return 1;
365
376
  }
366
377
 
367
378
  async count(filters)
@@ -540,6 +551,9 @@ class PrismaDriver extends BaseDriver
540
551
 
541
552
  async executeQueryWithRelations(modelMethod, query, relations, errorMessage, defaultReturn)
542
553
  {
554
+ if(sc.isString(relations)){
555
+ relations = this.parseRelationsString(relations);
556
+ }
543
557
  if(!sc.isArray(relations) || 0 === relations.length){
544
558
  relations = this.getAllRelations();
545
559
  }
@@ -35,12 +35,11 @@ class PrismaFilterProcessor
35
35
  let processedFilters = {};
36
36
  for(let key of Object.keys(filters)){
37
37
  let value = filters[key];
38
- if('AND' === key && sc.isArray(value)){
39
- processedFilters.AND = value.map(condition => this.processFilters(condition));
40
- continue;
41
- }
42
- if('OR' === key && sc.isArray(value)){
43
- processedFilters.OR = value.map(condition => this.processFilters(condition));
38
+ if(('AND' === key || 'OR' === key) && sc.isArray(value)){
39
+ if(0 === value.length){
40
+ continue;
41
+ }
42
+ processedFilters[key] = value.map(condition => this.processFilters(condition));
44
43
  continue;
45
44
  }
46
45
  let translatedKey = this.translateRelationKey(key);
@@ -0,0 +1,236 @@
1
+ /**
2
+ *
3
+ * Reldens - PrismaModelsGeneration
4
+ *
5
+ */
6
+
7
+ const { sc } = require('@reldens/utils');
8
+ const { TypeMapper } = require('../type-mapper');
9
+ const { RELATION_PREFIX } = require('../relation-key');
10
+
11
+ class PrismaModelsGeneration
12
+ {
13
+
14
+ constructor(relationsDetection)
15
+ {
16
+ this.relationsDetection = relationsDetection;
17
+ }
18
+
19
+ modelTableName(tableName)
20
+ {
21
+ return tableName.toLowerCase();
22
+ }
23
+
24
+ generateFkMappings(tableName, tableData)
25
+ {
26
+ return '';
27
+ }
28
+
29
+ generateIdColumn(columns)
30
+ {
31
+ let primaryKeyColumn = this.relationsDetection.detectPrimaryKeyColumn(columns);
32
+ if(!primaryKeyColumn || 'id' === primaryKeyColumn){
33
+ return '';
34
+ }
35
+ return '\n static get idColumn()\n {\n return \''+primaryKeyColumn+'\';\n }\n';
36
+ }
37
+
38
+ generateEntityProperties(columns, tableName, tableData)
39
+ {
40
+ let prismaProps = [];
41
+ for(let columnName of Object.keys(columns)){
42
+ let column = columns[columnName];
43
+ let propDef = columnName+': {\n type: \''+TypeMapper.mapDbTypeToPrismaType(column.type)+'\'';
44
+ if('PRI' === column.key){
45
+ propDef += ',\n id: true';
46
+ }
47
+ if(column.nullable){
48
+ propDef += ',\n optional: true';
49
+ }
50
+ prismaProps.push(propDef+'\n }');
51
+ }
52
+ return prismaProps.join(',\n ');
53
+ }
54
+
55
+ generateRelations(tableName, tableData, relationMetadata)
56
+ {
57
+ if(!relationMetadata || 0 === Object.keys(relationMetadata).length){
58
+ return '';
59
+ }
60
+ let relationTypesArray = [];
61
+ for(let prismaRelationName of Object.keys(relationMetadata)){
62
+ relationTypesArray.push(
63
+ ' '+prismaRelationName+': \''+relationMetadata[prismaRelationName].type+'\''
64
+ );
65
+ }
66
+ let allMappings = Object.assign(
67
+ {},
68
+ this.buildForwardMappings(tableData, relationMetadata),
69
+ this.buildReverseMappings(tableName, relationMetadata)
70
+ );
71
+ let relationMappingsArray = [];
72
+ for(let reldensKey of Object.keys(allMappings)){
73
+ relationMappingsArray.push(' \''+reldensKey+'\': \''+allMappings[reldensKey]+'\'');
74
+ }
75
+ return this.staticGetter('relationTypes', relationTypesArray)
76
+ +this.staticGetter('relationMappings', relationMappingsArray);
77
+ }
78
+
79
+ staticGetter(getterName, entries)
80
+ {
81
+ if(0 === entries.length){
82
+ return '';
83
+ }
84
+ return '\n\n static get '+getterName+'()\n {\n return {\n'+entries.join(',\n')+'\n };\n }';
85
+ }
86
+
87
+ buildForwardMappings(tableData, relationMetadata)
88
+ {
89
+ let mappings = {};
90
+ let referenceCounts = this.relationsDetection.countReferencesPerTable(tableData);
91
+ for(let columnName of Object.keys(tableData.columns)){
92
+ let column = tableData.columns[columnName];
93
+ if(!sc.hasOwn(column, 'referencedTable')){
94
+ continue;
95
+ }
96
+ let prismaName = this.findRelationForFk(columnName, column.referencedTable, relationMetadata);
97
+ if(!prismaName){
98
+ continue;
99
+ }
100
+ let reldensKey = this.relationsDetection.generateForwardRelationKey(
101
+ column.referencedTable,
102
+ columnName,
103
+ referenceCounts
104
+ );
105
+ mappings[reldensKey] = prismaName;
106
+ }
107
+ return mappings;
108
+ }
109
+
110
+ findRelationForFk(columnName, referencedTable, relationMetadata)
111
+ {
112
+ let lowerColumn = columnName.toLowerCase();
113
+ let candidates = this.singleRelationCandidates(referencedTable, relationMetadata);
114
+ for(let prismaName of candidates){
115
+ let lowerPrisma = prismaName.toLowerCase();
116
+ if(lowerPrisma.includes(lowerColumn.replace('_id', ''))){
117
+ return prismaName;
118
+ }
119
+ if(lowerPrisma.includes(lowerColumn)){
120
+ return prismaName;
121
+ }
122
+ }
123
+ if(0 === candidates.length){
124
+ return null;
125
+ }
126
+ return [...candidates].shift();
127
+ }
128
+
129
+ singleRelationCandidates(referencedTable, relationMetadata)
130
+ {
131
+ let lowerTable = referencedTable.toLowerCase();
132
+ let candidates = [];
133
+ for(let prismaName of Object.keys(relationMetadata)){
134
+ let relation = relationMetadata[prismaName];
135
+ if(relation.model.toLowerCase() !== lowerTable){
136
+ continue;
137
+ }
138
+ if(relation.isList){
139
+ continue;
140
+ }
141
+ candidates.push(prismaName);
142
+ }
143
+ return candidates;
144
+ }
145
+
146
+ buildReverseMappings(tableName, relationMetadata)
147
+ {
148
+ let mappings = {};
149
+ let reverseRelations = this.groupReverseRelationsByTable(relationMetadata);
150
+ for(let relatedTable of Object.keys(reverseRelations)){
151
+ Object.assign(
152
+ mappings,
153
+ this.tableReverseMappings(relatedTable, reverseRelations[relatedTable], tableName)
154
+ );
155
+ }
156
+ return mappings;
157
+ }
158
+
159
+ tableReverseMappings(relatedTable, relations, tableName)
160
+ {
161
+ let mappings = {};
162
+ for(let relInfo of relations){
163
+ let reldensKey = this.reverseRelationKey(relatedTable, relInfo.prismaName, tableName, relations.length);
164
+ mappings[reldensKey] = relInfo.prismaName;
165
+ }
166
+ return mappings;
167
+ }
168
+
169
+ groupReverseRelationsByTable(relationMetadata)
170
+ {
171
+ let reverseRelations = {};
172
+ for(let prismaName of Object.keys(relationMetadata)){
173
+ let relation = relationMetadata[prismaName];
174
+ if(this.isForwardRelation(relation)){
175
+ continue;
176
+ }
177
+ let relatedTable = relation.model.toLowerCase();
178
+ if(!reverseRelations[relatedTable]){
179
+ reverseRelations[relatedTable] = [];
180
+ }
181
+ reverseRelations[relatedTable].push({prismaName, relation});
182
+ }
183
+ return reverseRelations;
184
+ }
185
+
186
+ isForwardRelation(relation)
187
+ {
188
+ return sc.hasOwn(relation, 'foreignKeys')
189
+ && sc.isArray(relation.foreignKeys)
190
+ && 0 < relation.foreignKeys.length;
191
+ }
192
+
193
+ reverseRelationKey(relatedTable, prismaName, tableName, referenceCount)
194
+ {
195
+ if(1 === referenceCount){
196
+ return RELATION_PREFIX+relatedTable;
197
+ }
198
+ let columnHint = this.extractColumnHint(prismaName, relatedTable, tableName);
199
+ if(columnHint){
200
+ return RELATION_PREFIX+relatedTable+'_'+columnHint;
201
+ }
202
+ return RELATION_PREFIX+relatedTable;
203
+ }
204
+
205
+ extractColumnHint(prismaName, relatedTable, tableName)
206
+ {
207
+ let lowerName = prismaName.toLowerCase();
208
+ let lowerTable = tableName.toLowerCase();
209
+ let lowerRelated = relatedTable.toLowerCase();
210
+ let toSuffix = 'to'+lowerTable;
211
+ if(!lowerName.endsWith(toSuffix)){
212
+ toSuffix = 'to'+lowerRelated;
213
+ if(!lowerName.endsWith(toSuffix)){
214
+ return null;
215
+ }
216
+ }
217
+ let withoutSuffix = prismaName.slice(0, -toSuffix.length);
218
+ let prefixes = [
219
+ lowerRelated+'_'+lowerRelated+'_',
220
+ lowerRelated+'_',
221
+ lowerTable+'_'
222
+ ];
223
+ for(let prefix of prefixes){
224
+ if(withoutSuffix.toLowerCase().startsWith(prefix)){
225
+ withoutSuffix = withoutSuffix.slice(prefix.length);
226
+ }
227
+ }
228
+ if(withoutSuffix.endsWith('_id')){
229
+ withoutSuffix = withoutSuffix.slice(0, -3);
230
+ }
231
+ return withoutSuffix.toLowerCase();
232
+ }
233
+
234
+ }
235
+
236
+ module.exports.PrismaModelsGeneration = PrismaModelsGeneration;
@@ -139,7 +139,11 @@ class PrismaSchemaGenerator
139
139
 
140
140
  generateConfigFile()
141
141
  {
142
- let configContent = 'process.loadEnvFile(\'.env\');\n'
142
+ let configContent = 'try {\n'
143
+ +' process.loadEnvFile(\'.env\');\n'
144
+ +'} catch(error) {\n'
145
+ +' process.env.RELDENS_PRISMA_ENV_ERROR = error.message;\n'
146
+ +'}\n'
143
147
  +'module.exports = { datasource: { url: process.env.RELDENS_DB_URL } };\n';
144
148
  let configPath = FileHandler.joinPaths(process.cwd(), 'prisma.config.js');
145
149
  FileHandler.writeFile(configPath, configContent);
@@ -248,6 +248,17 @@ class PrismaTypeCaster
248
248
  return casted;
249
249
  }
250
250
 
251
+ isDecimalInstance(value)
252
+ {
253
+ if(!sc.isObject(value) || !sc.isFunction(value.toString)){
254
+ return false;
255
+ }
256
+ if(!sc.hasOwn(value, 'd') || !sc.hasOwn(value, 'e') || !sc.hasOwn(value, 's')){
257
+ return false;
258
+ }
259
+ return sc.isArray(value.d);
260
+ }
261
+
251
262
  normalizeReturnData(data)
252
263
  {
253
264
  if(null === data || undefined === data){
@@ -262,6 +273,9 @@ class PrismaTypeCaster
262
273
  if(data instanceof Date){
263
274
  return data;
264
275
  }
276
+ if(this.isDecimalInstance(data)){
277
+ return data.toString();
278
+ }
265
279
  if(sc.isArray(data)){
266
280
  return data.map(item => this.normalizeReturnData(item));
267
281
  }
@@ -0,0 +1,210 @@
1
+ /**
2
+ *
3
+ * Reldens - QueryBuilderDriver
4
+ *
5
+ */
6
+
7
+ const { BaseDriver } = require('./base-driver');
8
+ const { RelationsLoader } = require('./relations-loader');
9
+ const { ErrorManager, sc } = require('@reldens/utils');
10
+
11
+ class QueryBuilderDriver extends BaseDriver
12
+ {
13
+
14
+ constructor(props)
15
+ {
16
+ super(props);
17
+ this.server = sc.get(props, 'server', false);
18
+ this.relationsLoader = new RelationsLoader({driver: this, server: this.server});
19
+ }
20
+
21
+ databaseName()
22
+ {
23
+ return sc.get(this.config, 'database', '');
24
+ }
25
+
26
+ id()
27
+ {
28
+ return this.rawId || this.rawModel.tableName;
29
+ }
30
+
31
+ name()
32
+ {
33
+ return this.rawName || this.rawModel.tableName;
34
+ }
35
+
36
+ tableName()
37
+ {
38
+ return this.rawModel.tableName;
39
+ }
40
+
41
+ idColumn()
42
+ {
43
+ return sc.get(this.rawModel, 'idColumn', 'id');
44
+ }
45
+
46
+ property(propertyName)
47
+ {
48
+ return this.rawModel[propertyName] || null;
49
+ }
50
+
51
+ insertRow(data)
52
+ {
53
+ ErrorManager.error('QueryBuilderDriver insertRow() not implemented.');
54
+ }
55
+
56
+ selectRows(filters, options)
57
+ {
58
+ ErrorManager.error('QueryBuilderDriver selectRows() not implemented.');
59
+ }
60
+
61
+ selectOptions(useLimit)
62
+ {
63
+ return {
64
+ select: this.select,
65
+ limit: useLimit ? this.limit : 0,
66
+ offset: this.offset,
67
+ sortBy: this.sortBy,
68
+ sortDirection: this.sortDirection
69
+ };
70
+ }
71
+
72
+ createSingleFilter(field, fieldValue, operator = null)
73
+ {
74
+ if(null === operator){
75
+ return {[field]: fieldValue};
76
+ }
77
+ return {[field]: {operator, value: fieldValue}};
78
+ }
79
+
80
+ async create(params)
81
+ {
82
+ let insertId = await this.insertRow(params);
83
+ let createdId = insertId ? insertId : sc.get(params, this.idColumn(), false);
84
+ if(!createdId){
85
+ return false;
86
+ }
87
+ return await this.loadById(createdId);
88
+ }
89
+
90
+ createWithRelations(params, relations)
91
+ {
92
+ return this.relationsLoader.createWithRelations(params, relations);
93
+ }
94
+
95
+ updateBy(field, fieldValue, updatePatch, operator = null)
96
+ {
97
+ return this.update(this.createSingleFilter(field, fieldValue, operator), updatePatch);
98
+ }
99
+
100
+ async updateById(id, params)
101
+ {
102
+ await this.update({[this.idColumn()]: id}, params);
103
+ return await this.loadById(id);
104
+ }
105
+
106
+ async upsert(params, filters)
107
+ {
108
+ let idColumn = this.idColumn();
109
+ let providedId = sc.get(params, idColumn, false);
110
+ if(providedId){
111
+ let existentById = await this.loadById(providedId);
112
+ if(existentById){
113
+ return await this.updateById(providedId, params);
114
+ }
115
+ }
116
+ if(filters){
117
+ let existent = await this.loadOne(filters);
118
+ if(existent){
119
+ return await this.updateById(existent[idColumn], params);
120
+ }
121
+ }
122
+ return await this.create(params);
123
+ }
124
+
125
+ deleteById(id)
126
+ {
127
+ return this.delete({[this.idColumn()]: id});
128
+ }
129
+
130
+ countWithRelations(filters, relations)
131
+ {
132
+ return this.count(filters);
133
+ }
134
+
135
+ loadAll()
136
+ {
137
+ return this.selectRows({}, this.selectOptions(true));
138
+ }
139
+
140
+ load(filters = {})
141
+ {
142
+ return this.selectRows(filters, this.selectOptions(true));
143
+ }
144
+
145
+ async loadOne(filters)
146
+ {
147
+ let options = this.selectOptions(false);
148
+ options.limit = 1;
149
+ let rows = await this.selectRows(filters, options);
150
+ return rows.shift() || null;
151
+ }
152
+
153
+ loadBy(field, fieldValue, operator = null)
154
+ {
155
+ return this.load(this.createSingleFilter(field, fieldValue, operator));
156
+ }
157
+
158
+ loadOneBy(field, fieldValue, operator = null)
159
+ {
160
+ return this.loadOne(this.createSingleFilter(field, fieldValue, operator));
161
+ }
162
+
163
+ loadById(id)
164
+ {
165
+ return this.loadOne({[this.idColumn()]: id});
166
+ }
167
+
168
+ loadByIds(ids)
169
+ {
170
+ return this.load({[this.idColumn()]: {operator: 'IN', value: ids}});
171
+ }
172
+
173
+ async loadAllWithRelations(relations)
174
+ {
175
+ return await this.relationsLoader.populate(await this.loadAll(), relations);
176
+ }
177
+
178
+ async loadWithRelations(filters, relations)
179
+ {
180
+ return await this.relationsLoader.populate(await this.load(filters), relations);
181
+ }
182
+
183
+ async loadByWithRelations(field, fieldValue, relations, operator = null)
184
+ {
185
+ return await this.relationsLoader.populate(await this.loadBy(field, fieldValue, operator), relations);
186
+ }
187
+
188
+ async loadByIdWithRelations(id, relations)
189
+ {
190
+ return await this.relationsLoader.populate(await this.loadById(id), relations);
191
+ }
192
+
193
+ async loadOneWithRelations(filters, relations)
194
+ {
195
+ return await this.relationsLoader.populate(await this.loadOne(filters), relations);
196
+ }
197
+
198
+ async loadOneByWithRelations(field, fieldValue, relations, operator = null)
199
+ {
200
+ return await this.relationsLoader.populate(await this.loadOneBy(field, fieldValue, operator), relations);
201
+ }
202
+
203
+ rawQuery(content)
204
+ {
205
+ return this.server.rawQuery(content);
206
+ }
207
+
208
+ }
209
+
210
+ module.exports.QueryBuilderDriver = QueryBuilderDriver;