@nocobase/database 2.3.0-beta.6 → 2.4.0-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.d.ts +2 -0
- package/lib/collection.js +24 -6
- package/lib/fields/field.js +20 -2
- package/lib/model.js +4 -0
- package/lib/operators/string.js +17 -13
- package/lib/sync-runner.d.ts +4 -0
- package/lib/sync-runner.js +45 -1
- package/package.json +4 -4
package/lib/collection.d.ts
CHANGED
|
@@ -136,6 +136,8 @@ export declare class Collection<TModelAttributes extends {} = any, TCreationAttr
|
|
|
136
136
|
setSortable(sortable: any): void;
|
|
137
137
|
updateField(name: string, options: FieldOptions): void;
|
|
138
138
|
private normalizeFieldName;
|
|
139
|
+
private findAttributeByIndexField;
|
|
140
|
+
private normalizeIndexFields;
|
|
139
141
|
addIndex(index: string | string[] | {
|
|
140
142
|
fields: string[];
|
|
141
143
|
unique?: boolean;
|
package/lib/collection.js
CHANGED
|
@@ -607,6 +607,20 @@ const _Collection = class _Collection extends import_events.EventEmitter {
|
|
|
607
607
|
}
|
|
608
608
|
return Array.isArray(val) ? val.map((v) => (0, import_utils.snakeCase)(v)) : (0, import_utils.snakeCase)(val);
|
|
609
609
|
}
|
|
610
|
+
findAttributeByIndexField(field) {
|
|
611
|
+
var _a;
|
|
612
|
+
const attributes = this.model.getAttributes();
|
|
613
|
+
const normalizedField = this.normalizeFieldName(field);
|
|
614
|
+
return (_a = Object.entries(attributes).find(([name, attribute]) => {
|
|
615
|
+
return this.normalizeFieldName(name) === normalizedField || attribute.field === normalizedField;
|
|
616
|
+
})) == null ? void 0 : _a[1];
|
|
617
|
+
}
|
|
618
|
+
normalizeIndexFields(fields) {
|
|
619
|
+
return fields.map((field) => {
|
|
620
|
+
const attribute = this.findAttributeByIndexField(field);
|
|
621
|
+
return (attribute == null ? void 0 : attribute.field) || this.normalizeFieldName(field);
|
|
622
|
+
});
|
|
623
|
+
}
|
|
610
624
|
addIndex(index) {
|
|
611
625
|
if (!index) {
|
|
612
626
|
return;
|
|
@@ -628,19 +642,22 @@ const _Collection = class _Collection extends import_events.EventEmitter {
|
|
|
628
642
|
indexItem = index;
|
|
629
643
|
indexName = index.fields;
|
|
630
644
|
}
|
|
631
|
-
|
|
645
|
+
const normalizedIndexName = this.normalizeIndexFields(indexName);
|
|
646
|
+
const normalizedPrimaryKeyAttributes = this.normalizeIndexFields(this.model.primaryKeyAttributes);
|
|
647
|
+
if (import_lodash.default.isEqual(normalizedPrimaryKeyAttributes, normalizedIndexName)) {
|
|
632
648
|
return;
|
|
633
649
|
}
|
|
634
|
-
const name =
|
|
635
|
-
if (name.startsWith(`${
|
|
650
|
+
const name = normalizedPrimaryKeyAttributes.join(",");
|
|
651
|
+
if (name.startsWith(`${normalizedIndexName.join(",")},`)) {
|
|
636
652
|
return;
|
|
637
653
|
}
|
|
638
654
|
for (const item of indexes) {
|
|
639
|
-
|
|
655
|
+
const normalizedFields = this.normalizeIndexFields(item.fields);
|
|
656
|
+
if (import_lodash.default.isEqual(normalizedFields, normalizedIndexName)) {
|
|
640
657
|
return;
|
|
641
658
|
}
|
|
642
|
-
const name2 =
|
|
643
|
-
if (name2.startsWith(`${
|
|
659
|
+
const name2 = normalizedFields.join(",");
|
|
660
|
+
if (name2.startsWith(`${normalizedIndexName.join(",")},`)) {
|
|
644
661
|
return;
|
|
645
662
|
}
|
|
646
663
|
}
|
|
@@ -675,6 +692,7 @@ const _Collection = class _Collection extends import_events.EventEmitter {
|
|
|
675
692
|
const attributes = {};
|
|
676
693
|
for (const [name, field] of Object.entries(this.model.getAttributes())) {
|
|
677
694
|
attributes[this.normalizeFieldName(name)] = field;
|
|
695
|
+
attributes[field.field] = field;
|
|
678
696
|
}
|
|
679
697
|
this.model._indexes = import_lodash.default.uniqBy(
|
|
680
698
|
indexes.filter((item) => {
|
package/lib/fields/field.js
CHANGED
|
@@ -41,7 +41,8 @@ __export(field_exports, {
|
|
|
41
41
|
});
|
|
42
42
|
module.exports = __toCommonJS(field_exports);
|
|
43
43
|
var import_lodash = __toESM(require("lodash"));
|
|
44
|
-
var import_utils = require("
|
|
44
|
+
var import_utils = require("@nocobase/utils");
|
|
45
|
+
var import_utils2 = require("../utils");
|
|
45
46
|
const _Field = class _Field {
|
|
46
47
|
options;
|
|
47
48
|
context;
|
|
@@ -94,7 +95,7 @@ const _Field = class _Field {
|
|
|
94
95
|
return this.options.field;
|
|
95
96
|
}
|
|
96
97
|
if (this.database.options.underscored) {
|
|
97
|
-
return (0,
|
|
98
|
+
return (0, import_utils2.snakeCase)(this.name);
|
|
98
99
|
}
|
|
99
100
|
return this.name;
|
|
100
101
|
}
|
|
@@ -156,6 +157,23 @@ const _Field = class _Field {
|
|
|
156
157
|
Object.assign(opts, { type: this.database.sequelize.normalizeDataType(this.dataType) });
|
|
157
158
|
}
|
|
158
159
|
Object.assign(opts, this.additionalSequelizeOptions());
|
|
160
|
+
if (this.options.interface === "richText") {
|
|
161
|
+
const { name } = this;
|
|
162
|
+
const originalSetter = opts.set;
|
|
163
|
+
const sanitizeValue = /* @__PURE__ */ __name((value) => typeof value === "string" ? (0, import_utils.sanitizeRichTextHtml)(value) : value, "sanitizeValue");
|
|
164
|
+
opts.set = function(value) {
|
|
165
|
+
if (typeof originalSetter === "function") {
|
|
166
|
+
originalSetter.call(this, sanitizeValue(value));
|
|
167
|
+
const currentValue = this.getDataValue(name);
|
|
168
|
+
const sanitizedValue = sanitizeValue(currentValue);
|
|
169
|
+
if (!Object.is(sanitizedValue, currentValue)) {
|
|
170
|
+
this.setDataValue(name, sanitizedValue);
|
|
171
|
+
}
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
this.setDataValue(name, sanitizeValue(value));
|
|
175
|
+
};
|
|
176
|
+
}
|
|
159
177
|
return opts;
|
|
160
178
|
}
|
|
161
179
|
additionalSequelizeOptions() {
|
package/lib/model.js
CHANGED
|
@@ -43,6 +43,7 @@ __export(model_exports, {
|
|
|
43
43
|
});
|
|
44
44
|
module.exports = __toCommonJS(model_exports);
|
|
45
45
|
var import_lodash = __toESM(require("lodash"));
|
|
46
|
+
var import_utils = require("@nocobase/utils");
|
|
46
47
|
var import_sequelize = require("sequelize");
|
|
47
48
|
var import_sync_runner = require("./sync-runner");
|
|
48
49
|
const _ = import_lodash.default;
|
|
@@ -65,6 +66,9 @@ const _Model = class _Model extends import_sequelize.Model {
|
|
|
65
66
|
} else {
|
|
66
67
|
result[key] = values[key];
|
|
67
68
|
}
|
|
69
|
+
if ((field == null ? void 0 : field.options.interface) === "richText" && typeof result[key] === "string") {
|
|
70
|
+
result[key] = (0, import_utils.sanitizeRichTextHtml)(result[key]);
|
|
71
|
+
}
|
|
68
72
|
}
|
|
69
73
|
return result;
|
|
70
74
|
}
|
package/lib/operators/string.js
CHANGED
|
@@ -32,7 +32,11 @@ __export(string_exports, {
|
|
|
32
32
|
module.exports = __toCommonJS(string_exports);
|
|
33
33
|
var import_sequelize = require("sequelize");
|
|
34
34
|
var import_utils = require("./utils");
|
|
35
|
-
function escapeLike(value) {
|
|
35
|
+
function escapeLike(value, ctx) {
|
|
36
|
+
if (ctx.db.sequelize.getDialect() === "mssql") {
|
|
37
|
+
const escapedValue = value.replaceAll("[", "[[]");
|
|
38
|
+
return escapedValue.replace(/[%_]/g, (character) => `[${character}]`);
|
|
39
|
+
}
|
|
36
40
|
return value.replace(/[_%]/g, "\\$&");
|
|
37
41
|
}
|
|
38
42
|
__name(escapeLike, "escapeLike");
|
|
@@ -85,13 +89,13 @@ var string_default = {
|
|
|
85
89
|
}
|
|
86
90
|
if (Array.isArray(value)) {
|
|
87
91
|
const conditions = value.map(
|
|
88
|
-
(item) => getFieldExpression(`%${escapeLike(item)}%`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE")
|
|
92
|
+
(item) => getFieldExpression(`%${escapeLike(item, ctx)}%`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE")
|
|
89
93
|
);
|
|
90
94
|
return {
|
|
91
95
|
[import_sequelize.Op.or]: conditions
|
|
92
96
|
};
|
|
93
97
|
}
|
|
94
|
-
return getFieldExpression(`%${escapeLike(value)}%`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE");
|
|
98
|
+
return getFieldExpression(`%${escapeLike(value, ctx)}%`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE");
|
|
95
99
|
},
|
|
96
100
|
$notIncludes(value, ctx) {
|
|
97
101
|
if (value === null) {
|
|
@@ -101,56 +105,56 @@ var string_default = {
|
|
|
101
105
|
}
|
|
102
106
|
if (Array.isArray(value)) {
|
|
103
107
|
const conditions = value.map(
|
|
104
|
-
(item) => getFieldExpression(`%${escapeLike(item)}%`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE")
|
|
108
|
+
(item) => getFieldExpression(`%${escapeLike(item, ctx)}%`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE")
|
|
105
109
|
);
|
|
106
110
|
return {
|
|
107
111
|
[import_sequelize.Op.and]: conditions
|
|
108
112
|
};
|
|
109
113
|
}
|
|
110
|
-
return getFieldExpression(`%${escapeLike(value)}%`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE");
|
|
114
|
+
return getFieldExpression(`%${escapeLike(value, ctx)}%`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE");
|
|
111
115
|
},
|
|
112
116
|
$startsWith(value, ctx) {
|
|
113
117
|
if (Array.isArray(value)) {
|
|
114
118
|
const conditions = value.map(
|
|
115
|
-
(item) => getFieldExpression(`${escapeLike(item)}%`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE")
|
|
119
|
+
(item) => getFieldExpression(`${escapeLike(item, ctx)}%`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE")
|
|
116
120
|
);
|
|
117
121
|
return {
|
|
118
122
|
[import_sequelize.Op.or]: conditions
|
|
119
123
|
};
|
|
120
124
|
}
|
|
121
|
-
return getFieldExpression(`${escapeLike(value)}%`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE");
|
|
125
|
+
return getFieldExpression(`${escapeLike(value, ctx)}%`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE");
|
|
122
126
|
},
|
|
123
127
|
$notStartsWith(value, ctx) {
|
|
124
128
|
if (Array.isArray(value)) {
|
|
125
129
|
const conditions = value.map(
|
|
126
|
-
(item) => getFieldExpression(`${escapeLike(item)}%`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE")
|
|
130
|
+
(item) => getFieldExpression(`${escapeLike(item, ctx)}%`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE")
|
|
127
131
|
);
|
|
128
132
|
return {
|
|
129
133
|
[import_sequelize.Op.and]: conditions
|
|
130
134
|
};
|
|
131
135
|
}
|
|
132
|
-
return getFieldExpression(`${escapeLike(value)}%`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE");
|
|
136
|
+
return getFieldExpression(`${escapeLike(value, ctx)}%`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE");
|
|
133
137
|
},
|
|
134
138
|
$endWith(value, ctx) {
|
|
135
139
|
if (Array.isArray(value)) {
|
|
136
140
|
const conditions = value.map(
|
|
137
|
-
(item) => getFieldExpression(`%${escapeLike(item)}`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE")
|
|
141
|
+
(item) => getFieldExpression(`%${escapeLike(item, ctx)}`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE")
|
|
138
142
|
);
|
|
139
143
|
return {
|
|
140
144
|
[import_sequelize.Op.or]: conditions
|
|
141
145
|
};
|
|
142
146
|
}
|
|
143
|
-
return getFieldExpression(`%${escapeLike(value)}`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE");
|
|
147
|
+
return getFieldExpression(`%${escapeLike(value, ctx)}`, ctx, (0, import_utils.isPg)(ctx) ? "ILIKE" : "LIKE");
|
|
144
148
|
},
|
|
145
149
|
$notEndWith(value, ctx) {
|
|
146
150
|
if (Array.isArray(value)) {
|
|
147
151
|
const conditions = value.map(
|
|
148
|
-
(item) => getFieldExpression(`%${escapeLike(item)}`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE")
|
|
152
|
+
(item) => getFieldExpression(`%${escapeLike(item, ctx)}`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE")
|
|
149
153
|
);
|
|
150
154
|
return {
|
|
151
155
|
[import_sequelize.Op.and]: conditions
|
|
152
156
|
};
|
|
153
157
|
}
|
|
154
|
-
return getFieldExpression(`%${escapeLike(value)}`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE");
|
|
158
|
+
return getFieldExpression(`%${escapeLike(value, ctx)}`, ctx, (0, import_utils.isPg)(ctx) ? "NOT ILIKE" : "NOT LIKE");
|
|
155
159
|
}
|
|
156
160
|
};
|
package/lib/sync-runner.d.ts
CHANGED
|
@@ -35,6 +35,10 @@ export declare class SyncRunner {
|
|
|
35
35
|
isParentColumn(columnName: string, options: any): Promise<boolean>;
|
|
36
36
|
removeUnusedColumns(columns: any, options: any): Promise<void>;
|
|
37
37
|
findAttributeByColumnName(columnName: string): any;
|
|
38
|
+
private getIndexFields;
|
|
39
|
+
private indexExists;
|
|
40
|
+
private deduplicateIndexes;
|
|
41
|
+
private filterExistingIndexesBeforeSync;
|
|
38
42
|
performSync(options: any): Promise<any>;
|
|
39
43
|
handleZeroColumnModel(options: any): Promise<void>;
|
|
40
44
|
handleSchema(options: any): Promise<void>;
|
package/lib/sync-runner.js
CHANGED
|
@@ -286,8 +286,52 @@ const _SyncRunner = class _SyncRunner {
|
|
|
286
286
|
return attribute.field == columnName;
|
|
287
287
|
});
|
|
288
288
|
}
|
|
289
|
+
getIndexFields(index) {
|
|
290
|
+
return (index.fields || []).map((field) => {
|
|
291
|
+
if (typeof field === "string") {
|
|
292
|
+
return field;
|
|
293
|
+
}
|
|
294
|
+
return field.attribute || field.name;
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
indexExists(modelIndex, existingIndexes) {
|
|
298
|
+
const modelIndexFields = this.getIndexFields(modelIndex);
|
|
299
|
+
return existingIndexes.some((existingIndex) => {
|
|
300
|
+
if (modelIndex.name && existingIndex.name === modelIndex.name) {
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
return Boolean(modelIndex.unique) === Boolean(existingIndex.unique) && JSON.stringify(modelIndexFields) === JSON.stringify(this.getIndexFields(existingIndex));
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
deduplicateIndexes(indexes) {
|
|
307
|
+
return indexes.filter((index, indexPosition) => {
|
|
308
|
+
return indexes.findIndex((item) => this.indexExists(index, [item])) === indexPosition;
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
async filterExistingIndexesBeforeSync(options) {
|
|
312
|
+
const indexes = this.model["_indexes"];
|
|
313
|
+
if (!(indexes == null ? void 0 : indexes.length)) {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const deduplicatedIndexes = this.deduplicateIndexes(indexes);
|
|
317
|
+
this.model["_indexes"] = deduplicatedIndexes;
|
|
318
|
+
try {
|
|
319
|
+
const existingIndexes = await this.queryInterface.showIndex(this.tableName, options);
|
|
320
|
+
this.model["_indexes"] = deduplicatedIndexes.filter((index) => !this.indexExists(index, existingIndexes));
|
|
321
|
+
} catch (error) {
|
|
322
|
+
}
|
|
323
|
+
}
|
|
289
324
|
async performSync(options) {
|
|
290
|
-
|
|
325
|
+
if (this.collection.isInherited()) {
|
|
326
|
+
return await import_inherited_sync_runner.InheritedSyncRunner.syncInheritModel(this.model, options);
|
|
327
|
+
}
|
|
328
|
+
const indexes = this.model["_indexes"];
|
|
329
|
+
try {
|
|
330
|
+
await this.filterExistingIndexesBeforeSync(options);
|
|
331
|
+
return await import_sequelize.Model.sync.call(this.model, options);
|
|
332
|
+
} finally {
|
|
333
|
+
this.model["_indexes"] = indexes;
|
|
334
|
+
}
|
|
291
335
|
}
|
|
292
336
|
async handleZeroColumnModel(options) {
|
|
293
337
|
if (Object.keys(this.model.tableAttributes).length === 0) {
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/database",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0-alpha.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"types": "./lib/index.d.ts",
|
|
7
7
|
"license": "Apache-2.0",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@nocobase/logger": "2.
|
|
10
|
-
"@nocobase/utils": "2.
|
|
9
|
+
"@nocobase/logger": "2.4.0-alpha.2",
|
|
10
|
+
"@nocobase/utils": "2.4.0-alpha.2",
|
|
11
11
|
"async-mutex": "^0.3.2",
|
|
12
12
|
"chalk": "^4.1.1",
|
|
13
13
|
"cron-parser": "4.4.0",
|
|
@@ -38,5 +38,5 @@
|
|
|
38
38
|
"url": "git+https://github.com/nocobase/nocobase.git",
|
|
39
39
|
"directory": "packages/database"
|
|
40
40
|
},
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "706bb81047a05f39f2ddce781ebcafeea4ec9cd5"
|
|
42
42
|
}
|