@cheetah.js/orm 0.1.45 → 0.1.47

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 (56) hide show
  1. package/dist/SqlBuilder.d.ts +18 -33
  2. package/dist/SqlBuilder.js +102 -584
  3. package/dist/driver/bun-driver.base.d.ts +35 -1
  4. package/dist/driver/bun-driver.base.js +132 -0
  5. package/dist/driver/bun-mysql.driver.d.ts +13 -6
  6. package/dist/driver/bun-mysql.driver.js +47 -72
  7. package/dist/driver/bun-pg.driver.d.ts +12 -6
  8. package/dist/driver/bun-pg.driver.js +27 -59
  9. package/dist/driver/driver.interface.d.ts +1 -1
  10. package/dist/index.d.ts +1 -0
  11. package/dist/index.js +1 -0
  12. package/dist/query/model-transformer.d.ts +21 -0
  13. package/dist/query/model-transformer.js +118 -0
  14. package/dist/query/sql-column-manager.d.ts +25 -0
  15. package/dist/query/sql-column-manager.js +124 -0
  16. package/dist/query/sql-condition-builder.d.ts +36 -0
  17. package/dist/query/sql-condition-builder.js +160 -0
  18. package/dist/query/sql-join-manager.d.ts +39 -0
  19. package/dist/query/sql-join-manager.js +224 -0
  20. package/dist/repository/Repository.d.ts +125 -0
  21. package/dist/repository/Repository.js +150 -0
  22. package/dist/utils/value-processor.d.ts +14 -0
  23. package/dist/utils/value-processor.js +84 -0
  24. package/package.json +3 -3
  25. package/build.ts +0 -14
  26. package/cheetah.config.ts +0 -14
  27. package/dist/SqlBuilder.js.map +0 -1
  28. package/dist/bun/index.js +0 -233434
  29. package/dist/bun/index.js.map +0 -336
  30. package/dist/common/email.vo.js.map +0 -1
  31. package/dist/common/uuid.js.map +0 -1
  32. package/dist/common/value-object.js.map +0 -1
  33. package/dist/constants.js.map +0 -1
  34. package/dist/decorators/entity.decorator.js.map +0 -1
  35. package/dist/decorators/enum.decorator.js.map +0 -1
  36. package/dist/decorators/event-hook.decorator.js.map +0 -1
  37. package/dist/decorators/index.decorator.js.map +0 -1
  38. package/dist/decorators/one-many.decorator.js.map +0 -1
  39. package/dist/decorators/primary-key.decorator.js.map +0 -1
  40. package/dist/decorators/property.decorator.js.map +0 -1
  41. package/dist/domain/base-entity.js.map +0 -1
  42. package/dist/domain/collection.js.map +0 -1
  43. package/dist/domain/entities.js.map +0 -1
  44. package/dist/domain/reference.js.map +0 -1
  45. package/dist/driver/bun-driver.base.js.map +0 -1
  46. package/dist/driver/bun-mysql.driver.js.map +0 -1
  47. package/dist/driver/bun-pg.driver.js.map +0 -1
  48. package/dist/driver/driver.interface.js.map +0 -1
  49. package/dist/driver/pg-driver.d.ts +0 -63
  50. package/dist/driver/pg-driver.js +0 -335
  51. package/dist/driver/pg-driver.js.map +0 -1
  52. package/dist/entry.js.map +0 -1
  53. package/dist/index.js.map +0 -1
  54. package/dist/orm.js.map +0 -1
  55. package/dist/orm.service.js.map +0 -1
  56. package/dist/utils.js.map +0 -1
@@ -1,13 +1,14 @@
1
1
  import { EntityStorage } from './domain/entities';
2
2
  import { Orm } from './orm';
