@sqb/connect 4.12.0 → 4.13.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 (65) hide show
  1. package/cjs/client/cursor.js +1 -1
  2. package/cjs/client/extensions.js +38 -11
  3. package/cjs/client/helpers.js +7 -6
  4. package/cjs/client/sqb-client.js +2 -2
  5. package/cjs/client/sqb-connection.js +7 -4
  6. package/cjs/index.js +19 -20
  7. package/cjs/orm/backward.js +5 -5
  8. package/cjs/orm/commands/command.helper.js +39 -11
  9. package/cjs/orm/commands/create.command.js +2 -1
  10. package/cjs/orm/commands/find.command.js +21 -35
  11. package/cjs/orm/commands/row-converter.js +5 -10
  12. package/cjs/orm/decorators/column.decorator.js +6 -3
  13. package/cjs/orm/decorators/embedded.decorator.js +1 -2
  14. package/cjs/orm/decorators/entity.decorator.js +1 -2
  15. package/cjs/orm/decorators/events.decorator.js +6 -7
  16. package/cjs/orm/decorators/foreignkey.decorator.js +1 -2
  17. package/cjs/orm/decorators/index.decorator.js +1 -2
  18. package/cjs/orm/decorators/link.decorator.js +7 -5
  19. package/cjs/orm/decorators/primarykey.decorator.js +3 -3
  20. package/cjs/orm/decorators/transform.decorator.js +2 -3
  21. package/cjs/orm/model/association.js +9 -11
  22. package/cjs/orm/model/entity-metadata.js +4 -2
  23. package/cjs/orm/orm.const.js +2 -1
  24. package/cjs/orm/repository.class.js +31 -36
  25. package/cjs/orm/util/apply-mixins.js +3 -3
  26. package/cjs/orm/util/extract-keyvalues.js +7 -5
  27. package/cjs/orm/util/orm.helper.js +8 -12
  28. package/cjs/orm/util/parse-fields-projection.js +64 -0
  29. package/cjs/orm/util/serialize-field.js +1 -2
  30. package/esm/client/cursor.js +1 -1
  31. package/esm/client/extensions.js +36 -8
  32. package/esm/client/helpers.js +2 -0
  33. package/esm/client/sqb-client.js +3 -3
  34. package/esm/client/sqb-connection.js +7 -4
  35. package/esm/index.js +18 -17
  36. package/esm/orm/commands/command.helper.js +36 -7
  37. package/esm/orm/commands/create.command.js +2 -1
  38. package/esm/orm/commands/find.command.js +21 -35
  39. package/esm/orm/commands/row-converter.js +5 -10
  40. package/esm/orm/decorators/column.decorator.js +5 -1
  41. package/esm/orm/decorators/link.decorator.js +6 -3
  42. package/esm/orm/decorators/primarykey.decorator.js +2 -1
  43. package/esm/orm/model/association.js +9 -11
  44. package/esm/orm/model/entity-metadata.js +4 -2
  45. package/esm/orm/orm.const.js +1 -0
  46. package/esm/orm/repository.class.js +31 -36
  47. package/esm/orm/util/apply-mixins.js +2 -1
  48. package/esm/orm/util/extract-keyvalues.js +6 -3
  49. package/esm/orm/util/orm.helper.js +2 -6
  50. package/esm/orm/util/parse-fields-projection.js +59 -0
  51. package/package.json +10 -6
  52. package/types/client/adapter.d.ts +1 -1
  53. package/types/client/cursor-stream.d.ts +0 -1
  54. package/types/client/extensions.d.ts +14 -3
  55. package/types/client/sqb-client.d.ts +1 -1
  56. package/types/client/sqb-connection.d.ts +1 -1
  57. package/types/client/types.d.ts +1 -1
  58. package/types/index.d.ts +18 -17
  59. package/types/orm/commands/find.command.d.ts +3 -4
  60. package/types/orm/commands/row-converter.d.ts +1 -1
  61. package/types/orm/decorators/column.decorator.d.ts +1 -0
  62. package/types/orm/model/field-metadata.d.ts +1 -1
  63. package/types/orm/orm.const.d.ts +1 -0
  64. package/types/orm/repository.class.d.ts +11 -8
  65. package/types/orm/util/parse-fields-projection.d.ts +10 -0
package/esm/index.js CHANGED
@@ -1,23 +1,12 @@
1
1
  import 'reflect-metadata';
2
- export * from './client/types.js';
3
2
  export * from './client/adapter.js';
