@nocobase/database 0.9.1-alpha.1 → 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 (173) 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 +26 -4
  5. package/lib/collection.js +190 -234
  6. package/lib/database-utils/index.js +3 -15
  7. package/lib/database.d.ts +9 -0
  8. package/lib/database.js +171 -299
  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 +29 -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 +38 -102
  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 +18 -0
  73. package/lib/query-interface/mysql-query-interface.js +88 -0
  74. package/lib/query-interface/postgres-query-interface.d.ts +14 -0
  75. package/lib/query-interface/postgres-query-interface.js +99 -0
  76. package/lib/query-interface/query-interface-builder.d.ts +2 -0
  77. package/lib/query-interface/query-interface-builder.js +18 -0
  78. package/lib/query-interface/query-interface.d.ts +21 -0
  79. package/lib/query-interface/query-interface.js +48 -0
  80. package/lib/query-interface/sqlite-query-interface.d.ts +17 -0
  81. package/lib/query-interface/sqlite-query-interface.js +89 -0
  82. package/lib/relation-repository/belongs-to-many-repository.js +21 -78
  83. package/lib/relation-repository/belongs-to-repository.js +0 -3
  84. package/lib/relation-repository/hasmany-repository.js +8 -44
  85. package/lib/relation-repository/hasone-repository.js +0 -3
  86. package/lib/relation-repository/multiple-relation-repository.js +16 -68
  87. package/lib/relation-repository/relation-repository.js +5 -42
  88. package/lib/relation-repository/single-relation-repository.js +6 -43
  89. package/lib/repository.d.ts +1 -0
  90. package/lib/repository.js +36 -182
  91. package/lib/sql-parser/index.js +10527 -0
  92. package/lib/sql-parser/sql.pegjs +1297 -0
  93. package/lib/sync-runner.d.ts +1 -1
  94. package/lib/sync-runner.js +26 -64
  95. package/lib/update-associations.js +58 -157
  96. package/lib/update-guard.js +10 -49
  97. package/lib/utils.js +16 -54
  98. package/lib/value-parsers/array-value-parser.js +3 -21
  99. package/lib/value-parsers/base-value-parser.js +0 -13
  100. package/lib/value-parsers/boolean-value-parser.js +4 -10
  101. package/lib/value-parsers/date-value-parser.js +0 -23
  102. package/lib/value-parsers/index.js +0 -10
  103. package/lib/value-parsers/json-value-parser.js +0 -7
  104. package/lib/value-parsers/number-value-parser.js +0 -9
  105. package/lib/value-parsers/string-value-parser.js +3 -20
  106. package/lib/value-parsers/to-many-value-parser.js +1 -42
  107. package/lib/value-parsers/to-one-value-parser.js +0 -14
  108. package/lib/view/field-type-map.d.ts +47 -0
  109. package/lib/view/field-type-map.js +56 -0
  110. package/lib/view/view-inference.d.ts +31 -0
  111. package/lib/view/view-inference.js +92 -0
  112. package/lib/view-collection.d.ts +6 -0
  113. package/lib/view-collection.js +24 -0
  114. package/package.json +4 -3
  115. package/src/__tests__/collection.test.ts +44 -0
  116. package/src/__tests__/fields/date.test.ts +75 -0
  117. package/src/__tests__/fields/sort-field.test.ts +100 -0
  118. package/src/__tests__/filter.test.ts +60 -0
  119. package/src/__tests__/group.test.ts +50 -0
  120. package/src/__tests__/inhertits/collection-inherits.test.ts +114 -0
  121. package/src/__tests__/operator/date-operator.test.ts +244 -98
  122. package/src/__tests__/operator/eq.test.ts +76 -0
  123. package/src/__tests__/operator/ne.test.ts +19 -1
  124. package/src/__tests__/relation-repository/belongs-to-many-repository.test.ts +82 -0
  125. package/src/__tests__/repository/find.test.ts +33 -0
  126. package/src/__tests__/repository.test.ts +88 -0
  127. package/src/__tests__/sql-parser.test.ts +13 -0
  128. package/src/__tests__/tree.test.ts +217 -0
  129. package/src/__tests__/view/list-view.test.ts +34 -0
  130. package/src/__tests__/view/view-collection.test.ts +199 -0
  131. package/src/__tests__/view/view-inference.test.ts +145 -0
  132. package/src/__tests__/view/view-repository.test.ts +67 -0
  133. package/src/collection-group-manager.ts +94 -0
  134. package/src/collection.ts +126 -16
  135. package/src/database-utils/index.ts +1 -0
  136. package/src/database.ts +98 -17
  137. package/src/features/ReferencesMap.ts +3 -2
  138. package/src/fields/belongs-to-many-field.ts +23 -4
  139. package/src/fields/date-field.ts +18 -0
  140. package/src/fields/field.ts +17 -7
  141. package/src/fields/json-field.ts +1 -0
  142. package/src/fields/sort-field.ts +90 -29
  143. package/src/filter-parser.ts +2 -1
  144. package/src/index.ts +3 -1
  145. package/src/listeners/adjacency-list.ts +60 -0
  146. package/src/listeners/index.ts +7 -0
  147. package/src/mock-database.ts +14 -2
  148. package/src/model.ts +4 -0
  149. package/src/operators/child-collection.ts +24 -0
  150. package/src/operators/date.ts +108 -24
  151. package/src/operators/eq.ts +14 -0
  152. package/src/operators/index.ts +2 -0
  153. package/src/operators/ne.ts +12 -7
  154. package/src/options-parser.ts +25 -11
  155. package/src/query-interface/mysql-query-interface.ts +72 -0
  156. package/src/query-interface/postgres-query-interface.ts +103 -0
  157. package/src/query-interface/query-interface-builder.ts +14 -0
  158. package/src/query-interface/query-interface.ts +43 -0
  159. package/src/query-interface/sqlite-query-interface.ts +79 -0
  160. package/src/relation-repository/belongs-to-many-repository.ts +20 -1
  161. package/src/relation-repository/hasmany-repository.ts +5 -3
  162. package/src/relation-repository/multiple-relation-repository.ts +13 -1
  163. package/src/relation-repository/single-relation-repository.ts +2 -0
  164. package/src/repository.ts +6 -13
  165. package/src/sql-parser/index.js +10698 -0
  166. package/src/sql-parser/readme.md +2 -0
  167. package/src/sql-parser/sql.pegjs +1297 -0
  168. package/src/sync-runner.ts +27 -32
  169. package/src/update-associations.ts +26 -22
  170. package/src/utils.ts +4 -3
  171. package/src/view/field-type-map.ts +56 -0
  172. package/src/view/view-inference.ts +106 -0
  173. package/src/view-collection.ts +21 -0
