@nocobase/database 0.9.1-alpha.2 → 0.9.2-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.
Files changed (168) hide show
  1. package/lib/collection-group-manager.d.ts +13 -0
  2. package/lib/collection-group-manager.js +91 -0
  3. package/lib/collection-importer.js +0 -24
  4. package/lib/collection.d.ts +24 -3
  5. package/lib/collection.js +176 -236
  6. package/lib/database-utils/index.js +3 -15
  7. package/lib/database.d.ts +3 -0
  8. package/lib/database.js +160 -298
  9. package/lib/decorators/must-have-filter-decorator.js +0 -7
  10. package/lib/decorators/transaction-decorator.js +5 -18
  11. package/lib/errors/identifier-error.js +0 -3
  12. package/lib/features/ReferencesMap.js +1 -14
  13. package/lib/features/referential-integrity-check.js +7 -21
  14. package/lib/field-repository/array-field-repository.js +5 -45
  15. package/lib/fields/array-field.js +0 -13
  16. package/lib/fields/belongs-to-field.js +24 -50
  17. package/lib/fields/belongs-to-many-field.js +23 -47
  18. package/lib/fields/boolean-field.js +0 -7
  19. package/lib/fields/context-field.js +2 -23
  20. package/lib/fields/date-field.d.ts +4 -0
  21. package/lib/fields/date-field.js +15 -7
  22. package/lib/fields/field.js +32 -85
  23. package/lib/fields/has-many-field.js +16 -49
  24. package/lib/fields/has-one-field.js +18 -52
  25. package/lib/fields/index.js +0 -44
  26. package/lib/fields/json-field.js +0 -12
  27. package/lib/fields/number-field.js +0 -23
  28. package/lib/fields/password-field.js +8 -35
  29. package/lib/fields/radio-field.js +0 -18
  30. package/lib/fields/relation-field.js +4 -16
  31. package/lib/fields/set-field.js +0 -8
  32. package/lib/fields/sort-field.js +84 -73
  33. package/lib/fields/string-field.js +0 -7
  34. package/lib/fields/text-field.js +0 -7
  35. package/lib/fields/time-field.js +0 -7
  36. package/lib/fields/uid-field.js +4 -22
  37. package/lib/fields/uuid-field.js +3 -12
  38. package/lib/fields/virtual-field.js +0 -7
  39. package/lib/filter-match.js +7 -22
  40. package/lib/filter-parser.js +37 -101
  41. package/lib/index.d.ts +3 -0
  42. package/lib/index.js +36 -42
  43. package/lib/inherited-collection.js +15 -62
  44. package/lib/inherited-map.js +7 -48
  45. package/lib/listeners/adjacency-list.d.ts +3 -0
  46. package/lib/listeners/adjacency-list.js +91 -0
  47. package/lib/listeners/index.d.ts +2 -0
  48. package/lib/listeners/index.js +12 -0
  49. package/lib/magic-attribute-model.js +58 -114
  50. package/lib/migration.js +7 -28
  51. package/lib/mock-database.d.ts +4 -4
  52. package/lib/mock-database.js +15 -18
  53. package/lib/model-hook.js +4 -35
  54. package/lib/model.js +12 -54
  55. package/lib/operators/array.js +2 -32
  56. package/lib/operators/association.js +0 -6
  57. package/lib/operators/boolean.js +0 -6
  58. package/lib/operators/child-collection.d.ts +2 -0
  59. package/lib/operators/child-collection.js +32 -0
  60. package/lib/operators/date.js +123 -60
  61. package/lib/operators/empty.js +3 -32
  62. package/lib/operators/eq.d.ts +2 -0
  63. package/lib/operators/eq.js +26 -0
  64. package/lib/operators/index.js +4 -7
  65. package/lib/operators/ne.js +5 -5
  66. package/lib/operators/notIn.js +0 -5
  67. package/lib/operators/string.js +0 -11
  68. package/lib/operators/utils.js +0 -6
  69. package/lib/options-parser.d.ts +1 -1
  70. package/lib/options-parser.js +47 -107
  71. package/lib/playground.js +0 -4
  72. package/lib/query-interface/mysql-query-interface.d.ts +11 -0
  73. package/lib/query-interface/mysql-query-interface.js +59 -10
  74. package/lib/query-interface/postgres-query-interface.d.ts +8 -0
  75. package/lib/query-interface/postgres-query-interface.js +70 -12
  76. package/lib/query-interface/query-interface-builder.js +0 -5
  77. package/lib/query-interface/query-interface.d.ts +12 -0
  78. package/lib/query-interface/query-interface.js +33 -3
  79. package/lib/query-interface/sqlite-query-interface.d.ts +11 -0
  80. package/lib/query-interface/sqlite-query-interface.js +61 -10
  81. package/lib/relation-repository/belongs-to-many-repository.js +21 -78
  82. package/lib/relation-repository/belongs-to-repository.js +0 -3
  83. package/lib/relation-repository/hasmany-repository.js +8 -44
  84. package/lib/relation-repository/hasone-repository.js +0 -3
  85. package/lib/relation-repository/multiple-relation-repository.js +14 -68
  86. package/lib/relation-repository/relation-repository.js +5 -42
  87. package/lib/relation-repository/single-relation-repository.js +5 -43
  88. package/lib/repository.d.ts +1 -0
  89. package/lib/repository.js +36 -182
  90. package/lib/sql-parser/index.js +10527 -0
  91. package/lib/sql-parser/sql.pegjs +1297 -0
  92. package/lib/sync-runner.js +19 -54
  93. package/lib/update-associations.js +58 -157
  94. package/lib/update-guard.js +10 -49
  95. package/lib/utils.js +6 -54
  96. package/lib/value-parsers/array-value-parser.js +3 -21
  97. package/lib/value-parsers/base-value-parser.js +0 -13
  98. package/lib/value-parsers/boolean-value-parser.js +4 -10
  99. package/lib/value-parsers/date-value-parser.js +0 -23
  100. package/lib/value-parsers/index.js +0 -10
  101. package/lib/value-parsers/json-value-parser.js +0 -7
  102. package/lib/value-parsers/number-value-parser.js +0 -9
  103. package/lib/value-parsers/string-value-parser.js +3 -20
  104. package/lib/value-parsers/to-many-value-parser.js +1 -42
  105. package/lib/value-parsers/to-one-value-parser.js +0 -14
  106. package/lib/view/field-type-map.d.ts +47 -0
  107. package/lib/view/field-type-map.js +56 -0
  108. package/lib/view/view-inference.d.ts +31 -0
  109. package/lib/view/view-inference.js +92 -0
  110. package/lib/view-collection.d.ts +6 -0
  111. package/lib/view-collection.js +24 -0
  112. package/package.json +4 -4
  113. package/src/__tests__/collection.test.ts +44 -0
  114. package/src/__tests__/fields/date.test.ts +75 -0
  115. package/src/__tests__/fields/sort-field.test.ts +100 -0
  116. package/src/__tests__/filter.test.ts +3 -3
  117. package/src/__tests__/group.test.ts +50 -0
  118. package/src/__tests__/inhertits/collection-inherits.test.ts +114 -0
  119. package/src/__tests__/operator/date-operator.test.ts +244 -98
  120. package/src/__tests__/operator/eq.test.ts +76 -0
  121. package/src/__tests__/operator/ne.test.ts +19 -1
  122. package/src/__tests__/relation-repository/belongs-to-many-repository.test.ts +82 -0
  123. package/src/__tests__/repository/find.test.ts +33 -0
  124. package/src/__tests__/repository.test.ts +88 -0
  125. package/src/__tests__/sql-parser.test.ts +13 -0
  126. package/src/__tests__/tree.test.ts +217 -0
  127. package/src/__tests__/view/list-view.test.ts +34 -0
  128. package/src/__tests__/view/view-collection.test.ts +199 -0
  129. package/src/__tests__/view/view-inference.test.ts +145 -0
  130. package/src/__tests__/view/view-repository.test.ts +67 -0
  131. package/src/collection-group-manager.ts +94 -0
  132. package/src/collection.ts +108 -14
  133. package/src/database-utils/index.ts +1 -0
  134. package/src/database.ts +79 -12
  135. package/src/features/ReferencesMap.ts +3 -2
  136. package/src/fields/belongs-to-many-field.ts +15 -2
  137. package/src/fields/date-field.ts +18 -0
  138. package/src/fields/field.ts +16 -8
  139. package/src/fields/json-field.ts +1 -0
  140. package/src/fields/sort-field.ts +90 -29
  141. package/src/filter-parser.ts +1 -0
  142. package/src/index.ts +3 -1
  143. package/src/listeners/adjacency-list.ts +60 -0
  144. package/src/listeners/index.ts +7 -0
  145. package/src/mock-database.ts +14 -2
  146. package/src/model.ts +4 -0
  147. package/src/operators/child-collection.ts +24 -0
  148. package/src/operators/date.ts +108 -24
  149. package/src/operators/eq.ts +14 -0
  150. package/src/operators/index.ts +2 -0
  151. package/src/operators/ne.ts +12 -7
  152. package/src/options-parser.ts +25 -11
  153. package/src/query-interface/mysql-query-interface.ts +53 -1
  154. package/src/query-interface/postgres-query-interface.ts +84 -3
  155. package/src/query-interface/query-interface.ts +31 -0
  156. package/src/query-interface/sqlite-query-interface.ts +62 -1
  157. package/src/relation-repository/belongs-to-many-repository.ts +20 -1
  158. package/src/relation-repository/hasmany-repository.ts +5 -3
  159. package/src/relation-repository/multiple-relation-repository.ts +9 -1
  160. package/src/repository.ts +6 -13
  161. package/src/sql-parser/index.js +10698 -0
  162. package/src/sql-parser/readme.md +2 -0
  163. package/src/sql-parser/sql.pegjs +1297 -0
  164. package/src/sync-runner.ts +13 -15
  165. package/src/update-associations.ts +26 -22
  166. package/src/view/field-type-map.ts +56 -0
  167. package/src/view/view-inference.ts +106 -0
  168. package/src/view-collection.ts +21 -0
