@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.
- package/cjs/client/cursor.js +1 -1
- package/cjs/client/extensions.js +38 -11
- package/cjs/client/helpers.js +7 -6
- package/cjs/client/sqb-client.js +2 -2
- package/cjs/client/sqb-connection.js +7 -4
- package/cjs/index.js +19 -20
- package/cjs/orm/backward.js +5 -5
- package/cjs/orm/commands/command.helper.js +39 -11
- package/cjs/orm/commands/create.command.js +2 -1
- package/cjs/orm/commands/find.command.js +21 -35
- package/cjs/orm/commands/row-converter.js +5 -10
- package/cjs/orm/decorators/column.decorator.js +6 -3
- package/cjs/orm/decorators/embedded.decorator.js +1 -2
- package/cjs/orm/decorators/entity.decorator.js +1 -2
- package/cjs/orm/decorators/events.decorator.js +6 -7
- package/cjs/orm/decorators/foreignkey.decorator.js +1 -2
- package/cjs/orm/decorators/index.decorator.js +1 -2
- package/cjs/orm/decorators/link.decorator.js +7 -5
- package/cjs/orm/decorators/primarykey.decorator.js +3 -3
- package/cjs/orm/decorators/transform.decorator.js +2 -3
- package/cjs/orm/model/association.js +9 -11
- package/cjs/orm/model/entity-metadata.js +4 -2
- package/cjs/orm/orm.const.js +2 -1
- package/cjs/orm/repository.class.js +31 -36
- package/cjs/orm/util/apply-mixins.js +3 -3
- package/cjs/orm/util/extract-keyvalues.js +7 -5
- package/cjs/orm/util/orm.helper.js +8 -12
- package/cjs/orm/util/parse-fields-projection.js +64 -0
- package/cjs/orm/util/serialize-field.js +1 -2
- package/esm/client/cursor.js +1 -1
- package/esm/client/extensions.js +36 -8
- package/esm/client/helpers.js +2 -0
- package/esm/client/sqb-client.js +3 -3
- package/esm/client/sqb-connection.js +7 -4
- package/esm/index.js +18 -17
- package/esm/orm/commands/command.helper.js +36 -7
- package/esm/orm/commands/create.command.js +2 -1
- package/esm/orm/commands/find.command.js +21 -35
- package/esm/orm/commands/row-converter.js +5 -10
- package/esm/orm/decorators/column.decorator.js +5 -1
- package/esm/orm/decorators/link.decorator.js +6 -3
- package/esm/orm/decorators/primarykey.decorator.js +2 -1
- package/esm/orm/model/association.js +9 -11
- package/esm/orm/model/entity-metadata.js +4 -2
- package/esm/orm/orm.const.js +1 -0
- package/esm/orm/repository.class.js +31 -36
- package/esm/orm/util/apply-mixins.js +2 -1
- package/esm/orm/util/extract-keyvalues.js +6 -3
- package/esm/orm/util/orm.helper.js +2 -6
- package/esm/orm/util/parse-fields-projection.js +59 -0
- package/package.json +10 -6
- package/types/client/adapter.d.ts +1 -1
- package/types/client/cursor-stream.d.ts +0 -1
- package/types/client/extensions.d.ts +14 -3
- package/types/client/sqb-client.d.ts +1 -1
- package/types/client/sqb-connection.d.ts +1 -1
- package/types/client/types.d.ts +1 -1
- package/types/index.d.ts +18 -17
- package/types/orm/commands/find.command.d.ts +3 -4
- package/types/orm/commands/row-converter.d.ts +1 -1
- package/types/orm/decorators/column.decorator.d.ts +1 -0
- package/types/orm/model/field-metadata.d.ts +1 -1
- package/types/orm/orm.const.d.ts +1 -0
- package/types/orm/repository.class.d.ts +11 -8
- 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/
|
|
7
|
-
export
|
|
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
|
|
31
|
-
export * from './orm/
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
108
|
-
|
|
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
|
-
|
|
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
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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;
|
package/esm/orm/orm.const.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
47
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
|
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
|
-
|
|
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
|
+
}
|