@@ -32,9 +32,21 @@ export class BelongsToManyField extends RelationField {
32
32
 
33
33
  const onDelete = this.options.onDelete || 'CASCADE';
34
34
 
35
+ const targetAssociation = association.toTarget;
36
+
37
+ if (association.targetKey) {
38
+ targetAssociation.targetKey = association.targetKey;
39
+ }
40
+
41
+ const sourceAssociation = association.toSource;
42
+
43
+ if (association.sourceKey) {
44
+ sourceAssociation.targetKey = association.sourceKey;
45
+ }
46
+
35
47
  return [
36
- BelongsToField.toReference(db, association.toSource, onDelete),
37
- BelongsToField.toReference(db, association.toTarget, onDelete),
48
+ BelongsToField.toReference(db, targetAssociation, onDelete),
49
+ BelongsToField.toReference(db, sourceAssociation, onDelete),
38
50
  ];
39
51
  }
40
52
 
@@ -55,9 +67,16 @@ export class BelongsToManyField extends RelationField {
55
67
  if (database.hasCollection(through)) {
56
68
  Through = database.getCollection(through);
57
69
  } else {
58
- Through = database.collection({
70
+ const throughCollectionOptions = {
59
71
  name: through,
60
- });
72
+ };
73
+
74
+ // set through collection schema
75
+ if (this.collection.collectionSchema()) {
76
+ throughCollectionOptions['schema'] = this.collection.collectionSchema();
77
+ }
78
+
79
+ Through = database.collection(throughCollectionOptions);
61
80
 
62
81
  Object.defineProperty(Through.model, 'isThrough', { value: true });
63
82
  }
@@ -5,6 +5,24 @@ export class DateField extends Field {
5
5
  get dataType() {
6
6
  return DataTypes.DATE(3);
7
7
  }
8
+
9
+ get timezone() {
10
+ return this.isGMT() ? '+00:00' : null;
11
+ }
12
+
13
+ getProps() {
14
+ return this.options?.uiSchema?.['x-component-props'] || {};
15
+ }
16
+
17
+ isDateOnly() {
18
+ const props = this.getProps();
19
+ return !props.showTime;
20
+ }
21
+
22
+ isGMT() {
23
+ const props = this.getProps();
24
+ return props.gmt;
25
+ }
8
26
  }
9
27
 
10
28
  export interface DateFieldOptions extends BaseColumnFieldOptions {
@@ -6,7 +6,7 @@ import {
6
6
  ModelIndexesOptions,
7
7
  QueryInterfaceOptions,
8
8
  SyncOptions,
9
- Transactionable
9
+ Transactionable,
10
10
  } from 'sequelize';
11
11
  import { Collection } from '../collection';
12
12
  import { Database } from '../database';
@@ -157,6 +157,11 @@ export abstract class Field {
157
157
  // return;
158
158
  // }
159
159
 
160
+ if (this.collection.isView()) {
161
+ this.remove();
162
+ return;
163
+ }
164
+
160
165
  const columnReferencesCount = _.filter(
161
166
  this.collection.model.rawAttributes,
162
167
  (attr) => attr.field == this.columnName(),
@@ -169,7 +174,7 @@ export abstract class Field {
169
174
  columnReferencesCount == 1
170
175
  ) {
171
176
  const queryInterface = this.database.sequelize.getQueryInterface();
172
- await queryInterface.removeColumn(this.collection.model.tableName, this.columnName(), options);
177
+ await queryInterface.removeColumn(this.collection.getTableNameWithSchema(), this.columnName(), options);
173
178
  }
174
179
 
175
180
  this.remove();
@@ -181,20 +186,24 @@ export abstract class Field {
181
186
  };
182
187
  let sql;
183
188
  if (this.database.sequelize.getDialect() === 'sqlite') {
184
- sql = `SELECT * from pragma_table_info('${this.collection.model.tableName}') WHERE name = '${this.columnName()}'`;
189
+ sql = `SELECT *
190
+ from pragma_table_info('${this.collection.model.tableName}')
191
+ WHERE name = '${this.columnName()}'`;
185
192
  } else if (this.database.inDialect('mysql')) {
186
193
  sql = `
187
194
  select column_name
188
195
  from INFORMATION_SCHEMA.COLUMNS
189
- where TABLE_SCHEMA='${this.database.options.database}' AND TABLE_NAME='${
190
- this.collection.model.tableName
191
- }' AND column_name='${this.columnName()}'
196
+ where TABLE_SCHEMA = '${this.database.options.database}'
197
+ AND TABLE_NAME = '${this.collection.model.tableName}'
198
+ AND column_name = '${this.columnName()}'
192
199
  `;
193
200
  } else {
194
201
  sql = `
195
202
  select column_name
196
203
  from INFORMATION_SCHEMA.COLUMNS
197
- where TABLE_NAME='${this.collection.model.tableName}' AND column_name='${this.columnName()}'
204
+ where TABLE_NAME = '${this.collection.model.tableName}'
205
+ AND column_name = '${this.columnName()}'
206
+ AND table_schema = '${this.collection.collectionSchema() || 'public'}'
198
207
  `;
199
208
  }
200
209
  const [rows] = await this.database.sequelize.query(sql, opts);
@@ -228,6 +237,7 @@ export abstract class Field {
228
237
  if (this.dataType) {
229
238
  Object.assign(opts, { type: this.dataType });
230
239
  }
240
+
231
241
  return opts;
232
242
  }
233
243
 
@@ -20,6 +20,7 @@ export class JsonbField extends Field {
20
20
  return DataTypes.JSON;
21
21
  }
22
22
  }
23
+
23
24
  export interface JsonbFieldOptions extends BaseColumnFieldOptions {
24
25
  type: 'jsonb';
25
26
  }
@@ -42,49 +42,110 @@ export class SortField extends Field {
42
42
  };
43
43
 
44
44
  initRecordsSortValue = async ({ transaction }) => {
45
- const totalCount = await this.collection.repository.count({
46
- transaction,
47
- });
48
-
49
- const emptyCount = await this.collection.repository.count({
50
- filter: {
51
- [this.name]: null,
52
- },
53
- transaction,
54
- });
55
-
56
- const orderKey = (() => {
45
+ const orderField = (() => {
57
46
  const model = this.collection.model;
58
47
  if (model.primaryKeyAttribute) {
59
48
  return model.primaryKeyAttribute;
60
49
  }
61
50
  if (model.rawAttributes['createdAt']) {
62
- return 'createdAt';
51
+ return model.rawAttributes['createdAt'].field;
63
52
  }
64
53
 
65
54
  throw new Error(`can not find order key for collection ${this.collection.name}`);
66
55
  })();
67
56
 
68
- if (emptyCount === totalCount && emptyCount > 0) {
69
- const records = await this.collection.repository.find({
70
- order: [orderKey],
57
+ const needInit = async (scopeKey = null, scopeValue = null) => {
58
+ const filter = {};
59
+ if (scopeKey && scopeValue) {
60
+ filter[scopeKey] = scopeValue;
61
+ }
62
+
63
+ const totalCount = await this.collection.repository.count({
64
+ filter,
65
+ transaction,
66
+ });
67
+
68
+ const emptyCount = await this.collection.repository.count({
69
+ filter: {
70
+ [this.name]: null,
71
+ ...filter,
72
+ },
71
73
  transaction,
72
74
  });
73
75
 
74
- let start = 1;
75
- for (const record of records) {
76
- await record.update(
77
- {
78
- sort: start,
79
- },
80
- {
81
- transaction,
82
- silent: true,
83
- },
84
- );
85
-
86
- start += 1;
76
+ return emptyCount === totalCount && emptyCount > 0;
77
+ };
78
+
79
+ const doInit = async (scopeKey = null, scopeValue = null) => {
80
+ const queryInterface = this.collection.db.sequelize.getQueryInterface();
81
+
82
+ const quotedOrderField = queryInterface.quoteIdentifier(orderField);
83
+
84
+ const sql = `
85
+ WITH ordered_table AS (
86
+ SELECT *, ROW_NUMBER() OVER (${
87
+ scopeKey ? `PARTITION BY ${queryInterface.quoteIdentifier(scopeKey)}` : ''
88
+ } ORDER BY ${quotedOrderField}) AS new_sequence_number
89
+ FROM ${this.collection.quotedTableName()}
90
+ ${(() => {
91
+ if (scopeKey && scopeValue) {
92
+ const hasNull = scopeValue.includes(null);
93
+
94
+ return `WHERE ${queryInterface.quoteIdentifier(scopeKey)} IN (${scopeValue
95
+ .filter((v) => v !== null)
96
+ .map((v) => `'${v}'`)
97
+ .join(',')}) ${hasNull ? `OR ${queryInterface.quoteIdentifier(scopeKey)} IS NULL` : ''} `;
98
+ }
99
+
100
+ return '';
101
+ })()}
102
+
103
+ )
104
+ ${
105
+ this.collection.db.inDialect('mysql')
106
+ ? `
107
+ UPDATE ${this.collection.quotedTableName()}, ordered_table
108
+ SET ${this.collection.quotedTableName()}.${this.name} = ordered_table.new_sequence_number
109
+ WHERE ${this.collection.quotedTableName()}.${quotedOrderField} = ordered_table.${quotedOrderField}
110
+ `
111
+ : `
112
+ UPDATE ${this.collection.quotedTableName()}
113
+ SET ${queryInterface.quoteIdentifier(this.name)} = ordered_table.new_sequence_number
114
+ FROM ordered_table
115
+ WHERE ${this.collection.quotedTableName()}.${quotedOrderField} = ${queryInterface.quoteIdentifier(
116
+ 'ordered_table',
117
+ )}.${quotedOrderField};
118
+ `
119
+ }
120
+
121
+ `;
122
+
123
+ await this.collection.db.sequelize.query(sql, {
124
+ transaction,
125
+ });
126
+ };
127
+
128
+ const scopeKey = this.options.scopeKey;
129
+ if (scopeKey) {
130
+ const groups = await this.collection.repository.find({
131
+ attributes: [scopeKey],
132
+ group: [scopeKey],
133
+ raw: true,
134
+ transaction,
135
+ });
136
+
137
+ const needInitGroups = [];
138
+ for (const group of groups) {
139
+ if (await needInit(scopeKey, group[scopeKey])) {
140
+ needInitGroups.push(group[scopeKey]);
141
+ }
142
+ }
143
+
144
+ if (needInitGroups.length > 0) {
145
+ await doInit(scopeKey, needInitGroups);
87
146
  }
147
+ } else if (await needInit()) {
148
+ await doInit();
88
149
  }
89
150
  };
90
151
 
@@ -135,6 +135,7 @@ export default class FilterParser {
135
135
  path: skipPrefix,
136
136
  fullName,
137
137
  fieldName,
138
+ fieldPath: `${this.collection.name}.${fullName}`,
138
139
  model: this.model,
139
140
  });
140
141
  break;
@@ -178,7 +179,7 @@ export default class FilterParser {
178
179
  origins.push(attr);
179
180
  // if it is target model attribute
180
181
  if (target.rawAttributes[attr]) {
181
- associationKeys.push(attr);
182
+ associationKeys.push(target.rawAttributes[attr].field || attr);
182
183
  target = null;
183
184
  } else if (target.associations[attr]) {
184
185
  // if it is target model association (nested association filter)
package/src/index.ts CHANGED
@@ -20,4 +20,6 @@ export * from './repository';
20
20
  export * from './update-associations';
21
21
  export { snakeCase } from './utils';
22
22
  export * from './value-parsers';
23
-
23
+ export * from './collection-group-manager';
24
+ export * from './view-collection';
25
+ export * from './view/view-inference';
@@ -0,0 +1,60 @@
1
+ import lodash from 'lodash';
2
+ import { Collection, CollectionOptions } from '../collection';
3
+ import { Model } from '../model';
4
+
5
+ export const beforeDefineAdjacencyListCollection = (options: CollectionOptions) => {
6
+ if (!options.tree) {
7
+ return;
8
+ }
9
+ (options.fields || []).forEach((field) => {
10
+ if (field.treeParent || field.treeChildren) {
11
+ if (!field.target) {
12
+ field.target = options.name;
13
+ }
14
+ if (!field.foreignKey) {
15
+ field.foreignKey = 'parentId';
16
+ }
17
+ }
18
+ });
19
+ };
20
+
21
+ export const afterDefineAdjacencyListCollection = (collection: Collection) => {
22
+ if (!collection.options.tree) {
23
+ return;
24
+ }
25
+ collection.model.afterFind(async (instances, options: any) => {
26
+ if (!options.tree) {
27
+ return;
28
+ }
29
+ const foreignKey = collection.treeParentField?.foreignKey ?? 'parentId';
30
+ const childrenKey = collection.treeChildrenField?.name ?? 'children';
31
+ const arr: Model[] = Array.isArray(instances) ? instances : [instances];
32
+ let index = 0;
33
+ for (const instance of arr) {
34
+ const opts = {
35
+ ...lodash.pick(options, ['tree', 'fields', 'appends', 'except', 'sort']),
36
+ };
37
+ let __index = `${index++}`;
38
+ if (options.parentIndex) {
39
+ __index = `${options.parentIndex}.${__index}`;
40
+ }
41
+ instance.setDataValue('__index', __index);
42
+ const children = await collection.repository.find({
43
+ filter: {
44
+ [foreignKey]: instance.id,
45
+ },
46
+ transaction: options.transaction,
47
+ ...opts,
48
+ // @ts-ignore
49
+ parentIndex: `${__index}.${childrenKey}`,
50
+ context: options.context,
51
+ });
52
+ if (children?.length > 0) {
53
+ instance.setDataValue(
54
+ childrenKey,
55
+ children.map((r) => r.toJSON()),
56
+ );
57
+ }
58
+ }
59
+ });
60
+ };
@@ -0,0 +1,7 @@
1
+ import { Database } from '../database';
2
+ import { afterDefineAdjacencyListCollection, beforeDefineAdjacencyListCollection } from './adjacency-list';
3
+
4
+ export const registerBuiltInListeners = (db: Database) => {
5
+ db.on('beforeDefineCollection', beforeDefineAdjacencyListCollection);
6
+ db.on('afterDefineCollection', afterDefineAdjacencyListCollection);
7
+ };
@@ -14,14 +14,14 @@ export class MockDatabase extends Database {
14
14
  }
15
15
 
16
16
  export function getConfigByEnv() {
17
- return {
17
+ const options = {
18
18
  username: process.env.DB_USER,
19
19
  password: process.env.DB_PASSWORD,
20
20
  database: process.env.DB_DATABASE,
21
21
  host: process.env.DB_HOST,
22
22
  port: process.env.DB_PORT,
23
23
  dialect: process.env.DB_DIALECT || 'sqlite',
24
- logging: process.env.DB_LOGGING === 'on' ? console.log : false,
24
+ logging: process.env.DB_LOGGING === 'on' ? customLogger : false,
25
25
  storage:
26
26
  process.env.DB_STORAGE && process.env.DB_STORAGE !== ':memory:'
27
27
  ? resolve(process.cwd(), process.env.DB_STORAGE)
@@ -33,7 +33,19 @@ export function getConfigByEnv() {
33
33
  timezone: process.env.DB_TIMEZONE,
34
34
  underscored: process.env.DB_UNDERSCORED === 'true',
35
35
  schema: process.env.DB_SCHEMA !== 'public' ? process.env.DB_SCHEMA : undefined,
36
+ dialectOptions: {},
36
37
  };
38
+
39
+ if (process.env.DB_DIALECT == 'postgres') {
40
+ options.dialectOptions['application_name'] = 'nocobase.main';
41
+ }
42
+
43
+ return options;
44
+ }
45
+
46
+ function customLogger(queryString, queryObject) {
47
+ console.log(queryString); // outputs a string
48
+ console.log(queryObject.bind); // outputs an array
37
49
  }
38
50
 
39
51
  export function mockDatabase(options: IDatabaseOptions = {}): MockDatabase {
package/src/model.ts CHANGED
@@ -151,6 +151,10 @@ export class Model<TModelAttributes extends {} = any, TCreationAttributes extend
151
151
  }
152
152
 
153
153
  static async sync(options) {
154
+ if (this.collection.isView()) {
155
+ return;
156
+ }
157
+
154
158
  const model = this as any;
155
159
 
156
160
  const _schema = model._schema;
@@ -0,0 +1,24 @@
1
+ import { Op, Sequelize } from 'sequelize';
2
+
3
+ const mapVal = (values, db) =>
4
+ values.map((v) => {
5
+ const collection = db.getCollection(v);
6
+ return Sequelize.literal(`'${collection.tableNameAsString()}'::regclass`);
7
+ });
8
+
9
+ export default {
10
+ $childIn(values, ctx: any) {
11
+ const db = ctx.db;
12
+
13
+ return {
14
+ [Op.in]: mapVal(values, db),
15
+ };
16
+ },
17
+ $childNotIn(values, ctx: any) {
18
+ const db = ctx.db;
19
+
20
+ return {
21
+ [Op.notIn]: mapVal(values, db),
22
+ };
23
+ },
24
+ } as Record<string, any>;
@@ -1,41 +1,125 @@
1
+ import { parseDate } from '@nocobase/utils';
1
2
  import { Op } from 'sequelize';
2
- import moment, { MomentInput } from 'moment';
3
- function stringToDate(value: string): Date {
4
- return moment(value).toDate();
5
- }
6
3
 
7
- function getNextDay(value: MomentInput): Date {
8
- return moment(value).add(1, 'd').toDate();
4
+ function isDate(input) {
5
+ return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]';
9
6
  }
10
7
 
8
+ const toDate = (date) => {
9
+ if (isDate(date)) {
10
+ return date;
11
+ }
12
+ return new Date(date);
13
+ };
14
+
11
15
  export default {
12
- $dateOn(value) {
13
- return {
14
- [Op.and]: [{ [Op.gte]: stringToDate(value) }, { [Op.lt]: getNextDay(value) }],
15
- };
16
+ $dateOn(value, ctx) {
17
+ const r = parseDate(value, {
18
+ timezone: ctx.db.options.timezone,
19
+ });
20
+ if (typeof r === 'string') {
21
+ return {
22
+ [Op.eq]: toDate(r),
23
+ };
24
+ }
25
+ if (Array.isArray(r)) {
26
+ return {
27
+ [Op.and]: [{ [Op.gte]: toDate(r[0]) }, { [Op.lt]: toDate(r[1]) }],
28
+ };
29
+ }
30
+ throw new Error(`Invalid Date ${JSON.stringify(value)}`);
31
+ },
32
+
33
+ $dateNotOn(value, ctx) {
34
+ const r = parseDate(value, {
35
+ timezone: ctx.db.options.timezone,
36
+ });
37
+ if (typeof r === 'string') {
38
+ return {
39
+ [Op.ne]: toDate(r),
40
+ };
41
+ }
42
+ if (Array.isArray(r)) {
43
+ return {
44
+ [Op.or]: [{ [Op.lt]: toDate(r[0]) }, { [Op.gte]: toDate(r[1]) }],
45
+ };
46
+ }
47
+ throw new Error(`Invalid Date ${JSON.stringify(value)}`);
16
48
  },
17
49
 
18
- $dateNotOn(value) {
19
- return {
20
- [Op.or]: [{ [Op.lt]: stringToDate(value) }, { [Op.gte]: getNextDay(value) }],
21
- };
50
+ $dateBefore(value, ctx) {
51
+ const r = parseDate(value, {
52
+ timezone: ctx.db.options.timezone,
53
+ });
54
+ if (typeof r === 'string') {
55
+ return {
56
+ [Op.lt]: toDate(r),
57
+ };
58
+ } else if (Array.isArray(r)) {
59
+ return {
60
+ [Op.lt]: toDate(r[0]),
61
+ };
62
+ }
63
+ throw new Error(`Invalid Date ${JSON.stringify(value)}`);
22
64
  },
23
65
 
24
- $dateBefore(value) {
25
- return { [Op.lt]: stringToDate(value) };
66
+ $dateNotBefore(value, ctx) {
67
+ const r = parseDate(value, {
68
+ timezone: ctx.db.options.timezone,
69
+ });
70
+ if (typeof r === 'string') {
71
+ return {
72
+ [Op.gte]: toDate(r),
73
+ };
74
+ } else if (Array.isArray(r)) {
75
+ return {
76
+ [Op.gte]: toDate(r[0]),
77
+ };
78
+ }
79
+ throw new Error(`Invalid Date ${JSON.stringify(value)}`);
26
80
  },
27
81
 
28
- $dateNotBefore(value) {
29
- return {
30
- [Op.gte]: stringToDate(value),
31
- };
82
+ $dateAfter(value, ctx) {
83
+ const r = parseDate(value, {
84
+ timezone: ctx.db.options.timezone,
85
+ });
86
+ if (typeof r === 'string') {
87
+ return {
88
+ [Op.gt]: toDate(r),
89
+ };
90
+ } else if (Array.isArray(r)) {
91
+ return {
92
+ [Op.gte]: toDate(r[1]),
93
+ };
94
+ }
95
+ throw new Error(`Invalid Date ${JSON.stringify(value)}`);
32
96
  },
33
97
 
34
- $dateAfter(value) {
35
- return { [Op.gte]: getNextDay(value) };
98
+ $dateNotAfter(value, ctx) {
99
+ const r = parseDate(value, {
100
+ timezone: ctx.db.options.timezone,
101
+ });
102
+ if (typeof r === 'string') {
103
+ return {
104
+ [Op.lte]: toDate(r),
105
+ };
106
+ } else if (Array.isArray(r)) {
107
+ return {
108
+ [Op.lt]: toDate(r[1]),
109
+ };
110
+ }
111
+ throw new Error(`Invalid Date ${JSON.stringify(value)}`);
36
112
  },
37
113
 
38
- $dateNotAfter(value) {
39
- return { [Op.lt]: getNextDay(value) };
114
+ $dateBetween(value, ctx) {
115
+ const r = parseDate(value, {
116
+ timezone: ctx.db.options.timezone,
117
+ });
118
+ if (r) {
119
+ return {
120
+ [Op.and]: [{ [Op.gte]: toDate(r[0]) }, { [Op.lt]: toDate(r[1]) }],
121
+ };
122
+ }
123
+ throw new Error(`Invalid Date ${JSON.stringify(value)}`);
40
124
  },
41
125
  } as Record<string, any>;
@@ -0,0 +1,14 @@
1
+ import { Op } from 'sequelize';
2
+
3
+ export default {
4
+ $eq(val: any) {
5
+ if (Array.isArray(val)) {
6
+ return {
7
+ [Op.in]: val,
8
+ };
9
+ }
10
+ return {
11
+ [Op.eq]: val,
12
+ };
13
+ },
14
+ } as Record<string, any>;
@@ -4,7 +4,9 @@ export default {
4
4
  ...require('./array').default,
5
5
  ...require('./empty').default,
6
6
  ...require('./string').default,
7
+ ...require('./eq').default,
7
8
  ...require('./ne').default,
8
9
  ...require('./notIn').default,
9
10
  ...require('./boolean').default,
11
+ ...require('./child-collection').default,
10
12
  };
@@ -2,15 +2,20 @@ import { Op } from 'sequelize';
2
2
 
3
3
  export default {
4
4
  $ne(val, ctx) {
5
+ if (Array.isArray(val)) {
6
+ return {
7
+ [Op.notIn]: val,
8
+ };
9
+ }
5
10
  return val === null
6
11
  ? {
7
- [Op.ne]: null,
8
- }
12
+ [Op.ne]: null,
13
+ }
9
14
  : {
10
- [Op.or]: {
11
- [Op.ne]: val,
12
- [Op.is]: null,
13
- },
14
- };
15
+ [Op.or]: {
16
+ [Op.ne]: val,
17
+ [Op.is]: null,
18
+ },
19
+ };
15
20
  },
16
21
  } as Record<string, any>;