@nocobase/database 1.6.0-alpha.1 → 1.6.0-alpha.11

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.
@@ -29,15 +29,18 @@ export type DumpRules = BuiltInGroup | ({
29
29
  } & BaseDumpRules) | ({
30
30
  group: BuiltInGroup | string;
31
31
  } & BaseDumpRules);
32
+ export type MigrationRule = 'overwrite' | 'skip' | 'upsert' | 'schema-only' | 'insert-ignore';
32
33
  export interface CollectionOptions extends Omit<ModelOptions, 'name' | 'hooks'> {
33
34
  name: string;
34
35
  title?: string;
35
36
  namespace?: string;
37
+ migrationRules?: MigrationRule[];
36
38
  dumpRules?: DumpRules;
37
39
  tableName?: string;
38
40
  inherits?: string[] | string;
39
41
  viewName?: string;
40
42
  writableView?: boolean;
43
+ isThrough?: boolean;
41
44
  filterTargetKey?: string | string[];
42
45
  fields?: FieldOptions[];
43
46
  model?: string | ModelStatic<Model>;
package/lib/database.js CHANGED
@@ -220,6 +220,7 @@ const _Database = class _Database extends import_events.EventEmitter {
220
220
  autoGenId: false,
221
221
  timestamps: false,
222
222
  dumpRules: "required",
223
+ migrationRules: ["schema-only", "overwrite", "skip"],
223
224
  origin: "@nocobase/database",
224
225
  fields: [{ type: "string", name: "name", primaryKey: true }]
225
226
  });
@@ -14,4 +14,5 @@ export declare class MysqlDialect extends BaseDialect {
14
14
  get: (v: string) => string;
15
15
  version: string;
16
16
  };
17
+ getSequelizeOptions(options: any): any;
17
18
  }
@@ -32,6 +32,7 @@ __export(mysql_dialect_exports, {
32
32
  MysqlDialect: () => MysqlDialect
33
33
  });
34
34
  module.exports = __toCommonJS(mysql_dialect_exports);
35
+ var import_utils = require("@nocobase/utils");
35
36
  var import_base_dialect = require("./base-dialect");
36
37
  const _MysqlDialect = class _MysqlDialect extends import_base_dialect.BaseDialect {
37
38
  getVersionGuard() {
@@ -44,6 +45,10 @@ const _MysqlDialect = class _MysqlDialect extends import_base_dialect.BaseDialec
44
45
  version: ">=8.0.17"
45
46
  };
46
47
  }
48
+ getSequelizeOptions(options) {
49
+ import_utils.lodash.set(options, "dialectOptions.multipleStatements", true);
50
+ return options;
51
+ }
47
52
  };
48
53
  __name(_MysqlDialect, "MysqlDialect");
49
54
  __publicField(_MysqlDialect, "dialectName", "mysql");
@@ -125,7 +125,10 @@ const _BelongsToManyField = class _BelongsToManyField extends import_relation_fi
125
125
  Through = database.getCollection(through);
