@nocobase/database 1.7.0-beta.11 → 1.7.0-beta.13

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.
@@ -31,8 +31,8 @@ export declare class BelongsToArrayAssociation {
31
31
  targetKey: string;
32
32
  });
33
33
  get target(): import("sequelize/types").ModelStatic<Model<any, any>>;
34
- generateInclude(): {
35
- on: import("sequelize/types/utils").Literal;
34
+ generateInclude(parentAs?: string): {
35
+ on: void;
36
36
  };
37
37
  update(instance: Model, value: any, options?: UpdateAssociationOptions): Promise<void>;
38
38
  }
@@ -78,19 +78,18 @@ const _BelongsToArrayAssociation = class _BelongsToArrayAssociation {
78
78
  get target() {
79
79
  return this.db.getModel(this.targetName);
80
80
  }
81
- generateInclude() {
82
- if (this.db.sequelize.getDialect() !== "postgres") {
83
- throw new Error("Filtering by many to many (array) associations is only supported on postgres");
84
- }
81
+ generateInclude(parentAs) {
85
82
  const targetCollection = this.db.getCollection(this.targetName);
86
83
  const targetField = targetCollection.getField(this.targetKey);
87
84
  const sourceCollection = this.db.getCollection(this.source.name);
88
85
  const foreignField = sourceCollection.getField(this.foreignKey);
89
86
  const queryInterface = this.db.sequelize.getQueryInterface();
90
- const left = queryInterface.quoteIdentifiers(`${this.as}.${targetField.columnName()}`);
91
- const right = queryInterface.quoteIdentifiers(`${this.source.collection.name}.${foreignField.columnName()}`);
87
+ const asLeft = parentAs ? `${parentAs}->${this.as}` : this.as;
88
+ const asRight = parentAs || this.source.collection.name;
89
+ const left = queryInterface.quoteIdentifiers(`${asLeft}.${targetField.columnName()}`);
90
+ const right = queryInterface.quoteIdentifiers(`${asRight}.${foreignField.columnName()}`);
92
91
  return {
93
- on: this.db.sequelize.literal(`${left}=any(${right})`)
92
+ on: this.db.queryInterface.generateJoinOnForJSONArray(left, right)
94
93
  };
95
94
  }
96
95
  async update(instance, value, options = {}) {
@@ -85,6 +85,25 @@ const queryParentSQL = /* @__PURE__ */ __name((options) => {
85
85
  )
86
86
  SELECT ${q(targetKeyField)} AS ${q(targetKey)}, ${q(foreignKeyField)} AS ${q(foreignKey)} FROM cte`;
87
87
  }, "queryParentSQL");
88
+ const processIncludes = /* @__PURE__ */ __name((includes, model, parentAs = "") => {
89
+ includes.forEach((include, index) => {
90
+ const association = model.associations[include.association];
91
+ if (association == null ? void 0 : association.generateInclude) {
92
+ includes[index] = {
93
+ ...include,
94
+ ...association.generateInclude(parentAs)
95
+ };
96
+ }
97
+ if (include.include && Array.isArray(include.include) && include.include.length > 0) {
98
+ const nextModel = association == null ? void 0 : association.target;
99
+ if (!nextModel) {
100
+ return;
101
+ }
102
+ processIncludes(include.include, nextModel, parentAs ? `${parentAs}->${association.as}` : association.as);
103
+ }
104
+ });
105
+ return includes;
106
+ }, "processIncludes");
88
107
  const _EagerLoadingTree = class _EagerLoadingTree {
89
108
  root;
90
109
  db;
@@ -208,22 +227,13 @@ const _EagerLoadingTree = class _EagerLoadingTree {
208
227
  if (!primaryKeyField) {
209
228
  throw new Error(`Model ${node.model.name} does not have primary key`);
210
229
  }
211
- includeForFilter.forEach((include, index) => {
212
- const association = node.model.associations[include.association];
213
- if ((association == null ? void 0 : association.associationType) == "BelongsToArray") {
214
- includeForFilter[index] = {
215
- ...include,
216
- ...association.generateInclude()
217
- };
218
- }
219
- });
220
230
  const ids2 = (await node.model.findAll({
221
231
  ...this.rootQueryOptions,
222
232
  includeIgnoreAttributes: false,
223
233
  attributes: [primaryKeyField],
224
234
  group: `${node.model.name}.${primaryKeyField}`,
225
235
  transaction,
226
- include: includeForFilter
236
+ include: processIncludes(includeForFilter, node.model)
227
237
  })).map((row) => {
228
238
  return { row, pk: row[primaryKeyField] };
229
239
  });
@@ -13,7 +13,7 @@ declare class DatetimeNoTzTypeMySQL extends DataTypes.ABSTRACT {
13
13
  }
14
14
  export declare class DatetimeNoTzField extends Field {
15
15
  get dataType(): typeof DatetimeNoTzTypeMySQL;
16
- init(): void;
16
+ beforeSave: (instance: any, options: any) => Promise<void>;
17
17
  additionalSequelizeOptions(): {};
18
18
  bind(): void;
19
19
  unbind(): void;
@@ -61,48 +61,49 @@ const _DatetimeNoTzField = class _DatetimeNoTzField extends import_field.Field {
61
61
  if (this.database.isMySQLCompatibleDialect()) {
62
62
  return DatetimeNoTzTypeMySQL;
63
63
  }
64
- return import_sequelize.DataTypes.STRING;
64
+ return import_sequelize.DataTypes.DATE;
65
65
  }
66
- init() {
66
+ beforeSave = /* @__PURE__ */ __name(async (instance, options) => {
67
67
  const { name, defaultToCurrentTime, onUpdateToCurrentTime } = this.options;
68
- this.beforeSave = async (instance, options) => {
69
- const value = instance.get(name);
70
- if (!value && instance.isNewRecord && defaultToCurrentTime) {
71
- instance.set(name, /* @__PURE__ */ new Date());
72
- return;
73
- }
74
- if (onUpdateToCurrentTime) {
75
- instance.set(name, /* @__PURE__ */ new Date());
76
- return;
77
- }
78
- };
79
- }
68
+ const value = instance.get(name);
69
+ if (!value && instance.isNewRecord && defaultToCurrentTime) {
70
+ instance.set(name, /* @__PURE__ */ new Date());
71
+ return;
72
+ }
73
+ if (onUpdateToCurrentTime) {
74
+ instance.set(name, /* @__PURE__ */ new Date());
75
+ return;
76
+ }
77
+ }, "beforeSave");
80
78
  additionalSequelizeOptions() {
81
79
  const { name } = this.options;
82
80
  const timezone = this.database.options.rawTimezone || "+00:00";
83
81
  const isPg = this.database.inDialect("postgres");
82
+ const isMySQLCompatibleDialect = this.database.isMySQLCompatibleDialect();
84
83
  return {
85
84
  get() {
86
85
  const val = this.getDataValue(name);
87
86
  if (val instanceof Date) {
88
- if (isPg) {
89
- return (0, import_moment.default)(val).format("YYYY-MM-DD HH:mm:ss");
90
- }
91
- const momentVal = (0, import_moment.default)(val).utcOffset(timezone);
87
+ const momentVal = (0, import_moment.default)(val);
92
88
  return momentVal.format("YYYY-MM-DD HH:mm:ss");
93
89
  }
94
90
  return val;
95
91
  },
96
92
  set(val) {
97
- if (typeof val === "string" && isIso8601(val)) {
98
- const momentVal = (0, import_moment.default)(val).utcOffset(timezone);
99
- val = momentVal.format("YYYY-MM-DD HH:mm:ss");
93
+ if (val == null) {
94
+ return this.setDataValue(name, null);
95
+ }
96
+ const dateOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset();
97
+ const momentVal = (0, import_moment.default)(val);
98
+ if (typeof val === "string" && isIso8601(val) || val instanceof Date) {
99
+ momentVal.utcOffset(timezone);
100
+ momentVal.utcOffset(-dateOffset, true);
100
101
  }
101
- if (val && val instanceof Date) {
102
- const momentVal = (0, import_moment.default)(val).utcOffset(timezone);
103
- val = momentVal.format("YYYY-MM-DD HH:mm:ss");
102
+ if (isMySQLCompatibleDialect) {
103
+ momentVal.millisecond(0);
104
104
  }
105
- return this.setDataValue(name, val);
105
+ const date = momentVal.toDate();
106
+ return this.setDataValue(name, date);
106
107
  }
107
108
  };
108
109
  }
@@ -10,8 +10,12 @@ import { DataTypes } from 'sequelize';
10
10
  import { BaseColumnFieldOptions, Field } from './field';
11
11
  export declare class StringField extends Field {
12
12
  get dataType(): DataTypes.StringDataTypeConstructor | DataTypes.StringDataType;
13
+ additionalSequelizeOptions(): {
14
+ set(value: any): void;
15
+ };
13
16
  }
14
17
  export interface StringFieldOptions extends BaseColumnFieldOptions {
15
18
  type: 'string';
16
19
  length?: number;
20
+ trim?: boolean;
17
21
  }
@@ -39,6 +39,14 @@ const _StringField = class _StringField extends import_field.Field {
39
39
  }
40
40
  return import_sequelize.DataTypes.STRING;
41
41
  }
42
+ additionalSequelizeOptions() {
43
+ const { name, trim } = this.options;
44
+ return {
45
+ set(value) {
46
+ this.setDataValue(name, trim ? value == null ? void 0 : value.trim() : value);
47
+ }
48
+ };
49
+ }
42
50
  };
43
51
  __name(_StringField, "StringField");
44
52
  let StringField = _StringField;
@@ -11,8 +11,12 @@ import { BaseColumnFieldOptions, Field } from './field';
11
11
  export declare class TextField extends Field {
12
12
  get dataType(): DataTypes.TextDataTypeConstructor | DataTypes.TextDataType;
13
13
  init(): void;
14
+ additionalSequelizeOptions(): {
15
+ set(value: any): void;
16
+ };
14
17
  }
15
18
  export interface TextFieldOptions extends BaseColumnFieldOptions {
16
19
  type: 'text';
17
20
  length?: 'tiny' | 'medium' | 'long';
21
+ trim?: boolean;
18
22
  }
@@ -44,6 +44,14 @@ const _TextField = class _TextField extends import_field.Field {
44
44
  this.options.defaultValue = null;
45
45
  }
46
46
  }
47
+ additionalSequelizeOptions() {
48
+ const { name, trim } = this.options;
49
+ return {
50
+ set(value) {
51
+ this.setDataValue(name, trim ? value == null ? void 0 : value.trim() : value);
52
+ }
53
+ };
54
+ }
47
55
  };
48
56
  __name(_TextField, "TextField");
49
57
  let TextField = _TextField;
@@ -41,4 +41,5 @@ export default class MysqlQueryInterface extends QueryInterface {
41
41
  currentVal: number;
42
42
  transaction?: Transaction;
43
43
  }): Promise<void>;
44
+ generateJoinOnForJSONArray(left: string, right: string): import("sequelize/types/utils").Literal;
44
45
  }
@@ -129,6 +129,9 @@ const _MysqlQueryInterface = class _MysqlQueryInterface extends import_query_int
129
129
  await this.db.sequelize.query(sql, { transaction });
130
130
  }
131
131
  }
132
+ generateJoinOnForJSONArray(left, right) {
133
+ return this.db.sequelize.literal(`JSON_CONTAINS(${right}, JSON_ARRAY(${left}))`);
134
+ }
132
135
  };
133
136
  __name(_MysqlQueryInterface, "MysqlQueryInterface");
134
137
  let MysqlQueryInterface = _MysqlQueryInterface;
@@ -38,4 +38,5 @@ export default class PostgresQueryInterface extends QueryInterface {
38
38
  };
39
39
  }>;
40
40
  showTableDefinition(tableInfo: TableInfo): Promise<any>;
41
+ generateJoinOnForJSONArray(left: string, right: string): import("sequelize/types/utils").Literal;
41
42
  }
@@ -202,6 +202,9 @@ $BODY$
202
202
  );
203
203
  return res[0]["show_create_table"];
204
204
  }
205
+ generateJoinOnForJSONArray(left, right) {
206
+ return this.db.sequelize.literal(`${left}=any(${right})`);
207
+ }
205
208
  };
206
209
  __name(_PostgresQueryInterface, "PostgresQueryInterface");
207
210
  let PostgresQueryInterface = _PostgresQueryInterface;
@@ -49,4 +49,5 @@ export default abstract class QueryInterface {
49
49
  transaction?: Transaction;
50
50
  }): Promise<void>;
51
51
  quoteIdentifier(identifier: string): any;
52
+ generateJoinOnForJSONArray(left: string, right: string): void;
52
53
  }
@@ -57,6 +57,10 @@ const _QueryInterface = class _QueryInterface {
57
57
  quoteIdentifier(identifier) {
58
58
  return this.db.sequelize.getQueryInterface().queryGenerator.quoteIdentifier(identifier);
59
59
  }
60
+ generateJoinOnForJSONArray(left, right) {
61
+ const dialect = this.db.sequelize.getDialect();
62
+ throw new Error(`Filtering by many to many (array) associations is not supported on ${dialect}`);
63
+ }
60
64
  };
61
65
  __name(_QueryInterface, "QueryInterface");
62
66
  let QueryInterface = _QueryInterface;
@@ -41,4 +41,5 @@ export default class SqliteQueryInterface extends QueryInterface {
41
41
  currentVal: number;
42
42
  transaction?: Transaction;
43
43
  }): Promise<void>;
44
+ generateJoinOnForJSONArray(left: string, right: string): import("sequelize/types/utils").Literal;
44
45
  }
@@ -136,6 +136,9 @@ const _SqliteQueryInterface = class _SqliteQueryInterface extends import_query_i
136
136
  WHERE name = '${tableName}';`;
137
137
  await this.db.sequelize.query(sql, { transaction });
138
138
  }
139
+ generateJoinOnForJSONArray(left, right) {
140
+ return this.db.sequelize.literal(`${left} in (SELECT value from json_each(${right}))`);
141
+ }
139
142
  };
140
143
  __name(_SqliteQueryInterface, "SqliteQueryInterface");
141
144
  let SqliteQueryInterface = _SqliteQueryInterface;
@@ -80,7 +80,13 @@ const _HasManyRepository = class _HasManyRepository extends import_multiple_rela
80
80
  if (options && options["filter"]) {
81
81
  const filterResult = this.parseFilter(options["filter"], options);
82
82
  if (filterResult.include && filterResult.include.length > 0) {
83
- return await this.destroyByFilter(options["filter"], transaction2);
83
+ return await this.destroyByFilter(
84
+ {
85
+ filter: options["filter"],
86
+ filterByTk: options["filterByTk"]
87
+ },
88
+ transaction2
89
+ );
84
90
  }
85
91
  where.push(filterResult.where);
86
92
  }
@@ -18,7 +18,10 @@ export declare abstract class MultipleRelationRepository extends RelationReposit
18
18
  remove(options: TargetKey | TargetKey[] | AssociatedOptions): Promise<void>;
19
19
  update(options?: UpdateOptions): Promise<any>;
20
20
  destroy(options?: TargetKey | DestroyOptions): Promise<boolean>;
21
- protected destroyByFilter(filter: Filter, transaction?: Transaction): Promise<boolean>;
21
+ protected destroyByFilter(options: {
22
+ filter?: Filter;
23
+ filterByTk?: TargetKey | TargetKey[];
24
+ }, transaction?: Transaction): Promise<boolean>;
22
25
  protected filterHasInclude(filter: Filter, options?: any): boolean;
23
26
  protected accessors(): MultiAssociationAccessors;
24
27
  updateOrCreate(options: FirstOrCreateOptions): Promise<any>;
@@ -174,9 +174,9 @@ const _MultipleRelationRepository = class _MultipleRelationRepository extends im
174
174
  async destroy(options) {
175
175
  return false;
176
176
  }
177
- async destroyByFilter(filter, transaction2) {
177
+ async destroyByFilter(options, transaction2) {
178
178
  const instances = await this.find({
179
- filter,
179
+ ...options,
180
180
  transaction: transaction2
181
181
  });
182
182
  return await this.destroy({
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@nocobase/database",
3
- "version": "1.7.0-beta.11",
3
+ "version": "1.7.0-beta.13",
4
4
  "description": "",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./lib/index.d.ts",
7
7
  "license": "AGPL-3.0",
8
8
  "dependencies": {
9
- "@nocobase/logger": "1.7.0-beta.11",
10
- "@nocobase/utils": "1.7.0-beta.11",
9
+ "@nocobase/logger": "1.7.0-beta.13",
10
+ "@nocobase/utils": "1.7.0-beta.13",
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": "a5227915c2ef65501965dbff8be0e0f51039296e"
41
+ "gitHead": "e5c152b727dbedd54b67e450b11523df5acc51c6"
42
42
  }