@@ -0,0 +1,75 @@
1
+ import { mockDatabase } from '../';
2
+ import { Database } from '../../database';
3
+ import { Repository } from '../../repository';
4
+
5
+ describe('date-field', () => {
6
+ let db: Database;
7
+ let repository: Repository;
8
+
9
+ beforeEach(async () => {
10
+ db = mockDatabase();
11
+ db.collection({
12
+ name: 'tests',
13
+ fields: [
14
+ { name: 'date1', type: 'date' },
15
+ ],
16
+ });
17
+ await db.sync();
18
+ repository = db.getRepository('tests');
19
+ });
20
+
21
+ afterEach(async () => {
22
+ await db.close();
23
+ });
24
+
25
+ const createExpectToBe = async (key, actual, expected) => {
26
+ const instance = await repository.create({
27
+ values: {
28
+ [key]: actual,
29
+ },
30
+ });
31
+ return expect(instance.get(key).toISOString()).toEqual(expected);
32
+ };
33
+
34
+ test('create', async () => {
35
+ // sqlite 时区不能自定义,只有 +00:00,postgres 和 mysql 可以自定义 DB_TIMEZONE
36
+ await createExpectToBe('date1', '2023-03-24', '2023-03-24T00:00:00.000Z');
37
+ await createExpectToBe('date1', '2023-03-24T16:00:00.000Z', '2023-03-24T16:00:00.000Z');
38
+ });
39
+
40
+ // dateXX 相关 Operator 都是去 time 比较的
41
+ describe('dateOn', () => {
42
+ test('dateOn operator', async () => {
43
+ console.log('timezone', db.options.timezone);
44
+ // 默认的情况,时区为 db.options.timezone
45
+ await repository.find({
46
+ filter: {
47
+ date1: {
48
+ // 由 db.options.timezone 来处理日期转换,假设是 +08:00 的时区
49
+ // 2023-03-24表示的范围:2023-03-23T16:00:00 ~ 2023-03-24T16:00:00
50
+ $dateOn: '2023-03-24',
51
+ },
52
+ },
53
+ });
54
+
55
+ await repository.find({
56
+ filter: {
57
+ date1: {
58
+ // +06:00 时区 2023-03-24 的范围:2023-03-23T18:00:00 ~ 2023-03-24T18:00:00
59
+ $dateOn: '2023-03-24+06:00',
60
+ },
61
+ },
62
+ });
63
+
64
+ await repository.find({
65
+ filter: {
66
+ date1: {
67
+ // 2023-03-23T20:00:00+08:00 在 +08:00 时区的时间是:2023-03-24 04:00:00
68
+ // 也就是 +08:00 时区 2023-03-24 这一天的范围:2023-03-23T16:00:00 ~ 2023-03-24T16:00:00
69
+ $dateOn: '2023-03-23T20:00:00+08:00',
70
+ },
71
+ },
72
+ });
73
+ });
74
+ });
75
+ });
@@ -17,6 +17,106 @@ describe('string field', () => {
17
17
  await db.close();
18
18
  });
