@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.
Files changed (122) hide show
  1. package/lib/collection.d.ts +7 -3
  2. package/lib/collection.js +126 -22
  3. package/lib/database.d.ts +13 -7
  4. package/lib/database.js +39 -8
  5. package/lib/decorators/must-have-filter-decorator.js +4 -0
  6. package/lib/decorators/transaction-decorator.js +1 -0
  7. package/lib/features/ReferencesMap.js +14 -9
  8. package/lib/field-repository/array-field-repository.d.ts +28 -0
  9. package/lib/field-repository/array-field-repository.js +208 -0
  10. package/lib/fields/array-field.d.ts +1 -1
  11. package/lib/fields/array-field.js +15 -11
  12. package/lib/fields/belongs-to-field.d.ts +9 -1
  13. package/lib/fields/belongs-to-field.js +21 -7
  14. package/lib/fields/belongs-to-many-field.d.ts +4 -0
  15. package/lib/fields/belongs-to-many-field.js +24 -0
  16. package/lib/fields/field.d.ts +3 -2
  17. package/lib/fields/field.js +19 -13
  18. package/lib/fields/has-many-field.d.ts +2 -1
  19. package/lib/fields/has-many-field.js +18 -10
  20. package/lib/fields/has-one-field.d.ts +1 -0
  21. package/lib/fields/has-one-field.js +22 -10
  22. package/lib/fields/index.d.ts +3 -3
  23. package/lib/fields/index.js +14 -34
  24. package/lib/fields/relation-field.d.ts +2 -1
  25. package/lib/fields/relation-field.js +16 -0
  26. package/lib/fields/set-field.d.ts +10 -0
  27. package/lib/fields/set-field.js +35 -0
  28. package/lib/fields/sort-field.d.ts +1 -1
  29. package/lib/fields/sort-field.js +1 -1
  30. package/lib/filter-match.d.ts +1 -0
  31. package/lib/filter-match.js +84 -0
  32. package/lib/filter-parser.d.ts +2 -2
  33. package/lib/index.d.ts +5 -1
  34. package/lib/index.js +56 -0
  35. package/lib/inherited-collection.d.ts +13 -0
  36. package/lib/inherited-collection.js +210 -0
  37. package/lib/inherited-map.d.ts +21 -0
  38. package/lib/inherited-map.js +203 -0
  39. package/lib/model-hook.d.ts +1 -1
  40. package/lib/model.d.ts +1 -0
  41. package/lib/model.js +47 -0
  42. package/lib/operators/array.d.ts +1 -25
  43. package/lib/operators/association.d.ts +1 -9
  44. package/lib/operators/boolean.d.ts +1 -12
  45. package/lib/operators/date.d.ts +1 -33
  46. package/lib/operators/empty.d.ts +2 -25
  47. package/lib/operators/ne.d.ts +1 -13
  48. package/lib/operators/notIn.d.ts +1 -9
  49. package/lib/options-parser.d.ts +3 -2
  50. package/lib/options-parser.js +16 -1
  51. package/lib/relation-repository/relation-repository.d.ts +2 -2
  52. package/lib/relation-repository/single-relation-repository.js +2 -2
  53. package/lib/repository.d.ts +18 -10
  54. package/lib/repository.js +172 -38
  55. package/lib/sync-runner.d.ts +4 -0
  56. package/lib/sync-runner.js +181 -0
  57. package/lib/types.d.ts +2 -2
  58. package/lib/update-associations.d.ts +1 -0
  59. package/lib/update-associations.js +21 -2
  60. package/lib/update-guard.d.ts +3 -3
  61. package/lib/utils.js +5 -0
  62. package/package.json +4 -4
  63. package/src/__tests__/bigint.test.ts +48 -0
  64. package/src/__tests__/collection.test.ts +48 -13
  65. package/src/__tests__/database.test.ts +10 -0
  66. package/src/__tests__/field-repository/array-field-repository.test.ts +94 -0
  67. package/src/__tests__/fields/set.test.ts +37 -0
  68. package/src/__tests__/filter-match.test.ts +52 -0
  69. package/src/__tests__/inhertits/collection-inherits-sync.test.ts +38 -0
  70. package/src/__tests__/inhertits/collection-inherits.test.ts +994 -0
  71. package/src/__tests__/inhertits/helper.ts +3 -0
  72. package/src/__tests__/inhertits/inherited-map.test.ts +27 -0
  73. package/src/__tests__/relation-repository/belongs-to-many-repository.test.ts +2 -0
  74. package/src/__tests__/relation-repository/has-many-repository.test.ts +1 -1
  75. package/src/__tests__/repository/destroy.test.ts +122 -1
  76. package/src/__tests__/repository/update-many.test.ts +57 -0
  77. package/src/__tests__/update-association-values.test.ts +232 -0
  78. package/src/__tests__/update-associations.test.ts +6 -1
  79. package/src/collection.ts +90 -8
  80. package/src/database.ts +53 -14
  81. package/src/decorators/must-have-filter-decorator.ts +4 -0
  82. package/src/decorators/transaction-decorator.ts +1 -0
  83. package/src/features/ReferencesMap.ts +20 -9
  84. package/src/features/referential-integrity-check.ts +1 -0
  85. package/src/field-repository/array-field-repository.ts +155 -0
  86. package/src/fields/array-field.ts +4 -4
  87. package/src/fields/belongs-to-field.ts +26 -10
  88. package/src/fields/belongs-to-many-field.ts +34 -0
  89. package/src/fields/field.ts +26 -13
  90. package/src/fields/has-many-field.ts +17 -9
  91. package/src/fields/has-one-field.ts +23 -9
  92. package/src/fields/index.ts +5 -5
  93. package/src/fields/relation-field.ts +16 -0
  94. package/src/fields/set-field.ts +25 -0
  95. package/src/fields/sort-field.ts +5 -4
  96. package/src/filter-match.ts +49 -0
  97. package/src/filter-parser.ts +2 -2
  98. package/src/index.ts +5 -2
  99. package/src/inherited-collection.ts +112 -0
  100. package/src/inherited-map.ts +97 -0
  101. package/src/model-hook.ts +2 -3
  102. package/src/model.ts +43 -3
  103. package/src/operators/array.ts +1 -1
  104. package/src/operators/association.ts +1 -1
  105. package/src/operators/boolean.ts +1 -1
  106. package/src/operators/date.ts +1 -1
  107. package/src/operators/empty.ts +1 -1
  108. package/src/operators/ne.ts +1 -1
  109. package/src/operators/notIn.ts +2 -1
  110. package/src/options-parser.ts +20 -4
  111. package/src/relation-repository/relation-repository.ts +2 -2
  112. package/src/relation-repository/single-relation-repository.ts +2 -2
  113. package/src/repository.ts +144 -30
  114. package/src/sync-runner.ts +162 -0
  115. package/src/types.ts +2 -2
  116. package/src/update-associations.ts +23 -7
  117. package/src/update-guard.ts +3 -3
  118. package/src/utils.ts +5 -1
  119. package/lib/fields/sequence-field.d.ts +0 -32
  120. package/lib/fields/sequence-field.js +0 -300
  121. package/src/__tests__/fields/sequence-field.test.ts +0 -480
  122. package/src/fields/sequence-field.ts +0 -202
@@ -0,0 +1,3 @@
1
+ const pgOnly = () => (process.env.DB_DIALECT == 'postgres' ? describe : describe.skip);
2
+
3
+ export default pgOnly;
@@ -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
- expect(post4.toJSON()).toMatchObject({
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 | ModelCtor<Model>;
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: ModelCtor<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: ModelCtor<Model> = Model;
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) => field.bind());
148
- this.on('field.afterRemove', (field: Field) => field.unbind());
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: ModelCtor<Model>[] = [];
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
  }