@nocobase/database 0.9.3-alpha.1 → 0.9.4-alpha.1
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 +9 -9
- package/lib/collection.js +100 -96
- package/lib/database.d.ts +4 -4
- package/lib/database.js +25 -53
- package/lib/eager-loading/eager-loading-tree.d.ts +23 -0
- package/lib/eager-loading/eager-loading-tree.js +338 -0
- package/lib/filter-parser.d.ts +1 -7
- package/lib/filter-parser.js +27 -7
- package/lib/listeners/append-child-collection-name-after-repository-find.d.ts +5 -0
- package/lib/listeners/append-child-collection-name-after-repository-find.js +40 -0
- package/lib/listeners/index.js +2 -0
- package/lib/mock-database.js +3 -1
- package/lib/operators/string.js +1 -1
- package/lib/options-parser.js +4 -0
- package/lib/query-interface/postgres-query-interface.js +2 -2
- package/lib/relation-repository/belongs-to-many-repository.d.ts +2 -1
- package/lib/relation-repository/belongs-to-many-repository.js +58 -37
- package/lib/relation-repository/hasmany-repository.d.ts +2 -1
- package/lib/relation-repository/hasmany-repository.js +31 -16
- package/lib/relation-repository/multiple-relation-repository.js +8 -26
- package/lib/relation-repository/relation-repository.d.ts +1 -7
- package/lib/relation-repository/single-relation-repository.d.ts +1 -1
- package/lib/relation-repository/single-relation-repository.js +10 -16
- package/lib/repository.d.ts +11 -8
- package/lib/repository.js +104 -89
- package/lib/sql-parser/postgres.js +41 -0
- package/lib/update-guard.d.ts +1 -1
- package/lib/update-guard.js +16 -13
- package/lib/utils.d.ts +0 -7
- package/lib/utils.js +0 -76
- package/package.json +4 -4
- package/src/__tests__/eager-loading/eager-loading-tree.test.ts +393 -0
- package/src/__tests__/migrator.test.ts +4 -0
- package/src/__tests__/relation-repository/hasone-repository.test.ts +1 -0
- package/src/__tests__/repository/aggregation.test.ts +297 -0
- package/src/__tests__/repository/count.test.ts +1 -1
- package/src/__tests__/repository/find.test.ts +10 -1
- package/src/__tests__/repository.test.ts +30 -0
- package/src/__tests__/update-guard.test.ts +13 -0
- package/src/collection.ts +74 -66
- package/src/database.ts +26 -42
- package/src/eager-loading/eager-loading-tree.ts +304 -0
- package/src/filter-parser.ts +16 -2
- package/src/listeners/adjacency-list.ts +1 -3
- package/src/listeners/append-child-collection-name-after-repository-find.ts +31 -0
- package/src/listeners/index.ts +2 -0
- package/src/mock-database.ts +3 -1
- package/src/operators/notIn.ts +1 -0
- package/src/operators/string.ts +1 -1
- package/src/options-parser.ts +5 -0
- package/src/query-interface/postgres-query-interface.ts +1 -1
- package/src/relation-repository/belongs-to-many-repository.ts +33 -1
- package/src/relation-repository/hasmany-repository.ts +17 -0
- package/src/relation-repository/multiple-relation-repository.ts +14 -19
- package/src/relation-repository/single-relation-repository.ts +13 -15
- package/src/repository.ts +79 -36
- package/src/sql-parser/postgres.js +25505 -0
- package/src/update-guard.ts +21 -16
- package/src/utils.ts +0 -61
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { MultiAssociationAccessors, Op, Sequelize, Transaction, Transactionable } from 'sequelize';
|
|
1
|
+
import { MultiAssociationAccessors, Sequelize, Transaction, Transactionable } from 'sequelize';
|
|
3
2
|
import {
|
|
4
3
|
CommonFindOptions,
|
|
5
4
|
CountOptions,
|
|
@@ -14,7 +13,7 @@ import {
|
|
|
14
13
|
import { updateModelByValues } from '../update-associations';
|
|
15
14
|
import { UpdateGuard } from '../update-guard';
|
|
16
15
|
import { RelationRepository, transaction } from './relation-repository';
|
|
17
|
-
import {
|
|
16
|
+
import { EagerLoadingTree } from '../eager-loading/eager-loading-tree';
|
|
18
17
|
|
|
19
18
|
export type FindAndCountOptions = CommonFindOptions;
|
|
20
19
|
|
|
@@ -61,23 +60,19 @@ export abstract class MultipleRelationRepository extends RelationRepository {
|
|
|
61
60
|
return [];
|
|
62
61
|
}
|
|
63
62
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
include: [include],
|
|
70
|
-
where: {
|
|
71
|
-
[this.targetKey()]: {
|
|
72
|
-
[Op.in]: ids.map((id) => id.pk),
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
transaction,
|
|
76
|
-
}).then((rows) => {
|
|
77
|
-
return { rows, include };
|
|
78
|
-
});
|
|
79
|
-
}),
|
|
63
|
+
const eagerLoadingTree = EagerLoadingTree.buildFromSequelizeOptions({
|
|
64
|
+
model: this.targetModel,
|
|
65
|
+
rootAttributes: findOptions.attributes,
|
|
66
|
+
includeOption: findOptions.include,
|
|
67
|
+
rootOrder: findOptions.order,
|
|
80
68
|
});
|
|
69
|
+
|
|
70
|
+
await eagerLoadingTree.load(
|
|
71
|
+
ids.map((i) => i.pk),
|
|
72
|
+
transaction,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
return eagerLoadingTree.root.instances;
|
|
81
76
|
}
|
|
82
77
|
|
|
83
78
|
const data = await sourceModel[getAccessor]({
|
|
@@ -3,8 +3,8 @@ 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 { handleAppendsQuery } from '../utils';
|
|
7
6
|
import { RelationRepository, transaction } from './relation-repository';
|
|
7
|
+
import { EagerLoadingTree } from '../eager-loading/eager-loading-tree';
|
|
8
8
|
|
|
9
9
|
export interface SingleRelationFindOption extends Transactionable {
|
|
10
10
|
fields?: Fields;
|
|
@@ -44,7 +44,7 @@ export abstract class SingleRelationRepository extends RelationRepository {
|
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
async find(options?: SingleRelationFindOption): Promise<
|
|
47
|
+
async find(options?: SingleRelationFindOption): Promise<any> {
|
|
48
48
|
const transaction = await this.getTransaction(options);
|
|
49
49
|
|
|
50
50
|
const findOptions = this.buildQueryOptions({
|
|
@@ -61,23 +61,21 @@ export abstract class SingleRelationRepository extends RelationRepository {
|
|
|
61
61
|
...findOptions,
|
|
62
62
|
includeIgnoreAttributes: false,
|
|
63
63
|
transaction,
|
|
64
|
-
attributes: [this.
|
|
65
|
-
group: `${this.targetModel.name}.${this.
|
|
64
|
+
attributes: [this.targetModel.primaryKeyAttribute],
|
|
65
|
+
group: `${this.targetModel.name}.${this.targetModel.primaryKeyAttribute}`,
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}).then((row) => {
|
|
75
|
-
return { rows: [row], include };
|
|
76
|
-
});
|
|
77
|
-
}),
|
|
68
|
+
if (!templateModel) return null;
|
|
69
|
+
|
|
70
|
+
const eagerLoadingTree = EagerLoadingTree.buildFromSequelizeOptions({
|
|
71
|
+
model: this.targetModel,
|
|
72
|
+
rootAttributes: findOptions.attributes,
|
|
73
|
+
includeOption: findOptions.include,
|
|
78
74
|
});
|
|
79
75
|
|
|
80
|
-
|
|
76
|
+
await eagerLoadingTree.load([templateModel.get(this.targetModel.primaryKeyAttribute)], transaction);
|
|
77
|
+
|
|
78
|
+
return eagerLoadingTree.root.instances[0];
|
|
81
79
|
}
|
|
82
80
|
|
|
83
81
|
return await sourceModel[getAccessor]({
|
package/src/repository.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import lodash
|
|
1
|
+
import lodash from 'lodash';
|
|
2
2
|
import {
|
|
3
3
|
Association,
|
|
4
4
|
BulkCreateOptions,
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
FindOptions as SequelizeFindOptions,
|
|
10
10
|
ModelStatic,
|
|
11
11
|
Op,
|
|
12
|
+
Sequelize,
|
|
12
13
|
Transactionable,
|
|
13
14
|
UpdateOptions as SequelizeUpdateOptions,
|
|
14
15
|
WhereOperators,
|
|
@@ -30,7 +31,7 @@ import { HasOneRepository } from './relation-repository/hasone-repository';
|
|
|
30
31
|
import { RelationRepository } from './relation-repository/relation-repository';
|
|
31
32
|
import { updateAssociations, updateModelByValues } from './update-associations';
|
|
32
33
|
import { UpdateGuard } from './update-guard';
|
|
33
|
-
import {
|
|
34
|
+
import { EagerLoadingTree } from './eager-loading/eager-loading-tree';
|
|
34
35
|
|
|
35
36
|
const debug = require('debug')('noco-database');
|
|
36
37
|
|
|
@@ -189,10 +190,6 @@ class RelationRepositoryBuilder<R extends RelationRepository> {
|
|
|
189
190
|
}
|
|
190
191
|
}
|
|
191
192
|
|
|
192
|
-
protected builder() {
|
|
193
|
-
return this.builderMap;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
193
|
of(id: string | number): R {
|
|
197
194
|
if (!this.association) {
|
|
198
195
|
return;
|
|
@@ -200,6 +197,17 @@ class RelationRepositoryBuilder<R extends RelationRepository> {
|
|
|
200
197
|
const klass = this.builder()[this.association.associationType];
|
|
201
198
|
return new klass(this.collection, this.associationName, id);
|
|
202
199
|
}
|
|
200
|
+
|
|
201
|
+
protected builder() {
|
|
202
|
+
return this.builderMap;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export interface AggregateOptions {
|
|
207
|
+
method: 'avg' | 'count' | 'min' | 'max' | 'sum';
|
|
208
|
+
field?: string;
|
|
209
|
+
filter?: Filter;
|
|
210
|
+
distinct?: boolean;
|
|
203
211
|
}
|
|
204
212
|
|
|
205
213
|
export class Repository<TModelAttributes extends {} = any, TCreationAttributes extends {} = TModelAttributes>
|
|
@@ -259,6 +267,59 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|
|
259
267
|
return count;
|
|
260
268
|
}
|
|
261
269
|
|
|
270
|
+
async aggregate(options: AggregateOptions & { optionsTransformer?: (options: any) => any }): Promise<any> {
|
|
271
|
+
const { method, field } = options;
|
|
272
|
+
|
|
273
|
+
const queryOptions = this.buildQueryOptions({
|
|
274
|
+
...options,
|
|
275
|
+
fields: [],
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
options.optionsTransformer?.(queryOptions);
|
|
279
|
+
const hasAssociationFilter = () => {
|
|
280
|
+
if (queryOptions.include && queryOptions.include.length > 0) {
|
|
281
|
+
const filterInclude = queryOptions.include.filter((include) => {
|
|
282
|
+
return (
|
|
283
|
+
Object.keys(include.where || {}).length > 0 ||
|
|
284
|
+
JSON.stringify(queryOptions?.filter)?.includes(include.association)
|
|
285
|
+
);
|
|
286
|
+
});
|
|
287
|
+
return filterInclude.length > 0;
|
|
288
|
+
}
|
|
289
|
+
return false;
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
if (hasAssociationFilter()) {
|
|
293
|
+
const primaryKeyField = this.model.primaryKeyAttribute;
|
|
294
|
+
const queryInterface = this.database.sequelize.getQueryInterface();
|
|
295
|
+
|
|
296
|
+
const findOptions = {
|
|
297
|
+
...queryOptions,
|
|
298
|
+
raw: true,
|
|
299
|
+
includeIgnoreAttributes: false,
|
|
300
|
+
attributes: [
|
|
301
|
+
[
|
|
302
|
+
Sequelize.literal(
|
|
303
|
+
`DISTINCT ${queryInterface.quoteIdentifiers(`${this.collection.name}.${primaryKeyField}`)}`,
|
|
304
|
+
),
|
|
305
|
+
primaryKeyField,
|
|
306
|
+
],
|
|
307
|
+
],
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
const ids = await this.model.findAll(findOptions);
|
|
311
|
+
|
|
312
|
+
return await this.model.aggregate(field, method, {
|
|
313
|
+
...lodash.omit(queryOptions, ['where', 'include']),
|
|
314
|
+
where: {
|
|
315
|
+
[primaryKeyField]: ids.map((node) => node[primaryKeyField]),
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return await this.model.aggregate(field, method, queryOptions);
|
|
321
|
+
}
|
|
322
|
+
|
|
262
323
|
/**
|
|
263
324
|
* find
|
|
264
325
|
* @param options
|
|
@@ -300,38 +361,20 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|
|
300
361
|
return [];
|
|
301
362
|
}
|
|
302
363
|
|
|
303
|
-
// find
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
limit: 1,
|
|
311
|
-
offset: 0,
|
|
312
|
-
} as any);
|
|
364
|
+
// find all rows
|
|
365
|
+
const eagerLoadingTree = EagerLoadingTree.buildFromSequelizeOptions({
|
|
366
|
+
model,
|
|
367
|
+
rootAttributes: opts.attributes,
|
|
368
|
+
includeOption: opts.include,
|
|
369
|
+
rootOrder: opts.order,
|
|
370
|
+
});
|
|
313
371
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
};
|
|
372
|
+
await eagerLoadingTree.load(
|
|
373
|
+
ids.map((i) => i.pk),
|
|
374
|
+
transaction,
|
|
375
|
+
);
|
|
319
376
|
|
|
320
|
-
rows =
|
|
321
|
-
queryPromises: opts.include.map((include) => {
|
|
322
|
-
const options = {
|
|
323
|
-
...omit(opts, ['limit', 'offset', 'filter']),
|
|
324
|
-
include: include,
|
|
325
|
-
where,
|
|
326
|
-
transaction,
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
return model.findAll(options).then((rows) => {
|
|
330
|
-
return { rows, include };
|
|
331
|
-
});
|
|
332
|
-
}),
|
|
333
|
-
templateModel: templateModel,
|
|
334
|
-
});
|
|
377
|
+
rows = eagerLoadingTree.root.instances;
|
|
335
378
|
} else {
|
|
336
379
|
rows = await model.findAll({
|
|
337
380
|
...opts,
|