126
126
  } else {
127
127
  const throughCollectionOptions = {
128
- name: through
128
+ name: through,
129
+ isThrough: true,
130
+ sourceCollectionName: this.collection.name,
131
+ targetCollectionName: this.target
129
132
  };
130
133
  if (this.collection.options.dumpRules) {
131
134
  throughCollectionOptions["dumpRules"] = this.collection.options.dumpRules;
@@ -52,7 +52,7 @@ const _UnixTimestampField = class _UnixTimestampField extends import_date_field.
52
52
  if (accuracy === "millisecond") {
53
53
  rationalNumber = 1;
54
54
  }
55
- return Math.floor(new Date(val).getTime() / rationalNumber);
55
+ return Math.floor(typeof val === "number" ? val : new Date(val).getTime() / rationalNumber);
56
56
  }
57
57
  additionalSequelizeOptions() {
58
58
  var _a, _b, _c, _d, _e;
@@ -71,16 +71,19 @@ const _UnixTimestampField = class _UnixTimestampField extends import_date_field.
71
71
  return {
72
72
  get() {
73
73
  const value = this.getDataValue(name);
74
- if (value === null || value === void 0) {
74
+ if (value == null) {
75
75
  return value;
76
76
  }
77
77
  return new Date(value * rationalNumber);
78
78
  },
79
79
  set(value) {
80
- if (value === null || value === void 0) {
80
+ if (value == null) {
81
81
  this.setDataValue(name, value);
82
82
  } else {
83
- this.setDataValue(name, Math.floor(new Date(value).getTime() / rationalNumber));
83
+ this.setDataValue(
84
+ name,
85
+ Math.floor(typeof value === "number" ? value : new Date(value).getTime() / rationalNumber)
86
+ );
84
87
  }
85
88
  }
86
89
  };
@@ -69,7 +69,9 @@ function filterMatch(model, where) {
69
69
  $ne: /* @__PURE__ */ __name((value, condition) => value !== condition, "$ne"),
70
70
  $in: /* @__PURE__ */ __name((value, condition) => condition.includes(value), "$in"),
71
71
  $or: /* @__PURE__ */ __name((model2, conditions) => Object.values(conditions).some((condition) => filterMatch(model2, condition)), "$or"),
72
- $and: /* @__PURE__ */ __name((model2, conditions) => Object.values(conditions).every((condition) => filterMatch(model2, condition)), "$and")
72
+ $and: /* @__PURE__ */ __name((model2, conditions) => Object.values(conditions).every((condition) => filterMatch(model2, condition)), "$and"),
73
+ // boolean
74
+ $isFalsy: /* @__PURE__ */ __name((value) => !value, "$isFalsy")
73
75
  };
74
76
  for (const [key, value] of Object.entries(where)) {
75
77
  if (operatorFunctions[key] !== void 0) {
@@ -8,6 +8,29 @@
8
8
  */
9
9
  import { BaseInterface } from './base-interface';
10
10
  export declare class DatetimeInterface extends BaseInterface {
11
+ protected parseDateString(value: string): {
12
+ year: string;
13
+ month: string;
14
+ day: string;
15
+ hour: string;
16
+ minute: string;
17
+ second: string;
18
+ } | {
19
+ year: string;
20
+ month: string;
21
+ day: string;
22
+ hour?: undefined;
23
+ minute?: undefined;
24
+ second?: undefined;
25
+ };
26
+ protected formatDateTimeToISO(dateInfo: {
27
+ year: string;
28
+ month: string;
29
+ day: string;
30
+ hour?: string;
31
+ minute?: string;
32
+ second?: string;
33
+ }): string;
11
34
  toValue(value: any, ctx?: any): Promise<any>;
12
35
  toString(value: any, ctx?: any): any;
13
36
  }
@@ -62,15 +62,39 @@ function resolveTimeZoneFromCtx(ctx) {
62
62
  }
63
63
  __name(resolveTimeZoneFromCtx, "resolveTimeZoneFromCtx");
64
64
  const _DatetimeInterface = class _DatetimeInterface extends import_base_interface.BaseInterface {
65
+ parseDateString(value) {
66
+ const dateOnlyMatch = /^(\d{4})[-/]?(\d{2})[-/]?(\d{2})$/.exec(value);
67
+ const dateTimeMatch = /^(\d{4})(\d{2})(\d{2})\s+(\d{2}):(\d{2}):(\d{2})$/.exec(value);
68
+ if (dateTimeMatch) {
69
+ const [_, year, month, day, hour, minute, second] = dateTimeMatch;
70
+ return { year, month, day, hour, minute, second };
71
+ }
72
+ if (dateOnlyMatch) {
73
+ const [_, year, month, day] = dateOnlyMatch;
74
+ return { year, month, day };
75
+ }
76
+ return null;
77
+ }
78
+ formatDateTimeToISO(dateInfo) {
79
+ const { year, month, day, hour = "00", minute = "00", second = "00" } = dateInfo;
80
+ const m = (0, import_dayjs.default)(`${year}-${month}-${day} ${hour}:${minute}:${second}.000`);
81
+ return m.toISOString();
82
+ }
65
83
  async toValue(value, ctx = {}) {
66
84
  if (!value) {
67
85
  return null;
68
86
  }
87
+ if (typeof value === "number") {
88
+ const valueStr = value.toString();
89
+ const dateOnlyMatch = /^(\d{4})[-/]?(\d{2})[-/]?(\d{2})$/.exec(valueStr);
90
+ if (dateOnlyMatch) {
91
+ value = valueStr;
92
+ }
93
+ }
69
94
  if (typeof value === "string") {
70
- const match = /^(\d{4})[-/]?(\d{2})[-/]?(\d{2})$/.exec(value);
71
- if (match) {
72
- const m = (0, import_dayjs.default)(`${match[1]}-${match[2]}-${match[3]} 00:00:00.000`);
73
- return m.toISOString();
95
+ const dateInfo = this.parseDateString(value);
96
+ if (dateInfo) {
97
+ return this.formatDateTimeToISO(dateInfo);
74
98
  }
75
99
  }
76
100
  if (import_dayjs.default.isDayjs(value)) {
@@ -1,5 +1,13 @@
1
1
  import { DatetimeInterface } from './datetime-interface';
2
2
  export declare class DatetimeNoTzInterface extends DatetimeInterface {
3
+ protected formatDateTimeToString(dateInfo: {
4
+ year: string;
5
+ month: string;
6
+ day: string;
7
+ hour?: string;
8
+ minute?: string;
9
+ second?: string;
10
+ }): string;
3
11
  toValue(value: any, ctx?: any): Promise<any>;
4
12
  toString(value: any, ctx?: any): any;
5
13
  }
@@ -55,14 +55,21 @@ function isNumeric(str) {
55
55
  }
56
56
  __name(isNumeric, "isNumeric");
57
57
  const _DatetimeNoTzInterface = class _DatetimeNoTzInterface extends import_datetime_interface.DatetimeInterface {
58
+ formatDateTimeToString(dateInfo) {
59
+ const { year, month, day, hour, minute, second } = dateInfo;
60
+ if (hour !== void 0 && minute !== void 0 && second !== void 0) {
61
+ return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
62
+ }
63
+ return `${year}-${month}-${day}`;
64
+ }
58
65
  async toValue(value, ctx = {}) {
59
66
  if (!value) {
60
67
  return null;
61
68
  }
62
69
  if (typeof value === "string") {
63
- const match = /^(\d{4})[-/]?(\d{2})[-/]?(\d{2})$/.exec(value);
64
- if (match) {
65
- return `${match[1]}-${match[2]}-${match[3]}`;
70
+ const dateInfo = this.parseDateString(value);
71
+ if (dateInfo) {
72
+ return this.formatDateTimeToString(dateInfo);
66
73
  }
67
74
  }
68
75
  if (import_dayjs.default.isDayjs(value)) {
@@ -38,7 +38,7 @@ const _ToManyInterface = class _ToManyInterface extends import_base_interface.Ba
38
38
  }
39
39
  str = `${str}`.trim();
40
40
  const items = str.split(",");
41
- const { filterKey, targetCollection, transaction } = ctx;
41
+ const { filterKey, targetCollection, transaction, field } = ctx;
42
42
  const targetInstances = await targetCollection.repository.find({
43
43
  filter: {
44
44
  [filterKey]: items
@@ -51,7 +51,17 @@ const _ToManyInterface = class _ToManyInterface extends import_base_interface.Ba
51
51
  }
52
52
  });
53
53
  const primaryKeyAttribute = targetCollection.model.primaryKeyAttribute;
54
- return targetInstances.map((targetInstance) => targetInstance[primaryKeyAttribute]);
54
+ const targetKey = field.options.targetKey;
55
+ const values = targetInstances.map((targetInstance) => {
56
+ const result = {
57
+ [targetKey]: targetInstance[targetKey]
58
+ };
59
+ if (targetKey !== primaryKeyAttribute) {
60
+ result[primaryKeyAttribute] = targetInstance[primaryKeyAttribute];
61
+ }
62
+ return result;
63
+ });
64
+ return values;
55
65
  }
56
66
  };
57
67
  __name(_ToManyInterface, "ToManyInterface");
@@ -7,7 +7,8 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
  import { AggregateOptions, DestroyOptions, FindOptions, TargetKey, TK } from '../repository';
10
- import { AssociatedOptions, MultipleRelationRepository } from './multiple-relation-repository';
10
+ import { MultipleRelationRepository } from './multiple-relation-repository';
11
+ import { AssociatedOptions } from './types';
11
12
  export declare class HasManyRepository extends MultipleRelationRepository {
12
13
  find(options?: FindOptions): Promise<any>;
13
14
  aggregate(options: AggregateOptions): Promise<any>;
@@ -6,24 +6,20 @@
6
6
  * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
- import { MultiAssociationAccessors, Transaction, Transactionable } from 'sequelize';
10
- import { CommonFindOptions, CountOptions, DestroyOptions, Filter, FindOneOptions, FindOptions, TargetKey, TK, UpdateOptions } from '../repository';
9
+ import { MultiAssociationAccessors, Transaction } from 'sequelize';
11
10
  import { RelationRepository } from './relation-repository';
12
- type FindAndCountOptions = CommonFindOptions;
13
- export interface AssociatedOptions extends Transactionable {
14
- tk?: TK;
15
- }
11
+ import { AssociatedOptions, CountOptions, DestroyOptions, Filter, FindOptions, TargetKey, UpdateOptions, FirstOrCreateOptions } from './types';
16
12
  export declare abstract class MultipleRelationRepository extends RelationRepository {
17
13
  targetRepositoryFilterOptionsBySourceValue(): Promise<any>;
18
14
  find(options?: FindOptions): Promise<any>;
19
- findAndCount(options?: FindAndCountOptions): Promise<[any[], number]>;
15
+ findAndCount(options?: FindOptions): Promise<[any[], number]>;
20
16
  count(options?: CountOptions): Promise<number>;
21
- findOne(options?: FindOneOptions): Promise<any>;
17
+ findOne(options?: FindOptions): Promise<any>;
22
18
  remove(options: TargetKey | TargetKey[] | AssociatedOptions): Promise<void>;
23
19
  update(options?: UpdateOptions): Promise<any>;
24
- destroy(options?: TK | DestroyOptions): Promise<boolean>;
20
+ destroy(options?: TargetKey | DestroyOptions): Promise<boolean>;
25
21
  protected destroyByFilter(filter: Filter, transaction?: Transaction): Promise<boolean>;
26
22
  protected filterHasInclude(filter: Filter, options?: any): boolean;
27
23
  protected accessors(): MultiAssociationAccessors;
24
+ updateOrCreate(options: FirstOrCreateOptions): Promise<any>;
28
25
  }
29
- export {};
@@ -191,6 +191,10 @@ const _MultipleRelationRepository = class _MultipleRelationRepository extends im
191
191
  accessors() {
192
192
  return super.accessors();
193
193
  }
194
+ async updateOrCreate(options) {
195
+ const result = await super.updateOrCreate(options);
196
+ return Array.isArray(result) ? result[0] : result;
197
+ }
194
198
  };
195
199
  __name(_MultipleRelationRepository, "MultipleRelationRepository");
196
200
  __decorateClass([
@@ -205,6 +209,9 @@ __decorateClass([
205
209
  (0, import_relation_repository.transaction)(),
206
210
  import_target_collection_decorator.default
207
211
  ], _MultipleRelationRepository.prototype, "update", 1);
212
+ __decorateClass([
213
+ (0, import_relation_repository.transaction)()
214
+ ], _MultipleRelationRepository.prototype, "updateOrCreate", 1);
208
215
  let MultipleRelationRepository = _MultipleRelationRepository;
209
216
  // Annotate the CommonJS export names for ESM import in node:
210
217
  0 && (module.exports = {
@@ -11,7 +11,7 @@ import { Collection } from '../collection';
11
11
  import Database from '../database';
12
12
  import { RelationField } from '../fields/relation-field';
13
13
  import { Model } from '../model';
14
- import { CreateOptions, Filter, FindOptions, TargetKey } from '../repository';
14
+ import { CreateOptions, Filter, FindOptions, FirstOrCreateOptions, TargetKey, UpdateOptions } from './types';
15
15
  export declare const transaction: (transactionInjector?: any) => (target: any, name: any, descriptor: any) => any;
16
16
  export declare abstract class RelationRepository {
17
17
  sourceCollection: Collection;
@@ -30,6 +30,8 @@ export declare abstract class RelationRepository {
30
30
  isMultiTargetKey(value?: any): boolean;
31
31
  get collection(): Collection<any, any>;
32
32
  abstract find(options?: FindOptions): Promise<any>;
33
+ abstract findOne(options?: FindOptions): Promise<any>;
34
+ abstract update(options: UpdateOptions): Promise<any>;
33
35
  chunk(options: FindOptions & {
34
36
  chunkSize: number;
35
37
  callback: (rows: Model[], options: FindOptions) => Promise<void>;
@@ -37,6 +39,8 @@ export declare abstract class RelationRepository {
37
39
  convertTk(options: any): any;
38
40
  convertTks(options: any): any[];
39
41
  targetKey(): any;
42
+ firstOrCreate(options: FirstOrCreateOptions): Promise<any>;
43
+ updateOrCreate(options: FirstOrCreateOptions): Promise<any>;
40
44
  create(options?: CreateOptions): Promise<any>;
41
45
  getSourceModel(transaction?: Transaction): Promise<Model<any, any>>;
42
46
  protected accessors(): import("sequelize").SingleAssociationAccessors | import("sequelize").MultiAssociationAccessors;
@@ -55,6 +55,7 @@ var import_filter_parser = __toESM(require("../filter-parser"));
55
55
  var import_options_parser = require("../options-parser");
56
56
  var import_update_associations = require("../update-associations");
57
57
  var import_update_guard = require("../update-guard");
58
+ var import_filter_utils = require("../utils/filter-utils");
58
59
  const transaction = (0, import_transaction_decorator.transactionWrapperBuilder)(function() {
59
60
  return this.sourceCollection.model.sequelize.transaction();
60
61
  });
@@ -141,6 +142,32 @@ const _RelationRepository = class _RelationRepository {
141
142
  targetKey() {
142
143
  return this.associationField.targetKey;
143
144
  }
145
+ async firstOrCreate(options) {
146
+ const { filterKeys, values, transaction: transaction2, hooks, context } = options;
147
+ const filter = (0, import_filter_utils.valuesToFilter)(values, filterKeys);
148
+ const instance = await this.findOne({ filter, transaction: transaction2, context });
149
+ if (instance) {
150
+ return instance;
151
+ }
152
+ return this.create({ values, transaction: transaction2, hooks, context });
153
+ }
154
+ async updateOrCreate(options) {
155
+ const { filterKeys, values, transaction: transaction2, hooks, context } = options;
156
+ const filter = (0, import_filter_utils.valuesToFilter)(values, filterKeys);
157
+ const instance = await this.findOne({ filter, transaction: transaction2, context });
158
+ if (instance) {
159
+ return await this.update({
160
+ filterByTk: instance.get(
161
+ this.targetCollection.filterTargetKey || this.targetCollection.model.primaryKeyAttribute
162
+ ),
163
+ values,
164
+ transaction: transaction2,
165
+ hooks,
166
+ context
167
+ });
168
+ }
169
+ return this.create({ values, transaction: transaction2, hooks, context });
170
+ }
144
171
  async create(options) {
145
172
  if (Array.isArray(options.values)) {
146
173
  return Promise.all(options.values.map((record) => this.create({ ...options, values: record })));
@@ -210,6 +237,12 @@ const _RelationRepository = class _RelationRepository {
210
237
  }
211
238
  };
212
239
  __name(_RelationRepository, "RelationRepository");
240
+ __decorateClass([
241
+ transaction()
242
+ ], _RelationRepository.prototype, "firstOrCreate", 1);
243
+ __decorateClass([
244
+ transaction()
245
+ ], _RelationRepository.prototype, "updateOrCreate", 1);
213
246
  __decorateClass([
214
247
  transaction()
215
248
  ], _RelationRepository.prototype, "create", 1);
@@ -8,15 +8,8 @@
8
8
  */
9
9
  import { SingleAssociationAccessors, Transactionable } from 'sequelize';
10
10
  import { Model } from '../model';
11
- import { Appends, Except, Fields, Filter, TargetKey, UpdateOptions } from '../repository';
11
+ import { FindOptions, TargetKey, UpdateOptions } from './types';
12
12
  import { RelationRepository } from './relation-repository';
13
- export interface SingleRelationFindOption extends Transactionable {
14
- fields?: Fields;
15
- except?: Except;
16
- appends?: Appends;
17
- filter?: Filter;
18
- targetCollection?: string;
19
- }
20
13
  interface SetOption extends Transactionable {
21
14
  tk?: TargetKey;
22
15
  }
@@ -24,8 +17,8 @@ export declare abstract class SingleRelationRepository extends RelationRepositor
24
17
  abstract filterOptions(sourceModel: any): any;
25
18
  remove(options?: Transactionable): Promise<void>;
26
19
  set(options: TargetKey | SetOption): Promise<void>;
27
- find(options?: SingleRelationFindOption): Promise<any>;
28
- findOne(options?: SingleRelationFindOption): Promise<Model<any>>;
20
+ find(options?: FindOptions): Promise<any>;
21
+ findOne(options?: FindOptions): Promise<Model<any>>;
29
22
  destroy(options?: Transactionable): Promise<boolean>;
30
23
  update(options: UpdateOptions): Promise<any>;
31
24
  /**
@@ -48,7 +48,6 @@ __export(single_relation_repository_exports, {
48
48
  SingleRelationRepository: () => SingleRelationRepository
49
49
  });
50
50
  module.exports = __toCommonJS(single_relation_repository_exports);
51
- var import_lodash = __toESM(require("lodash"));
52
51
  var import_target_collection_decorator = __toESM(require("../decorators/target-collection-decorator"));
53
52
  var import_update_associations = require("../update-associations");
54
53
  var import_relation_repository = require("./relation-repository");
@@ -97,13 +96,14 @@ const _SingleRelationRepository = class _SingleRelationRepository extends import
97
96
  const transaction2 = await this.getTransaction(options);
98
97
  const target = await this.find({
99
98
  transaction: transaction2,
99
+ // @ts-ignore
100
100
  targetCollection: options.targetCollection
101
101
  });
102
102
  if (!target) {
103
103
  throw new Error("The record does not exist");
104
104
  }
105
105
  await (0, import_update_associations.updateModelByValues)(target, options == null ? void 0 : options.values, {
106
- ...import_lodash.default.omit(options, "values"),
106
+ ...options,
107
107
  transaction: transaction2
108
108
  });
109
109
  if (options.hooks !== false) {
@@ -6,10 +6,81 @@
6
6
  * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
- import { TargetKey, Values } from '../repository';
10
- import { Transactionable } from 'sequelize';
11
- export type PrimaryKeyWithThroughValues = [TargetKey, Values];
12
- export interface AssociatedOptions extends Transactionable {
13
- tk?: TargetKey | TargetKey[] | PrimaryKeyWithThroughValues | PrimaryKeyWithThroughValues[];
9
+ import { AssociationKeysToBeUpdate, BlackList, Values, WhiteList } from '@nocobase/database';
10
+ import { Transaction } from 'sequelize';
11
+ import { CreateOptions as SequelizeCreateOptions, UpdateOptions as SequelizeUpdateOptions } from 'sequelize/types/model';
12
+ export type TargetKey = string | number | {
13
+ [key: string]: any;
14
+ };
15
+ export interface Filter {
16
+ [key: string]: any;
17
+ }
18
+ export interface Appends {
19
+ [key: string]: true | Appends;
20
+ }
21
+ export interface Except {
22
+ [key: string]: true | Except;
23
+ }
24
+ export interface CommonOptions {
25
+ transaction?: Transaction;
26
+ context?: any;
27
+ }
28
+ export interface FindOptions extends CommonOptions {
29
+ filter?: Filter;
30
+ filterByTk?: TargetKey;
31
+ fields?: string[];
32
+ appends?: string[];
33
+ except?: string[];
34
+ sort?: string[];
35
+ limit?: number;
36
+ offset?: number;
37
+ raw?: boolean;
38
+ targetCollection?: string;
39
+ }
40
+ export interface CountOptions extends CommonOptions {
41
+ filter?: Filter;
42
+ }
43
+ export interface CreateOptions extends SequelizeCreateOptions {
44
+ values?: Values | Values[];
45
+ whitelist?: WhiteList;
46
+ blacklist?: BlackList;
47
+ updateAssociationValues?: AssociationKeysToBeUpdate;
48
+ context?: any;
49
+ }
50
+ export interface UpdateOptions extends Omit<SequelizeUpdateOptions, 'where'> {
51
+ values: Values;
52
+ filter?: Filter;
53
+ filterByTk?: TargetKey;
54
+ whitelist?: WhiteList;
55
+ blacklist?: BlackList;
56
+ updateAssociationValues?: AssociationKeysToBeUpdate;
57
+ targetCollection?: string;
58
+ context?: any;
59
+ }
60
+ export interface DestroyOptions extends CommonOptions {
61
+ filter?: Filter;
62
+ filterByTk?: TargetKey;
63
+ truncate?: boolean;
64
+ context?: any;
65
+ }
66
+ export interface FirstOrCreateOptions extends CommonOptions {
67
+ filterKeys: string[];
68
+ values: any;
69
+ hooks?: boolean;
70
+ context?: any;
71
+ }
72
+ export interface ThroughValues {
73
+ [key: string]: any;
74
+ }
75
+ export interface AssociatedOptions extends CommonOptions {
76
+ tk?: TargetKey | TargetKey[];
77
+ transaction?: Transaction;
78
+ }
79
+ export interface PrimaryKeyWithThroughValues {
80
+ pk: any;
81
+ throughValues?: ThroughValues;
82
+ }
83
+ export interface ToggleOptions extends CommonOptions {
84
+ tk?: TargetKey;
85
+ transaction?: Transaction;
14
86
  }
15
- export type setAssociationOptions = TargetKey | TargetKey[] | PrimaryKeyWithThroughValues | PrimaryKeyWithThroughValues[] | AssociatedOptions;
@@ -7,18 +7,19 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
  /// <reference types="node" />
10
- import { Association, BulkCreateOptions, CountOptions as SequelizeCountOptions, CreateOptions as SequelizeCreateOptions, DestroyOptions as SequelizeDestroyOptions, FindAndCountOptions as SequelizeAndCountOptions, FindOptions as SequelizeFindOptions, ModelStatic, Transactionable, UpdateOptions as SequelizeUpdateOptions, WhereOperators } from 'sequelize';
10
+ import { Association, BulkCreateOptions, ModelStatic, FindAndCountOptions as SequelizeAndCountOptions, CountOptions as SequelizeCountOptions, CreateOptions as SequelizeCreateOptions, DestroyOptions as SequelizeDestroyOptions, FindOptions as SequelizeFindOptions, UpdateOptions as SequelizeUpdateOptions, Transactionable, WhereOperators } from 'sequelize';
11
11
  import { Collection } from './collection';
12
12
  import { Database } from './database';
13
13
  import { ArrayFieldRepository } from './field-repository/array-field-repository';
14
14
  import { Model } from './model';
15
15
  import operators from './operators';
16
+ import { BelongsToArrayRepository } from './relation-repository/belongs-to-array-repository';
16
17
  import { BelongsToManyRepository } from './relation-repository/belongs-to-many-repository';
17
18
  import { BelongsToRepository } from './relation-repository/belongs-to-repository';
18
19
  import { HasManyRepository } from './relation-repository/hasmany-repository';
19
20
  import { HasOneRepository } from './relation-repository/hasone-repository';
20
21
  import { RelationRepository } from './relation-repository/relation-repository';
21
- import { BelongsToArrayRepository } from './relation-repository/belongs-to-array-repository';
22
+ import { valuesToFilter } from './utils/filter-utils';
22
23
  interface CreateManyOptions extends BulkCreateOptions {
23
24
  records: Values[];
24
25
  }
@@ -134,19 +135,18 @@ export interface AggregateOptions {
134
135
  filter?: Filter;
135
136
  distinct?: boolean;
136
137
  }
137
- interface FirstOrCreateOptions extends Transactionable {
138
+ export interface FirstOrCreateOptions extends Transactionable {
138
139
  filterKeys: string[];
139
140
  values?: Values;
140
141
  hooks?: boolean;
142
+ context?: any;
141
143
  }
142
144
  export declare class Repository<TModelAttributes extends {} = any, TCreationAttributes extends {} = TModelAttributes> {
143
145
  database: Database;
144
146
  collection: Collection;
145
147
  model: ModelStatic<Model>;
146
148
  constructor(collection: Collection);
147
- static valuesToFilter(values: Values, filterKeys: Array<string>): {
148
- $and: any[];
149
- };
149
+ static valuesToFilter: typeof valuesToFilter;
150
150
  /**
151
151
  * return count by filter
152
152
  */
package/lib/repository.js CHANGED
@@ -13,6 +13,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
13
13
  var __getOwnPropNames = Object.getOwnPropertyNames;
14
14
  var __getProtoOf = Object.getPrototypeOf;
15
15
  var __hasOwnProp = Object.prototype.hasOwnProperty;
16
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
16
17
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
17
18
  var __export = (target, all) => {
18
19
  for (var name in all)
@@ -43,16 +44,16 @@ var __decorateClass = (decorators, target, key, kind) => {
43
44
  if (kind && result) __defProp(target, key, result);
44
45
  return result;
45
46
  };
47
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
46
48
  var repository_exports = {};
47
49
  __export(repository_exports, {
48
50
  Repository: () => Repository,
49
51
  Transactionable: () => import_sequelize2.Transactionable
50
52
  });
51
53
  module.exports = __toCommonJS(repository_exports);
52
- var import_flat = require("flat");
54
+ var import_utils = require("@nocobase/utils");
53
55
  var import_lodash = __toESM(require("lodash"));
54
56
  var import_sequelize = require("sequelize");
55
- var import_utils = require("@nocobase/utils");
56
57
  var import_must_have_filter_decorator = __toESM(require("./decorators/must-have-filter-decorator"));
57
58
  var import_target_collection_decorator = __toESM(require("./decorators/target-collection-decorator"));
58
59
  var import_transaction_decorator = require("./decorators/transaction-decorator");
@@ -61,13 +62,14 @@ var import_array_field_repository = require("./field-repository/array-field-repo
61
62
  var import_fields = require("./fields");
62
63
  var import_filter_parser = __toESM(require("./filter-parser"));
63
64
  var import_options_parser = require("./options-parser");
65
+ var import_belongs_to_array_repository = require("./relation-repository/belongs-to-array-repository");
64
66
  var import_belongs_to_many_repository = require("./relation-repository/belongs-to-many-repository");
65
67
  var import_belongs_to_repository = require("./relation-repository/belongs-to-repository");
66
68
  var import_hasmany_repository = require("./relation-repository/hasmany-repository");
67
69
  var import_hasone_repository = require("./relation-repository/hasone-repository");
68
70
  var import_update_associations = require("./update-associations");
69
71
  var import_update_guard = require("./update-guard");
70
- var import_belongs_to_array_repository = require("./relation-repository/belongs-to-array-repository");
72
+ var import_filter_utils = require("./utils/filter-utils");
71
73
  var import_sequelize2 = require("sequelize");
72
74
  const debug = require("debug")("noco-database");
73
75
  const transaction = (0, import_transaction_decorator.transactionWrapperBuilder)(function() {
@@ -124,43 +126,6 @@ const _Repository = class _Repository {
124
126
  this.collection = collection;
125
127
  this.model = collection.model;
126
128
  }
127
- static valuesToFilter(values = {}, filterKeys) {
128
- const removeArrayIndexInKey = /* @__PURE__ */ __name((key) => {
129
- const chunks = key.split(".");
130
- return chunks.filter((chunk) => {
131
- return !chunk.match(/\d+/);
132
- }).join(".");
133
- }, "removeArrayIndexInKey");
134
- const filterAnd = [];
135
- const flattedValues = (0, import_flat.flatten)(values);
136
- const flattedValuesObject = {};
137
- for (const key in flattedValues) {
138
- const keyWithoutArrayIndex = removeArrayIndexInKey(key);
139
- if (flattedValuesObject[keyWithoutArrayIndex]) {
140
- if (!Array.isArray(flattedValuesObject[keyWithoutArrayIndex])) {
141
- flattedValuesObject[keyWithoutArrayIndex] = [flattedValuesObject[keyWithoutArrayIndex]];
142
- }
143
- flattedValuesObject[keyWithoutArrayIndex].push(flattedValues[key]);
144
- } else {
145
- flattedValuesObject[keyWithoutArrayIndex] = [flattedValues[key]];
146
- }
147
- }
148
- for (const filterKey of filterKeys) {
149
- const filterValue = flattedValuesObject[filterKey] ? flattedValuesObject[filterKey] : import_lodash.default.get(values, filterKey);
150
- if (filterValue) {
151
- filterAnd.push({
152
- [filterKey]: filterValue
153
- });
154
- } else {
155
- filterAnd.push({
156
- [filterKey]: null
157
- });
158
- }
159
- }
160
- return {
161
- $and: filterAnd
162
- };
163
- }
164
129
  /**
165
130
  * return count by filter
166
131
  */
@@ -339,27 +304,28 @@ const _Repository = class _Repository {
339
304
  * Get the first record matching the attributes or create it.
340
305
  */
341
306
  async firstOrCreate(options) {
342
- const { filterKeys, values, transaction: transaction2, hooks } = options;
307
+ const { filterKeys, values, transaction: transaction2, hooks, context } = options;
343
308
  const filter = _Repository.valuesToFilter(values, filterKeys);
344
- const instance = await this.findOne({ filter, transaction: transaction2 });
309
+ const instance = await this.findOne({ filter, transaction: transaction2, context });
345
310
  if (instance) {
346
311
  return instance;
347
312
  }
348
- return this.create({ values, transaction: transaction2, hooks });
313
+ return this.create({ values, transaction: transaction2, hooks, context });
349
314
  }
350
315
  async updateOrCreate(options) {
351
- const { filterKeys, values, transaction: transaction2, hooks } = options;
316
+ const { filterKeys, values, transaction: transaction2, hooks, context } = options;
352
317
  const filter = _Repository.valuesToFilter(values, filterKeys);
353
- const instance = await this.findOne({ filter, transaction: transaction2 });
318
+ const instance = await this.findOne({ filter, transaction: transaction2, context });
354
319
  if (instance) {
355
320
  return await this.update({
356
321
  filterByTk: instance.get(this.collection.filterTargetKey || this.collection.model.primaryKeyAttribute),
357
322
  values,
358
323
  transaction: transaction2,
359
- hooks
324
+ hooks,
325
+ context
360
326
  });
361
327
  }
362
- return this.create({ values, transaction: transaction2, hooks });
328
+ return this.create({ values, transaction: transaction2, hooks, context });
363
329
  }
364
330
  async create(options) {
365
331
  if (Array.isArray(options.values)) {
@@ -500,7 +466,7 @@ const _Repository = class _Repository {
500
466
  throw new Error(`filterByTk is not supported for collection that has no primary key`);
501
467
  }
502
468
  }
503
- if (filterByTk && !options.filter) {
469
+ if (filterByTk && !(0, import_utils.isValidFilter)(options.filter)) {
504
470
  const where = [];
505
471
  for (const tk of filterByTk) {
506
472
  const optionParser = new import_options_parser.OptionsParser(
@@ -595,6 +561,7 @@ const _Repository = class _Repository {
595
561
  }
596
562
  };
597
563
  __name(_Repository, "Repository");
564
+ __publicField(_Repository, "valuesToFilter", import_filter_utils.valuesToFilter);
598
565
  __decorateClass([
599
566
  transaction()
600
567
  ], _Repository.prototype, "create", 1);
@@ -0,0 +1,13 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ type Values = Record<string, any>;
10
+ export declare function valuesToFilter(values: Values, filterKeys: Array<string>): {
11
+ $and: any[];
12
+ };
13
+ export {};
@@ -0,0 +1,86 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __create = Object.create;
11
+ var __defProp = Object.defineProperty;
12
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
13
+ var __getOwnPropNames = Object.getOwnPropertyNames;
14
+ var __getProtoOf = Object.getPrototypeOf;
15
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
16
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
17
+ var __export = (target, all) => {
18
+ for (var name in all)
19
+ __defProp(target, name, { get: all[name], enumerable: true });
20
+ };
21
+ var __copyProps = (to, from, except, desc) => {
22
+ if (from && typeof from === "object" || typeof from === "function") {
23
+ for (let key of __getOwnPropNames(from))
24
+ if (!__hasOwnProp.call(to, key) && key !== except)
25
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
26
+ }
27
+ return to;
28
+ };
29
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
30
+ // If the importer is in node compatibility mode or this is not an ESM
31
+ // file that has been converted to a CommonJS file using a Babel-
32
+ // compatible transform (i.e. "__esModule" has not been set), then set
33
+ // "default" to the CommonJS "module.exports" for node compatibility.
34
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
35
+ mod
36
+ ));
37
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
38
+ var filter_utils_exports = {};
39
+ __export(filter_utils_exports, {
40
+ valuesToFilter: () => valuesToFilter
41
+ });
42
+ module.exports = __toCommonJS(filter_utils_exports);
43
+ var import_lodash = __toESM(require("lodash"));
44
+ var import_flat = require("flat");
45
+ function valuesToFilter(values = {}, filterKeys) {
46
+ const removeArrayIndexInKey = /* @__PURE__ */ __name((key) => {
47
+ const chunks = key.split(".");
48
+ return chunks.filter((chunk) => {
49
+ return !chunk.match(/\d+/);
50
+ }).join(".");
51
+ }, "removeArrayIndexInKey");
52
+ const filterAnd = [];
53
+ const flattedValues = (0, import_flat.flatten)(values);
54
+ const flattedValuesObject = {};
55
+ for (const key in flattedValues) {
56
+ const keyWithoutArrayIndex = removeArrayIndexInKey(key);
57
+ if (flattedValuesObject[keyWithoutArrayIndex]) {
58
+ if (!Array.isArray(flattedValuesObject[keyWithoutArrayIndex])) {
59
+ flattedValuesObject[keyWithoutArrayIndex] = [flattedValuesObject[keyWithoutArrayIndex]];
60
+ }
61
+ flattedValuesObject[keyWithoutArrayIndex].push(flattedValues[key]);
62
+ } else {
63
+ flattedValuesObject[keyWithoutArrayIndex] = [flattedValues[key]];
64
+ }
65
+ }
66
+ for (const filterKey of filterKeys) {
67
+ const filterValue = flattedValuesObject[filterKey] ? flattedValuesObject[filterKey] : import_lodash.default.get(values, filterKey);
68
+ if (filterValue) {
69
+ filterAnd.push({
70
+ [filterKey]: filterValue
71
+ });
72
+ } else {
73
+ filterAnd.push({
74
+ [filterKey]: null
75
+ });
76
+ }
77
+ }
78
+ return {
79
+ $and: filterAnd
80
+ };
81
+ }
82
+ __name(valuesToFilter, "valuesToFilter");
83
+ // Annotate the CommonJS export names for ESM import in node:
84
+ 0 && (module.exports = {
85
+ valuesToFilter
86
+ });
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@nocobase/database",
3
- "version": "1.6.0-alpha.1",
3
+ "version": "1.6.0-alpha.11",
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.6.0-alpha.1",
10
- "@nocobase/utils": "1.6.0-alpha.1",
9
+ "@nocobase/logger": "1.6.0-alpha.11",
10
+ "@nocobase/utils": "1.6.0-alpha.11",
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": "a4c91015e34ec0c9a427451b5fbdfb5fedc4f3d7"
41
+ "gitHead": "2a07f8a9af90b3ce09229cae72a485c14df2361b"
42
42
  }