@nocobase/database 0.8.0-alpha.9 → 0.8.1-alpha.3
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/lib/collection.d.ts +7 -3
- package/lib/collection.js +126 -22
- package/lib/database.d.ts +13 -7
- package/lib/database.js +39 -8
- package/lib/decorators/must-have-filter-decorator.js +4 -0
- package/lib/decorators/transaction-decorator.js +1 -0
- package/lib/features/ReferencesMap.js +14 -9
- package/lib/field-repository/array-field-repository.d.ts +28 -0
- package/lib/field-repository/array-field-repository.js +208 -0
- package/lib/fields/array-field.d.ts +1 -1
- package/lib/fields/array-field.js +15 -11
- package/lib/fields/belongs-to-field.d.ts +9 -1
- package/lib/fields/belongs-to-field.js +21 -7
- package/lib/fields/belongs-to-many-field.d.ts +4 -0
- package/lib/fields/belongs-to-many-field.js +24 -0
- package/lib/fields/field.d.ts +3 -2
- package/lib/fields/field.js +19 -13
- package/lib/fields/has-many-field.d.ts +2 -1
- package/lib/fields/has-many-field.js +18 -10
- package/lib/fields/has-one-field.d.ts +1 -0
- package/lib/fields/has-one-field.js +22 -10
- package/lib/fields/index.d.ts +3 -3
- package/lib/fields/index.js +14 -34
- package/lib/fields/relation-field.d.ts +2 -1
- package/lib/fields/relation-field.js +16 -0
- package/lib/fields/set-field.d.ts +10 -0
- package/lib/fields/set-field.js +35 -0
- package/lib/fields/sort-field.d.ts +1 -1
- package/lib/fields/sort-field.js +1 -1
- package/lib/filter-match.d.ts +1 -0
- package/lib/filter-match.js +84 -0
- package/lib/filter-parser.d.ts +2 -2
- package/lib/index.d.ts +5 -1
- package/lib/index.js +56 -0
- package/lib/inherited-collection.d.ts +13 -0
- package/lib/inherited-collection.js +210 -0
- package/lib/inherited-map.d.ts +21 -0
- package/lib/inherited-map.js +203 -0
- package/lib/model-hook.d.ts +1 -1
- package/lib/model.d.ts +1 -0
- package/lib/model.js +47 -0
- package/lib/operators/array.d.ts +1 -25
- package/lib/operators/association.d.ts +1 -9
- package/lib/operators/boolean.d.ts +1 -12
- package/lib/operators/date.d.ts +1 -33
- package/lib/operators/empty.d.ts +2 -25
- package/lib/operators/ne.d.ts +1 -13
- package/lib/operators/notIn.d.ts +1 -9
- package/lib/options-parser.d.ts +3 -2
- package/lib/options-parser.js +16 -1
- package/lib/relation-repository/relation-repository.d.ts +2 -2
- package/lib/relation-repository/single-relation-repository.js +2 -2
- package/lib/repository.d.ts +18 -10
- package/lib/repository.js +172 -38
- package/lib/sync-runner.d.ts +4 -0
- package/lib/sync-runner.js +181 -0
- package/lib/types.d.ts +2 -2
- package/lib/update-associations.d.ts +1 -0
- package/lib/update-associations.js +21 -2
- package/lib/update-guard.d.ts +3 -3
- package/lib/utils.js +5 -0
- package/package.json +4 -4
- package/src/__tests__/bigint.test.ts +48 -0
- package/src/__tests__/collection.test.ts +48 -13
- package/src/__tests__/database.test.ts +10 -0
- package/src/__tests__/field-repository/array-field-repository.test.ts +94 -0
- package/src/__tests__/fields/set.test.ts +37 -0
- package/src/__tests__/filter-match.test.ts +52 -0
- package/src/__tests__/inhertits/collection-inherits-sync.test.ts +38 -0
- package/src/__tests__/inhertits/collection-inherits.test.ts +994 -0
- package/src/__tests__/inhertits/helper.ts +3 -0
- package/src/__tests__/inhertits/inherited-map.test.ts +27 -0
- package/src/__tests__/relation-repository/belongs-to-many-repository.test.ts +2 -0
- package/src/__tests__/relation-repository/has-many-repository.test.ts +1 -1
- package/src/__tests__/repository/destroy.test.ts +122 -1
- package/src/__tests__/repository/update-many.test.ts +57 -0
- package/src/__tests__/update-association-values.test.ts +232 -0
- package/src/__tests__/update-associations.test.ts +6 -1
- package/src/collection.ts +90 -8
- package/src/database.ts +53 -14
- package/src/decorators/must-have-filter-decorator.ts +4 -0
- package/src/decorators/transaction-decorator.ts +1 -0
- package/src/features/ReferencesMap.ts +20 -9
- package/src/features/referential-integrity-check.ts +1 -0
- package/src/field-repository/array-field-repository.ts +155 -0
- package/src/fields/array-field.ts +4 -4
- package/src/fields/belongs-to-field.ts +26 -10
- package/src/fields/belongs-to-many-field.ts +34 -0
- package/src/fields/field.ts +26 -13
- package/src/fields/has-many-field.ts +17 -9
- package/src/fields/has-one-field.ts +23 -9
- package/src/fields/index.ts +5 -5
- package/src/fields/relation-field.ts +16 -0
- package/src/fields/set-field.ts +25 -0
- package/src/fields/sort-field.ts +5 -4
- package/src/filter-match.ts +49 -0
- package/src/filter-parser.ts +2 -2
- package/src/index.ts +5 -2
- package/src/inherited-collection.ts +112 -0
- package/src/inherited-map.ts +97 -0
- package/src/model-hook.ts +2 -3
- package/src/model.ts +43 -3
- package/src/operators/array.ts +1 -1
- package/src/operators/association.ts +1 -1
- package/src/operators/boolean.ts +1 -1
- package/src/operators/date.ts +1 -1
- package/src/operators/empty.ts +1 -1
- package/src/operators/ne.ts +1 -1
- package/src/operators/notIn.ts +2 -1
- package/src/options-parser.ts +20 -4
- package/src/relation-repository/relation-repository.ts +2 -2
- package/src/relation-repository/single-relation-repository.ts +2 -2
- package/src/repository.ts +144 -30
- package/src/sync-runner.ts +162 -0
- package/src/types.ts +2 -2
- package/src/update-associations.ts +23 -7
- package/src/update-guard.ts +3 -3
- package/src/utils.ts +5 -1
- package/lib/fields/sequence-field.d.ts +0 -32
- package/lib/fields/sequence-field.js +0 -300
- package/src/__tests__/fields/sequence-field.test.ts +0 -480
- package/src/fields/sequence-field.ts +0 -202
package/src/options-parser.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FindAttributeOptions,
|
|
1
|
+
import { FindAttributeOptions, ModelStatic, Op, Sequelize } from 'sequelize';
|
|
2
2
|
import { Collection } from './collection';
|
|
3
3
|
import { Database } from './database';
|
|
4
4
|
import FilterParser from './filter-parser';
|
|
@@ -15,7 +15,7 @@ export class OptionsParser {
|
|
|
15
15
|
options: FindOptions;
|
|
16
16
|
database: Database;
|
|
17
17
|
collection: Collection;
|
|
18
|
-
model:
|
|
18
|
+
model: ModelStatic<any>;
|
|
19
19
|
filterParser: FilterParser;
|
|
20
20
|
context: OptionsParserContext;
|
|
21
21
|
|
|
@@ -104,6 +104,13 @@ export class OptionsParser {
|
|
|
104
104
|
return filterParams;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
protected inheritFromSubQuery(): any {
|
|
108
|
+
return [
|
|
109
|
+
Sequelize.literal(`(select relname from pg_class where pg_class.oid = "${this.collection.name}".tableoid)`),
|
|
110
|
+
'__tableName',
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
|
|
107
114
|
protected parseFields(filterParams: any) {
|
|
108
115
|
const appends = this.options?.appends || [];
|
|
109
116
|
const except = [];
|
|
@@ -113,6 +120,10 @@ export class OptionsParser {
|
|
|
113
120
|
exclude: [],
|
|
114
121
|
}; // out put all fields by default
|
|
115
122
|
|
|
123
|
+
if (this.collection.isParent()) {
|
|
124
|
+
attributes.include.push(this.inheritFromSubQuery());
|
|
125
|
+
}
|
|
126
|
+
|
|
116
127
|
if (this.options?.fields) {
|
|
117
128
|
// 将fields拆分为 attributes 和 appends
|
|
118
129
|
for (const field of this.options.fields) {
|
|
@@ -121,7 +132,12 @@ export class OptionsParser {
|
|
|
121
132
|
appends.push(field);
|
|
122
133
|
} else {
|
|
123
134
|
// field is model attribute, change attributes to array type
|
|
124
|
-
if (!Array.isArray(attributes))
|
|
135
|
+
if (!Array.isArray(attributes)) {
|
|
136
|
+
attributes = [];
|
|
137
|
+
if (this.collection.isParent()) {
|
|
138
|
+
attributes.push(this.inheritFromSubQuery());
|
|
139
|
+
}
|
|
140
|
+
}
|
|
125
141
|
|
|
126
142
|
attributes.push(field);
|
|
127
143
|
}
|
|
@@ -196,7 +212,7 @@ export class OptionsParser {
|
|
|
196
212
|
* @param queryParams
|
|
197
213
|
* @param append
|
|
198
214
|
*/
|
|
199
|
-
const setInclude = (model:
|
|
215
|
+
const setInclude = (model: ModelStatic<any>, queryParams: any, append: string) => {
|
|
200
216
|
const appendFields = append.split('.');
|
|
201
217
|
const appendAssociation = appendFields[0];
|
|
202
218
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import lodash from 'lodash';
|
|
2
|
-
import { Association, BelongsTo, BelongsToMany, HasMany, HasOne,
|
|
2
|
+
import { Association, BelongsTo, BelongsToMany, HasMany, HasOne, ModelStatic, Transaction } from 'sequelize';
|
|
3
3
|
import { Collection } from '../collection';
|
|
4
4
|
import Database from '../database';
|
|
5
5
|
import { transactionWrapperBuilder } from '../decorators/transaction-decorator';
|
|
@@ -18,7 +18,7 @@ export const transaction = transactionWrapperBuilder(function () {
|
|
|
18
18
|
export abstract class RelationRepository {
|
|
19
19
|
sourceCollection: Collection;
|
|
20
20
|
association: Association;
|
|
21
|
-
targetModel:
|
|
21
|
+
targetModel: ModelStatic<any>;
|
|
22
22
|
targetCollection: Collection;
|
|
23
23
|
associationName: string;
|
|
24
24
|
associationField: RelationField;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import lodash
|
|
1
|
+
import lodash from 'lodash';
|
|
2
2
|
import { SingleAssociationAccessors, Transactionable } from 'sequelize';
|
|
3
3
|
import { Model } from '../model';
|
|
4
4
|
import { Appends, Except, Fields, Filter, TargetKey, UpdateOptions } from '../repository';
|
|
5
5
|
import { updateModelByValues } from '../update-associations';
|
|
6
|
-
import { RelationRepository, transaction } from './relation-repository';
|
|
7
6
|
import { handleAppendsQuery } from '../utils';
|
|
7
|
+
import { RelationRepository, transaction } from './relation-repository';
|
|
8
8
|
|
|
9
9
|
export interface SingleRelationFindOption extends Transactionable {
|
|
10
10
|
fields?: Fields;
|
package/src/repository.ts
CHANGED
|
@@ -7,17 +7,18 @@ import {
|
|
|
7
7
|
DestroyOptions as SequelizeDestroyOptions,
|
|
8
8
|
FindAndCountOptions as SequelizeAndCountOptions,
|
|
9
9
|
FindOptions as SequelizeFindOptions,
|
|
10
|
-
|
|
10
|
+
ModelStatic,
|
|
11
11
|
Op,
|
|
12
12
|
Transactionable,
|
|
13
13
|
UpdateOptions as SequelizeUpdateOptions,
|
|
14
|
+
WhereOperators,
|
|
14
15
|
} from 'sequelize';
|
|
15
|
-
import { WhereOperators } from 'sequelize/types/lib/model';
|
|
16
16
|
import { Collection } from './collection';
|
|
17
17
|
import { Database } from './database';
|
|
18
18
|
import mustHaveFilter from './decorators/must-have-filter-decorator';
|
|
19
19
|
import { transactionWrapperBuilder } from './decorators/transaction-decorator';
|
|
20
|
-
import {
|
|
20
|
+
import { ArrayFieldRepository } from './field-repository/array-field-repository';
|
|
21
|
+
import { ArrayField, RelationField } from './fields';
|
|
21
22
|
import FilterParser from './filter-parser';
|
|
22
23
|
import { Model } from './model';
|
|
23
24
|
import operators from './operators';
|
|
@@ -86,10 +87,11 @@ export type AssociationKeysToBeUpdate = string[];
|
|
|
86
87
|
|
|
87
88
|
export type Values = any;
|
|
88
89
|
|
|
89
|
-
export
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
export type CountOptions = Omit<SequelizeCountOptions, 'distinct' | 'where' | 'include'> &
|
|
91
|
+
Transactionable & {
|
|
92
|
+
filter?: Filter;
|
|
93
|
+
context?: any;
|
|
94
|
+
} & FilterByTk;
|
|
93
95
|
|
|
94
96
|
export interface FilterByTk {
|
|
95
97
|
filterByTk?: TargetKey;
|
|
@@ -135,6 +137,10 @@ export interface UpdateOptions extends Omit<SequelizeUpdateOptions, 'where'> {
|
|
|
135
137
|
context?: any;
|
|
136
138
|
}
|
|
137
139
|
|
|
140
|
+
interface UpdateManyOptions extends UpdateOptions {
|
|
141
|
+
records: Values[];
|
|
142
|
+
}
|
|
143
|
+
|
|
138
144
|
interface RelatedQueryOptions {
|
|
139
145
|
database: Database;
|
|
140
146
|
field: RelationField;
|
|
@@ -157,19 +163,29 @@ const transaction = transactionWrapperBuilder(function () {
|
|
|
157
163
|
class RelationRepositoryBuilder<R extends RelationRepository> {
|
|
158
164
|
collection: Collection;
|
|
159
165
|
associationName: string;
|
|
160
|
-
association: Association;
|
|
166
|
+
association: Association | { associationType: string };
|
|
161
167
|
|
|
162
168
|
builderMap = {
|
|
163
169
|
HasOne: HasOneRepository,
|
|
164
170
|
BelongsTo: BelongsToRepository,
|
|
165
171
|
BelongsToMany: BelongsToManyRepository,
|
|
166
172
|
HasMany: HasManyRepository,
|
|
173
|
+
ArrayField: ArrayFieldRepository,
|
|
167
174
|
};
|
|
168
175
|
|
|
169
176
|
constructor(collection: Collection, associationName: string) {
|
|
170
177
|
this.collection = collection;
|
|
171
178
|
this.associationName = associationName;
|
|
172
179
|
this.association = this.collection.model.associations[this.associationName];
|
|
180
|
+
|
|
181
|
+
if (!this.association) {
|
|
182
|
+
const field = collection.getField(associationName);
|
|
183
|
+
if (field && field instanceof ArrayField) {
|
|
184
|
+
this.association = {
|
|
185
|
+
associationType: 'ArrayField',
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
173
189
|
}
|
|
174
190
|
|
|
175
191
|
protected builder() {
|
|
@@ -187,7 +203,7 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|
|
187
203
|
{
|
|
188
204
|
database: Database;
|
|
189
205
|
collection: Collection;
|
|
190
|
-
model:
|
|
206
|
+
model: ModelStatic<Model>;
|
|
191
207
|
|
|
192
208
|
constructor(collection: Collection) {
|
|
193
209
|
this.database = collection.context.database;
|
|
@@ -210,12 +226,32 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|
|
210
226
|
};
|
|
211
227
|
}
|
|
212
228
|
|
|
213
|
-
|
|
229
|
+
if (countOptions?.filterByTk) {
|
|
230
|
+
options['where'] = {
|
|
231
|
+
[Op.and]: [
|
|
232
|
+
options['where'] || {},
|
|
233
|
+
{
|
|
234
|
+
[this.collection.filterTargetKey]: options.filterByTk,
|
|
235
|
+
},
|
|
236
|
+
],
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const queryOptions: any = {
|
|
214
241
|
...options,
|
|
215
|
-
distinct:
|
|
242
|
+
distinct: Boolean(this.collection.model.primaryKeyAttribute),
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
if (queryOptions.include?.length === 0) {
|
|
246
|
+
delete queryOptions.include;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const count = await this.collection.model.count({
|
|
250
|
+
...queryOptions,
|
|
216
251
|
transaction,
|
|
217
252
|
});
|
|
218
253
|
|
|
254
|
+
// @ts-ignore
|
|
219
255
|
return count;
|
|
220
256
|
}
|
|
221
257
|
|
|
@@ -232,6 +268,8 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|
|
232
268
|
...this.buildQueryOptions(options),
|
|
233
269
|
};
|
|
234
270
|
|
|
271
|
+
let rows;
|
|
272
|
+
|
|
235
273
|
if (opts.include && opts.include.length > 0) {
|
|
236
274
|
// @ts-ignore
|
|
237
275
|
const primaryKeyField = model.primaryKeyField || model.primaryKeyAttribute;
|
|
@@ -243,7 +281,12 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|
|
243
281
|
attributes: [primaryKeyField],
|
|
244
282
|
group: `${model.name}.${primaryKeyField}`,
|
|
245
283
|
transaction,
|
|
246
|
-
|
|
284
|
+
include: opts.include.filter((include) => {
|
|
285
|
+
return (
|
|
286
|
+
Object.keys(include.where || {}).length > 0 || JSON.stringify(opts?.filter)?.includes(include.association)
|
|
287
|
+
);
|
|
288
|
+
}),
|
|
289
|
+
} as any)
|
|
247
290
|
).map((row) => {
|
|
248
291
|
return { row, pk: row.get(primaryKeyField) };
|
|
249
292
|
});
|
|
@@ -252,33 +295,54 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|
|
252
295
|
return [];
|
|
253
296
|
}
|
|
254
297
|
|
|
298
|
+
const templateModel = await model.findOne({
|
|
299
|
+
...opts,
|
|
300
|
+
includeIgnoreAttributes: false,
|
|
301
|
+
attributes: [primaryKeyField],
|
|
302
|
+
group: `${model.name}.${primaryKeyField}`,
|
|
303
|
+
transaction,
|
|
304
|
+
limit: 1,
|
|
305
|
+
offset: 0,
|
|
306
|
+
} as any);
|
|
307
|
+
|
|
255
308
|
const where = {
|
|
256
309
|
[primaryKeyField]: {
|
|
257
310
|
[Op.in]: ids.map((id) => id['pk']),
|
|
258
311
|
},
|
|
259
312
|
};
|
|
260
313
|
|
|
261
|
-
|
|
314
|
+
rows = await handleAppendsQuery({
|
|
262
315
|
queryPromises: opts.include.map((include) => {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
316
|
+
const options = {
|
|
317
|
+
...omit(opts, ['limit', 'offset']),
|
|
318
|
+
include: include,
|
|
319
|
+
where,
|
|
320
|
+
transaction,
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
return model.findAll(options).then((rows) => {
|
|
324
|
+
return { rows, include };
|
|
325
|
+
});
|
|
273
326
|
}),
|
|
274
|
-
templateModel:
|
|
327
|
+
templateModel: templateModel,
|
|
328
|
+
});
|
|
329
|
+
} else {
|
|
330
|
+
rows = await model.findAll({
|
|
331
|
+
...opts,
|
|
332
|
+
transaction,
|
|
275
333
|
});
|
|
276
334
|
}
|
|
277
335
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
336
|
+
if (this.collection.isParent()) {
|
|
337
|
+
for (const row of rows) {
|
|
338
|
+
const rowCollectionName = this.database.tableNameCollectionMap.get(row.get('__tableName')).name;
|
|
339
|
+
row.set('__collection', rowCollectionName, {
|
|
340
|
+
raw: true,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return rows;
|
|
282
346
|
}
|
|
283
347
|
|
|
284
348
|
/**
|
|
@@ -338,7 +402,6 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|
|
338
402
|
const guard = UpdateGuard.fromOptions(this.model, { ...options, action: 'create' });
|
|
339
403
|
const values = guard.sanitize(options.values || {});
|
|
340
404
|
|
|
341
|
-
|
|
342
405
|
const instance = await this.model.create<any>(values, {
|
|
343
406
|
...options,
|
|
344
407
|
transaction,
|
|
@@ -397,7 +460,14 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|
|
397
460
|
@transaction()
|
|
398
461
|
@mustHaveFilter()
|
|
399
462
|
async update(options: UpdateOptions & { forceUpdate?: boolean }) {
|
|
463
|
+
if (Array.isArray(options.values)) {
|
|
464
|
+
return this.updateMany({
|
|
465
|
+
...options,
|
|
466
|
+
records: options.values,
|
|
467
|
+
});
|
|
468
|
+
}
|
|
400
469
|
const transaction = await this.getTransaction(options);
|
|
470
|
+
|
|
401
471
|
const guard = UpdateGuard.fromOptions(this.model, options);
|
|
402
472
|
|
|
403
473
|
const values = guard.sanitize(options.values);
|
|
@@ -434,6 +504,24 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|
|
434
504
|
return instances;
|
|
435
505
|
}
|
|
436
506
|
|
|
507
|
+
@transaction()
|
|
508
|
+
async updateMany(options: UpdateManyOptions) {
|
|
509
|
+
const transaction = await this.getTransaction(options);
|
|
510
|
+
const { records } = options;
|
|
511
|
+
const instances = [];
|
|
512
|
+
|
|
513
|
+
for (const values of records) {
|
|
514
|
+
const filterByTk = values[this.model.primaryKeyAttribute];
|
|
515
|
+
if (!filterByTk) {
|
|
516
|
+
throw new Error('filterByTk invalid');
|
|
517
|
+
}
|
|
518
|
+
const instance = await this.update({ values, filterByTk, transaction });
|
|
519
|
+
instances.push(instance);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
return instances;
|
|
523
|
+
}
|
|
524
|
+
|
|
437
525
|
@transaction((args, transaction) => {
|
|
438
526
|
return {
|
|
439
527
|
filterByTk: args[0],
|
|
@@ -456,6 +544,18 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|
|
456
544
|
? [options.filterByTk]
|
|
457
545
|
: (options.filterByTk as TargetKey[] | undefined);
|
|
458
546
|
|
|
547
|
+
if (
|
|
548
|
+
this.collection.model.primaryKeyAttributes.length !== 1 &&
|
|
549
|
+
filterByTk &&
|
|
550
|
+
!lodash.get(this.collection.options, 'filterTargetKey')
|
|
551
|
+
) {
|
|
552
|
+
if (this.collection.model.primaryKeyAttributes.length > 1) {
|
|
553
|
+
throw new Error(`filterByTk is not supported for composite primary key`);
|
|
554
|
+
} else {
|
|
555
|
+
throw new Error(`filterByTk is not supported for collection that has no primary key`);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
459
559
|
if (filterByTk && !options.filter) {
|
|
460
560
|
return await this.model.destroy({
|
|
461
561
|
...options,
|
|
@@ -469,6 +569,20 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|
|
469
569
|
}
|
|
470
570
|
|
|
471
571
|
if (options.filter) {
|
|
572
|
+
if (
|
|
573
|
+
this.collection.model.primaryKeyAttributes.length !== 1 &&
|
|
574
|
+
!lodash.get(this.collection.options, 'filterTargetKey')
|
|
575
|
+
) {
|
|
576
|
+
const queryOptions = {
|
|
577
|
+
...this.buildQueryOptions(options),
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
return await this.model.destroy({
|
|
581
|
+
...queryOptions,
|
|
582
|
+
transaction,
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
|
|
472
586
|
let pks = (
|
|
473
587
|
await this.find({
|
|
474
588
|
filter: options.filter,
|
|
@@ -506,7 +620,7 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|
|
506
620
|
return new RelationRepositoryBuilder<R>(this.collection, association);
|
|
507
621
|
}
|
|
508
622
|
|
|
509
|
-
|
|
623
|
+
public buildQueryOptions(options: any) {
|
|
510
624
|
const parser = new OptionsParser(options, {
|
|
511
625
|
collection: this.collection,
|
|
512
626
|
});
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { InheritedCollection } from './inherited-collection';
|
|
2
|
+
import lodash from 'lodash';
|
|
3
|
+
import { Sequelize } from 'sequelize';
|
|
4
|
+
|
|
5
|
+
export class SyncRunner {
|
|
6
|
+
static async syncInheritModel(model: any, options: any) {
|
|
7
|
+
const { transaction } = options;
|
|
8
|
+
|
|
9
|
+
const inheritedCollection = model.collection as InheritedCollection;
|
|
10
|
+
const db = inheritedCollection.context.database;
|
|
11
|
+
const dialect = db.sequelize.getDialect();
|
|
12
|
+
|
|
13
|
+
const queryInterface = db.sequelize.getQueryInterface();
|
|
14
|
+
|
|
15
|
+
if (dialect != 'postgres') {
|
|
16
|
+
throw new Error('Inherit model is only supported on postgres');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const parents = inheritedCollection.parents;
|
|
20
|
+
|
|
21
|
+
if (!parents) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
`Inherit model ${inheritedCollection.name} can't be created without parents, parents option is ${lodash
|
|
24
|
+
.castArray(inheritedCollection.options.inherits)
|
|
25
|
+
.join(', ')}`,
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const parentTables = parents.map((parent) => parent.model.tableName);
|
|
30
|
+
|
|
31
|
+
const tableName = model.getTableName();
|
|
32
|
+
|
|
33
|
+
const attributes = model.tableAttributes;
|
|
34
|
+
|
|
35
|
+
const childAttributes = lodash.pickBy(attributes, (value) => {
|
|
36
|
+
return !value.inherit;
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
let maxSequenceVal = 0;
|
|
40
|
+
let maxSequenceName;
|
|
41
|
+
|
|
42
|
+
if (childAttributes.id && childAttributes.id.autoIncrement) {
|
|
43
|
+
for (const parent of parentTables) {
|
|
44
|
+
const sequenceNameResult = await queryInterface.sequelize.query(
|
|
45
|
+
`SELECT column_default FROM information_schema.columns WHERE
|
|
46
|
+
table_name='${parent}' and "column_name" = 'id';`,
|
|
47
|
+
{
|
|
48
|
+
transaction,
|
|
49
|
+
},
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
if (!sequenceNameResult[0].length) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const columnDefault = sequenceNameResult[0][0]['column_default'];
|
|
57
|
+
|
|
58
|
+
if (!columnDefault) {
|
|
59
|
+
throw new Error(`Can't find sequence name of ${parent}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const regex = new RegExp(/nextval\('("?\w+"?)\'.*\)/);
|
|
63
|
+
const match = regex.exec(columnDefault);
|
|
64
|
+
|
|
65
|
+
const sequenceName = match[1];
|
|
66
|
+
|
|
67
|
+
const sequenceCurrentValResult = await queryInterface.sequelize.query(
|
|
68
|
+
`select last_value from ${sequenceName}`,
|
|
69
|
+
{
|
|
70
|
+
transaction,
|
|
71
|
+
},
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const sequenceCurrentVal = parseInt(sequenceCurrentValResult[0][0]['last_value']);
|
|
75
|
+
|
|
76
|
+
if (sequenceCurrentVal > maxSequenceVal) {
|
|
77
|
+
maxSequenceName = sequenceName;
|
|
78
|
+
maxSequenceVal = sequenceCurrentVal;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
await this.createTable(tableName, childAttributes, options, model, parentTables);
|
|
84
|
+
|
|
85
|
+
if (maxSequenceName) {
|
|
86
|
+
const parentsDeep = Array.from(db.inheritanceMap.getParents(inheritedCollection.name)).map(
|
|
87
|
+
(parent) => db.getCollection(parent).model.tableName,
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const sequenceTables = [...parentsDeep, tableName];
|
|
91
|
+
|
|
92
|
+
for (const sequenceTable of sequenceTables) {
|
|
93
|
+
const queryName = Boolean(sequenceTable.match(/[A-Z]/)) ? `"${sequenceTable}"` : sequenceTable;
|
|
94
|
+
|
|
95
|
+
const idColumnQuery = await queryInterface.sequelize.query(
|
|
96
|
+
`
|
|
97
|
+
SELECT true
|
|
98
|
+
FROM pg_attribute
|
|
99
|
+
WHERE attrelid = '${queryName}'::regclass -- cast to a registered class (table)
|
|
100
|
+
AND attname = 'id'
|
|
101
|
+
AND NOT attisdropped
|
|
102
|
+
`,
|
|
103
|
+
{
|
|
104
|
+
transaction,
|
|
105
|
+
},
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
if (idColumnQuery[0].length == 0) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
await queryInterface.sequelize.query(
|
|
113
|
+
`alter table "${sequenceTable}" alter column id set default nextval('${maxSequenceName}')`,
|
|
114
|
+
{
|
|
115
|
+
transaction,
|
|
116
|
+
},
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (options.alter) {
|
|
122
|
+
const columns = await queryInterface.describeTable(tableName, options);
|
|
123
|
+
|
|
124
|
+
for (const columnName in childAttributes) {
|
|
125
|
+
if (!columns[columnName]) {
|
|
126
|
+
await queryInterface.addColumn(tableName, columnName, childAttributes[columnName], options);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
static async createTable(tableName, attributes, options, model, parentTables) {
|
|
133
|
+
let sql = '';
|
|
134
|
+
|
|
135
|
+
options = { ...options };
|
|
136
|
+
|
|
137
|
+
if (options && options.uniqueKeys) {
|
|
138
|
+
lodash.forOwn(options.uniqueKeys, (uniqueKey) => {
|
|
139
|
+
if (uniqueKey.customIndex === undefined) {
|
|
140
|
+
uniqueKey.customIndex = true;
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (model) {
|
|
146
|
+
options.uniqueKeys = options.uniqueKeys || model.uniqueKeys;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const queryGenerator = model.queryGenerator;
|
|
150
|
+
|
|
151
|
+
attributes = lodash.mapValues(attributes, (attribute) => model.sequelize.normalizeAttribute(attribute));
|
|
152
|
+
|
|
153
|
+
attributes = queryGenerator.attributesToSQL(attributes, { table: tableName, context: 'createTable' });
|
|
154
|
+
|
|
155
|
+
sql = `${queryGenerator.createTableQuery(tableName, attributes, options)}`.replace(
|
|
156
|
+
';',
|
|
157
|
+
` INHERITS (${parentTables.map((t) => `"${t}"`).join(', ')});`,
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
return await model.sequelize.query(sql, options);
|
|
161
|
+
}
|
|
162
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Model } from './model';
|
|
2
|
-
import type { ValidationOptions } from 'sequelize/types/lib/instance-validator';
|
|
3
|
-
import type { HookReturn } from 'sequelize/types/lib/hooks';
|
|
4
2
|
import type { CreateOptions, DestroyOptions, SaveOptions, SyncOptions, UpdateOptions } from 'sequelize/types';
|
|
5
3
|
import { Collection, CollectionOptions } from './collection';
|
|
4
|
+
import { HookReturn } from 'sequelize/types/hooks';
|
|
5
|
+
import { ValidationOptions } from 'sequelize/types/instance-validator';
|
|
6
6
|
|
|
7
7
|
export type CollectionNameType = string;
|
|
8
8
|
|
|
@@ -5,8 +5,8 @@ import {
|
|
|
5
5
|
HasMany,
|
|
6
6
|
HasOne,
|
|
7
7
|
Hookable,
|
|
8
|
-
|
|
9
|
-
Transactionable
|
|
8
|
+
ModelStatic,
|
|
9
|
+
Transactionable,
|
|
10
10
|
} from 'sequelize';
|
|
11
11
|
import { Model } from './model';
|
|
12
12
|
import { UpdateGuard } from './update-guard';
|
|
@@ -64,6 +64,7 @@ interface UpdateAssociationOptions extends Transactionable, Hookable {
|
|
|
64
64
|
sourceModel?: Model;
|
|
65
65
|
context?: any;
|
|
66
66
|
associationContext?: any;
|
|
67
|
+
recursive?: boolean;
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
export async function updateModelByValues(instance: Model, values: UpdateValue, options?: UpdateOptions) {
|
|
@@ -120,6 +121,10 @@ export async function updateAssociations(instance: Model, values: any, options:
|
|
|
120
121
|
return;
|
|
121
122
|
}
|
|
122
123
|
|
|
124
|
+
if (options?.updateAssociationValues) {
|
|
125
|
+
options.recursive = true;
|
|
126
|
+
}
|
|
127
|
+
|
|
123
128
|
let newTransaction = false;
|
|
124
129
|
let transaction = options.transaction;
|
|
125
130
|
|
|
@@ -262,7 +267,7 @@ export async function updateSingleAssociation(
|
|
|
262
267
|
throw new Error(`The value of '${key}' cannot be in array format`);
|
|
263
268
|
}
|
|
264
269
|
|
|
265
|
-
const { context, updateAssociationValues = [], transaction } = options;
|
|
270
|
+
const { recursive, context, updateAssociationValues = [], transaction } = options;
|
|
266
271
|
const keys = getKeysByPrefix(updateAssociationValues, key);
|
|
267
272
|
|
|
268
273
|
try {
|
|
@@ -292,13 +297,13 @@ export async function updateSingleAssociation(
|
|
|
292
297
|
|
|
293
298
|
const createAccessor = association.accessors.create;
|
|
294
299
|
let dataKey: string;
|
|
295
|
-
let M:
|
|
300
|
+
let M: ModelStatic<Model>;
|
|
296
301
|
if (association.associationType === 'BelongsTo') {
|
|
297
|
-
M = association.target as
|
|
302
|
+
M = association.target as ModelStatic<Model>;
|
|
298
303
|
// @ts-ignore
|
|
299
304
|
dataKey = association.targetKey;
|
|
300
305
|
} else {
|
|
301
|
-
M = association.target as
|
|
306
|
+
M = association.target as ModelStatic<Model>;
|
|
302
307
|
dataKey = M.primaryKeyAttribute;
|
|
303
308
|
}
|
|
304
309
|
|
|
@@ -313,6 +318,10 @@ export async function updateSingleAssociation(
|
|
|
313
318
|
if (instance) {
|
|
314
319
|
await model[setAccessor](instance, { context, transaction });
|
|
315
320
|
|
|
321
|
+
if (!recursive) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
316
325
|
if (updateAssociationValues.includes(key)) {
|
|
317
326
|
await instance.update(value, { ...options, transaction });
|
|
318
327
|
}
|
|
@@ -368,13 +377,14 @@ export async function updateMultipleAssociation(
|
|
|
368
377
|
return false;
|
|
369
378
|
}
|
|
370
379
|
|
|
371
|
-
const { context, updateAssociationValues = [], transaction } = options;
|
|
380
|
+
const { recursive, context, updateAssociationValues = [], transaction } = options;
|
|
372
381
|
const keys = getKeysByPrefix(updateAssociationValues, key);
|
|
373
382
|
|
|
374
383
|
try {
|
|
375
384
|
const setAccessor = association.accessors.set;
|
|
376
385
|
|
|
377
386
|
const createAccessor = association.accessors.create;
|
|
387
|
+
|
|
378
388
|
if (isUndefinedOrNull(value)) {
|
|
379
389
|
await model[setAccessor](null, { transaction, context });
|
|
380
390
|
model.setDataValue(key, null);
|
|
@@ -442,9 +452,15 @@ export async function updateMultipleAssociation(
|
|
|
442
452
|
const instance = await association.target.findByPk<any>(item[pk], {
|
|
443
453
|
transaction,
|
|
444
454
|
});
|
|
455
|
+
if (!instance) {
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
445
458
|
const addAccessor = association.accessors.add;
|
|
446
459
|
|
|
447
460
|
await model[addAccessor](item[pk], accessorOptions);
|
|
461
|
+
if (!recursive) {
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
448
464
|
if (updateAssociationValues.includes(key)) {
|
|
449
465
|
await instance.update(item, { ...options, transaction });
|
|
450
466
|
}
|
package/src/update-guard.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import lodash from 'lodash';
|
|
2
|
-
import {
|
|
2
|
+
import { ModelStatic } from 'sequelize';
|
|
3
3
|
import { Model } from './model';
|
|
4
4
|
import { AssociationKeysToBeUpdate, BlackList, WhiteList } from './repository';
|
|
5
5
|
|
|
@@ -11,7 +11,7 @@ type UpdateValues = {
|
|
|
11
11
|
|
|
12
12
|
type UpdateAction = 'create' | 'update';
|
|
13
13
|
export class UpdateGuard {
|
|
14
|
-
model:
|
|
14
|
+
model: ModelStatic<any>;
|
|
15
15
|
action: UpdateAction;
|
|
16
16
|
private associationKeysToBeUpdate: AssociationKeysToBeUpdate;
|
|
17
17
|
private blackList: BlackList;
|
|
@@ -21,7 +21,7 @@ export class UpdateGuard {
|
|
|
21
21
|
this.action = action;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
setModel(model:
|
|
24
|
+
setModel(model: ModelStatic<any>) {
|
|
25
25
|
this.model = model;
|
|
26
26
|
}
|
|
27
27
|
|