@nocobase/database 0.8.0-alpha.9 → 0.8.1-alpha.4
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
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import InheritanceMap from '../../inherited-map';
|
|
2
|
+
|
|
3
|
+
describe('InheritedMap', () => {
|
|
4
|
+
it('should setInherits', () => {
|
|
5
|
+
const map = new InheritanceMap();
|
|
6
|
+
map.setInheritance('b', 'a');
|
|
7
|
+
|
|
8
|
+
const nodeA = map.getNode('a');
|
|
9
|
+
const nodeB = map.getNode('b');
|
|
10
|
+
|
|
11
|
+
expect(nodeA.children.has(nodeB)).toBe(true);
|
|
12
|
+
expect(nodeB.parents.has(nodeA)).toBe(true);
|
|
13
|
+
|
|
14
|
+
expect(map.isParentNode('a')).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should get deep children', () => {
|
|
18
|
+
const map = new InheritanceMap();
|
|
19
|
+
map.setInheritance('b', 'a');
|
|
20
|
+
map.setInheritance('c', 'b');
|
|
21
|
+
map.setInheritance('c1', 'b');
|
|
22
|
+
map.setInheritance('d', 'c');
|
|
23
|
+
|
|
24
|
+
const children = map.getChildren('a');
|
|
25
|
+
expect(children.size).toBe(4);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -12,6 +12,7 @@ describe('belongs to many with target key', function () {
|
|
|
12
12
|
beforeEach(async () => {
|
|
13
13
|
db = mockDatabase();
|
|
14
14
|
|
|
15
|
+
await db.clean({ drop: true });
|
|
15
16
|
Post = db.collection({
|
|
16
17
|
name: 'posts',
|
|
17
18
|
filterTargetKey: 'title',
|
|
@@ -121,6 +122,7 @@ describe('belongs to many', () => {
|
|
|
121
122
|
|
|
122
123
|
beforeEach(async () => {
|
|
123
124
|
db = mockDatabase();
|
|
125
|
+
await db.clean({ drop: true });
|
|
124
126
|
PostTag = db.collection({
|
|
125
127
|
name: 'posts_tags',
|
|
126
128
|
fields: [{ type: 'string', name: 'tagged_at' }],
|
|
@@ -249,7 +249,7 @@ describe('has many repository', () => {
|
|
|
249
249
|
expect(p1.title).toEqual('u1t1');
|
|
250
250
|
});
|
|
251
251
|
|
|
252
|
-
test('find', async () => {
|
|
252
|
+
test('find with has many', async () => {
|
|
253
253
|
const u1 = await User.repository.create({ values: { name: 'u1' } });
|
|
254
254
|
|
|
255
255
|
const t1 = await Tag.repository.create({ values: { name: 't1' } });
|
|
@@ -13,6 +13,7 @@ describe('destroy with targetKey', function () {
|
|
|
13
13
|
|
|
14
14
|
beforeEach(async () => {
|
|
15
15
|
db = mockDatabase();
|
|
16
|
+
|
|
16
17
|
User = db.collection({
|
|
17
18
|
name: 'users',
|
|
18
19
|
autoGenId: false,
|
|
@@ -76,7 +77,7 @@ describe('destroy with targetKey', function () {
|
|
|
76
77
|
});
|
|
77
78
|
|
|
78
79
|
describe('destroy', () => {
|
|
79
|
-
let db;
|
|
80
|
+
let db: Database;
|
|
80
81
|
let User: Collection;
|
|
81
82
|
let Post: Collection;
|
|
82
83
|
|
|
@@ -86,6 +87,10 @@ describe('destroy', () => {
|
|
|
86
87
|
|
|
87
88
|
beforeEach(async () => {
|
|
88
89
|
db = mockDatabase();
|
|
90
|
+
await db.clean({
|
|
91
|
+
drop: true,
|
|
92
|
+
});
|
|
93
|
+
|
|
89
94
|
User = db.collection({
|
|
90
95
|
name: 'users',
|
|
91
96
|
fields: [
|
|
@@ -105,6 +110,122 @@ describe('destroy', () => {
|
|
|
105
110
|
await db.sync();
|
|
106
111
|
});
|
|
107
112
|
|
|
113
|
+
test('destroy records from no pk tables that filterTargetKey configured', async () => {
|
|
114
|
+
const Test = db.collection({
|
|
115
|
+
name: 'test',
|
|
116
|
+
timestamps: false,
|
|
117
|
+
autoGenId: false,
|
|
118
|
+
filterTargetKey: 'test',
|
|
119
|
+
fields: [
|
|
120
|
+
{
|
|
121
|
+
type: 'string',
|
|
122
|
+
name: 'test',
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
await db.sync();
|
|
128
|
+
|
|
129
|
+
const t1 = await Test.repository.create({
|
|
130
|
+
values: {
|
|
131
|
+
test: 't1',
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
await Test.repository.create({
|
|
136
|
+
values: {
|
|
137
|
+
test: 't2',
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
await Test.repository.destroy({
|
|
142
|
+
filterByTk: 't2',
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
expect(await Test.repository.count()).toEqual(1);
|
|
146
|
+
expect((await Test.repository.findOne()).get('test')).toEqual('t1');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test('destroy records from tables without primary keys', async () => {
|
|
150
|
+
const Test = db.collection({
|
|
151
|
+
name: 'test',
|
|
152
|
+
timestamps: false,
|
|
153
|
+
autoGenId: false,
|
|
154
|
+
fields: [
|
|
155
|
+
{
|
|
156
|
+
type: 'string',
|
|
157
|
+
name: 'test',
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
await db.sync();
|
|
163
|
+
|
|
164
|
+
const t1 = await Test.repository.create({
|
|
165
|
+
values: {
|
|
166
|
+
test: 't1',
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
await Test.repository.create({
|
|
171
|
+
values: {
|
|
172
|
+
test: 't2',
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const destroy = async () => {
|
|
177
|
+
await Test.repository.destroy({
|
|
178
|
+
filterByTk: 111,
|
|
179
|
+
});
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
await expect(destroy()).rejects.toThrowError('filterByTk is not supported for collection that has no primary key');
|
|
183
|
+
|
|
184
|
+
await Test.repository.destroy({
|
|
185
|
+
filter: {
|
|
186
|
+
test: 't2',
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
expect(await Test.repository.count()).toEqual(1);
|
|
191
|
+
expect((await Test.repository.findOne()).get('test')).toEqual('t1');
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test('destroy record has no primary key', async () => {
|
|
195
|
+
Post.addField('tags', {
|
|
196
|
+
type: 'belongsToMany',
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const tags = db.collection({
|
|
200
|
+
name: 'tags',
|
|
201
|
+
fields: [
|
|
202
|
+
{ type: 'belongsToMany', name: 'posts' },
|
|
203
|
+
{ type: 'string', name: 'name' },
|
|
204
|
+
],
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
await db.sync();
|
|
208
|
+
|
|
209
|
+
const post = await Post.repository.create({
|
|
210
|
+
values: {
|
|
211
|
+
title: 'p1',
|
|
212
|
+
tags: [{ name: 't1' }],
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
const throughCollection = db.getCollection(tags.getField('posts').options.through);
|
|
217
|
+
|
|
218
|
+
expect(await throughCollection.repository.count()).toEqual(1);
|
|
219
|
+
|
|
220
|
+
await throughCollection.repository.destroy({
|
|
221
|
+
filter: {
|
|
222
|
+
postId: post.get('id'),
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
expect(await throughCollection.repository.count()).toEqual(0);
|
|
227
|
+
});
|
|
228
|
+
|
|
108
229
|
test('destroy with filter and filterByPk', async () => {
|
|
109
230
|
const p1 = await Post.repository.create({
|
|
110
231
|
values: {
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Database, mockDatabase } from '@nocobase/database';
|
|
2
|
+
|
|
3
|
+
describe('update many', () => {
|
|
4
|
+
let db: Database;
|
|
5
|
+
afterEach(async () => {
|
|
6
|
+
await db.close();
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
db = mockDatabase();
|
|
11
|
+
db.collection({
|
|
12
|
+
name: 't1',
|
|
13
|
+
fields: [{ type: 'string', name: 'title' }],
|
|
14
|
+
});
|
|
15
|
+
await db.sync();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should update values', async () => {
|
|
19
|
+
const repository = db.getRepository('t1');
|
|
20
|
+
await repository.create({
|
|
21
|
+
values: [
|
|
22
|
+
{ id: 1, title: 't1' },
|
|
23
|
+
{ id: 2, title: 't2' },
|
|
24
|
+
],
|
|
25
|
+
});
|
|
26
|
+
await repository.update({
|
|
27
|
+
values: [
|
|
28
|
+
{ id: 1, title: 't11' },
|
|
29
|
+
{ id: 2, title: 't22' },
|
|
30
|
+
],
|
|
31
|
+
});
|
|
32
|
+
const items = await repository.find({
|
|
33
|
+
fields: ['title'],
|
|
34
|
+
sort: 'id',
|
|
35
|
+
});
|
|
36
|
+
expect(items.map((i) => i.title)).toEqual(['t11', 't22']);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should filterByTk invalid', async () => {
|
|
40
|
+
const repository = db.getRepository('t1');
|
|
41
|
+
await repository.create({
|
|
42
|
+
values: [
|
|
43
|
+
{ id: 1, title: 't1' },
|
|
44
|
+
{ id: 2, title: 't2' },
|
|
45
|
+
],
|
|
46
|
+
});
|
|
47
|
+
let err;
|
|
48
|
+
try {
|
|
49
|
+
await repository.update({
|
|
50
|
+
values: [{ id: 1, title: 't11' }, { title: 't22' }],
|
|
51
|
+
});
|
|
52
|
+
} catch (error) {
|
|
53
|
+
err = error;
|
|
54
|
+
}
|
|
55
|
+
expect(err?.message).toBe('filterByTk invalid');
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { Database } from '../database';
|
|
2
|
+
import { mockDatabase } from './';
|
|
3
|
+
|
|
4
|
+
describe('update associations', () => {
|
|
5
|
+
let db: Database;
|
|
6
|
+
beforeEach(async () => {
|
|
7
|
+
db = mockDatabase();
|
|
8
|
+
await db.clean({ drop: true });
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
afterEach(async () => {
|
|
12
|
+
await db.close();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('hasOne', async () => {
|
|
16
|
+
db.collection({
|
|
17
|
+
name: 'a',
|
|
18
|
+
fields: [
|
|
19
|
+
{
|
|
20
|
+
type: 'string',
|
|
21
|
+
name: 'name',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
type: 'hasOne',
|
|
25
|
+
name: 'b',
|
|
26
|
+
target: 'b',
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
});
|
|
30
|
+
db.collection({
|
|
31
|
+
name: 'b',
|
|
32
|
+
fields: [
|
|
33
|
+
{
|
|
34
|
+
type: 'string',
|
|
35
|
+
name: 'name',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
type: 'hasOne',
|
|
39
|
+
name: 'c',
|
|
40
|
+
target: 'c',
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
});
|
|
44
|
+
db.collection({
|
|
45
|
+
name: 'c',
|
|
46
|
+
fields: [
|
|
47
|
+
{
|
|
48
|
+
type: 'string',
|
|
49
|
+
name: 'name',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
type: 'hasOne',
|
|
53
|
+
name: 'd',
|
|
54
|
+
target: 'd',
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
});
|
|
58
|
+
db.collection({
|
|
59
|
+
name: 'd',
|
|
60
|
+
fields: [
|
|
61
|
+
{
|
|
62
|
+
type: 'string',
|
|
63
|
+
name: 'name',
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
});
|
|
67
|
+
await db.sync();
|
|
68
|
+
const b = await db.getRepository('b').create({
|
|
69
|
+
values: {},
|
|
70
|
+
});
|
|
71
|
+
const c = await db.getRepository('c').create({
|
|
72
|
+
values: {},
|
|
73
|
+
});
|
|
74
|
+
const d = await db.getRepository('d').create({
|
|
75
|
+
values: {},
|
|
76
|
+
});
|
|
77
|
+
await db.getRepository('a').create({
|
|
78
|
+
updateAssociationValues: ['b'],
|
|
79
|
+
values: {
|
|
80
|
+
name: 'a1',
|
|
81
|
+
b: {
|
|
82
|
+
id: b.id,
|
|
83
|
+
c: {
|
|
84
|
+
id: c.id,
|
|
85
|
+
d: {
|
|
86
|
+
id: d.id,
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
const d1 = await d.reload();
|
|
93
|
+
expect(d1.cId).toBe(c.id);
|
|
94
|
+
|
|
95
|
+
const b2 = await db.getRepository('b').create({
|
|
96
|
+
values: {},
|
|
97
|
+
});
|
|
98
|
+
const c2 = await db.getRepository('c').create({
|
|
99
|
+
values: {},
|
|
100
|
+
});
|
|
101
|
+
const d2 = await db.getRepository('d').create({
|
|
102
|
+
values: {},
|
|
103
|
+
});
|
|
104
|
+
await db.getRepository('a').create({
|
|
105
|
+
values: {
|
|
106
|
+
name: 'a1',
|
|
107
|
+
b: {
|
|
108
|
+
id: b2.id,
|
|
109
|
+
c: {
|
|
110
|
+
id: c2.id,
|
|
111
|
+
d: {
|
|
112
|
+
id: d2.id,
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
const c22 = await c2.reload();
|
|
119
|
+
expect(c22.bId).toBeNull();
|
|
120
|
+
const d22 = await d2.reload();
|
|
121
|
+
expect(d22.cId).toBeNull();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('hasMany', async () => {
|
|
125
|
+
db.collection({
|
|
126
|
+
name: 'a',
|
|
127
|
+
fields: [
|
|
128
|
+
{
|
|
129
|
+
type: 'string',
|
|
130
|
+
name: 'name',
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
type: 'hasMany',
|
|
134
|
+
name: 'b',
|
|
135
|
+
target: 'b',
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
});
|
|
139
|
+
db.collection({
|
|
140
|
+
name: 'b',
|
|
141
|
+
fields: [
|
|
142
|
+
{
|
|
143
|
+
type: 'string',
|
|
144
|
+
name: 'name',
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
type: 'hasMany',
|
|
148
|
+
name: 'c',
|
|
149
|
+
target: 'c',
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
});
|
|
153
|
+
db.collection({
|
|
154
|
+
name: 'c',
|
|
155
|
+
fields: [
|
|
156
|
+
{
|
|
157
|
+
type: 'string',
|
|
158
|
+
name: 'name',
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
type: 'hasMany',
|
|
162
|
+
name: 'd',
|
|
163
|
+
target: 'd',
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
});
|
|
167
|
+
db.collection({
|
|
168
|
+
name: 'd',
|
|
169
|
+
fields: [
|
|
170
|
+
{
|
|
171
|
+
type: 'string',
|
|
172
|
+
name: 'name',
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
await db.sync();
|
|
178
|
+
const b = await db.getRepository('b').create({
|
|
179
|
+
values: {},
|
|
180
|
+
});
|
|
181
|
+
const c = await db.getRepository('c').create({
|
|
182
|
+
values: {},
|
|
183
|
+
});
|
|
184
|
+
const d = await db.getRepository('d').create({
|
|
185
|
+
values: {},
|
|
186
|
+
});
|
|
187
|
+
await db.getRepository('a').create({
|
|
188
|
+
updateAssociationValues: ['b'],
|
|
189
|
+
values: {
|
|
190
|
+
name: 'a1',
|
|
191
|
+
b: {
|
|
192
|
+
id: b.id,
|
|
193
|
+
c: {
|
|
194
|
+
id: c.id,
|
|
195
|
+
d: {
|
|
196
|
+
id: d.id,
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
const d1 = await d.reload();
|
|
203
|
+
expect(d1.cId).toBe(c.id);
|
|
204
|
+
const b2 = await db.getRepository('b').create({
|
|
205
|
+
values: {},
|
|
206
|
+
});
|
|
207
|
+
const c2 = await db.getRepository('c').create({
|
|
208
|
+
values: {},
|
|
209
|
+
});
|
|
210
|
+
const d2 = await db.getRepository('d').create({
|
|
211
|
+
values: {},
|
|
212
|
+
});
|
|
213
|
+
await db.getRepository('a').create({
|
|
214
|
+
values: {
|
|
215
|
+
name: 'a1',
|
|
216
|
+
b: {
|
|
217
|
+
id: b2.id,
|
|
218
|
+
c: {
|
|
219
|
+
id: c2.id,
|
|
220
|
+
d: {
|
|
221
|
+
id: d2.id,
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
const c22 = await c2.reload();
|
|
228
|
+
expect(c22.bId).toBeNull();
|
|
229
|
+
const d22 = await d2.reload();
|
|
230
|
+
expect(d22.cId).toBeNull();
|
|
231
|
+
});
|
|
232
|
+
});
|
|
@@ -83,7 +83,12 @@ describe('update associations', () => {
|
|
|
83
83
|
},
|
|
84
84
|
});
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
const p4 = await db.getRepository('posts').findOne({
|
|
87
|
+
filterByTk: post4.id,
|
|
88
|
+
appends: ['user'],
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
expect(p4?.toJSON()).toMatchObject({
|
|
87
92
|
id: 4,
|
|
88
93
|
name: 'post4',
|
|
89
94
|
userId: 1,
|
package/src/collection.ts
CHANGED
|
@@ -2,11 +2,11 @@ import merge from 'deepmerge';
|
|
|
2
2
|
import { EventEmitter } from 'events';
|
|
3
3
|
import { default as lodash, default as _ } from 'lodash';
|
|
4
4
|
import {
|
|
5
|
-
ModelCtor,
|
|
6
5
|
ModelOptions,
|
|
7
6
|
QueryInterfaceDropTableOptions,
|
|
8
7
|
SyncOptions,
|
|
9
8
|
Transactionable,
|
|
9
|
+
ModelStatic,
|
|
10
10
|
Utils,
|
|
11
11
|
} from 'sequelize';
|
|
12
12
|
import { Database } from './database';
|
|
@@ -22,9 +22,10 @@ export type CollectionSortable = string | boolean | { name?: string; scopeKey?:
|
|
|
22
22
|
export interface CollectionOptions extends Omit<ModelOptions, 'name' | 'hooks'> {
|
|
23
23
|
name: string;
|
|
24
24
|
tableName?: string;
|
|
25
|
+
inherits?: string[] | string;
|
|
25
26
|
filterTargetKey?: string;
|
|
26
27
|
fields?: FieldOptions[];
|
|
27
|
-
model?: string |
|
|
28
|
+
model?: string | ModelStatic<Model>;
|
|
28
29
|
repository?: string | RepositoryType;
|
|
29
30
|
sortable?: CollectionSortable;
|
|
30
31
|
/**
|
|
@@ -50,7 +51,7 @@ export class Collection<
|
|
|
50
51
|
context: CollectionContext;
|
|
51
52
|
isThrough?: boolean;
|
|
52
53
|
fields: Map<string, any> = new Map<string, any>();
|
|
53
|
-
model:
|
|
54
|
+
model: ModelStatic<Model>;
|
|
54
55
|
repository: Repository<TModelAttributes, TCreationAttributes>;
|
|
55
56
|
|
|
56
57
|
get filterTargetKey() {
|
|
@@ -74,9 +75,14 @@ export class Collection<
|
|
|
74
75
|
|
|
75
76
|
this.bindFieldEventListener();
|
|
76
77
|
this.modelInit();
|
|
78
|
+
|
|
77
79
|
this.db.modelCollection.set(this.model, this);
|
|
80
|
+
this.db.tableNameCollectionMap.set(this.model.tableName, this);
|
|
81
|
+
|
|
82
|
+
if (!options.inherits) {
|
|
83
|
+
this.setFields(options.fields);
|
|
84
|
+
}
|
|
78
85
|
|
|
79
|
-
this.setFields(options.fields);
|
|
80
86
|
this.setRepository(options.repository);
|
|
81
87
|
this.setSortable(options.sortable);
|
|
82
88
|
}
|
|
@@ -103,7 +109,7 @@ export class Collection<
|
|
|
103
109
|
return;
|
|
104
110
|
}
|
|
105
111
|
const { name, model, autoGenId = true } = this.options;
|
|
106
|
-
let M:
|
|
112
|
+
let M: ModelStatic<Model> = Model;
|
|
107
113
|
if (this.context.database.sequelize.isDefined(name)) {
|
|
108
114
|
const m = this.context.database.sequelize.model(name);
|
|
109
115
|
if ((m as any).isThrough) {
|
|
@@ -144,8 +150,13 @@ export class Collection<
|
|
|
144
150
|
}
|
|
145
151
|
|
|
146
152
|
private bindFieldEventListener() {
|
|
147
|
-
this.on('field.afterAdd', (field: Field) =>
|
|
148
|
-
|
|
153
|
+
this.on('field.afterAdd', (field: Field) => {
|
|
154
|
+
field.bind();
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
this.on('field.afterRemove', (field: Field) => {
|
|
158
|
+
field.unbind();
|
|
159
|
+
});
|
|
149
160
|
}
|
|
150
161
|
|
|
151
162
|
forEachField(callback: (field: Field) => void) {
|
|
@@ -181,9 +192,39 @@ export class Collection<
|
|
|
181
192
|
},
|
|
182
193
|
);
|
|
183
194
|
|
|
195
|
+
const oldField = this.fields.get(name);
|
|
196
|
+
|
|
197
|
+
if (oldField && oldField.options.inherit && field.typeToString() != oldField.typeToString()) {
|
|
198
|
+
throw new Error(
|
|
199
|
+
`Field type conflict: cannot set "${name}" on "${this.name}" to ${options.type}, parent "${name}" type is ${oldField.options.type}`,
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (this.options.autoGenId !== false && options.primaryKey) {
|
|
204
|
+
this.model.removeAttribute('id');
|
|
205
|
+
}
|
|
206
|
+
|
|
184
207
|
this.removeField(name);
|
|
185
208
|
this.fields.set(name, field);
|
|
186
209
|
this.emit('field.afterAdd', field);
|
|
210
|
+
|
|
211
|
+
// refresh children models
|
|
212
|
+
if (this.isParent()) {
|
|
213
|
+
for (const child of this.context.database.inheritanceMap.getChildren(this.name, {
|
|
214
|
+
deep: false,
|
|
215
|
+
})) {
|
|
216
|
+
const childCollection = this.db.getCollection(child);
|
|
217
|
+
const existField = childCollection.getField(name);
|
|
218
|
+
|
|
219
|
+
if (!existField || existField.options.inherit) {
|
|
220
|
+
childCollection.setField(name, {
|
|
221
|
+
...options,
|
|
222
|
+
inherit: true,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
187
228
|
return field;
|
|
188
229
|
}
|
|
189
230
|
|
|
@@ -232,11 +273,27 @@ export class Collection<
|
|
|
232
273
|
if (!this.fields.has(name)) {
|
|
233
274
|
return;
|
|
234
275
|
}
|
|
276
|
+
|
|
235
277
|
const field = this.fields.get(name);
|
|
278
|
+
|
|
236
279
|
const bool = this.fields.delete(name);
|
|
280
|
+
|
|
237
281
|
if (bool) {
|
|
282
|
+
if (this.isParent()) {
|
|
283
|
+
for (const child of this.db.inheritanceMap.getChildren(this.name, {
|
|
284
|
+
deep: false,
|
|
285
|
+
})) {
|
|
286
|
+
const childCollection = this.db.getCollection(child);
|
|
287
|
+
const existField = childCollection.getField(name);
|
|
288
|
+
if (existField && existField.options.inherit) {
|
|
289
|
+
childCollection.removeField(name);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
238
294
|
this.emit('field.afterRemove', field);
|
|
239
295
|
}
|
|
296
|
+
|
|
240
297
|
return field as Field;
|
|
241
298
|
}
|
|
242
299
|
|
|
@@ -349,6 +406,7 @@ export class Collection<
|
|
|
349
406
|
}
|
|
350
407
|
return item;
|
|
351
408
|
});
|
|
409
|
+
this.refreshIndexes();
|
|
352
410
|
}
|
|
353
411
|
|
|
354
412
|
removeIndex(fields: any) {
|
|
@@ -361,6 +419,21 @@ export class Collection<
|
|
|
361
419
|
this.model._indexes = indexes.filter((item) => {
|
|
362
420
|
return !lodash.isEqual(item.fields, fields);
|
|
363
421
|
});
|
|
422
|
+
this.refreshIndexes();
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
refreshIndexes() {
|
|
426
|
+
// @ts-ignore
|
|
427
|
+
const indexes: any[] = this.model._indexes;
|
|
428
|
+
// @ts-ignore
|
|
429
|
+
this.model._indexes = indexes.filter((item) => {
|
|
430
|
+
for (const field of item.fields) {
|
|
431
|
+
if (!this.model.rawAttributes[field]) {
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return true;
|
|
436
|
+
});
|
|
364
437
|
}
|
|
365
438
|
|
|
366
439
|
async sync(syncOptions?: SyncOptions) {
|
|
@@ -371,12 +444,13 @@ export class Collection<
|
|
|
371
444
|
for (const associationKey in associations) {
|
|
372
445
|
const association = associations[associationKey];
|
|
373
446
|
modelNames.add(association.target.name);
|
|
447
|
+
|
|
374
448
|
if ((<any>association).through) {
|
|
375
449
|
modelNames.add((<any>association).through.model.name);
|
|
376
450
|
}
|
|
377
451
|
}
|
|
378
452
|
|
|
379
|
-
const models:
|
|
453
|
+
const models: ModelStatic<Model>[] = [];
|
|
380
454
|
// @ts-ignore
|
|
381
455
|
this.context.database.sequelize.modelManager.forEachModel((model) => {
|
|
382
456
|
if (modelNames.has(model.name)) {
|
|
@@ -388,4 +462,12 @@ export class Collection<
|
|
|
388
462
|
await model.sync(syncOptions);
|
|
389
463
|
}
|
|
390
464
|
}
|
|
465
|
+
|
|
466
|
+
public isInherited() {
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
public isParent() {
|
|
471
|
+
return this.context.database.inheritanceMap.isParentNode(this.name);
|
|
472
|
+
}
|
|
391
473
|
}
|