19
19
 
20
+ it('should init sorted value with thousand records', async () => {
21
+ const Test = db.collection({
22
+ name: 'tests',
23
+ fields: [
24
+ {
25
+ type: 'string',
26
+ name: 'name',
27
+ },
28
+ {
29
+ type: 'string',
30
+ name: 'group',
31
+ },
32
+ ],
33
+ });
34
+
35
+ await db.sync();
36
+
37
+ await Test.model.bulkCreate(
38
+ (() => {
39
+ const values = [];
40
+ for (let i = 0; i < 100000; i++) {
41
+ values.push({
42
+ group: 'a',
43
+ name: `r${i}`,
44
+ });
45
+
46
+ values.push({
47
+ group: 'b',
48
+ name: `r${i}`,
49
+ });
50
+ }
51
+ return values;
52
+ })(),
53
+ );
54
+
55
+ Test.setField('sort', { type: 'sort', scopeKey: 'group' });
56
+
57
+ const begin = Date.now();
58
+ await db.sync();
59
+ const end = Date.now();
60
+ // log time cost as milliseconds
61
+ console.log(end - begin);
62
+ });
63
+
64
+ it('should init sorted value with scopeKey', async () => {
65
+ const Test = db.collection({
66
+ name: 'tests',
67
+ fields: [
68
+ {
69
+ type: 'string',
70
+ name: 'name',
71
+ },
72
+ {
73
+ type: 'string',
74
+ name: 'group',
75
+ },
76
+ ],
77
+ });
78
+
79
+ await db.sync();
80
+ await Test.repository.create({
81
+ values: [
82
+ {
83
+ group: 'a',
84
+ name: 'r1',
85
+ },
86
+ {
87
+ group: 'b',
88
+ name: 'r2',
89
+ },
90
+ {
91
+ group: 'a',
92
+ name: 'r3',
93
+ },
94
+ {
95
+ group: 'b',
96
+ name: 'r4',
97
+ },
98
+ {
99
+ group: null,
100
+ name: 'r5',
101
+ },
102
+ {
103
+ group: null,
104
+ name: 'r6',
105
+ },
106
+ ],
107
+ });
108
+
109
+ Test.setField('sort', { type: 'sort', scopeKey: 'group' });
110
+
111
+ await db.sync();
112
+
113
+ const records = await Test.repository.find({});
114
+ const r3 = records.find((r) => r.get('name') === 'r3');
115
+ expect(r3.get('sort')).toBe(2);
116
+ const r5 = records.find((r) => r.get('name') === 'r5');
117
+ expect(r5.get('sort')).toBe(1);
118
+ });
119
+
20
120
  it('should init sorted value by createdAt when primaryKey not exists', async () => {
21
121
  const Test = db.collection({
22
122
  autoGenId: false,
@@ -47,14 +47,14 @@ describe('filter', () => {
47
47
  },
48
48
  });
49
49
 
50
- const response = await UserCollection.repository.find({
50
+ const count = await PostCollection.repository.count({
51
51
  filter: {
52
- 'posts.createdAt': {
52
+ 'user.createdAt': {
53
53
  $dateOn: user.get('createdAt'),
54
54
  },
55
55
  },
56
56
  });
57
57
 
58
- expect(response).toHaveLength(1);
58
+ expect(count).toBe(2);
59
59
  });
60
60
  });
@@ -0,0 +1,50 @@
1
+ import { Database, mockDatabase } from '@nocobase/database';
2
+ import sequelize from 'sequelize';
3
+
4
+ describe('migrator', () => {
5
+ let db: Database;
6
+
7
+ beforeEach(async () => {
8
+ db = mockDatabase({
9
+ tablePrefix: 'test_',
10
+ });
11
+
12
+ await db.clean({ drop: true });
13
+ });
14
+
15
+ afterEach(async () => {
16
+ await db.close();
17
+ });
18
+
19
+ test('addMigrations', async () => {
20
+ db.collection({
21
+ name: 'test',
22
+ fields: [
23
+ {
24
+ type: 'string',
25
+ name: 'status',
26
+ },
27
+ ],
28
+ });
29
+
30
+ await db.sync();
31
+
32
+ const r = db.getRepository('test');
33
+
34
+ await r.create({
35
+ values: [{ status: 's1' }, { status: 's1' }, { status: 's1' }, { status: 's2' }, { status: 's3' }],
36
+ });
37
+
38
+ const result = await r.find({
39
+ attributes: ['status', [sequelize.fn('COUNT', sequelize.col('id')), 'count']],
40
+ sort: 'status',
41
+ group: 'status',
42
+ });
43
+
44
+ expect(result.map((r) => r.toJSON())).toMatchObject([
45
+ { status: 's1', count: 3 },
46
+ { status: 's2', count: 1 },
47
+ { status: 's3', count: 1 },
48
+ ]);
49
+ });
50
+ });
@@ -1,6 +1,7 @@
1
1
  import Database from '../../database';
2
2
  import { InheritedCollection } from '../../inherited-collection';
3
3
  import { mockDatabase } from '../index';
4
+ import { BelongsToManyRepository } from '@nocobase/database';
4
5
  import pgOnly from './helper';
5
6
 
6
7
  pgOnly()('collection inherits', () => {
@@ -15,6 +16,119 @@ pgOnly()('collection inherits', () => {
15
16
  await db.close();
16
17
  });
17
18
 
19
+ it('should list data filtered by child type', async () => {
20
+ const rootCollection = db.collection({
21
+ name: 'root',
22
+ fields: [{ name: 'name', type: 'string' }],
23
+ });
24
+
25
+ const child1Collection = db.collection({
26
+ name: 'child1',
27
+ inherits: ['root'],
28
+ });
29
+
30
+ const child2Collection = db.collection({
31
+ name: 'child2',
32
+ inherits: ['root'],
33
+ });
34
+
35
+ await db.sync();
36
+
37
+ await rootCollection.repository.create({
38
+ values: {
39
+ name: 'root1',
40
+ },
41
+ });
42
+
43
+ await child1Collection.repository.create({
44
+ values: [{ name: 'child1-1' }, { name: 'child1-2' }],
45
+ });
46
+
47
+ await child2Collection.repository.create({
48
+ values: [{ name: 'child2-1' }, { name: 'child2-2' }],
49
+ });
50
+
51
+ const records = await rootCollection.repository.find({
52
+ filter: {
53
+ 'tableoid.$childIn': [child1Collection.name],
54
+ },
55
+ });
56
+
57
+ expect(records.every((r) => r.get('__collection') === child1Collection.name)).toBe(true);
58
+
59
+ const records2 = await rootCollection.repository.find({
60
+ filter: {
61
+ 'tableoid.$childNotIn': [child1Collection.name],
62
+ },
63
+ });
64
+ expect(records2.every((r) => r.get('__collection') !== child1Collection.name)).toBe(true);
65
+ });
66
+
67
+ it('should list collection name in relation repository', async () => {
68
+ const personTagCollection = db.collection({
69
+ name: 'personTags',
70
+ fields: [{ name: 'name', type: 'string' }],
71
+ });
72
+
73
+ const studentTagCollection = db.collection({
74
+ name: 'studentTags',
75
+ inherits: ['personTags'],
76
+ fields: [{ name: 'school', type: 'string' }],
77
+ });
78
+
79
+ const personCollection = db.collection({
80
+ name: 'person',
81
+ fields: [{ name: 'tags', type: 'belongsToMany', target: 'personTags' }],
82
+ });
83
+
84
+ const studentCollection = db.collection({
85
+ name: 'student',
86
+ inherits: ['person'],
87
+ fields: [],
88
+ });
89
+
90
+ await db.sync();
91
+
92
+ const studentTag1 = await studentTagCollection.repository.create({
93
+ values: {
94
+ name: 'studentTag',
95
+ school: 'school1',
96
+ },
97
+ });
98
+
99
+ const personTag1 = await personTagCollection.repository.create({
100
+ values: {
101
+ name: 'personTag',
102
+ },
103
+ });
104
+
105
+ await studentCollection.repository.create({
106
+ values: {
107
+ tags: [
108
+ {
109
+ id: studentTag1.get('id'),
110
+ },
111
+ {
112
+ id: personTag1.get('id'),
113
+ },
114
+ ],
115
+ },
116
+ });
117
+
118
+ const person1 = await personCollection.repository.findOne({});
119
+
120
+ const tags = await personCollection.repository
121
+ .relation<BelongsToManyRepository>('tags')
122
+ .of(person1.get('id'))
123
+ .find({});
124
+
125
+ const personTag = tags.find((tag) => tag.get('name') === 'personTag');
126
+ const studentTag = tags.find((tag) => tag.get('name') === 'studentTag');
127
+
128
+ expect(personTag.get('__collection')).toEqual(personTagCollection.name);
129
+ expect(studentTag.get('__collection')).toEqual(studentTagCollection.name);
130
+ });
131
+
18
132
  it('should create inherits with table name contains upperCase', async () => {
19
133
  db.collection({
20
134
  name: 'parent',