@tachybase/database 0.23.22 → 0.23.40

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.
@@ -107,17 +107,17 @@ const _EagerLoadingTree = class _EagerLoadingTree {
107
107
  children: [],
108
108
  includeOption: include.options || {}
109
109
  });
110
- if (associationType == "HasOne" || associationType == "HasMany") {
110
+ if (associationType === "HasOne" || associationType === "HasMany") {
111
111
  const { sourceKey, foreignKey } = association;
112
112
  pushAttribute(eagerLoadingTreeParent, sourceKey);
113
113
  pushAttribute(child, foreignKey);
114
114
  }
115
- if (associationType == "BelongsTo") {
115
+ if (associationType === "BelongsTo") {
116
116
  const { targetKey, foreignKey } = association;
117
117
  pushAttribute(eagerLoadingTreeParent, foreignKey);
118
118
  pushAttribute(child, targetKey);
119
119
  }
120
- if (associationType == "BelongsToMany") {
120
+ if (associationType === "BelongsToMany") {
121
121
  const { sourceKey } = association;
122
122
  pushAttribute(eagerLoadingTreeParent, sourceKey);
123
123
  }
@@ -144,21 +144,40 @@ const _EagerLoadingTree = class _EagerLoadingTree {
144
144
  return order;
145
145
  }, "orderOption");
146
146
  const loadRecursive = /* @__PURE__ */ __name(async (node, ids = []) => {
147
- var _a;
147
+ var _a, _b;
148
148
  let instances = [];
149
149
  if (!node.parent) {
150
150
  const rootInclude = ((_a = this.rootQueryOptions) == null ? void 0 : _a.include) || node.includeOption;
151
151
  const includeForFilter = rootInclude.filter((include) => {
152
- var _a2, _b;
153
- return Object.keys(include.where || {}).length > 0 || ((_b = JSON.stringify((_a2 = this.rootQueryOptions) == null ? void 0 : _a2.filter)) == null ? void 0 : _b.includes(include.association));
152
+ var _a2, _b2;
153
+ return Object.keys(include.where || {}).length > 0 || ((_b2 = JSON.stringify((_a2 = this.rootQueryOptions) == null ? void 0 : _a2.filter)) == null ? void 0 : _b2.includes(include.association));
154
154
  });
155
+ const sortModelSet = /* @__PURE__ */ new Set();
156
+ if ((_b = this.rootQueryOptions) == null ? void 0 : _b.order) {
157
+ for (const order of this.rootQueryOptions.order) {
158
+ for (const field of order) {
159
+ if (field.as) {
160
+ sortModelSet.add(field.as);
161
+ }
162
+ }
163
+ }
164
+ }
165
+ const inCludeForSort = [];
166
+ for (const sortModel of sortModelSet) {
167
+ if (includeForFilter.find((v) => v.association === sortModel)) {
168
+ continue;
169
+ }
170
+ inCludeForSort.push({
171
+ association: sortModel
172
+ });
173
+ }
155
174
  const isBelongsToAssociationOnly = /* @__PURE__ */ __name((includes, model) => {
156
175
  for (const include of includes) {
157
176
  const association = model.associations[include.association];
158
177
  if (!association) {
159
178
  return false;
160
179
  }
161
- if (association.associationType != "BelongsTo") {
180
+ if (association.associationType !== "BelongsTo") {
162
181
  return false;
163
182
  }
164
183
  if (!isBelongsToAssociationOnly(include.include || [], association.target)) {
@@ -173,7 +192,7 @@ const _EagerLoadingTree = class _EagerLoadingTree {
173
192
  ...this.rootQueryOptions,
174
193
  attributes: node.attributes,
175
194
  distinct: true,
176
- include: includeForFilter,
195
+ include: includeForFilter.concat(inCludeForSort),
177
196
  transaction
178
197
  });
179
198
  } else {
@@ -187,7 +206,7 @@ const _EagerLoadingTree = class _EagerLoadingTree {
187
206
  attributes: [primaryKeyField],
188
207
  group: `${node.model.name}.${primaryKeyField}`,
189
208
  transaction,
190
- include: includeForFilter
209
+ include: includeForFilter.concat(inCludeForSort)
191
210
  })).map((row) => {
192
211
  return { row, pk: row[primaryKeyField] };
193
212
  });
@@ -222,7 +241,7 @@ const _EagerLoadingTree = class _EagerLoadingTree {
222
241
  });
223
242
  params = parser.toSequelizeParams();
224
243
  }
225
- if (associationType == "HasOne" || associationType == "HasMany") {
244
+ if (associationType === "HasOne" || associationType === "HasMany") {
226
245
  const foreignKey = association.foreignKey;
227
246
  const foreignKeyValues = node.parent.instances.map((instance) => instance.get(association.sourceKey));
228
247
  let where = { [foreignKey]: foreignKeyValues };
@@ -239,7 +258,7 @@ const _EagerLoadingTree = class _EagerLoadingTree {
239
258
  };
240
259
  instances = await node.model.findAll(findOptions);
241
260
  }
242
- if (associationType == "BelongsTo") {
261
+ if (associationType === "BelongsTo") {
243
262
  const foreignKey = association.foreignKey;
244
263
  const parentInstancesForeignKeyValues = node.parent.instances.map((instance) => instance.get(foreignKey));
245
264
  const collection2 = this.db.modelCollection.get(node.model);
@@ -272,7 +291,7 @@ const _EagerLoadingTree = class _EagerLoadingTree {
272
291
  });
273
292
  const setInstanceParent = /* @__PURE__ */ __name((instance) => {
274
293
  const parentInstance = parentInstances.find(
275
- (parentInstance2) => parentInstance2.get(targetKey) == instance.get(foreignKey)
294
+ (parentInstance2) => parentInstance2.get(targetKey) === instance.get(foreignKey)
276
295
  );
277
296
  if (!parentInstance) {
278
297
  return;
@@ -285,7 +304,7 @@ const _EagerLoadingTree = class _EagerLoadingTree {
285
304
  }
286
305
  }
287
306
  }
288
- if (associationType == "BelongsToMany") {
307
+ if (associationType === "BelongsToMany") {
289
308
  const foreignKeyValues = node.parent.instances.map((instance) => instance.get(association.sourceKey));
290
309
  const hasOneOptions = {
291
310
  as: "_pivot_",
@@ -329,19 +348,19 @@ const _EagerLoadingTree = class _EagerLoadingTree {
329
348
  const isEmpty = !children;
330
349
  parentInstance[key] = parentInstance.dataValues[key] = isEmpty ? null : children;
331
350
  } else {
332
- const isEmpty = !children || children.length == 0;
351
+ const isEmpty = !children || children.length === 0;
333
352
  parentInstance[key] = parentInstance.dataValues[key] = isEmpty ? [] : children;
334
353
  }
335
354
  }, "setParentAccessor");
336
- if (associationType == "HasMany" || associationType == "HasOne") {
355
+ if (associationType === "HasMany" || associationType === "HasOne") {
337
356
  const foreignKey = association.foreignKey;
338
357
  const sourceKey = association.sourceKey;
339
358
  for (const instance of node.instances) {
340
359
  const parentInstance = node.parent.instances.find(
341
- (parentInstance2) => parentInstance2.get(sourceKey) == instance.get(foreignKey)
360
+ (parentInstance2) => parentInstance2.get(sourceKey) === instance.get(foreignKey)
342
361
  );
343
362
  if (parentInstance) {
344
- if (associationType == "HasMany") {
363
+ if (associationType === "HasMany") {
345
364
  const children = parentInstance.getDataValue(association.as);
346
365
  if (!children) {
347
366
  parentInstance.setDataValue(association.as, [instance]);
@@ -349,26 +368,26 @@ const _EagerLoadingTree = class _EagerLoadingTree {
349
368
  children.push(instance);
350
369
  }
351
370
  }
352
- if (associationType == "HasOne") {
371
+ if (associationType === "HasOne") {
353
372
  const key = association.options.realAs || association.as;
354
373
  parentInstance[key] = parentInstance.dataValues[key] = instance;
355
374
  }
356
375
  }
357
376
  }
358
377
  }
359
- if (associationType == "BelongsTo") {
378
+ if (associationType === "BelongsTo") {
360
379
  const foreignKey = association.foreignKey;
361
380
  const targetKey = association.targetKey;
362
381
  for (const instance of node.instances) {
363
382
  const parentInstances = node.parent.instances.filter(
364
- (parentInstance) => parentInstance.get(foreignKey) == instance.get(targetKey)
383
+ (parentInstance) => parentInstance.get(foreignKey) === instance.get(targetKey)
365
384
  );
366
385
  for (const parentInstance of parentInstances) {
367
386
  parentInstance.setDataValue(association.as, instance);
368
387
  }
369
388
  }
370
389
  }
371
- if (associationType == "BelongsToMany") {
390
+ if (associationType === "BelongsToMany") {
372
391
  const sourceKey = association.sourceKey;
373
392
  const foreignKey = association.foreignKey;
374
393
  const as = association.oneFromTarget.as;
@@ -377,7 +396,7 @@ const _EagerLoadingTree = class _EagerLoadingTree {
377
396
  delete instance.dataValues["_pivot_"];
378
397
  delete instance["_pivot_"];
379
398
  const parentInstance = node.parent.instances.find(
380
- (parentInstance2) => parentInstance2.get(sourceKey) == instance.dataValues[as].get(foreignKey)
399
+ (parentInstance2) => parentInstance2.get(sourceKey) === instance.dataValues[as].get(foreignKey)
381
400
  );
382
401
  if (parentInstance) {
383
402
  const children = parentInstance.getDataValue(association.as);
@@ -405,7 +424,7 @@ const _EagerLoadingTree = class _EagerLoadingTree {
405
424
  dataCollection: this.db.modelCollection.get(node.model)
406
425
  });
407
426
  }
408
- if (((_a = node.association) == null ? void 0 : _a.as) == "_pivot_") {
427
+ if (((_a = node.association) == null ? void 0 : _a.as) === "_pivot_") {
409
428
  return;
410
429
  }
411
430
  const nodeRawAttributes = node.rawAttributes || [];
@@ -18,6 +18,7 @@ export declare class OptionsParser {
18
18
  static appendInheritInspectAttribute(include: any, collection: any): any;
19
19
  isAssociation(key: string): boolean;
20
20
  isAssociationPath(path: string): boolean;
21
+ filterByTkToWhereOption(): {};
21
22
  toSequelizeParams(): any;
22
23
  /**
23
24
  * parser sort options
@@ -81,20 +81,40 @@ const _OptionsParser = class _OptionsParser {
81
81
  isAssociationPath(path) {
82
82
  return this.isAssociation(path.split(".")[0]);
83
83
  }
84
+ filterByTkToWhereOption() {
85
+ var _a;
86
+ const filterByTkOption = (_a = this.options) == null ? void 0 : _a.filterByTk;
87
+ if (!filterByTkOption) {
88
+ return {};
89
+ }
90
+ if (import_lodash.default.isPlainObject(this.options.filterByTk)) {
91
+ const where = {};
92
+ for (const [key, value] of Object.entries(filterByTkOption)) {
93
+ where[key] = value;
94
+ }
95
+ return where;
96
+ }
97
+ const filterTargetKey = this.context.targetKey || this.collection.filterTargetKey;
98
+ if (Array.isArray(filterTargetKey)) {
99
+ throw new Error("multi filter target key value must be object");
100
+ }
101
+ return {
102
+ [filterTargetKey]: filterByTkOption
103
+ };
104
+ }
84
105
  toSequelizeParams() {
85
- var _a, _b;
106
+ var _a, _b, _c;
86
107
  const queryParams = this.filterParser.toSequelizeParams();
87
108
  if ((_a = this.options) == null ? void 0 : _a.filterByTk) {
109
+ const filterByTkWhere = this.filterByTkToWhereOption();
88
110
  queryParams.where = {
89
- [import_sequelize.Op.and]: [
90
- queryParams.where,
91
- {
92
- [this.context.targetKey || this.collection.filterTargetKey]: this.options.filterByTk
93
- }
94
- ]
111
+ [import_sequelize.Op.and]: [queryParams.where, filterByTkWhere]
95
112
  };
96
113
  }
97
- if ((_b = this.options) == null ? void 0 : _b.include) {
114
+ if (queryParams.include && !((_b = this.options) == null ? void 0 : _b.include)) {
115
+ this.options.include = [];
116
+ }
117
+ if ((_c = this.options) == null ? void 0 : _c.include) {
98
118
  if (!queryParams.include) {
99
119
  queryParams.include = [];
100
120
  }
@@ -108,32 +128,66 @@ const _OptionsParser = class _OptionsParser {
108
128
  * @protected
109
129
  */
110
130
  parseSort(filterParams) {
111
- var _a;
131
+ var _a, _b;
112
132
  let sort = ((_a = this.options) == null ? void 0 : _a.sort) || [];
113
133
  if (typeof sort === "string") {
114
134
  sort = sort.split(",");
115
135
  }
136
+ let defaultSortField = this.model.primaryKeyAttribute;
137
+ if (Array.isArray(this.collection.filterTargetKey)) {
138
+ defaultSortField = this.collection.filterTargetKey;
139
+ }
140
+ if (!defaultSortField && this.collection.filterTargetKey && !Array.isArray(this.collection.filterTargetKey)) {
141
+ defaultSortField = this.collection.filterTargetKey;
142
+ }
143
+ if (defaultSortField && !((_b = this.options) == null ? void 0 : _b.group)) {
144
+ defaultSortField = import_lodash.default.castArray(defaultSortField);
145
+ for (const key of defaultSortField) {
146
+ if (!sort.includes(key)) {
147
+ sort.push(key);
148
+ }
149
+ }
150
+ }
116
151
  const orderParams = [];
117
152
  for (const sortKey of sort) {
118
153
  let direction = sortKey.startsWith("-") ? "DESC" : "ASC";
119
- const sortField = sortKey.replace("-", "").split(".");
154
+ const sortField = sortKey.startsWith("-") ? sortKey.replace("-", "").split(".") : sortKey.split(".");
120
155
  if (this.database.inDialect("postgres", "sqlite")) {
121
156
  direction = `${direction} NULLS LAST`;
122
157
  }
158
+ let valid = true;
123
159
  if (sortField.length > 1) {
124
160
  let associationModel = this.model;
125
161
  for (let i = 0; i < sortField.length - 1; i++) {
126
162
  const associationKey = sortField[i];
127
- sortField[i] = associationModel.associations[associationKey].target;
128
- associationModel = sortField[i];
163
+ const associationEntity = associationModel.associations[associationKey];
164
+ if (!associationEntity) {
165
+ valid = false;
166
+ break;
167
+ }
168
+ if (!["BelongsTo", "HasOne"].includes(associationEntity.associationType)) {
169
+ continue;
170
+ }
171
+ const model = associationEntity.target;
172
+ sortField[i] = {
173
+ model,
174
+ as: associationKey
175
+ };
176
+ associationModel = model;
129
177
  }
130
178
  } else {
131
179
  const rawField = this.model.rawAttributes[sortField[0]];
132
180
  sortField[0] = (rawField == null ? void 0 : rawField.field) || sortField[0];
133
181
  }
182
+ if (!valid) {
183
+ continue;
184
+ }
134
185
  sortField.push(direction);
135
186
  if (this.database.isMySQLCompatibleDialect()) {
136
- orderParams.push([import_sequelize.Sequelize.fn("ISNULL", import_sequelize.Sequelize.col(`${this.model.name}.${sortField[0]}`))]);
187
+ const fieldName = sortField[0];
188
+ if (this.model.fieldRawAttributesMap[fieldName]) {
189
+ orderParams.push([import_sequelize.Sequelize.fn("ISNULL", import_sequelize.Sequelize.col(`${this.model.name}.${sortField[0]}`))]);
190
+ }
137
191
  }
138
192
  orderParams.push(sortField);
139
193
  }
@@ -195,8 +249,8 @@ const _OptionsParser = class _OptionsParser {
195
249
  const exceptPath = except2.split(".");
196
250
  const association = exceptPath[0];
197
251
  const lastLevel = exceptPath.length <= 2;
198
- const existIncludeIndex = queryParams["include"].findIndex((include) => include["association"] == association);
199
- if (existIncludeIndex == -1) {
252
+ const existIncludeIndex = queryParams["include"].findIndex((include) => include["association"] === association);
253
+ if (existIncludeIndex === -1) {
200
254
  return;
201
255
  }
202
256
  if (lastLevel) {
@@ -240,10 +294,10 @@ const _OptionsParser = class _OptionsParser {
240
294
  const appendAssociation = appendFields[0];
241
295
  const associations = model.associations;
242
296
  let lastLevel = false;
243
- if (appendFields.length == 1) {
297
+ if (appendFields.length === 1) {
244
298
  lastLevel = true;
245
299
  }
246
- if (appendFields.length == 2) {
300
+ if (appendFields.length === 2) {
247
301
  const association = associations[appendFields[0]];
248
302
  if (!association) {
249
303
  throw new Error(`association ${appendFields[0]} in ${model.name} not found`);
@@ -253,24 +307,24 @@ const _OptionsParser = class _OptionsParser {
253
307
  lastLevel = true;
254
308
  }
255
309
  }
256
- if (queryParams["include"] == void 0) {
310
+ if (queryParams["include"] === void 0) {
257
311
  queryParams["include"] = [];
258
312
  }
259
313
  let existIncludeIndex = queryParams["include"].findIndex(
260
- (include) => include["association"] == appendAssociation
314
+ (include) => include["association"] === appendAssociation
261
315
  );
262
- if (existIncludeIndex != -1) {
316
+ if (existIncludeIndex !== -1) {
263
317
  delete queryParams["include"][existIncludeIndex]["fromFilter"];
264
- if (Array.isArray(queryParams["include"][existIncludeIndex]["attributes"]) && queryParams["include"][existIncludeIndex]["attributes"].length == 0) {
318
+ if (Array.isArray(queryParams["include"][existIncludeIndex]["attributes"]) && queryParams["include"][existIncludeIndex]["attributes"].length === 0) {
265
319
  queryParams["include"][existIncludeIndex]["attributes"] = {
266
320
  include: []
267
321
  };
268
322
  }
269
323
  }
270
- if (lastLevel && existIncludeIndex != -1 && ((_a = import_lodash.default.get(queryParams, ["include", existIncludeIndex, "attributes", "include"])) == null ? void 0 : _a.length) == 0) {
324
+ if (lastLevel && existIncludeIndex !== -1 && ((_a = import_lodash.default.get(queryParams, ["include", existIncludeIndex, "attributes", "include"])) == null ? void 0 : _a.length) === 0) {
271
325
  return;
272
326
  }
273
- if (existIncludeIndex == -1) {
327
+ if (existIncludeIndex === -1) {
274
328
  queryParams["include"].push({
275
329
  association: appendAssociation,
276
330
  options: appendWithOptions.options || {}
@@ -282,14 +336,14 @@ const _OptionsParser = class _OptionsParser {
282
336
  include: []
283
337
  // all fields are output by default
284
338
  };
285
- if (appendFields.length == 2) {
339
+ if (appendFields.length === 2) {
286
340
  if (!Array.isArray(attributes)) {
287
341
  attributes = [];
288
342
  }
289
343
  const attributeName = appendFields[1];
290
344
  attributes.push(attributeName);
291
345
  } else {
292
- if (Array.isArray(attributes) && attributes.length == 0) {
346
+ if (Array.isArray(attributes) && attributes.length === 0) {
293
347
  attributes = {
294
348
  include: []
295
349
  };
@@ -301,7 +355,7 @@ const _OptionsParser = class _OptionsParser {
301
355
  };
302
356
  } else {
303
357
  const existInclude = queryParams["include"][existIncludeIndex];
304
- if (existInclude.attributes && Array.isArray(existInclude.attributes) && existInclude.attributes.length == 0) {
358
+ if (existInclude.attributes && Array.isArray(existInclude.attributes) && existInclude.attributes.length === 0) {
305
359
  existInclude.attributes = {
306
360
  include: []
307
361
  };
@@ -62,7 +62,7 @@ function belongsToManyAssociations(instance) {
62
62
  const associations = modelAssociations(instance);
63
63
  return Object.entries(associations).filter((entry) => {
64
64
  const [key, association] = entry;
65
- return association.associationType == "BelongsToMany";
65
+ return association.associationType === "BelongsToMany";
66
66
  }).map((association) => {
67
67
  return association[1];
68
68
  });
@@ -155,12 +155,12 @@ function isReverseAssociationPair(a, b) {
155
155
  const typeSet = /* @__PURE__ */ new Set();
156
156
  typeSet.add(a.associationType);
157
157
  typeSet.add(b.associationType);
158
- if (typeSet.size == 1 && typeSet.has("BelongsToMany")) {
158
+ if (typeSet.size === 1 && typeSet.has("BelongsToMany")) {
159
159
  return a.through.tableName === b.through.tableName && a.target.name === b.source.name && b.target.name === a.source.name && a.foreignKey === b.otherKey && a.sourceKey === b.targetKey && a.otherKey === b.foreignKey && a.targetKey === b.sourceKey;
160
160
  }
161
161
  if (typeSet.has("HasOne") && typeSet.has("BelongsTo") || typeSet.has("HasMany") && typeSet.has("BelongsTo")) {
162
- const sourceAssoc = a.associationType == "BelongsTo" ? b : a;
163
- const targetAssoc = sourceAssoc == a ? b : a;
162
+ const sourceAssoc = a.associationType === "BelongsTo" ? b : a;
163
+ const targetAssoc = sourceAssoc === a ? b : a;
164
164
  return sourceAssoc.source.name === targetAssoc.target.name && sourceAssoc.target.name === targetAssoc.source.name && sourceAssoc.foreignKey === targetAssoc.foreignKey && sourceAssoc.sourceKey === targetAssoc.targetKey;
165
165
  }
166
166
  return false;
@@ -204,7 +204,10 @@ async function updateSingleAssociation(model, key, value, options = {}) {
204
204
  return true;
205
205
  }, "removeAssociation");
206
206
  if (isUndefinedOrNull(value)) {
207
- return await removeAssociation();
207
+ if (!isUndefinedOrNull(await model[association.accessors.get]())) {
208
+ return await removeAssociation();
209
+ }
210
+ return true;
208
211
  }
209
212
  if (association.associationType === "HasOne" && !model.get(association.sourceKeyAttribute)) {
210
213
  throw new Error(`The source key ${association.sourceKeyAttribute} is not set in ${model.constructor.name}`);
@@ -244,7 +247,7 @@ async function updateSingleAssociation(model, key, value, options = {}) {
244
247
  transaction
245
248
  });
246
249
  if (instance2) {
247
- await model[setAccessor](instance2, { context, transaction });
250
+ await model[setAccessor](instance2, { context, transaction, hooks: false });
248
251
  if (!recursive) {
249
252
  return;
250
253
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tachybase/database",
3
- "version": "0.23.22",
3
+ "version": "0.23.40",
4
4
  "description": "",
5
5
  "license": "Apache-2.0",
6
6
  "main": "./lib/index.js",
@@ -24,8 +24,8 @@
24
24
  "semver": "^7.6.3",
25
25
  "sequelize": "^6.37.5",
26
26
  "umzug": "^3.8.2",
27
- "@tachybase/utils": "0.23.22",
28
- "@tachybase/logger": "0.23.22"
27
+ "@tachybase/logger": "0.23.40",
28
+ "@tachybase/utils": "0.23.40"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/flat": "^5.0.5",