3
+ export * from './client/cursor.js';
4
+ export * from './client/extensions.js';
4
5
  export * from './client/sqb-client.js';
5
6
  export * from './client/sqb-connection.js';
6
- export * from './client/cursor.js';
7
- export { registerAdapter, unRegisterAdapter } from './client/extensions.js';
8
- export * from './orm/orm.type.js';
9
- export * from './orm/orm.const.js';
7
+ export * from './client/types.js';
8
+ export * from './orm/backward.js';
10
9
  export * from './orm/base-entity.js';
11
- export * from './orm/repository.class.js';
12
- export * from './orm/model/entity-metadata.js';
13
- export * from './orm/model/field-metadata.js';
14
- export * from './orm/model/column-field-metadata.js';
15
- export * from './orm/model/embedded-field-metadata.js';
16
- export * from './orm/model/association-field-metadata.js';
17
- export * from './orm/model/association.js';
18
- export * from './orm/model/association-node.js';
19
- export * from './orm/model/index-metadata.js';
20
- export * from './orm/model/link-chain.js';
21
10
  export * from './orm/decorators/column.decorator.js';
22
11
  export * from './orm/decorators/embedded.decorator.js';
23
12
  export * from './orm/decorators/entity.decorator.js';
@@ -27,5 +16,17 @@ export * from './orm/decorators/index.decorator.js';
27
16
  export * from './orm/decorators/link.decorator.js';
28
17
  export * from './orm/decorators/primarykey.decorator.js';
29
18
  export * from './orm/decorators/transform.decorator.js';
30
- export { isColumnField, isEmbeddedField, isAssociationField, isEntityClass } from './orm/util/orm.helper.js';
31
- export * from './orm/backward.js';
19
+ export * from './orm/model/association.js';
20
+ export * from './orm/model/association-field-metadata.js';
21
+ export * from './orm/model/association-node.js';
22
+ export * from './orm/model/column-field-metadata.js';
23
+ export * from './orm/model/embedded-field-metadata.js';
24
+ export * from './orm/model/entity-metadata.js';
25
+ export * from './orm/model/field-metadata.js';
26
+ export * from './orm/model/index-metadata.js';
27
+ export * from './orm/model/link-chain.js';
28
+ export * from './orm/orm.const.js';
29
+ export * from './orm/orm.type.js';
30
+ export * from './orm/repository.class.js';
31
+ export { isAssociationField, isColumnField, isEmbeddedField, isEntityClass } from './orm/util/orm.helper.js';
32
+ export * from './orm/util/parse-fields-projection.js';
@@ -1,4 +1,4 @@
1
- import { And, Eq, Exists, Field, InnerJoin, isCompOperator, isLogicalOperator, LeftOuterJoin, Raw, Select, } from '@sqb/builder';
1
+ import { And, Eq, Exists, Field, InnerJoin, isCompOperator, isLogicalOperator, LeftOuterJoin, op, OperatorType, Raw, Select, } from '@sqb/builder';
2
2
  import { EmbeddedFieldMetadata } from '../model/embedded-field-metadata.js';
3
3
  import { EntityMetadata } from '../model/entity-metadata.js';
4
4
  import { isAssociationField, isColumnField, isEmbeddedField } from '../util/orm.helper.js';
@@ -47,7 +47,7 @@ export async function joinAssociation(joinInfos, association, parentAlias, inner
47
47
  }
