@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.
- package/lib/collection-group-manager.d.ts +13 -0
- package/lib/collection-group-manager.js +91 -0
- package/lib/collection-importer.js +0 -24
- package/lib/collection.d.ts +24 -3
- package/lib/collection.js +176 -236
- package/lib/database-utils/index.js +3 -15
- package/lib/database.d.ts +3 -0
- package/lib/database.js +160 -298
- package/lib/decorators/must-have-filter-decorator.js +0 -7
- package/lib/decorators/transaction-decorator.js +5 -18
- package/lib/errors/identifier-error.js +0 -3
- package/lib/features/ReferencesMap.js +1 -14
- package/lib/features/referential-integrity-check.js +7 -21
- package/lib/field-repository/array-field-repository.js +5 -45
- package/lib/fields/array-field.js +0 -13
- package/lib/fields/belongs-to-field.js +24 -50
- package/lib/fields/belongs-to-many-field.js +23 -47
- package/lib/fields/boolean-field.js +0 -7
- package/lib/fields/context-field.js +2 -23
- package/lib/fields/date-field.d.ts +4 -0
- package/lib/fields/date-field.js +15 -7
- package/lib/fields/field.js +32 -85
- package/lib/fields/has-many-field.js +16 -49
- package/lib/fields/has-one-field.js +18 -52
- package/lib/fields/index.js +0 -44
- package/lib/fields/json-field.js +0 -12
- package/lib/fields/number-field.js +0 -23
- package/lib/fields/password-field.js +8 -35
- package/lib/fields/radio-field.js +0 -18
- package/lib/fields/relation-field.js +4 -16
- package/lib/fields/set-field.js +0 -8
- package/lib/fields/sort-field.js +84 -73
- package/lib/fields/string-field.js +0 -7
- package/lib/fields/text-field.js +0 -7
- package/lib/fields/time-field.js +0 -7
- package/lib/fields/uid-field.js +4 -22
- package/lib/fields/uuid-field.js +3 -12
- package/lib/fields/virtual-field.js +0 -7
- package/lib/filter-match.js +7 -22
- package/lib/filter-parser.js +37 -101
- package/lib/index.d.ts +3 -0
- package/lib/index.js +36 -42
- package/lib/inherited-collection.js +15 -62
- package/lib/inherited-map.js +7 -48
- package/lib/listeners/adjacency-list.d.ts +3 -0
- package/lib/listeners/adjacency-list.js +91 -0
- package/lib/listeners/index.d.ts +2 -0
- package/lib/listeners/index.js +12 -0
- package/lib/magic-attribute-model.js +58 -114
- package/lib/migration.js +7 -28
- package/lib/mock-database.d.ts +4 -4
- package/lib/mock-database.js +15 -18
- package/lib/model-hook.js +4 -35
- package/lib/model.js +12 -54
- package/lib/operators/array.js +2 -32
- package/lib/operators/association.js +0 -6
- package/lib/operators/boolean.js +0 -6
- package/lib/operators/child-collection.d.ts +2 -0
- package/lib/operators/child-collection.js +32 -0
- package/lib/operators/date.js +123 -60
- package/lib/operators/empty.js +3 -32
- package/lib/operators/eq.d.ts +2 -0
- package/lib/operators/eq.js +26 -0
- package/lib/operators/index.js +4 -7
- package/lib/operators/ne.js +5 -5
- package/lib/operators/notIn.js +0 -5
- package/lib/operators/string.js +0 -11
- package/lib/operators/utils.js +0 -6
- package/lib/options-parser.d.ts +1 -1
- package/lib/options-parser.js +47 -107
- package/lib/playground.js +0 -4
- package/lib/query-interface/mysql-query-interface.d.ts +11 -0
- package/lib/query-interface/mysql-query-interface.js +59 -10
- package/lib/query-interface/postgres-query-interface.d.ts +8 -0
- package/lib/query-interface/postgres-query-interface.js +70 -12
- package/lib/query-interface/query-interface-builder.js +0 -5
- package/lib/query-interface/query-interface.d.ts +12 -0
- package/lib/query-interface/query-interface.js +33 -3
- package/lib/query-interface/sqlite-query-interface.d.ts +11 -0
- package/lib/query-interface/sqlite-query-interface.js +61 -10
- package/lib/relation-repository/belongs-to-many-repository.js +21 -78
- package/lib/relation-repository/belongs-to-repository.js +0 -3
- package/lib/relation-repository/hasmany-repository.js +8 -44
- package/lib/relation-repository/hasone-repository.js +0 -3
- package/lib/relation-repository/multiple-relation-repository.js +14 -68
- package/lib/relation-repository/relation-repository.js +5 -42
- package/lib/relation-repository/single-relation-repository.js +5 -43
- package/lib/repository.d.ts +1 -0
- package/lib/repository.js +36 -182
- package/lib/sql-parser/index.js +10527 -0
- package/lib/sql-parser/sql.pegjs +1297 -0
- package/lib/sync-runner.js +19 -54
- package/lib/update-associations.js +58 -157
- package/lib/update-guard.js +10 -49
- package/lib/utils.js +6 -54
- package/lib/value-parsers/array-value-parser.js +3 -21
- package/lib/value-parsers/base-value-parser.js +0 -13
- package/lib/value-parsers/boolean-value-parser.js +4 -10
- package/lib/value-parsers/date-value-parser.js +0 -23
- package/lib/value-parsers/index.js +0 -10
- package/lib/value-parsers/json-value-parser.js +0 -7
- package/lib/value-parsers/number-value-parser.js +0 -9
- package/lib/value-parsers/string-value-parser.js +3 -20
- package/lib/value-parsers/to-many-value-parser.js +1 -42
- package/lib/value-parsers/to-one-value-parser.js +0 -14
- package/lib/view/field-type-map.d.ts +47 -0
- package/lib/view/field-type-map.js +56 -0
- package/lib/view/view-inference.d.ts +31 -0
- package/lib/view/view-inference.js +92 -0
- package/lib/view-collection.d.ts +6 -0
- package/lib/view-collection.js +24 -0
- package/package.json +4 -4
- package/src/__tests__/collection.test.ts +44 -0
- package/src/__tests__/fields/date.test.ts +75 -0
- package/src/__tests__/fields/sort-field.test.ts +100 -0
- package/src/__tests__/filter.test.ts +3 -3
- package/src/__tests__/group.test.ts +50 -0
- package/src/__tests__/inhertits/collection-inherits.test.ts +114 -0
- package/src/__tests__/operator/date-operator.test.ts +244 -98
- package/src/__tests__/operator/eq.test.ts +76 -0
- package/src/__tests__/operator/ne.test.ts +19 -1
- package/src/__tests__/relation-repository/belongs-to-many-repository.test.ts +82 -0
- package/src/__tests__/repository/find.test.ts +33 -0
- package/src/__tests__/repository.test.ts +88 -0
- package/src/__tests__/sql-parser.test.ts +13 -0
- package/src/__tests__/tree.test.ts +217 -0
- package/src/__tests__/view/list-view.test.ts +34 -0
- package/src/__tests__/view/view-collection.test.ts +199 -0
- package/src/__tests__/view/view-inference.test.ts +145 -0
- package/src/__tests__/view/view-repository.test.ts +67 -0
- package/src/collection-group-manager.ts +94 -0
- package/src/collection.ts +108 -14
- package/src/database-utils/index.ts +1 -0
- package/src/database.ts +79 -12
- package/src/features/ReferencesMap.ts +3 -2
- package/src/fields/belongs-to-many-field.ts +15 -2
- package/src/fields/date-field.ts +18 -0
- package/src/fields/field.ts +16 -8
- package/src/fields/json-field.ts +1 -0
- package/src/fields/sort-field.ts +90 -29
- package/src/filter-parser.ts +1 -0
- package/src/index.ts +3 -1
- package/src/listeners/adjacency-list.ts +60 -0
- package/src/listeners/index.ts +7 -0
- package/src/mock-database.ts +14 -2
- package/src/model.ts +4 -0
- package/src/operators/child-collection.ts +24 -0
- package/src/operators/date.ts +108 -24
- package/src/operators/eq.ts +14 -0
- package/src/operators/index.ts +2 -0
- package/src/operators/ne.ts +12 -7
- package/src/options-parser.ts +25 -11
- package/src/query-interface/mysql-query-interface.ts +53 -1
- package/src/query-interface/postgres-query-interface.ts +84 -3
- package/src/query-interface/query-interface.ts +31 -0
- package/src/query-interface/sqlite-query-interface.ts +62 -1
- package/src/relation-repository/belongs-to-many-repository.ts +20 -1
- package/src/relation-repository/hasmany-repository.ts +5 -3
- package/src/relation-repository/multiple-relation-repository.ts +9 -1
- package/src/repository.ts +6 -13
- package/src/sql-parser/index.js +10698 -0
- package/src/sql-parser/readme.md +2 -0
- package/src/sql-parser/sql.pegjs +1297 -0
- package/src/sync-runner.ts +13 -15
- package/src/update-associations.ts +26 -22
- package/src/view/field-type-map.ts +56 -0
- package/src/view/view-inference.ts +106 -0
- package/src/view-collection.ts +21 -0
package/src/fields/field.ts
CHANGED
|
@@ -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.
|
|
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 *
|
|
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}'
|
|
190
|
-
|
|
191
|
-
|
|
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.
|
|
199
|
-
|
|
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
|
|
package/src/fields/json-field.ts
CHANGED
package/src/fields/sort-field.ts
CHANGED
|
@@ -42,49 +42,110 @@ export class SortField extends Field {
|
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
initRecordsSortValue = async ({ transaction }) => {
|
|
45
|
-
const
|
|
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
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
|
package/src/filter-parser.ts
CHANGED
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
|
+
};
|
package/src/mock-database.ts
CHANGED
|
@@ -14,14 +14,14 @@ export class MockDatabase extends Database {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export function getConfigByEnv() {
|
|
17
|
-
|
|
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' ?
|
|
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>;
|
package/src/operators/date.ts
CHANGED
|
@@ -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
|
|
8
|
-
return
|
|
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
|
-
|
|
14
|
-
|
|
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
|
-
$
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
$
|
|
25
|
-
|
|
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
|
-
$
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
$
|
|
35
|
-
|
|
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
|
-
$
|
|
39
|
-
|
|
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>;
|
package/src/operators/index.ts
CHANGED
|
@@ -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
|
};
|
package/src/operators/ne.ts
CHANGED
|
@@ -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
|
-
|
|
8
|
-
|
|
12
|
+
[Op.ne]: null,
|
|
13
|
+
}
|
|
9
14
|
: {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
[Op.or]: {
|
|
16
|
+
[Op.ne]: val,
|
|
17
|
+
[Op.is]: null,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
15
20
|
},
|
|
16
21
|
} as Record<string, any>;
|
package/src/options-parser.ts
CHANGED
|
@@ -109,27 +109,48 @@ export class OptionsParser {
|
|
|
109
109
|
return filterParams;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
protected inheritFromSubQuery(): any {
|
|
113
|
-
|
|
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
|
-
|
|
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
|
}
|