3
- import { ValueObject } from './common/value-object';
4
- import { BaseEntity } from './domain/base-entity';
5
- import { extendsFrom } from './utils';
3
+ import { ValueProcessor } from './utils/value-processor';
4
+ import { SqlConditionBuilder } from './query/sql-condition-builder';
5
+ import { ModelTransformer } from './query/model-transformer';
6
+ import { SqlColumnManager } from './query/sql-column-manager';
7
+ import { SqlJoinManager } from './query/sql-join-manager';
6
8
  export class SqlBuilder {
7
9
  constructor(model) {
8
10
  this.statements = {};
9
11
  this.aliases = new Set();
10
- this.lastKeyNotOperator = '';
11
12
  this.updatedColumns = [];
12
13
  this.originalColumns = [];
13
14
  const orm = Orm.getInstance();
@@ -16,6 +17,13 @@ export class SqlBuilder {
16
17
  this.entityStorage = EntityStorage.getInstance();
17
18
  this.getEntity(model);
18
19
  this.statements.hooks = this.entity.hooks;
20
+ this.modelTransformer = new ModelTransformer(this.entityStorage);
21
+ this.columnManager = new SqlColumnManager(this.entityStorage, this.statements, this.entity);
22
+ const applyJoinWrapper = (relationship, value, alias) => {
23
+ return this.joinManager.applyJoin(relationship, value, alias);
24
+ };
25
+ this.conditionBuilder = new SqlConditionBuilder(this.entityStorage, applyJoinWrapper, this.statements);
26
+ this.joinManager = new SqlJoinManager(this.entityStorage, this.statements, this.entity, this.model, this.driver, this.logger, this.conditionBuilder, this.columnManager, this.modelTransformer, () => this.originalColumns, this.getAlias.bind(this));
19
27
  }
20
28
  select(columns) {
21
29
  const tableName = this.entity.tableName || this.model.name.toLowerCase();
@@ -37,25 +45,23 @@ export class SqlBuilder {
37
45
  }
38
46
  insert(values) {
39
47
  const { tableName, schema } = this.getTableName();
40
- // @ts-ignore
41
- values = processValuesForInsert(values, this.entity);
48
+ const processedValues = ValueProcessor.processForInsert(values, this.entity);
42
49
  this.statements.statement = 'insert';
43
- this.statements.instance = upEntity(values, this.model, 'insert');
50
+ this.statements.instance = ValueProcessor.createInstance(processedValues, this.model, 'insert');
44
51
  this.statements.alias = this.getAlias(tableName);
45
52
  this.statements.table = `"${schema}"."${tableName}"`;
46
- this.statements.values = this.withUpdatedValues(this.withDefaultValues(values, this.entity), this.entity);
53
+ this.statements.values = this.withUpdatedValues(this.withDefaultValues(processedValues, this.entity), this.entity);
47
54
  this.reflectToValues();
48
55
  return this;
49
56
  }
50
57
  update(values) {
51
58
  const { tableName, schema } = this.getTableName();
52
- // @ts-ignore
53
- values = processValuesForUpdate(values, this.entity);
59
+ const processedValues = ValueProcessor.processForUpdate(values, this.entity);
54
60
  this.statements.statement = 'update';
55
61
  this.statements.alias = this.getAlias(tableName);
56
62
  this.statements.table = `${schema}.${tableName}`;
57
- this.statements.values = this.withUpdatedValues(values, this.entity);
58
- this.statements.instance = upEntity(values, this.model, 'update');
63
+ this.statements.values = this.withUpdatedValues(processedValues, this.entity);
64
+ this.statements.instance = ValueProcessor.createInstance(processedValues, this.model, 'update');
59
65
  return this;
60
66
  }
61
67
  where(where) {
@@ -68,10 +74,10 @@ export class SqlBuilder {
68
74
  newWhere[key] = where[key];
69
75
  continue;
70
76
  }
71
- newWhere[getColumnName(key, this.entity)] = where[key];
77
+ newWhere[ValueProcessor.getColumnName(key, this.entity)] = where[key];
72
78
  }
73
79
  where = newWhere;
74
- this.statements.where = this.conditionToSql(where, this.statements.alias, this.model);
80
+ this.statements.where = this.conditionBuilder.build(where, this.statements.alias, this.model);
75
81
  return this;
76
82
  }
77
83
  orderBy(orderBy) {
@@ -91,7 +97,7 @@ export class SqlBuilder {
91
97
  }
92
98
  load(load) {
93
99
  load?.forEach(relationshipPath => {
94
- this.addJoinForRelationshipPath(this.entity, relationshipPath);
100
+ this.joinManager.addJoinForRelationshipPath(relationshipPath);
95
101
  });
96
102
  if (this.statements.join) {
97
103
  this.statements.join = this.statements.join?.reverse();
@@ -101,34 +107,6 @@ export class SqlBuilder {
101
107
  }
102
108
  return this;
103
109
  }
104
- addJoinForRelationshipPath(entity, relationshipPath) {
105
- const relationshipNames = relationshipPath.split('.');
106
- let currentEntity = entity;
107
- let currentAlias = this.statements.alias;
108
- let statement = this.statements.strategy === 'joined' ? this.statements.join : this.statements.selectJoin;
109
- let nameAliasProperty = this.statements.strategy === 'joined' ? 'joinAlias' : 'alias';
110
- relationshipNames.forEach((relationshipName, index) => {
111
- const relationship = currentEntity.relations.find(rel => rel.propertyKey === relationshipName);
112
- if (!relationship) {
113
- // @ts-ignore
114
- throw new Error(`Relationship "${relationshipName}" not found in entity "${currentEntity.name}"`);
115
- }
116
- const isLastRelationship = index === relationshipNames.length - 1;
117
- if (index === (relationshipNames.length - 2 >= 0 ? relationshipNames.length - 2 : 0)) {
118
- const join = statement?.find(j => j.joinProperty === relationshipName);
119
- if (join) {
120
- // @ts-ignore
121
- currentAlias = join[nameAliasProperty];
122
- }
123
- }
124
- if (relationship.relation === 'many-to-one' && isLastRelationship) {
125
- this.applyJoin(relationship, {}, currentAlias);
126
- statement = this.statements.strategy === 'joined' ? this.statements.join : this.statements.selectJoin;
127
- currentAlias = statement[statement.length - 1][nameAliasProperty];
128
- }
129
- currentEntity = this.entityStorage.get(relationship.entity());
130
- });
131
- }
132
110
  getPrimaryKeyColumnName(entity) {
133
111
  // Lógica para obter o nome da coluna de chave primária da entidade
134
112
  // Aqui você pode substituir por sua própria lógica, dependendo da estrutura do seu projeto
@@ -138,14 +116,12 @@ export class SqlBuilder {
138
116
  }
139
117
  async execute() {
140
118
  if (!this.statements.columns) {
141
- this.statements.columns = this.generateColumns();
119
+ this.statements.columns = this.columnManager.generateColumns(this.model, this.updatedColumns);
142
120
  }
143
121
  else {
144
- this.extractAliasForColumns();
145
- this.filterInvalidColumns();
122
+ this.statements.columns = [...this.columnManager.processUserColumns(this.statements.columns), ...this.updatedColumns];
146
123
  }
147
124
  this.statements.join = this.statements.join?.reverse();
148
- this.includeUpdatedColumns();
149
125
  this.beforeHooks();
150
126
  const result = await this.driver.executeStatement(this.statements);
151
127
  this.logExecution(result);
@@ -178,9 +154,9 @@ export class SqlBuilder {
178
154
  return undefined;
179
155
  }
180
156
  const entities = result.query.rows[0];
181
- const model = await this.transformToModel(this.model, this.statements, entities);
157
+ const model = await this.modelTransformer.transform(this.model, this.statements, entities);
182
158
  this.afterHooks(model);
183
- await this.handleSelectJoin(entities, model);
159
+ await this.joinManager.handleSelectJoin(entities, model);
184
160
  return model;
185
161
  }
186
162
  async executeAndReturnFirstOrFail() {
@@ -190,9 +166,9 @@ export class SqlBuilder {
190
166
  throw new Error('Result not found');
191
167
  }
192
168
  const entities = result.query.rows[0];
193
- const model = await this.transformToModel(this.model, this.statements, entities);
169
+ const model = await this.modelTransformer.transform(this.model, this.statements, entities);
194
170
  this.afterHooks(model);
195
- await this.handleSelectJoin(entities, model);
171
+ await this.joinManager.handleSelectJoin(entities, model);
196
172
  return model;
197
173
  }
198
174
  async executeAndReturnAll() {
@@ -203,133 +179,16 @@ export class SqlBuilder {
203
179
  const rows = result.query.rows;
204
180
  const results = [];
205
181
  for (const row of rows) {
206
- const models = this.transformToModel(this.model, this.statements, row);
182
+ const models = this.modelTransformer.transform(this.model, this.statements, row);
207
183
  this.afterHooks(models);
208
- await this.handleSelectJoin(row, models);
184
+ await this.joinManager.handleSelectJoin(row, models);
209
185
  results.push(models);
210
186
  }
211
187
  return results;
212
188
  }
213
- async handleSelectJoin(entities, models) {
214
- if (!this.statements.selectJoin || this.statements.selectJoin.length === 0) {
215
- return;
216
- }
217
- for (const join of this.statements.selectJoin.reverse()) {
218
- let ids = entities[`${join.originAlias}_${join.primaryKey}`];
219
- if (typeof ids === 'undefined') {
220
- // get of models
221
- const selectJoined = this.statements.selectJoin.find(j => j.joinEntity === join.originEntity);
222
- if (!selectJoined) {
223
- continue;
224
- }
225
- ids = this.findIdRecursively(models, selectJoined, join);
226
- }
227
- if (Array.isArray(ids)) {
228
- ids = ids.map((id) => this.t(id)).join(', ');
229
- }
230
- if (join.where) {
231
- join.where = `${join.where} AND ${join.alias}."${join.fkKey}" IN (${ids})`;
232
- }
233
- else {
234
- join.where = `${join.alias}."${join.fkKey}" IN (${ids})`;
235
- }
236
- if (join.columns && join.columns.length > 0) {
237
- join.columns = join.columns.map(
238
- // @ts-ignore
239
- (column) => `${join.alias}."${column}" as "${join.alias}_${column}"`);
240
- }
241
- else {
242
- join.columns = this.getColumnsEntity(join.joinEntity, join.alias);
243
- }
244
- const child = await this.driver.executeStatement(join);
245
- this.logger.debug(`SQL: ${child.sql} [${Date.now() - child.startTime}ms]`);
246
- const property = this.entityStorage.get(this.model).relations.find((rel) => rel.propertyKey === join.joinProperty);
247
- const values = child.query.rows.map((row) => this.transformToModel(join.joinEntity, join, row));
248
- const path = this.getPathForSelectJoin(join);
249
- this.setValueByPath(models, path, property?.type === Array ? [...values] : values[0]);
250
- }
251
- return models;
252
- }
253
- getPathForSelectJoin(selectJoin) {
254
- const path = this.getPathForSelectJoinRecursive(this.statements, selectJoin);
255
- return path.reverse();
256
- }
257
- setValueByPath(obj, path, value) {
258
- let currentObj = obj;
259
- for (let i = 0; i < path.length - 1; i++) {
260
- const key = path[i];
261
- currentObj[key] = currentObj[key] || {};
262
- currentObj = currentObj[key];
263
- }
264
- currentObj[path[path.length - 1]] = value;
265
- }
266
- getPathForSelectJoinRecursive(statements, selectJoin) {
267
- const originJoin = this.statements.selectJoin.find(j => j.joinEntity === selectJoin.originEntity);
268
- let pathInJoin = [];
269
- if (!originJoin) {
270
- return [selectJoin.joinProperty];
271
- }
272
- if (originJoin.originEntity !== statements.originEntity) {
273
- pathInJoin = this.getPathForSelectJoinRecursive(statements, originJoin);
274
- }
275
- return [selectJoin.joinProperty, ...pathInJoin];
276
- }
277
- findIdRecursively(models, selectJoined, join) {
278
- let ids = models[selectJoined.originProperty][join.primaryKey];
279
- if (typeof ids === 'undefined') {
280
- const nextSelectJoined = this.statements.selectJoin.find(j => j.joinEntity === selectJoined.originEntity);
281
- if (nextSelectJoined) {
282
- // Chamada recursiva para a próxima camada
283
- ids = this.findIdRecursively(models, nextSelectJoined, join);
284
- }
285
- }
286
- return ids;
287
- }
288
- generateColumns() {
289
- let columns = [
290
- ...this.getColumnsEntity(this.model, this.statements.alias),
291
- ];
292
- if (this.statements.join) {
293
- columns = [
294
- ...columns,
295
- ...this.statements.join.flatMap(join => this.getColumnsEntity(join.joinEntity, join.joinAlias)),
296
- ];
297
- }
298
- return columns;
299
- }
300
- extractAliasForColumns() {
301
- // @ts-ignore
302
- this.statements.columns = this.statements.columns.map((column) => {
303
- return this.discoverColumnAlias(column);
304
- }).flat();
305
- }
306
- filterInvalidColumns() {
307
- this.statements.columns = this.statements.columns.filter(Boolean);
308
- }
309
- includeUpdatedColumns() {
310
- this.statements.columns.push(...this.updatedColumns);
311
- }
312
189
  logExecution(result) {
313
190
  this.logger.debug(`SQL: ${result.sql} [${Date.now() - result.startTime}ms]`);
314
191
  }
315
- /**
316
- * @deprecated Use inTransaction() instead
317
- */
318
- startTransaction() {
319
- throw new Error('startTransaction() is deprecated. Use inTransaction() instead.');
320
- }
321
- /**
322
- * @deprecated Use inTransaction() instead
323
- */
324
- commit() {
325
- throw new Error('commit() is deprecated. Use inTransaction() instead.');
326
- }
327
- /**
328
- * @deprecated Use inTransaction() instead
329
- */
330
- rollback() {
331
- throw new Error('rollback() is deprecated. Use inTransaction() instead.');
332
- }
333
192
  async inTransaction(callback) {
334
193
  return await this.driver.transaction(async (tx) => {
335
194
  // @ts-ignore
@@ -337,243 +196,32 @@ export class SqlBuilder {
337
196
  });
338
197
  }
339
198
  objectToStringMap(obj, parentKey = '') {
340
- let result = [];
341
- for (let key in obj) {
342
- if (obj.hasOwnProperty(key)) {
343
- let fullKey = parentKey ? `${parentKey}.${key}` : key;
344
- if (typeof obj[key] === 'object' && obj[key] !== null) {
345
- result = result.concat(this.objectToStringMap(obj[key], fullKey));
346
- }
347
- else {
348
- result.push(`${this.discoverColumnAlias(fullKey, true)} ${obj[key]}`);
349
- }
350
- }
351
- }
352
- return result;
199
+ return Object.keys(obj)
200
+ .filter(key => obj.hasOwnProperty(key))
201
+ .flatMap(key => this.mapObjectKey(obj, key, parentKey));
353
202
  }
354
- discoverColumnAlias(column, onlyAlias = false) {
355
- if (!column.includes('.')) {
356
- if (onlyAlias) {
357
- return `${this.statements.alias}."${column}"`;
358
- }
359
- return `${this.statements.alias}."${column}" as ${this.statements.alias}_${column}`;
203
+ mapObjectKey(obj, key, parentKey) {
204
+ const fullKey = parentKey ? `${parentKey}.${key}` : key;
205
+ if (this.isNestedObject(obj[key])) {
206
+ return this.objectToStringMap(obj[key], fullKey);
360
207
  }
361
- if (typeof this.statements.join === 'undefined' && typeof this.statements.selectJoin === 'undefined') {
362
- throw new Error('Join not found');
363
- }
364
- const entities = column.split('.');
365
- let lastEntity = this.model;
366
- let lastAlias = this.statements.alias;
367
- const relationsMap = new Map(this.entity.relations.map(rel => [rel.propertyKey, rel]));
368
- const joinMap = new Map();
369
- const joinSelectMap = new Map();
370
- this.statements.join?.forEach(join => joinMap.set(join.joinProperty, join));
371
- this.statements.selectJoin?.forEach(join => joinSelectMap.set(join.joinProperty, join));
372
- for (let i = 0; i < entities.length; i++) {
373
- if (i === 0) {
374
- const relation = relationsMap.get(entities[i]);
375
- lastEntity = relation?.entity();
376
- // @ts-ignore
377
- if (joinMap.has(entities[i])) {
378
- lastAlias = joinMap.get(entities[i]).joinAlias;
379
- }
380
- else {
381
- lastAlias = joinSelectMap.get(entities[i])?.alias;
382
- return undefined;
383
- }
384
- }
385
- else {
386
- if ((i + 1) === entities.length) {
387
- if (onlyAlias) {
388
- return `${lastAlias}."${entities[i]}"`;
389
- }
390
- return `${lastAlias}."${entities[i]}" as ${lastAlias}_${entities[i]}`;
391
- }
392
- const lastStatement = joinMap.get(entities[i]);
393
- lastEntity = lastStatement?.joinEntity;
394
- // @ts-ignore
395
- lastAlias = lastStatement?.joinAlias;
396
- }
208
+ if (parentKey) {
209
+ return [`${this.columnManager.discoverAlias(fullKey, true)} ${obj[key]}`];
397
210
  }
398
- return '';
211
+ const columnName = ValueProcessor.getColumnName(key, this.entity);
212
+ return [`${this.columnManager.discoverAlias(columnName, true)} ${obj[key]}`];
213
+ }
214
+ isNestedObject(value) {
215
+ return typeof value === 'object' && value !== null;
399
216
  }
400
217
  getTableName() {
401
218
  const tableName = this.entity.tableName || this.model.name.toLowerCase();
402
219
  const schema = this.entity.schema || 'public';
403
220
  return { tableName, schema };
404
221
  }
405
- addSimpleConditionToSql(key, value, alias = null, operator = '=') {
406
- const aliasToUse = alias || this.statements.alias;
407
- const valueByType = (typeof value === 'string') ? `'${value}'` : value;
408
- return `${aliasToUse}.${key} ${operator} ${valueByType}`;
409
- }
410
- addInConditionToSql(key, values, alias = null) {
411
- const aliasToUse = alias || this.statements.alias;
412
- return `${aliasToUse}.${key} IN (${values.map(val => (typeof val === 'string') ? `'${val}'` : val).join(", ")})`;
413
- }
414
- addLogicalOperatorToSql(conditions, operator) {
415
- return `(${conditions.join(` ${operator} `)})`;
416
- }
417
- conditionToSql(condition, alias, model) {
418
- const sqlParts = [];
419
- const operators = ['$eq', '$ne', '$in', '$nin', '$like', '$gt', '$gte', '$lt', '$lte', '$and', '$or'];
420
- for (let [key, value] of Object.entries(condition)) {
421
- if (extendsFrom(ValueObject, value.constructor.prototype)) {
422
- value = value.getValue();
423
- }
424
- if (!operators.includes(key)) {
425
- this.lastKeyNotOperator = key;
426
- }
427
- const entity = this.entityStorage.get(model);
428
- const relationShip = entity.relations?.find(rel => rel.propertyKey === key);
429
- if (relationShip) {
430
- const sql = this.applyJoin(relationShip, value, alias);
431
- if (this.statements.strategy === 'joined') {
432
- sqlParts.push(sql);
433
- }
434
- }
435
- else if (typeof value !== 'object' || value === null) {
436
- if (key === '$eq') {
437
- sqlParts.push(this.addSimpleConditionToSql(this.lastKeyNotOperator, value, alias, '='));
438
- continue;
439
- }
440
- sqlParts.push(this.addSimpleConditionToSql(key, value, alias));
441
- }
442
- else if (!(operators.includes(key)) && Array.isArray(value)) {
443
- sqlParts.push(this.addInConditionToSql(key, value, alias));
444
- }
445
- else {
446
- if (['$or', '$and'].includes(key)) {
447
- sqlParts.push(this.addLogicalOperatorToSql(value.map((cond) => this.conditionToSql(cond, alias, model)), key.toUpperCase().replace('$', '')));
448
- }
449
- for (const operator of operators) {
450
- if (operator in value) {
451
- switch (operator) {
452
- case '$eq':
453
- sqlParts.push(this.addSimpleConditionToSql(key, value['$eq'], alias, '='));
454
- break;
455
- case '$ne':
456
- sqlParts.push(this.addSimpleConditionToSql(key, value['$ne'], alias, '!='));
457
- break;
458
- case '$in':
459
- sqlParts.push(this.addInConditionToSql(key, value['$in'], alias));
460
- break;
461
- case '$nin':
462
- sqlParts.push(`${alias}.${key} NOT IN (${value['$nin'].map((val) => this.t(val)).join(", ")})`);
463
- break;
464
- case '$like':
465
- sqlParts.push(`${alias}.${key} LIKE '${value['$like']}'`);
466
- break;
467
- case '$gt':
468
- sqlParts.push(`${alias}.${key} > ${value['$gt']}`);
469
- break;
470
- case '$gte':
471
- sqlParts.push(`${alias}.${key} >= ${value['$gte']}`);
472
- break;
473
- case '$lt':
474
- sqlParts.push(`${alias}.${key} < ${value['$lt']}`);
475
- break;
476
- case '$lte':
477
- sqlParts.push(`${alias}.${key} <= ${value['$lte']}`);
478
- break;
479
- case '$and':
480
- case '$or':
481
- const parts = value[operator].map((cond) => this.conditionToSql(cond, alias, model));
482
- sqlParts.push(this.addLogicalOperatorToSql(parts, operator.toUpperCase().replace('$', '')));
483
- break;
484
- }
485
- }
486
- }
487
- }
488
- }
489
- if (sqlParts.length === 0) {
490
- return '';
491
- }
492
- return this.addLogicalOperatorToSql(sqlParts, 'AND');
493
- }
494
222
  t(value) {
495
223
  return (typeof value === 'string') ? `'${value}'` : value;
496
224
  }
497
- applyJoin(relationShip, value, alias) {
498
- const { tableName, schema } = this.getTableName();
499
- const { tableName: joinTableName, schema: joinSchema, hooks: joinHooks, } = this.entityStorage.get(relationShip.entity()) || {
500
- tableName: relationShip.entity().name.toLowerCase(),
501
- schema: 'public',
502
- };
503
- let originPrimaryKey = 'id';
504
- for (const prop in this.entity.properties) {
505
- if (this.entity.properties[prop].options.isPrimary) {
506
- originPrimaryKey = prop;
507
- break;
508
- }
509
- }
510
- const joinAlias = `${this.getAlias(joinTableName)}`;
511
- const joinWhere = this.conditionToSql(value, joinAlias, relationShip.entity());
512
- let on = '';
513
- switch (relationShip.relation) {
514
- case "one-to-many":
515
- on = `${joinAlias}."${this.getFkKey(relationShip)}" = ${alias}."${originPrimaryKey}"`;
516
- break;
517
- case "many-to-one":
518
- on = `${alias}."${relationShip.columnName}" = ${joinAlias}."${this.getFkKey(relationShip)}"`;
519
- break;
520
- }
521
- if (this.statements.strategy === 'joined') {
522
- this.statements.join = this.statements.join || [];
523
- this.statements.join.push({
524
- joinAlias: joinAlias,
525
- joinTable: joinTableName,
526
- joinSchema: joinSchema || 'public',
527
- joinWhere: joinWhere,
528
- joinProperty: relationShip.propertyKey,
529
- originAlias: alias,
530
- originSchema: schema,
531
- originTable: tableName,
532
- propertyKey: relationShip.propertyKey,
533
- joinEntity: relationShip.entity(),
534
- type: 'LEFT',
535
- // @ts-ignore
536
- on,
537
- originalEntity: relationShip.originalEntity,
538
- hooks: joinHooks,
539
- });
540
- }
541
- else {
542
- this.statements.selectJoin = this.statements.selectJoin || [];
543
- this.statements.selectJoin.push({
544
- statement: 'select',
545
- columns: this.originalColumns.filter(column => column.startsWith(`${relationShip.propertyKey}`)).map(column => column.split('.')[1]) || [],
546
- table: `"${joinSchema || 'public'}"."${joinTableName}"`,
547
- alias: joinAlias,
548
- where: joinWhere,
549
- joinProperty: relationShip.propertyKey,
550
- fkKey: this.getFkKey(relationShip),
551
- primaryKey: originPrimaryKey,
552
- originAlias: alias,
553
- originProperty: relationShip.propertyKey,
554
- joinEntity: relationShip.entity(),
555
- originEntity: relationShip.originalEntity,
556
- hooks: joinHooks,
557
- });
558
- }
559
- return joinWhere;
560
- }
561
- getFkKey(relationShip) {
562
- // se for nullable, deverá retornar o primary key da entidade target
563
- if (typeof relationShip.fkKey === 'undefined') {
564
- return 'id'; // TODO: Pegar dinamicamente o primary key da entidade target
565
- }
566
- // se o fkKey é uma função, ele retornará a propriedade da entidade que é a chave estrangeira
567
- // precisamos pegar o nome dessa propriedade
568
- if (typeof relationShip.fkKey === 'string') {
569
- return relationShip.fkKey;
570
- }
571
- const match = /\.(?<propriedade>[\w]+)/.exec(relationShip.fkKey.toString());
572
- const propertyKey = match ? match.groups.propriedade : '';
573
- const entity = this.entityStorage.get(relationShip.entity());
574
- const property = Object.entries(entity.properties).find(([key, _value]) => key === propertyKey)?.[1];
575
- return property.options.columnName;
576
- }
577
225
  // private conditionLogicalOperatorToSql<T extends typeof BaseEntity>(conditions: Condition<T>[], operator: 'AND' | 'OR'): string {
578
226
  // const sqlParts = conditions.map(cond => this.conditionToSql(cond));
579
227
  // return this.addLogicalOperatorToSql(sqlParts, operator);
@@ -586,57 +234,6 @@ export class SqlBuilder {
586
234
  }
587
235
  this.entity = entity;
588
236
  }
589
- transformToModel(model, statement, data) {
590
- const instance = new model();
591
- instance.$_isPersisted = true;
592
- const entitiesByAlias = {
593
- [statement.alias]: instance,
594
- };
595
- const entitiesOptions = new Map();
596
- entitiesOptions.set(statement.alias, this.entityStorage.get(instance.constructor));
597
- if (this.statements.join) {
598
- this.statements.join.forEach(join => {
599
- const joinInstance = new join.joinEntity();
600
- joinInstance.$_isPersisted = true;
601
- entitiesByAlias[join.joinAlias] = joinInstance;
602
- entitiesOptions.set(join.joinAlias, this.entityStorage.get(joinInstance.constructor));
603
- });
604
- }
605
- Object.entries(data).forEach(([key, value]) => {
606
- const index = key.indexOf('_');
607
- const alias = key.substring(0, index);
608
- const prop = key.substring(index + 1);
609
- const entity = entitiesByAlias[alias];
610
- if (!entity) {
611
- return;
612
- }
613
- const ep = Object.entries(entitiesOptions.get(alias).properties).find(([_, value]) => value.options.columnName === prop);
614
- const entityProperty = ep?.[1];
615
- const keyProperty = ep?.[0];
616
- if (entityProperty) {
617
- if (extendsFrom(ValueObject, entityProperty.type.prototype)) {
618
- // @ts-ignore
619
- entity[keyProperty] = new entityProperty.type(value);
620
- return;
621
- }
622
- entity[keyProperty] = value;
623
- }
624
- });
625
- if (this.statements.join) {
626
- this.statements.join.forEach(join => {
627
- const { joinAlias, originAlias, propertyKey } = join;
628
- const originEntity = entitiesByAlias[originAlias];
629
- const joinEntity = entitiesByAlias[joinAlias];
630
- const property = entitiesOptions.get(originAlias).relations.find(rel => rel.propertyKey === propertyKey);
631
- if (!originEntity || !joinEntity) {
632
- return;
633
- }
634
- // @ts-ignore
635
- originEntity[propertyKey] = (property.type === Array) ? (originEntity[propertyKey]) ? [...originEntity[propertyKey], joinEntity] : [joinEntity] : joinEntity;
636
- });
637
- }
638
- return instance;
639
- }
640
237
  /**
641
238
  * Retrieves an alias for a given table name.
642
239
  *
@@ -645,170 +242,91 @@ export class SqlBuilder {
645
242
  * @returns {string} - The alias for the table name.
646
243
  */
647
244
  getAlias(tableName) {
648
- const alias = tableName.split('').shift() || '';
649
- let counter = 1;
650
- let uniqueAlias = `${alias}${counter}`;
651
- while (this.aliases.has(uniqueAlias)) {
652
- counter++;
653
- uniqueAlias = `${alias}${counter}`;
654
- }
245
+ const baseAlias = tableName.split('').shift() || '';
246
+ const uniqueAlias = this.generateUniqueAlias(baseAlias);
655
247
  this.aliases.add(uniqueAlias);
656
248
  return uniqueAlias;
657
249
  }
658
- getColumnsEntity(entity, alias) {
659
- const e = this.entityStorage.get(entity);
660
- if (!e) {
661
- throw new Error('Entity not found');
662
- }
663
- const columns = Object.keys(e.properties).map(key => `${alias}."${e.properties[key].options.columnName}" as "${alias}_${e.properties[key].options.columnName}"`);
664
- if (e.relations) {
665
- for (const relation of e.relations) {
666
- if (relation.relation === 'many-to-one') {
667
- // @ts-ignore
668
- columns.push(`${alias}."${relation.columnName}" as "${alias}_${relation.columnName}"`);
669
- }
670
- }
250
+ generateUniqueAlias(baseAlias) {
251
+ let counter = 1;
252
+ let candidate = `${baseAlias}${counter}`;
253
+ while (this.aliases.has(candidate)) {
254
+ counter++;
255
+ candidate = `${baseAlias}${counter}`;
671
256
  }
672
- return columns;
257
+ return candidate;
673
258
  }
674
259
  withDefaultValues(values, entityOptions) {
675
- const property = Object.entries(entityOptions.properties).filter(([_, value]) => value.options.onInsert);
260
+ this.applyDefaultProperties(values, entityOptions);
261
+ this.applyOnInsertProperties(values, entityOptions);
262
+ return values;
263
+ }
264
+ applyDefaultProperties(values, entityOptions) {
676
265
  const defaultProperties = Object.entries(entityOptions.properties).filter(([_, value]) => value.options.default);
677
266
  for (const [key, property] of defaultProperties) {
678
- if (typeof values[key] === 'undefined') {
679
- if (typeof property.options.default === 'function') {
680
- values[key] = eval(property.options.default());
681
- }
682
- else {
683
- values[key] = eval(property.options.default);
684
- }
685
- }
267
+ this.setDefaultValue(values, key, property);
686
268
  }
687
- property.forEach(([key, property]) => {
688
- values[key] = property.options.onInsert();
689
- this.updatedColumns.push(`${this.statements.alias}."${key}" as "${this.statements.alias}_${key}"`);
690
- });
691
- return values;
269
+ }
270
+ setDefaultValue(values, key, property) {
271
+ if (typeof values[key] !== 'undefined')
272
+ return;
273
+ values[key] = typeof property.options.default === 'function'
274
+ ? property.options.default()
275
+ : property.options.default;
276
+ }
277
+ applyOnInsertProperties(values, entityOptions) {
278
+ const properties = Object.entries(entityOptions.properties).filter(([_, value]) => value.options.onInsert);
279
+ properties.forEach(([key, property]) => this.applyOnInsert(values, key, property));
280
+ }
281
+ applyOnInsert(values, key, property) {
282
+ values[key] = property.options.onInsert();
283
+ this.updatedColumns.push(`${this.statements.alias}."${key}" as "${this.statements.alias}_${key}"`);
692
284
  }
693
285
  withUpdatedValues(values, entityOptions) {
694
- const property = Object.entries(entityOptions.properties).filter(([_, value]) => value.options.onUpdate);
695
- property.forEach(([key, property]) => {
696
- values[property.options.columnName] = property.options.onUpdate();
697
- this.updatedColumns.push(`${this.statements.alias}."${property.options.columnName}" as "${this.statements.alias}_${property.options.columnName}"`);
698
- });
286
+ const properties = Object.entries(entityOptions.properties).filter(([_, value]) => value.options.onUpdate);
287
+ properties.forEach(([key, property]) => this.applyOnUpdate(values, property));
699
288
  return values;
700
289
  }
290
+ applyOnUpdate(values, property) {
291
+ const columnName = property.options.columnName;
292
+ values[columnName] = property.options.onUpdate();
293
+ this.updatedColumns.push(`${this.statements.alias}."${columnName}" as "${this.statements.alias}_${columnName}"`);
294
+ }
701
295
  callHook(type, model) {
702
296
  const hooks = this.statements.hooks?.filter(hook => hook.type === type) || [];
703
297
  const instance = model || this.statements.instance;
704
- for (const hook of hooks) {
705
- instance[hook.propertyName]();
706
- if (!model) {
707
- this.reflectToValues();
708
- }
709
- }
298
+ hooks.forEach(hook => this.executeHook(hook, instance, !model));
299
+ }
300
+ executeHook(hook, instance, shouldReflect) {
301
+ instance[hook.propertyName]();
302
+ if (shouldReflect)
303
+ this.reflectToValues();
710
304
  }
711
305
  reflectToValues() {
712
306
  for (const key in this.statements.instance) {
713
- if (key.startsWith('$')) {
714
- continue;
715
- }
716
- if (key.startsWith('_')) {
717
- continue;
718
- }
719
- if (this.entity.properties[key]) {
720
- this.statements.values[this.entity.properties[key].options.columnName] = this.statements.instance[key];
307
+ if (this.shouldSkipKey(key))
721
308
  continue;
722
- }
723
- const rel = this.entity.relations.find(rel => rel.propertyKey === key);
724
- if (rel) {
725
- this.statements.values[rel.columnName] = this.statements.instance[key];
726
- }
309
+ this.reflectKey(key);
727
310
  }
728
311
  }
729
- }
730
- function processValuesForInsert(values, options) {
731
- const newValue = {};
732
- for (const value in values) {
733
- const columnName = getColumnName(value, options);
734
- if (extendsFrom(ValueObject, values[value].constructor.prototype)) {
735
- newValue[columnName] = values[value].getValue();
736
- continue;
737
- }
738
- if (values[value] instanceof BaseEntity) {
739
- // @ts-ignore
740
- newValue[columnName] = values[value].id; // TODO: get primary key
741
- continue;
742
- }
743
- newValue[columnName] = values[value];
312
+ shouldSkipKey(key) {
313
+ return key.startsWith('$') || key.startsWith('_');
744
314
  }
745
- return newValue;
746
- }
747
- function processValuesForUpdate(values, options) {
748
- const newValue = {};
749
- for (const value in values) {
750
- if (extendsFrom(ValueObject, values[value].constructor.prototype)) {
751
- newValue[getColumnName(value, options)] = values[value].getValue();
752
- continue;
315
+ reflectKey(key) {
316
+ if (this.entity.properties[key]) {
317
+ this.reflectProperty(key);
318
+ return;
753
319
  }
754
- newValue[getColumnName(value, options)] = values[value];
320
+ this.reflectRelation(key);
755
321
  }
756
- return newValue;
757
- }
758
- function getColumnName(propertyKey, entity) {
759
- const property = entity.properties[propertyKey];
760
- const relation = entity.relations?.find(rel => rel.propertyKey === propertyKey);
761
- if (propertyKey.startsWith('$')) {
762
- return propertyKey;
763
- }
764
- if (!property) {
765
- if (relation) {
766
- return relation.columnName || propertyKey;
767
- }
768
- throw new Error('Property not found');
322
+ reflectProperty(key) {
323
+ const columnName = this.entity.properties[key].options.columnName;
324
+ this.statements.values[columnName] = this.statements.instance[key];
769
325
  }
770
- return property.options.columnName || propertyKey;
771
- }
772
- function upEntity(values, entity, moment = undefined) {
773
- const entityStorage = EntityStorage.getInstance();
774
- const entityOptions = entityStorage.get(entity);
775
- // @ts-ignore
776
- const instance = new entity();
777
- if (!entityOptions) {
778
- throw new Error('Entity not found');
779
- }
780
- const property = Object.entries(entityOptions.properties);
781
- const relations = entityOptions.relations;
782
- property.forEach(([key, property]) => {
783
- if (property.options.onInsert && moment === 'insert') {
784
- instance[key] = property.options.onInsert();
785
- }
786
- if (property.options.onInsert && moment === 'update') {
787
- instance[key] = property.options.onUpdate();
788
- }
789
- if (key in values) {
790
- instance[key] = values[property.options.columnName];
791
- }
792
- });
793
- if (relations) {
794
- for (const relation of relations) {
795
- if (relation.relation === 'many-to-one') {
796
- // @ts-ignore
797
- instance[relation.propertyKey] = values[relation.columnName];
798
- }
799
- }
800
- }
801
- return instance;
802
- }
803
- function convertWhereObj(where, entity) {
804
- let newWhere = {};
805
- for (const key in where) {
806
- if (where[key] instanceof Object) { // Se o valor atual é um objeto, chame a mesma função recursivamente
807
- newWhere[getColumnName(key, entity)] = convertWhereObj(where[key], entity);
808
- }
809
- else {
810
- newWhere[getColumnName(key, entity)] = where[key];
326
+ reflectRelation(key) {
327
+ const rel = this.entity.relations.find(rel => rel.propertyKey === key);
328
+ if (rel) {
329
+ this.statements.values[rel.columnName] = this.statements.instance[key];
811
330
  }
812
331
  }
813
- return newWhere;
814
332
  }