@nocobase/database 0.7.1-alpha.7 → 0.7.2-alpha.3

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.
@@ -73,5 +73,7 @@ export declare class Collection<TModelAttributes extends {} = any, TCreationAttr
73
73
  * @param options
74
74
  */
75
75
  updateField(name: string, options: FieldOptions): void;
76
+ addIndex(index: any): void;
77
+ removeIndex(fields: any): void;
76
78
  sync(syncOptions?: SyncOptions): Promise<void>;
77
79
  }
package/lib/collection.js CHANGED
@@ -35,6 +35,16 @@ function _lodash() {
35
35
  return data;
36
36
  }
37
37
 
38
+ function _sequelize() {
39
+ const data = require("sequelize");
40
+
41
+ _sequelize = function _sequelize() {
42
+ return data;
43
+ };
44
+
45
+ return data;
46
+ }
47
+
38
48
  var _model = require("./model");
39
49
 
40
50
  var _repository = require("./repository");
@@ -356,6 +366,88 @@ class Collection extends _events().EventEmitter {
356
366
  this.setField(options.name || name, options);
357
367
  }
358
368
 
369
+ addIndex(index) {
370
+ if (!index) {
371
+ return;
372
+ }
373
+
374
+ let indexes = this.model.options.indexes || [];
375
+ let indexName = [];
376
+ let indexItem;
377
+
378
+ if (typeof index === 'string') {
379
+ indexItem = {
380
+ fields: [index]
381
+ };
382
+ indexName = [index];
383
+ } else if (Array.isArray(index)) {
384
+ indexItem = {
385
+ fields: index
386
+ };
387
+ indexName = index;
388
+ } else if (index === null || index === void 0 ? void 0 : index.fields) {
389
+ indexItem = index;
390
+ indexName = index.fields;
391
+ }
392
+
393
+ if (_lodash().default.isEqual(this.model.primaryKeyAttributes, indexName)) {
394
+ return;
395
+ }
396
+
397
+ const name = this.model.primaryKeyAttributes.join(',');
398
+
399
+ if (name.startsWith(`${indexName.join(',')},`)) {
400
+ return;
401
+ }
402
+
403
+ var _iterator3 = _createForOfIteratorHelper(indexes),
404
+ _step3;
405
+
406
+ try {
407
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
408
+ const item = _step3.value;
409
+
410
+ if (_lodash().default.isEqual(item.fields, indexName)) {
411
+ return;
412
+ }
413
+
414
+ const name = item.fields.join(',');
415
+
416
+ if (name.startsWith(`${indexName.join(',')},`)) {
417
+ return;
418
+ }
419
+ }
420
+ } catch (err) {
421
+ _iterator3.e(err);
422
+ } finally {
423
+ _iterator3.f();
424
+ }
425
+
426
+ if (!indexItem) {
427
+ return;
428
+ }
429
+
430
+ indexes.push(indexItem);
431
+ this.model.options.indexes = indexes;
432
+ const tableName = this.model.getTableName(); // @ts-ignore
433
+
434
+ this.model._indexes = this.model.options.indexes // @ts-ignore
435
+ .map(index => _sequelize().Utils.nameIndex(this.model._conformIndex(index), tableName));
436
+ }
437
+
438
+ removeIndex(fields) {
439
+ if (!fields) {
440
+ return;
441
+ } // @ts-ignore
442
+
443
+
444
+ const indexes = this.model._indexes; // @ts-ignore
445
+
446
+ this.model._indexes = indexes.filter(item => {
447
+ return !_lodash().default.isEqual(item.fields, fields);
448
+ });
449
+ }
450
+
359
451
  sync(syncOptions) {
360
452
  var _this3 = this;
361
453
 
package/lib/database.d.ts CHANGED
@@ -43,6 +43,11 @@ export declare type AddMigrationsOptions = {
43
43
  directory: string;
44
44
  };
45
45
  declare type OperatorFunc = (value: any, ctx?: RegisterOperatorsContext) => any;
46
+ declare class DatabaseVersion {
47
+ db: Database;
48
+ constructor(db: Database);
49
+ satisfies(versions: any): Promise<boolean>;
50
+ }
46
51
  export declare class Database extends EventEmitter implements AsyncEmitter {
47
52
  sequelize: Sequelize;
48
53
  migrator: Umzug;
@@ -56,6 +61,7 @@ export declare class Database extends EventEmitter implements AsyncEmitter {
56
61
  pendingFields: Map<string, FieldTypes.RelationField[]>;
57
62
  modelCollection: Map<ModelCtor<any>, Collection<any, any>>;
58
63
  modelHook: ModelHook;
64
+ version: DatabaseVersion;
59
65
  delayCollectionExtend: Map<string, {
60
66
  collectionOptions: CollectionOptions;
61
67
  mergeOptions?: any;
@@ -78,7 +84,6 @@ export declare class Database extends EventEmitter implements AsyncEmitter {
78
84
  getCollection(name: string): Collection;
79
85
  hasCollection(name: string): boolean;
80
86
  removeCollection(name: string): Collection<any, any>;
81
- getCollectionFieldByPath(path: string): FieldTypes.Field;
82
87
  getModel<M extends Model>(name: string): ModelCtor<M>;
83
88
  getRepository<R extends Repository>(name: string): R;
84
89
  getRepository<R extends RelationRepository>(name: string, relationId: string | number): R;
package/lib/database.js CHANGED
@@ -57,6 +57,16 @@ function _path() {
57
57
  return data;
58
58
  }
59
59
 
60
+ function _semver() {
61
+ const data = _interopRequireDefault(require("semver"));
62
+
63
+ _semver = function _semver() {
64
+ return data;
65
+ };
66
+
67
+ return data;
68
+ }
69
+
60
70
  function _sequelize() {
61
71
  const data = require("sequelize");
62
72
 
@@ -102,15 +112,13 @@ function _objectWithoutProperties(source, excluded) { if (source == null) return
102
112
 
103
113
  function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
104
114
 
105
- function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
106
-
107
- function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
115
+ function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e2) { throw _e2; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e3) { didErr = true; err = _e3; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
108
116
 
109
- function _toArray(arr) { return _arrayWithHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableRest(); }
117
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
110
118
 
111
- function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
119
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
112
120
 
113
- function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e2) { throw _e2; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e3) { didErr = true; err = _e3; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
121
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
114
122
 
115
123
  function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
116
124
 
@@ -124,11 +132,63 @@ function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Sy
124
132
 
125
133
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
126
134
 
127
- function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
135
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
128
136
 
129
- function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
137
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
130
138
 
131
- function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
139
+ class DatabaseVersion {
140
+ constructor(db) {
141
+ this.db = void 0;
142
+ this.db = db;
143
+ }
144
+
145
+ satisfies(versions) {
146
+ var _this = this;
147
+
148
+ return _asyncToGenerator(function* () {
149
+ const dialects = {
150
+ sqlite: {
151
+ sql: 'select sqlite_version() as version',
152
+ get: v => v
153
+ },
154
+ mysql: {
155
+ sql: 'select version() as version',
156
+ get: v => v
157
+ },
158
+ postgres: {
159
+ sql: 'select version() as version',
160
+ get: v => {
161
+ const keys = v.split(' ');
162
+ keys.shift();
163
+ return _semver().default.minVersion(keys.shift()).version;
164
+ }
165
+ }
166
+ };
167
+
168
+ for (var _i = 0, _Object$keys = Object.keys(dialects); _i < _Object$keys.length; _i++) {
169
+ const dialect = _Object$keys[_i];
170
+
171
+ if (_this.db.inDialect(dialect)) {
172
+ var _result$, _result$2;
173
+
174
+ if (!(versions === null || versions === void 0 ? void 0 : versions[dialect])) {
175
+ return false;
176
+ }
177
+
178
+ const _yield$_this$db$seque = yield _this.db.sequelize.query(dialects[dialect].sql),
179
+ _yield$_this$db$seque2 = _slicedToArray(_yield$_this$db$seque, 1),
180
+ result = _yield$_this$db$seque2[0];
181
+
182
+ console.log(`db version: ${dialects[dialect].get(result === null || result === void 0 ? void 0 : (_result$ = result[0]) === null || _result$ === void 0 ? void 0 : _result$.version)}`);
183
+ return _semver().default.satisfies(dialects[dialect].get(result === null || result === void 0 ? void 0 : (_result$2 = result[0]) === null || _result$2 === void 0 ? void 0 : _result$2.version), versions[dialect]);
184
+ }
185
+ }
186
+
187
+ return false;
188
+ })();
189
+ }
190
+
191
+ }
132
192
 
133
193
  class Database extends _events().EventEmitter {
134
194
  constructor(options) {
@@ -145,7 +205,9 @@ class Database extends _events().EventEmitter {
145
205
  this.pendingFields = new Map();
146
206
  this.modelCollection = new Map();
147
207
  this.modelHook = void 0;
208
+ this.version = void 0;
148
209
  this.delayCollectionExtend = new Map();
210
+ this.version = new DatabaseVersion(this);
149
211
 
150
212
  const opts = _objectSpread({
151
213
  sync: {
@@ -182,8 +244,8 @@ class Database extends _events().EventEmitter {
182
244
  });
183
245
  }); // register database field types
184
246
 
185
- for (var _i = 0, _Object$entries = Object.entries(FieldTypes); _i < _Object$entries.length; _i++) {
186
- const _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
247
+ for (var _i2 = 0, _Object$entries = Object.entries(FieldTypes); _i2 < _Object$entries.length; _i2++) {
248
+ const _Object$entries$_i = _slicedToArray(_Object$entries[_i2], 2),
187
249
  name = _Object$entries$_i[0],
188
250
  field = _Object$entries$_i[1];
189
251
 
@@ -319,36 +381,6 @@ class Database extends _events().EventEmitter {
319
381
  return collection;
320
382
  }
321
383
 
322
- getCollectionFieldByPath(path) {
323
- const _path$split = path.split('.'),
324
- _path$split2 = _toArray(_path$split),
325
- collectionName = _path$split2[0],
326
- fieldName = _path$split2[1],
327
- others = _path$split2.slice(2);
328
-
329
- const collection = this.getCollection(collectionName);
330
-
331
- if (!collection) {
332
- return;
333
- }
334
-
335
- const field = collection.getField(fieldName);
336
-
337
- if (others.length === 0) {
338
- return field;
339
- }
340
-
341
- if (field.target) {
342
- const target = this.getCollection(field.target);
343
-
344
- if (!target) {
345
- return;
346
- }
347
-
348
- return target.getField(others[0]);
349
- }
350
- }
351
-
352
384
  getModel(name) {
353
385
  return this.getCollection(name).model;
354
386
  }
@@ -388,8 +420,8 @@ class Database extends _events().EventEmitter {
388
420
  }
389
421
 
390
422
  registerFieldTypes(fieldTypes) {
391
- for (var _i2 = 0, _Object$entries2 = Object.entries(fieldTypes); _i2 < _Object$entries2.length; _i2++) {
392
- const _Object$entries2$_i = _slicedToArray(_Object$entries2[_i2], 2),
423
+ for (var _i3 = 0, _Object$entries2 = Object.entries(fieldTypes); _i3 < _Object$entries2.length; _i3++) {
424
+ const _Object$entries2$_i = _slicedToArray(_Object$entries2[_i3], 2),
393
425
  type = _Object$entries2$_i[0],
394
426
  fieldType = _Object$entries2$_i[1];
395
427
 
@@ -398,8 +430,8 @@ class Database extends _events().EventEmitter {
398
430
  }
399
431
 
400
432
  registerModels(models) {
401
- for (var _i3 = 0, _Object$entries3 = Object.entries(models); _i3 < _Object$entries3.length; _i3++) {
402
- const _Object$entries3$_i = _slicedToArray(_Object$entries3[_i3], 2),
433
+ for (var _i4 = 0, _Object$entries3 = Object.entries(models); _i4 < _Object$entries3.length; _i4++) {
434
+ const _Object$entries3$_i = _slicedToArray(_Object$entries3[_i4], 2),
403
435
  type = _Object$entries3$_i[0],
404
436
  schemaType = _Object$entries3$_i[1];
405
437
 
@@ -408,8 +440,8 @@ class Database extends _events().EventEmitter {
408
440
  }
409
441
 
410
442
  registerRepositories(repositories) {
411
- for (var _i4 = 0, _Object$entries4 = Object.entries(repositories); _i4 < _Object$entries4.length; _i4++) {
412
- const _Object$entries4$_i = _slicedToArray(_Object$entries4[_i4], 2),
443
+ for (var _i5 = 0, _Object$entries4 = Object.entries(repositories); _i5 < _Object$entries4.length; _i5++) {
444
+ const _Object$entries4$_i = _slicedToArray(_Object$entries4[_i5], 2),
413
445
  type = _Object$entries4$_i[0],
414
446
  schemaType = _Object$entries4$_i[1];
415
447
 
@@ -434,8 +466,8 @@ class Database extends _events().EventEmitter {
434
466
  }
435
467
 
436
468
  registerOperators(operators) {
437
- for (var _i5 = 0, _Object$entries5 = Object.entries(operators); _i5 < _Object$entries5.length; _i5++) {
438
- const _Object$entries5$_i = _slicedToArray(_Object$entries5[_i5], 2),
469
+ for (var _i6 = 0, _Object$entries5 = Object.entries(operators); _i6 < _Object$entries5.length; _i6++) {
470
+ const _Object$entries5$_i = _slicedToArray(_Object$entries5[_i6], 2),
439
471
  key = _Object$entries5$_i[0],
440
472
  operator = _Object$entries5$_i[1];
441
473
 
@@ -455,19 +487,19 @@ class Database extends _events().EventEmitter {
455
487
  }
456
488
 
457
489
  sync(options) {
458
- var _this = this;
490
+ var _this2 = this;
459
491
 
460
492
  return _asyncToGenerator(function* () {
461
- const isMySQL = _this.sequelize.getDialect() === 'mysql';
493
+ const isMySQL = _this2.sequelize.getDialect() === 'mysql';
462
494
 
463
495
  if (isMySQL) {
464
- yield _this.sequelize.query('SET FOREIGN_KEY_CHECKS = 0', null);
496
+ yield _this2.sequelize.query('SET FOREIGN_KEY_CHECKS = 0', null);
465
497
  }
466
498
 
467
- const result = yield _this.sequelize.sync(options);
499
+ const result = yield _this2.sequelize.sync(options);
468
500
 
469
501
  if (isMySQL) {
470
- yield _this.sequelize.query('SET FOREIGN_KEY_CHECKS = 1', null);
502
+ yield _this2.sequelize.query('SET FOREIGN_KEY_CHECKS = 1', null);
471
503
  }
472
504
 
473
505
  return result;
@@ -475,26 +507,26 @@ class Database extends _events().EventEmitter {
475
507
  }
476
508
 
477
509
  clean(options) {
478
- var _this2 = this;
510
+ var _this3 = this;
479
511
 
480
512
  return _asyncToGenerator(function* () {
481
513
  const drop = options.drop,
482
514
  others = _objectWithoutProperties(options, _excluded);
483
515
 
484
516
  if (drop) {
485
- yield _this2.sequelize.getQueryInterface().dropAllTables(others);
517
+ yield _this3.sequelize.getQueryInterface().dropAllTables(others);
486
518
  }
487
519
  })();
488
520
  }
489
521
 
490
522
  collectionExistsInDb(name, options) {
491
- var _this3 = this;
523
+ var _this4 = this;
492
524
 
493
525
  return _asyncToGenerator(function* () {
494
- const tables = yield _this3.sequelize.getQueryInterface().showAllTables({
526
+ const tables = yield _this4.sequelize.getQueryInterface().showAllTables({
495
527
  transaction: options === null || options === void 0 ? void 0 : options.transaction
496
528
  });
497
- return !!tables.find(table => table === `${_this3.getTablePrefix()}${name}`);
529
+ return !!tables.find(table => table === `${_this4.getTablePrefix()}${name}`);
498
530
  })();
499
531
  }
500
532
 
@@ -503,7 +535,7 @@ class Database extends _events().EventEmitter {
503
535
  }
504
536
 
505
537
  auth(options = {}) {
506
- var _this4 = this;
538
+ var _this5 = this;
507
539
 
508
540
  return _asyncToGenerator(function* () {
509
541
  const _options$retry = options.retry,
@@ -517,7 +549,7 @@ class Database extends _events().EventEmitter {
517
549
  const authenticate = /*#__PURE__*/function () {
518
550
  var _ref = _asyncToGenerator(function* () {
519
551
  try {
520
- yield _this4.sequelize.authenticate(others);
552
+ yield _this5.sequelize.authenticate(others);
521
553
  console.log('Connection has been established successfully.');
522
554
  return true;
523
555
  } catch (error) {
@@ -542,21 +574,21 @@ class Database extends _events().EventEmitter {
542
574
  }
543
575
 
544
576
  reconnect() {
545
- var _this5 = this;
577
+ var _this6 = this;
546
578
 
547
579
  return _asyncToGenerator(function* () {
548
- if (_this5.isSqliteMemory()) {
580
+ if (_this6.isSqliteMemory()) {
549
581
  return;
550
582
  } // @ts-ignore
551
583
 
552
584
 
553
- const ConnectionManager = _this5.sequelize.dialect.connectionManager.constructor; // @ts-ignore
585
+ const ConnectionManager = _this6.sequelize.dialect.connectionManager.constructor; // @ts-ignore
554
586
 
555
- const connectionManager = new ConnectionManager(_this5.sequelize.dialect, _this5.sequelize); // @ts-ignore
587
+ const connectionManager = new ConnectionManager(_this6.sequelize.dialect, _this6.sequelize); // @ts-ignore
556
588
 
557
- _this5.sequelize.dialect.connectionManager = connectionManager; // @ts-ignore
589
+ _this6.sequelize.dialect.connectionManager = connectionManager; // @ts-ignore
558
590
 
559
- _this5.sequelize.connectionManager = connectionManager;
591
+ _this6.sequelize.connectionManager = connectionManager;
560
592
  })();
561
593
  }
562
594
 
@@ -566,14 +598,14 @@ class Database extends _events().EventEmitter {
566
598
  }
567
599
 
568
600
  close() {
569
- var _this6 = this;
601
+ var _this7 = this;
570
602
 
571
603
  return _asyncToGenerator(function* () {
572
- if (_this6.isSqliteMemory()) {
604
+ if (_this7.isSqliteMemory()) {
573
605
  return;
574
606
  }
575
607
 
576
- return _this6.sequelize.close();
608
+ return _this7.sequelize.close();
577
609
  })();
578
610
  }
579
611
 
@@ -590,7 +622,7 @@ class Database extends _events().EventEmitter {
590
622
  }
591
623
 
592
624
  import(options) {
593
- var _this7 = this;
625
+ var _this8 = this;
594
626
 
595
627
  return _asyncToGenerator(function* () {
596
628
  const reader = new _collectionImporter.ImporterReader(options.directory, options.extensions);
@@ -607,17 +639,17 @@ class Database extends _events().EventEmitter {
607
639
  if (module.extend) {
608
640
  const collectionName = module.collectionOptions.name;
609
641
 
610
- const existCollection = _this7.getCollection(collectionName);
642
+ const existCollection = _this8.getCollection(collectionName);
611
643
 
612
644
  if (existCollection) {
613
645
  existCollection.updateOptions(module.collectionOptions, module.mergeOptions);
614
646
  } else {
615
- const existDelayExtends = _this7.delayCollectionExtend.get(collectionName) || [];
647
+ const existDelayExtends = _this8.delayCollectionExtend.get(collectionName) || [];
616
648
 
617
- _this7.delayCollectionExtend.set(collectionName, [...existDelayExtends, module]);
649
+ _this8.delayCollectionExtend.set(collectionName, [...existDelayExtends, module]);
618
650
  }
619
651
  } else {
620
- const collection = _this7.collection(module);
652
+ const collection = _this8.collection(module);
621
653
 
622
654
  result.set(collection.name, collection);
623
655
  }
@@ -59,7 +59,8 @@ class BelongsToField extends _relationField.RelationField {
59
59
 
60
60
 
61
61
  const association = collection.model.belongsTo(Target, _objectSpread({
62
- as: this.name
62
+ as: this.name,
63
+ constraints: false
63
64
  }, (0, _lodash().omit)(this.options, ['name', 'type', 'target']))); // inverse relation
64
65
  // this.TargetModel.hasMany(collection.model);
65
66
  // 建立关系之后从 pending 列表中删除
@@ -75,6 +76,7 @@ class BelongsToField extends _relationField.RelationField {
75
76
  this.options.sourceKey = association.sourceKey;
76
77
  }
77
78
 
79
+ this.collection.addIndex([this.options.foreignKey]);
78
80
  return true;
79
81
  }
80
82
 
@@ -99,7 +101,7 @@ class BelongsToField extends _relationField.RelationField {
99
101
 
100
102
  delete collection.model.associations[this.name]; // @ts-ignore
101
103
 
102
- collection.model.refreshAttributes();
104
+ collection.model.refreshAttributes(); // this.collection.removeIndex([this.options.foreignKey]);
103
105
  }
104
106
 
105
107
  }
@@ -63,7 +63,9 @@ class BelongsToManyField extends _relationField.RelationField {
63
63
  });
64
64
  }
65
65
 
66
- const association = collection.model.belongsToMany(Target, _objectSpread(_objectSpread({}, (0, _lodash().omit)(this.options, ['name', 'type', 'target'])), {}, {
66
+ const association = collection.model.belongsToMany(Target, _objectSpread(_objectSpread({
67
+ constraints: false
68
+ }, (0, _lodash().omit)(this.options, ['name', 'type', 'target'])), {}, {
67
69
  as: this.name,
68
70
  through: Through.model
69
71
  })); // 建立关系之后从 pending 列表中删除
@@ -86,6 +88,8 @@ class BelongsToManyField extends _relationField.RelationField {
86
88
  this.options.through = this.through;
87
89
  }
88
90
 
91
+ Through.addIndex([this.options.foreignKey]);
92
+ Through.addIndex([this.options.otherKey]);
89
93
  return true;
90
94
  }
91
95
 
@@ -209,11 +209,19 @@ class Field {
209
209
  model.rawAttributes[this.name] = this.toSequelize(); // @ts-ignore
210
210
 
211
211
  model.refreshAttributes();
212
+
213
+ if (this.options.index) {
214
+ this.context.collection.addIndex([this.name]);
215
+ }
212
216
  }
213
217
 
214
218
  unbind() {
215
219
  const model = this.context.collection.model;
216
220
  model.removeAttribute(this.name);
221
+
222
+ if (this.options.index) {
223
+ this.context.collection.removeIndex([this.name]);
224
+ }
217
225
  }
218
226
 
219
227
  toSequelize() {
@@ -58,10 +58,12 @@ class HasManyField extends _relationField.RelationField {
58
58
  delete collection.model.associations[this.name];
59
59
  }
60
60
 
61
- const association = collection.model.hasMany(Target, _objectSpread({
61
+ const association = collection.model.hasMany(Target, _objectSpread(_objectSpread({
62
+ constraints: false
63
+ }, (0, _lodash().omit)(this.options, ['name', 'type', 'target'])), {}, {
62
64
  as: this.name,
63
65
  foreignKey: this.foreignKey
64
- }, (0, _lodash().omit)(this.options, ['name', 'type', 'target']))); // inverse relation
66
+ })); // inverse relation
65
67
  // this.TargetModel.belongsTo(collection.model);
66
68
  // 建立关系之后从 pending 列表中删除
67
69
 
@@ -76,6 +78,18 @@ class HasManyField extends _relationField.RelationField {
76
78
  this.options.sourceKey = association.sourceKey;
77
79
  }
78
80
 
81
+ let tcoll;
82
+
83
+ if (this.target === collection.name) {
84
+ tcoll = collection;
85
+ } else {
86
+ tcoll = database.getCollection(this.target);
87
+ }
88
+
89
+ if (tcoll) {
90
+ tcoll.addIndex([this.options.foreignKey]);
91
+ }
92
+
79
93
  return true;
80
94
  }
81
95
 
@@ -86,7 +100,7 @@ class HasManyField extends _relationField.RelationField {
86
100
 
87
101
  database.removePendingField(this); // 如果关系表内没有显式的创建外键字段,删除关系时,外键也删除掉
88
102
 
89
- const tcoll = database.collections.get(this.target);
103
+ const tcoll = database.getCollection(this.target);
90
104
  const foreignKey = this.options.foreignKey;
91
105
  const field = tcoll.findField(field => {
92
106
  if (field.name === foreignKey) {
@@ -61,10 +61,12 @@ class HasOneField extends _relationField.RelationField {
61
61
  return false;
62
62
  }
63
63
 
64
- const association = collection.model.hasOne(Target, _objectSpread({
64
+ const association = collection.model.hasOne(Target, _objectSpread(_objectSpread({
65
+ constraints: false
66
+ }, (0, _lodash().omit)(this.options, ['name', 'type', 'target'])), {}, {
65
67
  as: this.name,
66
68
  foreignKey: this.foreignKey
67
- }, (0, _lodash().omit)(this.options, ['name', 'type', 'target']))); // 建立关系之后从 pending 列表中删除
69
+ })); // 建立关系之后从 pending 列表中删除
68
70
 
69
71
  database.removePendingField(this);
70
72
 
@@ -77,6 +79,18 @@ class HasOneField extends _relationField.RelationField {
77
79
  this.options.sourceKey = association.sourceKey;
78
80
  }
79
81
 
82
+ let tcoll;
83
+
84
+ if (this.target === collection.name) {
85
+ tcoll = collection;
86
+ } else {
87
+ tcoll = database.getCollection(this.target);
88
+ }
89
+
90
+ if (tcoll) {
91
+ tcoll.addIndex([this.options.foreignKey]);
92
+ }
93
+
80
94
  return true;
81
95
  }
82
96
 
@@ -16,6 +16,7 @@ export declare abstract class RelationRepository {
16
16
  sourceInstance: Model;
17
17
  db: Database;
18
18
  constructor(sourceCollection: Collection, association: string, sourceKeyValue: string | number);
19
+ get collection(): Collection<any, any>;
19
20
  targetKey(): any;
20
21
  protected accessors(): import("sequelize").SingleAssociationAccessors | import("sequelize").MultiAssociationAccessors;
21
22
  create(options?: CreateOptions): Promise<any>;
@@ -71,6 +71,10 @@ class RelationRepository {
71
71
  this.targetCollection = this.sourceCollection.context.database.modelCollection.get(this.targetModel);
72
72
  }
73
73
 
74
+ get collection() {
75
+ return this.db.getCollection(this.targetModel.name);
76
+ }
77
+
74
78
  targetKey() {
75
79
  return this.associationField.targetKey;
76
80
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/database",
3
- "version": "0.7.1-alpha.7",
3
+ "version": "0.7.2-alpha.3",
4
4
  "description": "",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./lib/index.d.ts",
@@ -12,12 +12,13 @@
12
12
  }
13
13
  ],
14
14
  "dependencies": {
15
- "@nocobase/utils": "0.7.1-alpha.7",
15
+ "@nocobase/utils": "0.7.2-alpha.3",
16
16
  "async-mutex": "^0.3.2",
17
17
  "deepmerge": "^4.2.2",
18
18
  "flat": "^5.0.2",
19
19
  "glob": "^7.1.6",
20
20
  "mathjs": "^10.6.1",
21
+ "semver": "^7.3.7",
21
22
  "sequelize": "^6.9.0",
22
23
  "umzug": "^3.1.1"
23
24
  },
@@ -29,5 +30,5 @@
29
30
  "url": "git+https://github.com/nocobase/nocobase.git",
30
31
  "directory": "packages/database"
31
32
  },
32
- "gitHead": "87601930b09f7df3eec5d493b62a7ec27871caa1"
33
+ "gitHead": "7ab5ec36f6408dd2fab545970a08679e8c2820e5"
33
34
  }
@@ -13,6 +13,22 @@ describe('collection', () => {
13
13
  await db.close();
14
14
  });
15
15
 
16
+ test.skip('indexes', async () => {
17
+ await db.clean({ drop: true });
18
+ const collection = db.collection({
19
+ name: 'test',
20
+ fields: [
21
+ {
22
+ type: 'string',
23
+ name: 'name',
24
+ index: true,
25
+ },
26
+ ],
27
+ });
28
+ collection.removeField('name');
29
+ await db.sync();
30
+ });
31
+
16
32
  test('removeFromDb', async () => {
17
33
  await db.clean({ drop: true });
18
34
  const collection = db.collection({
@@ -1,6 +1,6 @@
1
1
  import { Collection } from '../collection';
2
2
  import { Database } from '../database';
3
- import { updateAssociation, updateAssociations } from '../update-associations';
3
+ import { updateAssociations } from '../update-associations';
4
4
  import { mockDatabase } from './';
5
5
 
6
6
  describe('update associations', () => {
@@ -356,13 +356,14 @@ describe('update associations', () => {
356
356
  });
357
357
 
358
358
  describe('belongsToMany', () => {
359
- let db;
360
- let Post;
361
- let Tag;
362
- let PostTag;
359
+ let db: Database;
360
+ let Post: Collection;
361
+ let Tag: Collection;
362
+ let PostTag: Collection;
363
363
 
364
364
  beforeEach(async () => {
365
365
  db = mockDatabase();
366
+ await db.clean({ drop: true });
366
367
  PostTag = db.collection({
367
368
  name: 'posts_tags',
368
369
  fields: [{ type: 'string', name: 'tagged_at' }],
@@ -389,7 +390,7 @@ describe('update associations', () => {
389
390
  afterEach(async () => {
390
391
  await db.close();
391
392
  });
392
- test('set through value', async () => {
393
+ test.only('set through value', async () => {
393
394
  const p1 = await Post.repository.create({
394
395
  values: {
395
396
  title: 'hello',
@@ -404,9 +405,12 @@ describe('update associations', () => {
404
405
  ],
405
406
  },
406
407
  });
407
-
408
- const t1 = (await p1.getTags())[0];
409
- expect(t1.posts_tags.tagged_at).toEqual('123');
408
+ const count = await PostTag.repository.count({
409
+ filter: {
410
+ tagged_at: '123',
411
+ },
412
+ });
413
+ expect(count).toEqual(1);
410
414
  });
411
415
  });
412
416
  });
package/src/collection.ts CHANGED
@@ -1,7 +1,14 @@
1
1
  import merge from 'deepmerge';
2
2
  import { EventEmitter } from 'events';
3
3
  import { default as lodash, default as _ } from 'lodash';
4
- import { ModelCtor, ModelOptions, QueryInterfaceDropTableOptions, SyncOptions, Transactionable } from 'sequelize';
4
+ import {
5
+ ModelCtor,
6
+ ModelOptions,
7
+ QueryInterfaceDropTableOptions,
8
+ SyncOptions,
9
+ Transactionable,
10
+ Utils
11
+ } from 'sequelize';
5
12
  import { Database } from './database';
6
13
  import { Field, FieldOptions } from './fields';
7
14
  import { Model } from './model';
@@ -278,6 +285,67 @@ export class Collection<
278
285
  this.setField(options.name || name, options);
279
286
  }
280
287
 
288
+ addIndex(index: any) {
289
+ if (!index) {
290
+ return;
291
+ }
292
+ let indexes: any = this.model.options.indexes || [];
293
+ let indexName = [];
294
+ let indexItem;
295
+ if (typeof index === 'string') {
296
+ indexItem = {
297
+ fields: [index],
298
+ };
299
+ indexName = [index];
300
+ } else if (Array.isArray(index)) {
301
+ indexItem = {
302
+ fields: index,
303
+ };
304
+ indexName = index;
305
+ } else if (index?.fields) {
306
+ indexItem = index;
307
+ indexName = index.fields;
308
+ }
309
+ if (lodash.isEqual(this.model.primaryKeyAttributes, indexName)) {
310
+ return;
311
+ }
312
+ const name: string = this.model.primaryKeyAttributes.join(',');
313
+ if (name.startsWith(`${indexName.join(',')},`)) {
314
+ return;
315
+ }
316
+ for (const item of indexes) {
317
+ if (lodash.isEqual(item.fields, indexName)) {
318
+ return;
319
+ }
320
+ const name: string = item.fields.join(',');
321
+ if (name.startsWith(`${indexName.join(',')},`)) {
322
+ return;
323
+ }
324
+ }
325
+ if (!indexItem) {
326
+ return;
327
+ }
328
+ indexes.push(indexItem);
329
+ this.model.options.indexes = indexes;
330
+ const tableName = this.model.getTableName();
331
+ // @ts-ignore
332
+ this.model._indexes = this.model.options.indexes
333
+ // @ts-ignore
334
+ .map((index) => Utils.nameIndex(this.model._conformIndex(index), tableName));
335
+ }
336
+
337
+ removeIndex(fields: any) {
338
+ if (!fields) {
339
+ return;
340
+ }
341
+ // @ts-ignore
342
+ const indexes: any[] = this.model._indexes;
343
+ // @ts-ignore
344
+ this.model._indexes = indexes.filter((item) => {
345
+ return !lodash.isEqual(item.fields, fields);
346
+ });
347
+ }
348
+
281
349
  async sync(syncOptions?: SyncOptions) {
282
350
  const modelNames = [this.model.name];
283
351
 
package/src/database.ts CHANGED
@@ -4,6 +4,7 @@ import { EventEmitter } from 'events';
4
4
  import glob from 'glob';
5
5
  import lodash from 'lodash';
6
6
  import { basename, isAbsolute, resolve } from 'path';
7
+ import semver from 'semver';
7
8
  import {
8
9
  ModelCtor,
9
10
  Op,
@@ -65,6 +66,46 @@ export type AddMigrationsOptions = {
65
66
 
66
67
  type OperatorFunc = (value: any, ctx?: RegisterOperatorsContext) => any;
67
68
 
69
+ class DatabaseVersion {
70
+ db: Database;
71
+
72
+ constructor(db: Database) {
73
+ this.db = db;
74
+ }
75
+
76
+ async satisfies(versions) {
77
+ const dialects = {
78
+ sqlite: {
79
+ sql: 'select sqlite_version() as version',
80
+ get: (v) => v,
81
+ },
82
+ mysql: {
83
+ sql: 'select version() as version',
84
+ get: (v) => v,
85
+ },
86
+ postgres: {
87
+ sql: 'select version() as version',
88
+ get: (v) => {
89
+ const keys = v.split(' ');
90
+ keys.shift();
91
+ return semver.minVersion(keys.shift()).version;
92
+ },
93
+ },
94
+ };
95
+ for (const dialect of Object.keys(dialects)) {
96
+ if (this.db.inDialect(dialect)) {
97
+ if (!versions?.[dialect]) {
98
+ return false;
99
+ }
100
+ const [result] = (await this.db.sequelize.query(dialects[dialect].sql)) as any;
101
+ console.log(`db version: ${dialects[dialect].get(result?.[0]?.version)}`);
102
+ return semver.satisfies(dialects[dialect].get(result?.[0]?.version), versions[dialect]);
103
+ }
104
+ }
105
+ return false;
106
+ }
107
+ }
108
+
68
109
  export class Database extends EventEmitter implements AsyncEmitter {
69
110
  sequelize: Sequelize;
70
111
  migrator: Umzug;
@@ -79,12 +120,15 @@ export class Database extends EventEmitter implements AsyncEmitter {
79
120
  modelCollection = new Map<ModelCtor<any>, Collection>();
80
121
 
81
122
  modelHook: ModelHook;
123
+ version: DatabaseVersion;
82
124
 
83
125
  delayCollectionExtend = new Map<string, { collectionOptions: CollectionOptions; mergeOptions?: any }[]>();
84
126
 
85
127
  constructor(options: DatabaseOptions) {
86
128
  super();
87
129
 
130
+ this.version = new DatabaseVersion(this);
131
+
88
132
  const opts = {
89
133
  sync: {
90
134
  alter: {
@@ -28,6 +28,7 @@ export class BelongsToField extends RelationField {
28
28
  // define relation on sequelize model
29
29
  const association = collection.model.belongsTo(Target, {
30
30
  as: this.name,
31
+ constraints: false,
31
32
  ...omit(this.options, ['name', 'type', 'target']),
32
33
  });
33
34
 
@@ -45,7 +46,7 @@ export class BelongsToField extends RelationField {
45
46
  // @ts-ignore
46
47
  this.options.sourceKey = association.sourceKey;
47
48
  }
48
-
49
+ this.collection.addIndex([this.options.foreignKey]);
49
50
  return true;
50
51
  }
51
52
 
@@ -67,6 +68,7 @@ export class BelongsToField extends RelationField {
67
68
  delete collection.model.associations[this.name];
68
69
  // @ts-ignore
69
70
  collection.model.refreshAttributes();
71
+ // this.collection.removeIndex([this.options.foreignKey]);
70
72
  }
71
73
  }
72
74
 
@@ -37,6 +37,7 @@ export class BelongsToManyField extends RelationField {
37
37
  }
38
38
 
39
39
  const association = collection.model.belongsToMany(Target, {
40
+ constraints: false,
40
41
  ...omit(this.options, ['name', 'type', 'target']),
41
42
  as: this.name,
42
43
  through: Through.model,
@@ -57,6 +58,8 @@ export class BelongsToManyField extends RelationField {
57
58
  if (!this.options.through) {
58
59
  this.options.through = this.through;
59
60
  }
61
+ Through.addIndex([this.options.foreignKey]);
62
+ Through.addIndex([this.options.otherKey]);
60
63
  return true;
61
64
  }
62
65
 
@@ -171,11 +171,17 @@ export abstract class Field {
171
171
  model.rawAttributes[this.name] = this.toSequelize();
172
172
  // @ts-ignore
173
173
  model.refreshAttributes();
174
+ if (this.options.index) {
175
+ this.context.collection.addIndex([this.name]);
176
+ }
174
177
  }
175
178
 
176
179
  unbind() {
177
180
  const { model } = this.context.collection;
178
181
  model.removeAttribute(this.name);
182
+ if (this.options.index) {
183
+ this.context.collection.removeIndex([this.name]);
184
+ }
179
185
  }
180
186
 
181
187
  toSequelize(): any {
@@ -5,10 +5,10 @@ import {
5
5
  ForeignKeyOptions,
6
6
  HasManyOptions,
7
7
  HasManyOptions as SequelizeHasManyOptions,
8
- Utils,
8
+ Utils
9
9
  } from 'sequelize';
10
-
11
- import { BaseRelationFieldOptions, MultipleRelationFieldOptions, RelationField } from './relation-field';
10
+ import { Collection } from '../collection';
11
+ import { MultipleRelationFieldOptions, RelationField } from './relation-field';
12
12
 
13
13
  export interface HasManyFieldOptions extends HasManyOptions {
14
14
  /**
@@ -93,11 +93,11 @@ export class HasManyField extends RelationField {
93
93
  }
94
94
 
95
95
  const association = collection.model.hasMany(Target, {
96
+ constraints: false,
97
+ ...omit(this.options, ['name', 'type', 'target']),
96
98
  as: this.name,
97
99
  foreignKey: this.foreignKey,
98
- ...omit(this.options, ['name', 'type', 'target']),
99
100
  });
100
-
101
101
  // inverse relation
102
102
  // this.TargetModel.belongsTo(collection.model);
103
103
 
@@ -111,6 +111,15 @@ export class HasManyField extends RelationField {
111
111
  // @ts-ignore
112
112
  this.options.sourceKey = association.sourceKey;
113
113
  }
114
+ let tcoll: Collection;
115
+ if (this.target === collection.name) {
116
+ tcoll = collection;
117
+ } else {
118
+ tcoll = database.getCollection(this.target);
119
+ }
120
+ if (tcoll) {
121
+ tcoll.addIndex([this.options.foreignKey]);
122
+ }
114
123
  return true;
115
124
  }
116
125
 
@@ -119,7 +128,7 @@ export class HasManyField extends RelationField {
119
128
  // 如果关系字段还没建立就删除了,也同步删除待建立关联的关系字段
120
129
  database.removePendingField(this);
121
130
  // 如果关系表内没有显式的创建外键字段,删除关系时,外键也删除掉
122
- const tcoll = database.collections.get(this.target);
131
+ const tcoll = database.getCollection(this.target);
123
132
  const foreignKey = this.options.foreignKey;
124
133
  const field = tcoll.findField((field) => {
125
134
  if (field.name === foreignKey) {
@@ -7,6 +7,7 @@ import {
7
7
  HasOneOptions as SequelizeHasOneOptions,
8
8
  Utils
9
9
  } from 'sequelize';
10
+ import { Collection } from '../collection';
10
11
  import { BaseRelationFieldOptions, RelationField } from './relation-field';
11
12
 
12
13
  export interface HasOneFieldOptions extends HasOneOptions {
@@ -92,9 +93,10 @@ export class HasOneField extends RelationField {
92
93
  return false;
93
94
  }
94
95
  const association = collection.model.hasOne(Target, {
96
+ constraints: false,
97
+ ...omit(this.options, ['name', 'type', 'target']),
95
98
  as: this.name,
96
99
  foreignKey: this.foreignKey,
97
- ...omit(this.options, ['name', 'type', 'target']),
98
100
  });
99
101
  // 建立关系之后从 pending 列表中删除
100
102
  database.removePendingField(this);
@@ -105,6 +107,15 @@ export class HasOneField extends RelationField {
105
107
  // @ts-ignore
106
108
  this.options.sourceKey = association.sourceKey;
107
109
  }
110
+ let tcoll: Collection;
111
+ if (this.target === collection.name) {
112
+ tcoll = collection;
113
+ } else {
114
+ tcoll = database.getCollection(this.target);
115
+ }
116
+ if (tcoll) {
117
+ tcoll.addIndex([this.options.foreignKey]);
118
+ }
108
119
  return true;
109
120
  }
110
121
 
@@ -40,6 +40,10 @@ export abstract class RelationRepository {
40
40
  this.targetCollection = this.sourceCollection.context.database.modelCollection.get(this.targetModel);
41
41
  }
42
42
 
43
+ get collection() {
44
+ return this.db.getCollection(this.targetModel.name);
45
+ }
46
+
43
47
  targetKey() {
44
48
  return this.associationField.targetKey;
45
49
  }