48
48
  export async function prepareFilter(entityDef, filter, trgOp, tableAlias = 'T') {
49
49
  let srcOp;
50
- if (isLogicalOperator(filter))
50
+ if (isLogicalOperator(filter) && filter._operatorType === trgOp._operatorType)
51
51
  srcOp = filter;
52
52
  else {
53
53
  srcOp = And();
@@ -56,6 +56,7 @@ export async function prepareFilter(entityDef, filter, trgOp, tableAlias = 'T')
56
56
  else
57
57
  srcOp.add(filter);
58
58
  }
59
+ const associationPathCache = {};
59
60
  for (const item of srcOp._items) {
60
61
  if (isLogicalOperator(item)) {
61
62
  const ctor = Object.getPrototypeOf(item).constructor;
@@ -74,19 +75,34 @@ export async function prepareFilter(entityDef, filter, trgOp, tableAlias = 'T')
74
75
  let _curPrefix = '';
75
76
  let _curSuffix = '';
76
77
  let subSelect;
77
- for (let i = 0; i < l; i++) {
78
+ let currentOp = trgOp;
79
+ let i = 0;
80
+ const parentPath = itemPath
81
+ .slice(0, l - 2)
82
+ .join('.')
83
+ .toLowerCase();
84
+ const cached = associationPathCache[parentPath];
85
+ if (cached) {
86
+ /** Jump to last item */
87
+ i = l - 1;
88
+ _curEntity = cached._curEntity;
89
+ _curAlias = cached._curAlias;
90
+ currentOp = cached.currentOp;
91
+ }
92
+ for (i; i < l; i++) {
78
93
  pt = itemPath[i];
79
94
  const col = EntityMetadata.getField(_curEntity, pt);
80
95
  if (!col)
81
96
  throw new Error(`Unknown property (${item._left}) defined in filter`);
82
- // if last item on path
97
+ /** if last item on path */
83
98
  if (i === l - 1) {
84
99
  if (!isColumnField(col))
85
100
  throw new Error(`Invalid column expression (${item._left}) defined in filter`);
86
101
  const ctor = Object.getPrototypeOf(item).constructor;
87
- trgOp.add(new ctor(Field(_curAlias + '.' + _curPrefix + col.fieldName + _curSuffix, col.dataType, col.isArray), item._right));
102
+ currentOp.add(new ctor(Field(_curAlias + '.' + _curPrefix + col.fieldName + _curSuffix, col.dataType, col.isArray), item._right));
88
103
  }
89
104
  else {
105
+ /** if not last item on path */
90
106
  if (isColumnField(col))
91
107
  throw new Error(`Invalid column (${item._left}) defined in filter`);
92
108
  if (isEmbeddedField(col)) {
@@ -104,8 +120,13 @@ export async function prepareFilter(entityDef, filter, trgOp, tableAlias = 'T')
104
120
  const targetCol = await col.association.resolveTargetProperty();
105
121
  subSelect = Select(Raw('1')).from(_curEntity.tableName + ' K');
106
122
  subSelect.where(Eq(Field('K.' + targetCol.fieldName, targetCol.dataType, targetCol.isArray), Field(tableAlias + '.' + keyCol.fieldName, keyCol.dataType, keyCol.isArray)));
107
- trgOp.add(Exists(subSelect));
108
- trgOp = subSelect._where;
123
+ currentOp.add(Exists(subSelect));
124
+ if (currentOp._operatorType !== OperatorType.and) {
125
+ currentOp = op.or();
126
+ subSelect.where(currentOp);
127
+ }
128
+ else
129
+ currentOp = subSelect._where;
109
130
  if (col.association.conditions) {
110
131
  await prepareFilter(_curEntity, col.association.conditions, trgOp, 'K');
111
132
  }
@@ -124,6 +145,14 @@ export async function prepareFilter(entityDef, filter, trgOp, tableAlias = 'T')
124
145
  _curAlias = joinAlias;
125
146
  node = node.next;
126
147
  }
148
+ /** If next path is the last one */
149
+ if (i === l - 2) {
150
+ associationPathCache[parentPath] = {
151
+ _curEntity,
152
+ _curAlias,
153
+ currentOp,
154
+ };
155
+ }
127
156
  }
128
157
  }
129
158
  continue;
@@ -60,8 +60,9 @@ export class CreateCommand {
60
60
  if (typeof col.serialize === 'function')
61
61
  v = col.serialize(v, col.name);
62
62
  if (v == null) {
63
- if (col.notNull && !col.autoGenerated)
63
+ if (col.notNull && !col.autoGenerated) {
64
64
  throw new Error(`${entity.name}.${col.name} is required and can't be null`);
65
+ }
65
66
  continue;
66
67
  }
67
68
  ColumnFieldMetadata.checkEnumValue(col, v);
@@ -1,9 +1,9 @@
1
1
  import { And, In, Param, Select } from '@sqb/builder';
2
- import { Entity } from '../decorators/entity.decorator.js';
3
2
  import { AssociationNode } from '../model/association-node.js';
4
3
  import { EmbeddedFieldMetadata } from '../model/embedded-field-metadata.js';
5
4
  import { EntityMetadata } from '../model/entity-metadata.js';
6
5
  import { isAssociationField, isColumnField, isEmbeddedField } from '../util/orm.helper.js';
6
+ import { parseFieldsProjection } from '../util/parse-fields-projection.js';
7
7
  import { joinAssociationGetLast, prepareFilter } from './command.helper.js';
8
8
  import { RowConverter } from './row-converter.js';
9
9
  const SORT_ORDER_PATTERN = /^([-+])?(.*)$/;
@@ -53,9 +53,7 @@ export class FindCommand {
53
53
  maxEagerFetch: args.maxEagerFetch,
54
54
  });
55
55
  await command.addFields({
56
- pick: args.pick,
57
- include: args.include,
58
- omit: args.omit,
56
+ projection: args.projection,
59
57
  sort: args.sort,
60
58
  });
61
59
  if (args.filter)
@@ -68,22 +66,10 @@ export class FindCommand {
68
66
  const tableAlias = opts.tableAlias || this.resultAlias;
69
67
  const entity = opts.entity || this._getEntityFromAlias(tableAlias);
70
68
  const converter = opts.converter || this.converter;
71
- const _pick = opts.pick ? opts.pick.map(x => x.toLowerCase()) : undefined;
72
- const _omit = opts.omit ? opts.omit.map(x => x.toLowerCase()) : undefined;
73
- const _include = opts.include ? opts.include.map(x => x.toLowerCase()) : undefined;
74
- let requestedFields;
75
- if (_pick)
76
- requestedFields = [..._pick];
77
- else {
78
- requestedFields = Entity.getFieldNames(entity.ctor).reduce((a, x) => {
79
- const f = Entity.getField(entity.ctor, x);
80
- if (f && !f.exclusive)
81
- a.push(x.toLowerCase());
82
- return a;
83
- }, []);
84
- }
85
- if (_include)
86
- requestedFields.push(..._include);
69
+ const projection = typeof opts.projection === 'string' || Array.isArray(opts.projection)
70
+ ? parseFieldsProjection(opts.projection)
71
+ : opts.projection;
72
+ const defaultFields = !projection || !Object.values(projection).find(p => !p.sign);
87
73
  const sortFields = opts.sort && opts.sort.length ? opts.sort.map(x => x.toLowerCase()) : undefined;
88
74
  const prefix = opts.prefix || '';
89
75
  const suffix = opts.suffix || '';
@@ -92,12 +78,16 @@ export class FindCommand {
92
78
  if (!col || col.hidden)
93
79
  continue;
94
80
  const colNameLower = col.name.toLowerCase();
95
- // Ignore field if in excluded list
96
- if (_omit && _omit.includes(colNameLower))
97
- continue;
98
- // Check if field request list
99
- if (!requestedFields.find((x) => x === colNameLower || x.startsWith(colNameLower + '.')))
81
+ const p = projection?.[colNameLower];
82
+ if (
83
+ /** Ignore if field is omitted */
84
+ p?.sign === '-' ||
85
+ /** Ignore if default fields and field is not in projection */
86
+ (!defaultFields && !p) ||
87
+ /** Ignore if default fields enabled and fields is exclusive */
88
+ (defaultFields && col.exclusive && !p)) {
100
89
  continue;
90
+ }
101
91
  // Add field to select list if field is a column
102
92
  if (isColumnField(col)) {
103
93
  const fieldAlias = this._selectColumn(tableAlias, col, prefix, suffix);
@@ -122,9 +112,7 @@ export class FindCommand {
122
112
  entity: typ,
123
113
  prefix: col.fieldNamePrefix,
124
114
  suffix: col.fieldNameSuffix,
125
- pick: _pick?.includes(colNameLower) ? undefined : extractSubFields(colNameLower, _pick),
126
- include: extractSubFields(colNameLower, _include),
127
- omit: extractSubFields(colNameLower, _omit),
115
+ projection: p?.projection,
128
116
  sort: extractSubFields(colNameLower, sortFields),
129
117
  });
130
118
  continue;
@@ -142,9 +130,7 @@ export class FindCommand {
142
130
  tableAlias: joinInfo.joinAlias,
143
131
  converter: subConverter,
144
132
  entity: joinInfo.targetEntity,
145
- pick: _pick?.includes(colNameLower) ? undefined : extractSubFields(colNameLower, _pick),
146
- include: extractSubFields(colNameLower, _include),
147
- omit: extractSubFields(colNameLower, _omit),
133
+ projection: p?.projection,
148
134
  sort: extractSubFields(colNameLower, sortFields),
149
135
  });
150
136
  continue;
@@ -164,9 +150,7 @@ export class FindCommand {
164
150
  await findCommand.filter(In(targetCol.name, Param(parentField)));
165
151
  const sort = sortFields && extractSubFields(colNameLower, sortFields);
166
152
  await findCommand.addFields({
167
- pick: _pick?.includes(colNameLower) ? undefined : extractSubFields(colNameLower, _pick),
168
- include: extractSubFields(colNameLower, _include),
169
- omit: extractSubFields(colNameLower, _omit),
153
+ projection: p?.projection,
170
154
  sort,
171
155
  });
172
156
  if (sort)
@@ -260,16 +244,18 @@ export class FindCommand {
260
244
  if (args.offset)
261
245
  query.offset(args.offset);
262
246
  // joins must be added last
263
- if (this._joins)
247
+ if (this._joins) {
264
248
  for (const j of this._joins) {
265
249
  query.join(j.join);
266
250
  }
251
+ }
267
252
  // Execute query
268
253
  const resp = await args.connection.execute(query, {
269
254
  params: args?.params,
270
255
  fetchRows: args?.limit,
271
256
  objectRows: false,
272
257
  cursor: false,
258
+ prettyPrint: args.prettyPrint,
273
259
  });
274
260
  // Create objects
275
261
  if (resp.rows && resp.fields) {
@@ -1,13 +1,7 @@
1
1
  import { DataType } from '@sqb/builder';
2
- const isValueProperty = (prop) => {
3
- return prop.fieldAlias && !prop.converter;
4
- };
5
- const isObjectProperty = (prop) => {
6
- return prop.converter && !prop.findCommand;
7
- };
8
- const isNestedProperty = (prop) => {
9
- return prop.converter && prop.findCommand;
10
- };
2
+ const isValueProperty = (prop) => prop.fieldAlias && !prop.converter;
3
+ const isObjectProperty = (prop) => prop.converter && !prop.findCommand;
4
+ const isNestedProperty = (prop) => prop.converter && prop.findCommand;
11
5
  export class RowConverter {
12
6
  constructor(resultType, parent) {
13
7
  this.resultType = resultType;
@@ -150,8 +144,9 @@ export class RowConverter {
150
144
  }
151
145
  },
152
146
  });
153
- if (r.length > findCommand.maxEagerFetch)
147
+ if (r.length > findCommand.maxEagerFetch) {
154
148
  throw new Error(`Number of returning rows for "${propKey}" exceeds maxEagerFetch limit`);
149
+ }
155
150
  for (let i = 0; i < result.length; i++) {
156
151
  const row = rows[i];
157
152
  let obj = result[i];
@@ -1,5 +1,9 @@
1
1
  import { EntityMetadata } from '../model/entity-metadata.js';
2
+ import { DECORATOR_FACTORY } from '../orm.const.js';
2
3
  export function Column(arg0) {
4
+ return Column[DECORATOR_FACTORY](arg0);
5
+ }
6
+ Column[DECORATOR_FACTORY] = function (arg0) {
3
7
  return (target, propertyKey) => {
4
8
  if (typeof propertyKey !== 'string')
5
9
  throw new Error('Symbol properties are not accepted');
@@ -16,4 +20,4 @@ export function Column(arg0) {
16
20
  }
17
21
  EntityMetadata.defineColumnField(entity, propertyKey, options);
18
22
  };
19
- }
23
+ };
@@ -8,16 +8,19 @@ export function Link(options) {
8
8
  throw new TypeError('Symbol properties are not allowed');
9
9
  const reflectType = Reflect.getMetadata('design:type', target, propertyKey);
10
10
  if (!root) {
11
- if (reflectType === Array)
11
+ if (reflectType === Array) {
12
12
  throw new TypeError(`Can't get type information while it is an array. Please define entity type`);
13
+ }
13
14
  if (!EntityMetadata.get(reflectType))
14
15
  throw new TypeError(`No entity metadata found for type "${reflectType}"`);
15
16
  fn.toOne(reflectType);
16
17
  }
17
- if (reflectType !== Array && root.first.returnsMany())
18
+ if (reflectType !== Array && root.first.returnsMany()) {
18
19
  throw new TypeError(`Link returns single instance however property type is an array`);
19
- if (reflectType === Array && !root.first.returnsMany())
20
+ }
21
+ if (reflectType === Array && !root.first.returnsMany()) {
20
22
  throw new TypeError(`Link returns array of instances however property type is not an array`);
23
+ }
21
24
  const entity = EntityMetadata.define(target.constructor);
22
25
  // @ts-ignore
23
26
  // noinspection JSConstantReassignment
@@ -3,8 +3,9 @@ import { Column } from './column.decorator.js';
3
3
  export function PrimaryKey(arg0, arg1) {
4
4
  return function (target, propertyKey) {
5
5
  if (arguments.length === 1) {
6
- if (!(typeof arg0 === 'string' || Array.isArray(arg0)))
6
+ if (!(typeof arg0 === 'string' || Array.isArray(arg0))) {
7
7
  throw new Error(`You must specify primary index column(s)`);
8
+ }
8
9
  const meta = EntityMetadata.define(target);
9
10
  EntityMetadata.setPrimaryKeys(meta, arg0, arg1);
10
11
  return;
@@ -63,17 +63,15 @@ export class Association {
63
63
  this._targetProperty = foreign._targetProperty;
64
64
  return;
65
65
  }
66
- else {
67
- // Try to determine key fields from foreign key from target to source
68
- foreign = await EntityMetadata.getForeignKeyFor(target, source);
69
- if (foreign && foreign !== this) {
70
- await foreign._resolveKeys();
71
- this._sourceKey = foreign._targetKey;
72
- this._sourceProperty = foreign._targetProperty;
73
- this._targetKey = foreign._sourceKey;
74
- this._targetProperty = foreign._sourceProperty;
75
- return;
76
- }
66
+ // Try to determine key fields from foreign key from target to source
67
+ foreign = await EntityMetadata.getForeignKeyFor(target, source);
68
+ if (foreign && foreign !== this) {
69
+ await foreign._resolveKeys();
70
+ this._sourceKey = foreign._targetKey;
71
+ this._sourceProperty = foreign._targetProperty;
72
+ this._targetKey = foreign._sourceKey;
73
+ this._targetProperty = foreign._sourceProperty;
74
+ return;
77
75
  }
78
76
  if (this.many) {
79
77
  if (!sourceKey) {
@@ -56,8 +56,9 @@ export var EntityMetadata;
56
56
  EntityMetadata.getEmbeddedField = getEmbeddedField;
57
57
  function getAssociationField(entity, fieldName) {
58
58
  const el = getField(entity, fieldName);
59
- if (el && !isAssociationField(el))
59
+ if (el && !isAssociationField(el)) {
60
60
  throw new Error(`"${el.name}" requested as "association" but it is "${el.kind}"`);
61
+ }
61
62
  return el;
62
63
  }
63
64
  EntityMetadata.getAssociationField = getAssociationField;
@@ -85,13 +86,14 @@ export var EntityMetadata;
85
86
  return out;
86
87
  }
87
88
  // Create a cached name array
88
- if (!Object.prototype.hasOwnProperty.call(entity, '_fieldNames'))
89
+ if (!Object.prototype.hasOwnProperty.call(entity, '_fieldNames')) {
89
90
  Object.defineProperty(entity, '_fieldNames', {
90
91
  enumerable: false,
91
92
  configurable: true,
92
93
  writable: true,
93
94
  value: Object.values(entity.fields).map(m => m.name),
94
95
  });
96
+ }
95
97
  return entity._fieldNames;
96
98
  }
97
99
  EntityMetadata.getFieldNames = getFieldNames;
@@ -1,2 +1,3 @@
1
1
  export const ENTITY_METADATA_KEY = Symbol.for('SQB_ENTITY_METADATA');
2
2
  export const REPOSITORY_KEY = Symbol.for('SQB_REPOSITORY');
3
+ export const DECORATOR_FACTORY = Symbol.for('decorator.factory');
@@ -34,46 +34,31 @@ export class Repository extends TypedEventEmitterClass(AsyncEventEmitter) {
34
34
  }, options);
35
35
  }
36
36
  exists(keyValue, options) {
37
- return this._execute(async (connection) => {
38
- return this._exists(keyValue, { ...options, connection });
39
- }, options);
37
+ return this._execute(async (connection) => this._exists(keyValue, { ...options, connection }), options);
40
38
  }
41
- count(options) {
42
- return this._execute(async (connection) => {
43
- return this._count({ ...options, connection });
44
- }, options);
39
+ existsOne(options) {
40
+ return this._execute(async (connection) => this._existsOne({ ...options, connection }), options);
45
41
  }
46
- /** @deprecated - Use findById instead */
47
- find(keyValue, options) {
48
- return this.findById(keyValue, options);
42
+ count(options) {
43
+ return this._execute(async (connection) => this._count({ ...options, connection }), options);
49
44
  }
50
45
  findById(keyValue, options) {
51
- return this._execute(async (connection) => {
52
- return this._find(keyValue, { ...options, connection });
53
- }, options);
46
+ return this._execute(async (connection) => this._find(keyValue, { ...options, connection }), options);
54
47
  }
55
48
  findOne(options) {
56
- return this._execute(async (connection) => {
57
- return await this._findOne({
58
- ...options,
59
- connection,
60
- });
61
- }, options);
49
+ return this._execute(async (connection) => await this._findOne({
50
+ ...options,
51
+ connection,
52
+ }), options);
62
53
  }
63
54
  findMany(options) {
64
- return this._execute(async (connection) => {
65
- return this._findMany({ ...options, connection });
66
- }, options);
55
+ return this._execute(async (connection) => this._findMany({ ...options, connection }), options);
67
56
  }
68
57
  delete(keyValue, options) {
69
- return this._execute(async (connection) => {
70
- return this._delete(keyValue, { ...options, connection });
71
- }, options);
58
+ return this._execute(async (connection) => this._delete(keyValue, { ...options, connection }), options);
72
59
  }
73
60
  deleteMany(options) {
74
- return this._execute(async (connection) => {
75
- return this._deleteMany({ ...options, connection });
76
- }, options);
61
+ return this._execute(async (connection) => this._deleteMany({ ...options, connection }), options);
77
62
  }
78
63
  update(keyValue, values, options) {
79
64
  return this._execute(async (connection) => {
@@ -84,14 +69,10 @@ export class Repository extends TypedEventEmitterClass(AsyncEventEmitter) {
84
69
  }, options);
85
70
  }
86
71
  updateOnly(keyValue, values, options) {
87
- return this._execute(async (connection) => {
88
- return !!(await this._update(keyValue, values, { ...options, connection }));
89
- }, options);
72
+ return this._execute(async (connection) => !!(await this._update(keyValue, values, { ...options, connection })), options);
90
73
  }
91
74
  updateMany(values, options) {
92
- return this._execute(async (connection) => {
93
- return this._updateMany(values, { ...options, connection });
94
- });
75
+ return this._execute(async (connection) => this._updateMany(values, { ...options, connection }));
95
76
  }
96
77
  async _execute(fn, opts) {
97
78
  let connection = opts?.connection;
@@ -129,11 +110,25 @@ export class Repository extends TypedEventEmitterClass(AsyncEventEmitter) {
129
110
  else
130
111
  filter.push(options.filter);
131
112
  }
132
- return !!(await CountCommand.execute({
113
+ return this._existsOne({
114
+ ...options,
115
+ filter,
116
+ });
117
+ }
118
+ async _existsOne(options) {
119
+ const filter = [];
120
+ if (options && options.filter) {
121
+ if (Array.isArray(options.filter))
122
+ filter.push(...options.filter);
123
+ else
124
+ filter.push(options.filter);
125
+ }
126
+ const resp = await FindCommand.execute({
133
127
  ...options,
134
128
  filter,
135
129
  entity: this._entity,
136
- }));
130
+ });
131
+ return resp.length > 0;
137
132
  }
138
133
  async _count(options) {
139
134
  return CountCommand.execute({
@@ -4,8 +4,9 @@ export function applyMixins(derivedCtor, baseCtor, filter) {
4
4
  name === '__proto__' ||
5
5
  name === 'toJSON' ||
6
6
  name === 'toString' ||
7
- (filter && !filter(name)))
7
+ (filter && !filter(name))) {
8
8
  continue;
9
+ }
9
10
  Object.defineProperty(derivedCtor.prototype, name, Object.getOwnPropertyDescriptor(baseCtor.prototype, name) || Object.create(null));
10
11
  }
11
12
  }
@@ -8,14 +8,16 @@ export function extractKeyValues(entityDef, valueOrInstance, keepOther) {
8
8
  const col = EntityMetadata.getField(entityDef, k);
9
9
  if (!col)
10
10
  throw new Error(`Unknown column (${k}) defined as primary key in entity "${entityDef.name}"`);
11
- if (!isColumnField(col))
11
+ if (!isColumnField(col)) {
12
12
  throw new Error(`Column (${k}) defined as primary key in entity "${entityDef.name}" is not a data column`);
13
+ }
13
14
  };
14
15
  // if entity's primary key has more than one key field
15
16
  if (primaryIndex.columns.length > 1) {
16
- if (typeof valueOrInstance !== 'object')
17
+ if (typeof valueOrInstance !== 'object') {
17
18
  throw new Error(`"${entityDef.name}" entity` +
18
19
  ` has more than one primary key field and you must provide all values with an key/value pair`);
20
+ }
19
21
  const valueKeys = Object.keys(valueOrInstance);
20
22
  const valueKeysUpper = valueKeys.map(x => x.toUpperCase());
21
23
  const out = {};
@@ -41,8 +43,9 @@ export function extractKeyValues(entityDef, valueOrInstance, keepOther) {
41
43
  const valueKeys = Object.keys(valueOrInstance);
42
44
  const valueKeysUpper = valueKeys.map(x => x.toUpperCase());
43
45
  const k = valueKeysUpper.indexOf(primaryColumnName.toUpperCase());
44
- if (k < 0)
46
+ if (k < 0) {
45
47
  throw new Error(`Value of key field "${entityDef.name}.${primaryColumnName}" required to perform this operation`);
48
+ }
46
49
  const out = { [primaryColumnName]: valueOrInstance[valueKeys[k]] };
47
50
  if (keepOther) {
48
51
  for (let i = 0; i < valueKeys.length; i++) {
@@ -8,12 +8,8 @@ export function isEntityClass(fn) {
8
8
  export function isColumnField(f) {
9
9
  return !!(f && f.name && f.kind === 'column');
10
10
  }
11
- export const isEmbeddedField = (f) => {
12
- return !!(f && f.name && f.kind === 'object');
13
- };
14
- export const isAssociationField = (f) => {
15
- return !!(f && f.name && f.kind === 'association');
16
- };
11
+ export const isEmbeddedField = (f) => !!(f && f.name && f.kind === 'object');
12
+ export const isAssociationField = (f) => !!(f && f.name && f.kind === 'association');
17
13
  export async function resolveEntity(ctorThunk) {
18
14
  if (typeof ctorThunk !== 'function')
19
15
  return;
@@ -0,0 +1,59 @@
1
+ import { splitString } from 'fast-tokenizer';
2
+ const FIELD_PATTERN = /^([+-])?([a-z_$]\w*)$/i;
3
+ const NO_DOT_BRACKET_PATTERN = /[^.]\(/g;
4
+ export class FieldsProjection {
5
+ }
6
+ (function (FieldsProjection) {
7
+ class Item {
8
+ }
9
+ FieldsProjection.Item = Item;
10
+ })(FieldsProjection || (FieldsProjection = {}));
11
+ export function parseFieldsProjection(projection, keepCase) {
12
+ const arr = Array.isArray(projection) ? projection : [projection];
13
+ if (!(arr && arr.length))
14
+ return;
15
+ const out = new FieldsProjection();
16
+ for (let s of arr) {
17
+ if (!keepCase)
18
+ s = s.toLowerCase();
19
+ parse(s, out);
20
+ }
21
+ return out;
22
+ }
23
+ function parse(input, target) {
24
+ /** Add dot before brackets which is required to split fields */
25
+ input = input.replace(NO_DOT_BRACKET_PATTERN, s => s.charAt(0) + '.' + s.substring(1));
26
+ const fields = splitString(input, {
27
+ delimiters: '.',
28
+ brackets: true,
29
+ keepBrackets: false,
30
+ });
31
+ for (let i = 0; i < fields.length; i++) {
32
+ const f = fields[i];
33
+ if (f.includes(',')) {
34
+ const subFields = splitString(f, {
35
+ delimiters: ',',
36
+ brackets: true,
37
+ keepBrackets: true,
38
+ });
39
+ for (const n of subFields) {
40
+ parse(n, target);
41
+ }
42
+ continue;
43
+ }
44
+ const m = FIELD_PATTERN.exec(f);
45
+ /* istanbul ignore next */
46
+ if (!m)
47
+ throw new TypeError(`Invalid field path (${input})`);
48
+ const fieldName = m[2];
49
+ const treeItem = (target[fieldName] = target[fieldName] || new FieldsProjection.Item());
50
+ if (m[1])
51
+ treeItem.sign = m[1];
52
+ if (i === fields.length - 1) {
53
+ delete treeItem.projection;
54
+ }
55
+ else {
56
+ target = treeItem.projection = treeItem.projection || new FieldsProjection();
57
+ }
58
+ }
59
+ }