@nocobase/database 0.9.1-alpha.2 → 0.9.2-alpha.2

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
@@ -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.addSchemaTableName(), this.columnName(), options);
177
+ await queryInterface.removeColumn(this.collection.getTableNameWithSchema(), this.columnName(), options);
173
178
  }
174
179
 
175
180
  this.remove();
@@ -181,22 +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='${
198
- this.collection.model.tableName
199
- }' AND column_name='${this.columnName()}' AND table_schema='${this.collection.collectionSchema() || 'public'}'
204
+ where TABLE_NAME = '${this.collection.model.tableName}'
205
+ AND column_name = '${this.columnName()}'
206
+ AND table_schema = '${this.collection.collectionSchema() || 'public'}'
200
207
  `;
201
208
  }
202
209
  const [rows] = await this.database.sequelize.query(sql, opts);
@@ -230,6 +237,7 @@ export abstract class Field {
230
237
  if (this.dataType) {
231
238
  Object.assign(opts, { type: this.dataType });
232
239
  }
240
+
233
241
  return opts;
234
242
  }
235
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;
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>;
@@ -109,27 +109,48 @@ export class OptionsParser {
109
109
  return filterParams;
110
110
  }
111
111
 
112
- protected inheritFromSubQuery(): any {
113
- return [
112
+ protected inheritFromSubQuery(include): any {
113
+ include.push([
114
114
  Sequelize.literal(`(select relname from pg_class where pg_class.oid = "${this.collection.name}".tableoid)`),
115
115
  '__tableName',
116
- ];
116
+ ]);
117
+
118
+ include.push([
119
+ Sequelize.literal(`
120
+ (SELECT n.nspname
121
+ FROM pg_class c
122
+ JOIN pg_namespace n ON n.oid = c.relnamespace
123
+ WHERE c.oid = "${this.collection.name}".tableoid)
124
+ `),
125
+ '__schemaName',
126
+ ]);
117
127
  }
118
128
 
119
129
  protected parseFields(filterParams: any) {
120
130
  const appends = this.options?.appends || [];
121
131
  const except = [];
122
132
 
133
+ if (this.options?.attributes) {
134
+ return {
135
+ attributes: this.options.attributes,
136
+ };
137
+ }
138
+
123
139
  let attributes: FindAttributeOptions = {
124
140
  include: [],
125
141
  exclude: [],
126
142
  }; // out put all fields by default
127
143
 
128
144
  if (this.collection.isParent()) {
129
- attributes.include.push(this.inheritFromSubQuery());
145
+ this.inheritFromSubQuery(attributes.include);
130
146
  }
131
147
 
132
148
  if (this.options?.fields) {
149
+ attributes = [];
150
+ if (this.collection.isParent()) {
151
+ this.inheritFromSubQuery(attributes);
152
+ }
153
+
133
154
  // 将fields拆分为 attributes 和 appends
134
155
  for (const field of this.options.fields) {
135
156
  if (this.isAssociationPath(field)) {
@@ -137,13 +158,6 @@ export class OptionsParser {
137
158
  appends.push(field);
138
159
  } else {
139
160
  // field is model attribute, change attributes to array type
140
- if (!Array.isArray(attributes)) {
141
- attributes = [];
142
- if (this.collection.isParent()) {
143
- attributes.push(this.inheritFromSubQuery());
144
- }
145
- }
146
-
147
161
  attributes.push(field);
148
162
  }
149
163
  }