@nocobase/database 0.7.6-alpha.2 → 0.8.0-alpha.10
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 +1 -1
- package/lib/collection.js +1 -0
- package/lib/database.d.ts +17 -2
- package/lib/database.js +53 -41
- package/lib/decorators/transaction-decorator.js +1 -0
- package/lib/features/ReferencesMap.d.ts +15 -0
- package/lib/features/ReferencesMap.js +60 -0
- package/lib/features/referential-integrity-check.d.ts +8 -0
- package/lib/features/referential-integrity-check.js +89 -0
- package/lib/fields/belongs-to-field.d.ts +2 -0
- package/lib/fields/belongs-to-field.js +17 -2
- package/lib/fields/belongs-to-many-field.d.ts +1 -0
- package/lib/fields/belongs-to-many-field.js +9 -2
- package/lib/fields/context-field.d.ts +2 -1
- package/lib/fields/context-field.js +12 -8
- package/lib/fields/field.d.ts +2 -1
- package/lib/fields/field.js +1 -0
- package/lib/fields/formula-field.js +1 -1
- package/lib/fields/has-many-field.d.ts +2 -0
- package/lib/fields/has-many-field.js +21 -2
- package/lib/fields/has-one-field.d.ts +2 -0
- package/lib/fields/has-one-field.js +17 -2
- package/lib/fields/relation-field.d.ts +1 -0
- package/lib/fields/relation-field.js +16 -0
- package/lib/fields/sequence-field.d.ts +2 -1
- package/lib/fields/sequence-field.js +3 -2
- package/lib/filter-parser.js +5 -3
- package/lib/model.d.ts +1 -0
- package/lib/options-parser.js +3 -2
- package/lib/relation-repository/belongs-to-many-repository.d.ts +3 -3
- package/lib/relation-repository/hasmany-repository.d.ts +2 -2
- package/lib/relation-repository/hasone-repository.d.ts +2 -4
- package/lib/relation-repository/multiple-relation-repository.d.ts +2 -5
- package/lib/relation-repository/relation-repository.js +2 -2
- package/lib/relation-repository/single-relation-repository.d.ts +1 -1
- package/lib/repository.d.ts +25 -15
- package/lib/repository.js +3 -1
- package/lib/types.d.ts +43 -0
- package/lib/types.js +5 -0
- package/package.json +3 -3
- package/src/__tests__/fields/belongs-to-field.test.ts +112 -4
- package/src/__tests__/fields/has-many-field.test.ts +83 -0
- package/src/__tests__/fields/sequence-field.test.ts +25 -0
- package/src/__tests__/relation-repository/hasone-repository.test.ts +3 -3
- package/src/__tests__/repository/find.test.ts +10 -0
- package/src/collection.ts +3 -1
- package/src/database.ts +64 -15
- package/src/decorators/transaction-decorator.ts +1 -0
- package/src/features/ReferencesMap.ts +64 -0
- package/src/features/referential-integrity-check.ts +61 -0
- package/src/fields/belongs-to-field.ts +22 -1
- package/src/fields/belongs-to-many-field.ts +11 -0
- package/src/fields/context-field.ts +5 -7
- package/src/fields/field.ts +3 -2
- package/src/fields/formula-field.ts +4 -4
- package/src/fields/has-many-field.ts +26 -2
- package/src/fields/has-one-field.ts +23 -2
- package/src/fields/relation-field.ts +16 -0
- package/src/fields/sequence-field.ts +4 -2
- package/src/filter-parser.ts +5 -1
- package/src/model-hook.ts +0 -2
- package/src/model.ts +1 -0
- package/src/options-parser.ts +4 -2
- package/src/relation-repository/belongs-to-many-repository.ts +3 -3
- package/src/relation-repository/hasmany-repository.ts +8 -5
- package/src/relation-repository/hasone-repository.ts +2 -4
- package/src/relation-repository/multiple-relation-repository.ts +3 -4
- package/src/relation-repository/relation-repository.ts +1 -1
- package/src/relation-repository/single-relation-repository.ts +1 -1
- package/src/repository.ts +40 -19
- package/src/types.ts +64 -0
- package/src/update-associations.ts +2 -1
|
@@ -9,14 +9,12 @@ export class ContextField extends Field {
|
|
|
9
9
|
return DataTypes[type.toUpperCase()] || DataTypes.STRING;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
listener = async (model: Model, options) => {
|
|
13
13
|
const { name, dataIndex } = this.options;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
};
|
|
19
|
-
}
|
|
14
|
+
const { context } = options;
|
|
15
|
+
model.set(name, lodash.get(context, dataIndex));
|
|
16
|
+
model.changed(name, true);
|
|
17
|
+
};
|
|
20
18
|
|
|
21
19
|
bind() {
|
|
22
20
|
super.bind();
|
package/src/fields/field.ts
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from 'sequelize';
|
|
10
10
|
import { Collection } from '../collection';
|
|
11
11
|
import { Database } from '../database';
|
|
12
|
-
import {
|
|
12
|
+
import { ModelEventTypes } from '../types';
|
|
13
13
|
|
|
14
14
|
export interface FieldContext {
|
|
15
15
|
database: Database;
|
|
@@ -68,7 +68,7 @@ export abstract class Field {
|
|
|
68
68
|
// code
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
on(eventName:
|
|
71
|
+
on(eventName: ModelEventTypes, listener: (...args: any[]) => void) {
|
|
72
72
|
this.database.on(`${this.collection.name}.${eventName}`, listener);
|
|
73
73
|
return this;
|
|
74
74
|
}
|
|
@@ -83,6 +83,7 @@ export abstract class Field {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
remove() {
|
|
86
|
+
this.collection.removeIndex([this.name]);
|
|
86
87
|
return this.collection.removeField(this.name);
|
|
87
88
|
}
|
|
88
89
|
|
|
@@ -40,7 +40,7 @@ export class FormulaField extends Field {
|
|
|
40
40
|
);
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
-
}
|
|
43
|
+
};
|
|
44
44
|
|
|
45
45
|
caculateField = async (instance) => {
|
|
46
46
|
const { expression, name } = this.options;
|
|
@@ -50,10 +50,10 @@ export class FormulaField extends Field {
|
|
|
50
50
|
result = math.evaluate(expression, scope);
|
|
51
51
|
result = math.round(result, 9);
|
|
52
52
|
} catch {}
|
|
53
|
-
if (result) {
|
|
53
|
+
if (result === 0 || result) {
|
|
54
54
|
instance.set(name, result);
|
|
55
55
|
}
|
|
56
|
-
}
|
|
56
|
+
};
|
|
57
57
|
|
|
58
58
|
updateFieldData = async (instance, { transaction }) => {
|
|
59
59
|
if (this.collection.name === instance.collectionName && instance.name === this.options.name) {
|
|
@@ -80,7 +80,7 @@ export class FormulaField extends Field {
|
|
|
80
80
|
);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
-
}
|
|
83
|
+
};
|
|
84
84
|
|
|
85
85
|
bind() {
|
|
86
86
|
super.bind();
|
|
@@ -5,11 +5,12 @@ import {
|
|
|
5
5
|
ForeignKeyOptions,
|
|
6
6
|
HasManyOptions,
|
|
7
7
|
HasManyOptions as SequelizeHasManyOptions,
|
|
8
|
-
Utils
|
|
8
|
+
Utils,
|
|
9
9
|
} from 'sequelize';
|
|
10
10
|
import { Collection } from '../collection';
|
|
11
11
|
import { checkIdentifier } from '../utils';
|
|
12
12
|
import { MultipleRelationFieldOptions, RelationField } from './relation-field';
|
|
13
|
+
import { Reference } from '../features/ReferencesMap';
|
|
13
14
|
|
|
14
15
|
export interface HasManyFieldOptions extends HasManyOptions {
|
|
15
16
|
/**
|
|
@@ -81,6 +82,18 @@ export class HasManyField extends RelationField {
|
|
|
81
82
|
return Utils.camelize([model.options.name.singular, this.sourceKey || model.primaryKeyAttribute].join('_'));
|
|
82
83
|
}
|
|
83
84
|
|
|
85
|
+
reference(association): Reference {
|
|
86
|
+
const sourceKey = association.sourceKey;
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
sourceCollectionName: this.database.modelCollection.get(association.target).name,
|
|
90
|
+
sourceField: association.foreignKey,
|
|
91
|
+
targetField: sourceKey,
|
|
92
|
+
targetCollectionName: this.database.modelCollection.get(association.source).name,
|
|
93
|
+
onDelete: this.options.onDelete,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
84
97
|
bind() {
|
|
85
98
|
const { database, collection } = this.context;
|
|
86
99
|
const Target = this.TargetModel;
|
|
@@ -95,7 +108,7 @@ export class HasManyField extends RelationField {
|
|
|
95
108
|
|
|
96
109
|
const association = collection.model.hasMany(Target, {
|
|
97
110
|
constraints: false,
|
|
98
|
-
...omit(this.options, ['name', 'type', 'target']),
|
|
111
|
+
...omit(this.options, ['name', 'type', 'target', 'onDelete']),
|
|
99
112
|
as: this.name,
|
|
100
113
|
foreignKey: this.foreignKey,
|
|
101
114
|
});
|
|
@@ -130,6 +143,9 @@ export class HasManyField extends RelationField {
|
|
|
130
143
|
if (tcoll) {
|
|
131
144
|
tcoll.addIndex([this.options.foreignKey]);
|
|
132
145
|
}
|
|
146
|
+
|
|
147
|
+
this.database.referenceMap.addReference(this.reference(association));
|
|
148
|
+
|
|
133
149
|
return true;
|
|
134
150
|
}
|
|
135
151
|
|
|
@@ -149,6 +165,14 @@ export class HasManyField extends RelationField {
|
|
|
149
165
|
if (!field) {
|
|
150
166
|
tcoll.model.removeAttribute(foreignKey);
|
|
151
167
|
}
|
|
168
|
+
|
|
169
|
+
const association = collection.model.associations[this.name];
|
|
170
|
+
|
|
171
|
+
if (association) {
|
|
172
|
+
this.database.referenceMap.removeReference(this.reference(association));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
this.clearAccessors();
|
|
152
176
|
// 删掉 model 的关联字段
|
|
153
177
|
delete collection.model.associations[this.name];
|
|
154
178
|
// @ts-ignore
|
|
@@ -5,11 +5,12 @@ import {
|
|
|
5
5
|
ForeignKeyOptions,
|
|
6
6
|
HasOneOptions,
|
|
7
7
|
HasOneOptions as SequelizeHasOneOptions,
|
|
8
|
-
Utils
|
|
8
|
+
Utils,
|
|
9
9
|
} from 'sequelize';
|
|
10
10
|
import { Collection } from '../collection';
|
|
11
11
|
import { checkIdentifier } from '../utils';
|
|
12
12
|
import { BaseRelationFieldOptions, RelationField } from './relation-field';
|
|
13
|
+
import { Reference } from '../features/ReferencesMap';
|
|
13
14
|
|
|
14
15
|
export interface HasOneFieldOptions extends HasOneOptions {
|
|
15
16
|
/**
|
|
@@ -86,9 +87,22 @@ export class HasOneField extends RelationField {
|
|
|
86
87
|
return Utils.camelize([model.options.name.singular, model.primaryKeyAttribute].join('_'));
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
reference(association): Reference {
|
|
91
|
+
const sourceKey = association.sourceKey;
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
sourceCollectionName: this.database.modelCollection.get(association.target).name,
|
|
95
|
+
sourceField: association.foreignKey,
|
|
96
|
+
targetField: sourceKey,
|
|
97
|
+
targetCollectionName: this.database.modelCollection.get(association.source).name,
|
|
98
|
+
onDelete: this.options.onDelete,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
89
102
|
bind() {
|
|
90
103
|
const { database, collection } = this.context;
|
|
91
104
|
const Target = this.TargetModel;
|
|
105
|
+
|
|
92
106
|
if (!Target) {
|
|
93
107
|
database.addPendingField(this);
|
|
94
108
|
return false;
|
|
@@ -96,7 +110,7 @@ export class HasOneField extends RelationField {
|
|
|
96
110
|
|
|
97
111
|
const association = collection.model.hasOne(Target, {
|
|
98
112
|
constraints: false,
|
|
99
|
-
...omit(this.options, ['name', 'type', 'target']),
|
|
113
|
+
...omit(this.options, ['name', 'type', 'target', 'onDelete']),
|
|
100
114
|
as: this.name,
|
|
101
115
|
foreignKey: this.foreignKey,
|
|
102
116
|
});
|
|
@@ -129,6 +143,8 @@ export class HasOneField extends RelationField {
|
|
|
129
143
|
if (tcoll) {
|
|
130
144
|
tcoll.addIndex([this.options.foreignKey]);
|
|
131
145
|
}
|
|
146
|
+
|
|
147
|
+
this.database.referenceMap.addReference(this.reference(association));
|
|
132
148
|
return true;
|
|
133
149
|
}
|
|
134
150
|
|
|
@@ -148,6 +164,11 @@ export class HasOneField extends RelationField {
|
|
|
148
164
|
if (!field) {
|
|
149
165
|
tcoll.model.removeAttribute(foreignKey);
|
|
150
166
|
}
|
|
167
|
+
|
|
168
|
+
const association = collection.model.associations[this.name];
|
|
169
|
+
this.database.referenceMap.removeReference(this.reference(association));
|
|
170
|
+
|
|
171
|
+
this.clearAccessors();
|
|
151
172
|
// 删掉 model 的关联字段
|
|
152
173
|
delete collection.model.associations[this.name];
|
|
153
174
|
// @ts-ignore
|
|
@@ -34,4 +34,20 @@ export abstract class RelationField extends Field {
|
|
|
34
34
|
get TargetModel() {
|
|
35
35
|
return this.context.database.sequelize.models[this.target];
|
|
36
36
|
}
|
|
37
|
+
|
|
38
|
+
protected clearAccessors() {
|
|
39
|
+
const { collection } = this.context;
|
|
40
|
+
const association = collection.model.associations[this.name];
|
|
41
|
+
if (!association) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// @ts-ignore
|
|
46
|
+
const accessors = Object.values(association.accessors);
|
|
47
|
+
|
|
48
|
+
accessors.forEach((accessor) => {
|
|
49
|
+
// @ts-ignore
|
|
50
|
+
delete collection.model.prototype[accessor];
|
|
51
|
+
});
|
|
52
|
+
}
|
|
37
53
|
}
|
|
@@ -44,7 +44,7 @@ sequencePatterns.register('integer', {
|
|
|
44
44
|
const { lastRecord = null } = this.options;
|
|
45
45
|
|
|
46
46
|
if (typeof options.current === 'undefined') {
|
|
47
|
-
if (lastRecord) {
|
|
47
|
+
if (lastRecord && lastRecord.get(this.options.name)) {
|
|
48
48
|
// if match current pattern
|
|
49
49
|
const matcher = this.match(lastRecord.get(this.options.name));
|
|
50
50
|
if (matcher) {
|
|
@@ -115,6 +115,8 @@ export interface SequenceFieldOptions extends BaseColumnFieldOptions {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
export class SequenceField extends Field {
|
|
118
|
+
matcher: RegExp;
|
|
119
|
+
|
|
118
120
|
get dataType() {
|
|
119
121
|
return DataTypes.STRING;
|
|
120
122
|
}
|
|
@@ -170,7 +172,7 @@ export class SequenceField extends Field {
|
|
|
170
172
|
};
|
|
171
173
|
|
|
172
174
|
match(value) {
|
|
173
|
-
return value.match(this.matcher);
|
|
175
|
+
return typeof value === 'string' ? value.match(this.matcher) : null;
|
|
174
176
|
}
|
|
175
177
|
|
|
176
178
|
parse(value: string, patternIndex: number): string {
|
package/src/filter-parser.ts
CHANGED
|
@@ -78,7 +78,6 @@ export default class FilterParser {
|
|
|
78
78
|
|
|
79
79
|
const include = {};
|
|
80
80
|
const where = {};
|
|
81
|
-
const filter2 = lodash.cloneDeep(flattenedFilter);
|
|
82
81
|
|
|
83
82
|
let skipPrefix = null;
|
|
84
83
|
const associations = model.associations;
|
|
@@ -91,6 +90,11 @@ export default class FilterParser {
|
|
|
91
90
|
continue;
|
|
92
91
|
}
|
|
93
92
|
|
|
93
|
+
// skip empty logic operator value
|
|
94
|
+
if ((key == '$or' || key == '$and') && Array.isArray(value) && value.length == 0) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
94
98
|
debug('handle filter key "%s: "%s"', key, value);
|
|
95
99
|
let keys = key.split('.');
|
|
96
100
|
|
package/src/model-hook.ts
CHANGED
package/src/model.ts
CHANGED
|
@@ -23,6 +23,7 @@ export class Model<TModelAttributes extends {} = any, TCreationAttributes extend
|
|
|
23
23
|
public static database: Database;
|
|
24
24
|
public static collection: Collection;
|
|
25
25
|
|
|
26
|
+
[key: string]: any;
|
|
26
27
|
protected _changedWithAssociations = new Set();
|
|
27
28
|
protected _previousDataValuesWithAssociations = {};
|
|
28
29
|
|
package/src/options-parser.ts
CHANGED
|
@@ -70,6 +70,7 @@ export class OptionsParser {
|
|
|
70
70
|
if (typeof sort === 'string') {
|
|
71
71
|
sort = sort.split(',');
|
|
72
72
|
}
|
|
73
|
+
|
|
73
74
|
const orderParams = [];
|
|
74
75
|
for (const sortKey of sort) {
|
|
75
76
|
let direction = sortKey.startsWith('-') ? 'DESC' : 'ASC';
|
|
@@ -191,8 +192,9 @@ export class OptionsParser {
|
|
|
191
192
|
|
|
192
193
|
/**
|
|
193
194
|
* set include params
|
|
194
|
-
* @param
|
|
195
|
-
* @param
|
|
195
|
+
* @param model
|
|
196
|
+
* @param queryParams
|
|
197
|
+
* @param append
|
|
196
198
|
*/
|
|
197
199
|
const setInclude = (model: ModelCtor<any>, queryParams: any, append: string) => {
|
|
198
200
|
const appendFields = append.split('.');
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import lodash from 'lodash';
|
|
2
2
|
import { BelongsToMany, Op, Transaction } from 'sequelize';
|
|
3
3
|
import { Model } from '../model';
|
|
4
|
-
import { CreateOptions, DestroyOptions, FindOptions, TargetKey, UpdateOptions } from '../repository';
|
|
4
|
+
import { CreateOptions, DestroyOptions, FindOneOptions, FindOptions, TargetKey, UpdateOptions } from '../repository';
|
|
5
5
|
import { updateThroughTableValue } from '../update-associations';
|
|
6
|
-
import { FindAndCountOptions,
|
|
6
|
+
import { FindAndCountOptions, MultipleRelationRepository } from './multiple-relation-repository';
|
|
7
7
|
import { transaction } from './relation-repository';
|
|
8
8
|
import { AssociatedOptions, PrimaryKeyWithThroughValues } from './types';
|
|
9
9
|
|
|
@@ -14,7 +14,7 @@ interface IBelongsToManyRepository<M extends Model> {
|
|
|
14
14
|
findAndCount(options?: FindAndCountOptions): Promise<[M[], number]>;
|
|
15
15
|
findOne(options?: FindOneOptions): Promise<M>;
|
|
16
16
|
// 新增并关联,存在中间表数据
|
|
17
|
-
create(options?:
|
|
17
|
+
create(options?: CreateOptions): Promise<M>;
|
|
18
18
|
// 更新,存在中间表数据
|
|
19
19
|
update(options?: UpdateOptions): Promise<M>;
|
|
20
20
|
// 删除
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { omit } from 'lodash';
|
|
2
2
|
import { HasMany, Op } from 'sequelize';
|
|
3
3
|
import { Model } from '../model';
|
|
4
|
-
import { CreateOptions, DestroyOptions, FindOptions, TargetKey, TK, UpdateOptions } from '../repository';
|
|
5
4
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
CreateOptions,
|
|
6
|
+
DestroyOptions,
|
|
8
7
|
FindOneOptions,
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
FindOptions,
|
|
9
|
+
TargetKey,
|
|
10
|
+
TK,
|
|
11
|
+
UpdateOptions,
|
|
12
|
+
} from '../repository';
|
|
13
|
+
import { AssociatedOptions, FindAndCountOptions, MultipleRelationRepository } from './multiple-relation-repository';
|
|
11
14
|
import { transaction } from './relation-repository';
|
|
12
15
|
|
|
13
16
|
interface IHasManyRepository<M extends Model> {
|
|
@@ -2,12 +2,10 @@ import { Model } from '../model';
|
|
|
2
2
|
import { CreateOptions } from '../repository';
|
|
3
3
|
import { SingleRelationFindOption, SingleRelationRepository } from './single-relation-repository';
|
|
4
4
|
|
|
5
|
-
interface HasOneFindOptions extends SingleRelationFindOption {}
|
|
6
|
-
|
|
7
5
|
interface IHasOneRepository<M extends Model> {
|
|
8
6
|
// 不需要 findOne,find 就是 findOne
|
|
9
|
-
find(options?:
|
|
10
|
-
findOne(options?:
|
|
7
|
+
find(options?: SingleRelationFindOption): Promise<M>;
|
|
8
|
+
findOne(options?: SingleRelationFindOption): Promise<M>;
|
|
11
9
|
// 新增并关联,如果存在关联,解除之后,与新数据建立关联
|
|
12
10
|
create(options?: CreateOptions): Promise<M>;
|
|
13
11
|
// 更新
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
CountOptions,
|
|
6
6
|
DestroyOptions,
|
|
7
7
|
Filter,
|
|
8
|
-
|
|
8
|
+
FindOneOptions,
|
|
9
9
|
FindOptions,
|
|
10
10
|
TargetKey,
|
|
11
11
|
TK,
|
|
@@ -16,9 +16,7 @@ import { UpdateGuard } from '../update-guard';
|
|
|
16
16
|
import { RelationRepository, transaction } from './relation-repository';
|
|
17
17
|
import { handleAppendsQuery } from '../utils';
|
|
18
18
|
|
|
19
|
-
export
|
|
20
|
-
|
|
21
|
-
export interface FindOneOptions extends CommonFindOptions, FilterByTk {}
|
|
19
|
+
export type FindAndCountOptions = CommonFindOptions;
|
|
22
20
|
|
|
23
21
|
export interface AssociatedOptions extends Transactionable {
|
|
24
22
|
tk?: TK;
|
|
@@ -88,6 +86,7 @@ export abstract class MultipleRelationRepository extends RelationRepository {
|
|
|
88
86
|
|
|
89
87
|
async findAndCount(options?: FindAndCountOptions): Promise<[any[], number]> {
|
|
90
88
|
const transaction = await this.getTransaction(options, false);
|
|
89
|
+
|
|
91
90
|
return [
|
|
92
91
|
await this.find({
|
|
93
92
|
...options,
|
|
@@ -2,12 +2,12 @@ import lodash from 'lodash';
|
|
|
2
2
|
import { Association, BelongsTo, BelongsToMany, HasMany, HasOne, ModelCtor, Transaction } from 'sequelize';
|
|
3
3
|
import { Collection } from '../collection';
|
|
4
4
|
import Database from '../database';
|
|
5
|
+
import { transactionWrapperBuilder } from '../decorators/transaction-decorator';
|
|
5
6
|
import { RelationField } from '../fields/relation-field';
|
|
6
7
|
import FilterParser from '../filter-parser';
|
|
7
8
|
import { Model } from '../model';
|
|
8
9
|
import { OptionsParser } from '../options-parser';
|
|
9
10
|
import { CreateOptions, Filter, FindOptions } from '../repository';
|
|
10
|
-
import { transactionWrapperBuilder } from '../decorators/transaction-decorator';
|
|
11
11
|
import { updateAssociations } from '../update-associations';
|
|
12
12
|
import { UpdateGuard } from '../update-guard';
|
|
13
13
|
|
|
@@ -44,7 +44,7 @@ export abstract class SingleRelationRepository extends RelationRepository {
|
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
async find(options?: SingleRelationFindOption): Promise<Model<any
|
|
47
|
+
async find(options?: SingleRelationFindOption): Promise<Model<any> | null> {
|
|
48
48
|
const transaction = await this.getTransaction(options);
|
|
49
49
|
|
|
50
50
|
const findOptions = this.buildQueryOptions({
|
package/src/repository.ts
CHANGED
|
@@ -2,6 +2,7 @@ import lodash, { omit } from 'lodash';
|
|
|
2
2
|
import {
|
|
3
3
|
Association,
|
|
4
4
|
BulkCreateOptions,
|
|
5
|
+
CountOptions as SequelizeCountOptions,
|
|
5
6
|
CreateOptions as SequelizeCreateOptions,
|
|
6
7
|
DestroyOptions as SequelizeDestroyOptions,
|
|
7
8
|
FindAndCountOptions as SequelizeAndCountOptions,
|
|
@@ -9,8 +10,9 @@ import {
|
|
|
9
10
|
ModelCtor,
|
|
10
11
|
Op,
|
|
11
12
|
Transactionable,
|
|
12
|
-
UpdateOptions as SequelizeUpdateOptions
|
|
13
|
+
UpdateOptions as SequelizeUpdateOptions,
|
|
13
14
|
} from 'sequelize';
|
|
15
|
+
import { WhereOperators } from 'sequelize/types/lib/model';
|
|
14
16
|
import { Collection } from './collection';
|
|
15
17
|
import { Database } from './database';
|
|
16
18
|
import mustHaveFilter from './decorators/must-have-filter-decorator';
|
|
@@ -18,6 +20,7 @@ import { transactionWrapperBuilder } from './decorators/transaction-decorator';
|
|
|
18
20
|
import { RelationField } from './fields';
|
|
19
21
|
import FilterParser from './filter-parser';
|
|
20
22
|
import { Model } from './model';
|
|
23
|
+
import operators from './operators';
|
|
21
24
|
import { OptionsParser } from './options-parser';
|
|
22
25
|
import { BelongsToManyRepository } from './relation-repository/belongs-to-many-repository';
|
|
23
26
|
import { BelongsToRepository } from './relation-repository/belongs-to-repository';
|
|
@@ -45,7 +48,32 @@ export interface FilterAble {
|
|
|
45
48
|
export type TargetKey = string | number;
|
|
46
49
|
export type TK = TargetKey | TargetKey[];
|
|
47
50
|
|
|
48
|
-
|
|
51
|
+
type FieldValue = string | number | bigint | boolean | Date | Buffer | null | FieldValue[] | FilterWithOperator;
|
|
52
|
+
|
|
53
|
+
type Operators = keyof typeof operators & keyof WhereOperators;
|
|
54
|
+
|
|
55
|
+
export type FilterWithOperator = {
|
|
56
|
+
[key: string]:
|
|
57
|
+
| {
|
|
58
|
+
[K in Operators]: FieldValue;
|
|
59
|
+
}
|
|
60
|
+
| FieldValue;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export type FilterWithValue = {
|
|
64
|
+
[key: string]: FieldValue;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
type FilterAnd = {
|
|
68
|
+
$and: Filter[];
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
type FilterOr = {
|
|
72
|
+
$or: Filter[];
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export type Filter = FilterWithOperator | FilterWithValue | FilterAnd | FilterOr;
|
|
76
|
+
|
|
49
77
|
export type Appends = string[];
|
|
50
78
|
export type Except = string[];
|
|
51
79
|
export type Fields = string[];
|
|
@@ -53,12 +81,12 @@ export type Sort = string[] | string;
|
|
|
53
81
|
|
|
54
82
|
export type WhiteList = string[];
|
|
55
83
|
export type BlackList = string[];
|
|
84
|
+
|
|
56
85
|
export type AssociationKeysToBeUpdate = string[];
|
|
57
86
|
|
|
58
87
|
export type Values = any;
|
|
59
88
|
|
|
60
|
-
export interface CountOptions extends Omit<
|
|
61
|
-
fields?: Fields;
|
|
89
|
+
export interface CountOptions extends Omit<SequelizeCountOptions, 'distinct' | 'where' | 'include'>, Transactionable {
|
|
62
90
|
filter?: Filter;
|
|
63
91
|
context?: any;
|
|
64
92
|
}
|
|
@@ -67,7 +95,7 @@ export interface FilterByTk {
|
|
|
67
95
|
filterByTk?: TargetKey;
|
|
68
96
|
}
|
|
69
97
|
|
|
70
|
-
export
|
|
98
|
+
export type FindOptions = SequelizeFindOptions & CommonFindOptions & FilterByTk;
|
|
71
99
|
|
|
72
100
|
export interface CommonFindOptions extends Transactionable {
|
|
73
101
|
filter?: Filter;
|
|
@@ -78,7 +106,7 @@ export interface CommonFindOptions extends Transactionable {
|
|
|
78
106
|
context?: any;
|
|
79
107
|
}
|
|
80
108
|
|
|
81
|
-
|
|
109
|
+
export type FindOneOptions = Omit<FindOptions, 'limit'>;
|
|
82
110
|
|
|
83
111
|
export interface DestroyOptions extends SequelizeDestroyOptions {
|
|
84
112
|
filter?: Filter;
|
|
@@ -87,18 +115,7 @@ export interface DestroyOptions extends SequelizeDestroyOptions {
|
|
|
87
115
|
context?: any;
|
|
88
116
|
}
|
|
89
117
|
|
|
90
|
-
|
|
91
|
-
// 数据过滤
|
|
92
|
-
filter?: Filter;
|
|
93
|
-
// 输出结果显示哪些字段
|
|
94
|
-
fields?: Fields;
|
|
95
|
-
// 输出结果不显示哪些字段
|
|
96
|
-
except?: Except;
|
|
97
|
-
// 附加字段,用于控制关系字段的输出
|
|
98
|
-
appends?: Appends;
|
|
99
|
-
// 排序,字段前面加上 “-” 表示降序
|
|
100
|
-
sort?: Sort;
|
|
101
|
-
}
|
|
118
|
+
type FindAndCountOptions = Omit<SequelizeAndCountOptions, 'where' | 'include' | 'order'> & CommonFindOptions;
|
|
102
119
|
|
|
103
120
|
export interface CreateOptions extends SequelizeCreateOptions {
|
|
104
121
|
values?: Values | Values[];
|
|
@@ -275,7 +292,10 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|
|
275
292
|
transaction,
|
|
276
293
|
};
|
|
277
294
|
|
|
278
|
-
|
|
295
|
+
const count = await this.count(options);
|
|
296
|
+
const results = count ? await this.find(options) : [];
|
|
297
|
+
|
|
298
|
+
return [results, count];
|
|
279
299
|
}
|
|
280
300
|
|
|
281
301
|
/**
|
|
@@ -358,6 +378,7 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
|
|
358
378
|
const transaction = await this.getTransaction(options);
|
|
359
379
|
const { records } = options;
|
|
360
380
|
const instances = [];
|
|
381
|
+
|
|
361
382
|
for (const values of records) {
|
|
362
383
|
const instance = await this.create({ values, transaction });
|
|
363
384
|
instances.push(instance);
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
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
|
+
import type { CreateOptions, DestroyOptions, SaveOptions, SyncOptions, UpdateOptions } from 'sequelize/types';
|
|
5
|
+
import { Collection, CollectionOptions } from './collection';
|
|
6
|
+
|
|
7
|
+
export type CollectionNameType = string;
|
|
8
|
+
|
|
9
|
+
export type ModelSyncEventType = 'beforeSync' | 'afterSync';
|
|
10
|
+
export type ModelValidateEventType = 'beforeValidate' | 'afterValidate';
|
|
11
|
+
export type ModelCreateEventType = 'beforeCreate' | 'afterCreate';
|
|
12
|
+
export type ModelUpdateEventType = 'beforeUpdate' | 'afterUpdate';
|
|
13
|
+
export type ModelSaveEventType = 'beforeSave' | 'afterSave';
|
|
14
|
+
export type ModelDestroyEventType = 'beforeDestroy' | 'afterDestroy';
|
|
15
|
+
export type ModelCreateWithAssociationsEventType = 'afterCreateWithAssociations';
|
|
16
|
+
export type ModelUpdateWithAssociationsEventType = 'afterUpdateWithAssociations';
|
|
17
|
+
export type ModelSaveWithAssociationsEventType = 'afterSaveWithAssociations';
|
|
18
|
+
|
|
19
|
+
export type ModelValidateEventTypes = ModelValidateEventType | `${CollectionNameType}.${ModelValidateEventType}`;
|
|
20
|
+
export type ModelCreateEventTypes = ModelCreateEventType | `${CollectionNameType}.${ModelCreateEventType}`;
|
|
21
|
+
export type ModelUpdateEventTypes = ModelUpdateEventType | `${CollectionNameType}.${ModelUpdateEventType}`;
|
|
22
|
+
export type ModelSaveEventTypes = ModelSaveEventType | `${CollectionNameType}.${ModelSaveEventType}`;
|
|
23
|
+
export type ModelDestroyEventTypes = ModelDestroyEventType | `${CollectionNameType}.${ModelDestroyEventType}`;
|
|
24
|
+
export type ModelCreateWithAssociationsEventTypes = ModelCreateWithAssociationsEventType | `${CollectionNameType}.${ModelCreateWithAssociationsEventType}`;
|
|
25
|
+
export type ModelUpdateWithAssociationsEventTypes = ModelUpdateWithAssociationsEventType | `${CollectionNameType}.${ModelUpdateWithAssociationsEventType}`;
|
|
26
|
+
export type ModelSaveWithAssociationsEventTypes = ModelSaveWithAssociationsEventType | `${CollectionNameType}.${ModelSaveWithAssociationsEventType}`;
|
|
27
|
+
|
|
28
|
+
export type ModelEventTypes = ModelSyncEventType
|
|
29
|
+
| ModelValidateEventTypes
|
|
30
|
+
| ModelCreateEventTypes
|
|
31
|
+
| ModelUpdateEventTypes
|
|
32
|
+
| ModelSaveEventTypes
|
|
33
|
+
| ModelDestroyEventTypes
|
|
34
|
+
| ModelCreateWithAssociationsEventTypes
|
|
35
|
+
| ModelUpdateWithAssociationsEventTypes
|
|
36
|
+
| ModelSaveWithAssociationsEventTypes;
|
|
37
|
+
|
|
38
|
+
export type DatabaseBeforeDefineCollectionEventType = 'beforeDefineCollection';
|
|
39
|
+
export type DatabaseAfterDefineCollectionEventType = 'afterDefineCollection';
|
|
40
|
+
export type DatabaseBeforeRemoveCollectionEventType = 'beforeRemoveCollection';
|
|
41
|
+
export type DatabaseAfterRemoveCollectionEventType = 'afterRemoveCollection';
|
|
42
|
+
|
|
43
|
+
export type DatabaseEventTypes = DatabaseBeforeDefineCollectionEventType
|
|
44
|
+
| DatabaseAfterDefineCollectionEventType
|
|
45
|
+
| DatabaseBeforeRemoveCollectionEventType
|
|
46
|
+
| DatabaseAfterRemoveCollectionEventType;
|
|
47
|
+
|
|
48
|
+
export type EventType = ModelEventTypes | DatabaseEventTypes | string;
|
|
49
|
+
|
|
50
|
+
export type { HookReturn };
|
|
51
|
+
|
|
52
|
+
export type SyncListener = (options?: SyncOptions) => HookReturn;
|
|
53
|
+
export type ValidateListener = (model: Model, options?: ValidationOptions) => HookReturn;
|
|
54
|
+
export type CreateListener = (model: Model, options?: CreateOptions) => HookReturn;
|
|
55
|
+
export type UpdateListener = (model: Model, options?: UpdateOptions) => HookReturn;
|
|
56
|
+
export type SaveListener = (model: Model, options?: SaveOptions) => HookReturn;
|
|
57
|
+
export type DestroyListener = (model: Model, options?: DestroyOptions) => HookReturn;
|
|
58
|
+
export type CreateWithAssociationsListener = (model: Model, options?: CreateOptions) => HookReturn;
|
|
59
|
+
export type UpdateWithAssociationsListener = (model: Model, options?: UpdateOptions) => HookReturn;
|
|
60
|
+
export type SaveWithAssociationsListener = (model: Model, options?: SaveOptions) => HookReturn;
|
|
61
|
+
|
|
62
|
+
export type BeforeDefineCollectionListener = (options: CollectionOptions) => void;
|
|
63
|
+
export type AfterDefineCollectionListener = (collection: Collection) => void;
|
|
64
|
+
export type RemoveCollectionListener = (collection: Collection) => void;
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
HasOne,
|
|
7
7
|
Hookable,
|
|
8
8
|
ModelCtor,
|
|
9
|
-
Transactionable
|
|
9
|
+
Transactionable,
|
|
10
10
|
} from 'sequelize';
|
|
11
11
|
import { Model } from './model';
|
|
12
12
|
import { UpdateGuard } from './update-guard';
|
|
@@ -375,6 +375,7 @@ export async function updateMultipleAssociation(
|
|
|
375
375
|
const setAccessor = association.accessors.set;
|
|
376
376
|
|
|
377
377
|
const createAccessor = association.accessors.create;
|
|
378
|
+
|
|
378
379
|
if (isUndefinedOrNull(value)) {
|
|
379
380
|
await model[setAccessor](null, { transaction, context });
|
|
380
381
|
model.setDataValue(key, null);
|