@nocobase/database 0.7.1-alpha.6 → 0.7.2-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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;
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,11 +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); } }
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; } } }; }
106
116
 
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); }); }; }
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; }
108
118
 
109
- 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; } } }; }
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; }
120
+
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; }
110
122
 
111
123
  function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
112
124
 
@@ -120,11 +132,63 @@ function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Sy
120
132
 
121
133
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
122
134
 
123
- 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); } }
124
136
 
125
- 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); }); }; }
126
138
 
127
- 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
+ }
128
192
 
129
193
  class Database extends _events().EventEmitter {
130
194
  constructor(options) {
@@ -141,7 +205,9 @@ class Database extends _events().EventEmitter {
141
205
  this.pendingFields = new Map();
142
206
  this.modelCollection = new Map();
143
207
  this.modelHook = void 0;
208
+ this.version = void 0;
144
209
  this.delayCollectionExtend = new Map();
210
+ this.version = new DatabaseVersion(this);
145
211
 
146
212
  const opts = _objectSpread({
147
213
  sync: {
@@ -178,8 +244,8 @@ class Database extends _events().EventEmitter {
178
244
  });
179
245
  }); // register database field types
180
246
 
181
- for (var _i = 0, _Object$entries = Object.entries(FieldTypes); _i < _Object$entries.length; _i++) {
182
- 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),
183
249
  name = _Object$entries$_i[0],
184
250
  field = _Object$entries$_i[1];
185
251
 
@@ -354,8 +420,8 @@ class Database extends _events().EventEmitter {
354
420
  }
355
421
 
356
422
  registerFieldTypes(fieldTypes) {
357
- for (var _i2 = 0, _Object$entries2 = Object.entries(fieldTypes); _i2 < _Object$entries2.length; _i2++) {
358
- 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),
359
425
  type = _Object$entries2$_i[0],
360
426
  fieldType = _Object$entries2$_i[1];
361
427
 
@@ -364,8 +430,8 @@ class Database extends _events().EventEmitter {
364
430
  }
365
431
 
366
432
  registerModels(models) {
367
- for (var _i3 = 0, _Object$entries3 = Object.entries(models); _i3 < _Object$entries3.length; _i3++) {
368
- 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),
369
435
  type = _Object$entries3$_i[0],
370
436
  schemaType = _Object$entries3$_i[1];
371
437
 
@@ -374,8 +440,8 @@ class Database extends _events().EventEmitter {
374
440
  }
375
441
 
376
442
  registerRepositories(repositories) {
377
- for (var _i4 = 0, _Object$entries4 = Object.entries(repositories); _i4 < _Object$entries4.length; _i4++) {
378
- 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),
379
445
  type = _Object$entries4$_i[0],
380
446
  schemaType = _Object$entries4$_i[1];
381
447
 
@@ -400,8 +466,8 @@ class Database extends _events().EventEmitter {
400
466
  }
401
467
 
402
468
  registerOperators(operators) {
403
- for (var _i5 = 0, _Object$entries5 = Object.entries(operators); _i5 < _Object$entries5.length; _i5++) {
404
- 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),
405
471
  key = _Object$entries5$_i[0],
406
472
  operator = _Object$entries5$_i[1];
407
473
 
@@ -421,19 +487,19 @@ class Database extends _events().EventEmitter {
421
487
  }
422
488
 
423
489
  sync(options) {
424
- var _this = this;
490
+ var _this2 = this;
425
491
 
426
492
  return _asyncToGenerator(function* () {
427
- const isMySQL = _this.sequelize.getDialect() === 'mysql';
493
+ const isMySQL = _this2.sequelize.getDialect() === 'mysql';
428
494
 
429
495
  if (isMySQL) {
430
- yield _this.sequelize.query('SET FOREIGN_KEY_CHECKS = 0', null);
496
+ yield _this2.sequelize.query('SET FOREIGN_KEY_CHECKS = 0', null);
431
497
  }
432
498
 
433
- const result = yield _this.sequelize.sync(options);
499
+ const result = yield _this2.sequelize.sync(options);
434
500
 
435
501
  if (isMySQL) {
436
- yield _this.sequelize.query('SET FOREIGN_KEY_CHECKS = 1', null);
502
+ yield _this2.sequelize.query('SET FOREIGN_KEY_CHECKS = 1', null);
437
503
  }
438
504
 
439
505
  return result;
@@ -441,26 +507,26 @@ class Database extends _events().EventEmitter {
441
507
  }
442
508
 
443
509
  clean(options) {
444
- var _this2 = this;
510
+ var _this3 = this;
445
511
 
446
512
  return _asyncToGenerator(function* () {
447
513
  const drop = options.drop,
448
514
  others = _objectWithoutProperties(options, _excluded);
449
515
 
450
516
  if (drop) {
451
- yield _this2.sequelize.getQueryInterface().dropAllTables(others);
517
+ yield _this3.sequelize.getQueryInterface().dropAllTables(others);
452
518
  }
453
519
  })();
454
520
  }
455
521
 
456
522
  collectionExistsInDb(name, options) {
457
- var _this3 = this;
523
+ var _this4 = this;
458
524
 
459
525
  return _asyncToGenerator(function* () {
460
- const tables = yield _this3.sequelize.getQueryInterface().showAllTables({
526
+ const tables = yield _this4.sequelize.getQueryInterface().showAllTables({
461
527
  transaction: options === null || options === void 0 ? void 0 : options.transaction
462
528
  });
463
- return !!tables.find(table => table === `${_this3.getTablePrefix()}${name}`);
529
+ return !!tables.find(table => table === `${_this4.getTablePrefix()}${name}`);
464
530
  })();
465
531
  }
466
532
 
@@ -469,7 +535,7 @@ class Database extends _events().EventEmitter {
469
535
  }
470
536
 
471
537
  auth(options = {}) {
472
- var _this4 = this;
538
+ var _this5 = this;
473
539
 
474
540
  return _asyncToGenerator(function* () {
475
541
  const _options$retry = options.retry,
@@ -483,7 +549,7 @@ class Database extends _events().EventEmitter {
483
549
  const authenticate = /*#__PURE__*/function () {
484
550
  var _ref = _asyncToGenerator(function* () {
485
551
  try {
486
- yield _this4.sequelize.authenticate(others);
552
+ yield _this5.sequelize.authenticate(others);
487
553
  console.log('Connection has been established successfully.');
488
554
  return true;
489
555
  } catch (error) {
@@ -508,21 +574,21 @@ class Database extends _events().EventEmitter {
508
574
  }
509
575
 
510
576
  reconnect() {
511
- var _this5 = this;
577
+ var _this6 = this;
512
578
 
513
579
  return _asyncToGenerator(function* () {
514
- if (_this5.isSqliteMemory()) {
580
+ if (_this6.isSqliteMemory()) {
515
581
  return;
516
582
  } // @ts-ignore
517
583
 
518
584
 
519
- const ConnectionManager = _this5.sequelize.dialect.connectionManager.constructor; // @ts-ignore
585
+ const ConnectionManager = _this6.sequelize.dialect.connectionManager.constructor; // @ts-ignore
520
586
 
521
- const connectionManager = new ConnectionManager(_this5.sequelize.dialect, _this5.sequelize); // @ts-ignore
587
+ const connectionManager = new ConnectionManager(_this6.sequelize.dialect, _this6.sequelize); // @ts-ignore
522
588
 
523
- _this5.sequelize.dialect.connectionManager = connectionManager; // @ts-ignore
589
+ _this6.sequelize.dialect.connectionManager = connectionManager; // @ts-ignore
524
590
 
525
- _this5.sequelize.connectionManager = connectionManager;
591
+ _this6.sequelize.connectionManager = connectionManager;
526
592
  })();
527
593
  }
528
594
 
@@ -532,14 +598,14 @@ class Database extends _events().EventEmitter {
532
598
  }
533
599
 
534
600
  close() {
535
- var _this6 = this;
601
+ var _this7 = this;
536
602
 
537
603
  return _asyncToGenerator(function* () {
538
- if (_this6.isSqliteMemory()) {
604
+ if (_this7.isSqliteMemory()) {
539
605
  return;
540
606
  }
541
607
 
542
- return _this6.sequelize.close();
608
+ return _this7.sequelize.close();
543
609
  })();
544
610
  }
545
611
 
@@ -556,7 +622,7 @@ class Database extends _events().EventEmitter {
556
622
  }
557
623
 
558
624
  import(options) {
559
- var _this7 = this;
625
+ var _this8 = this;
560
626
 
561
627
  return _asyncToGenerator(function* () {
562
628
  const reader = new _collectionImporter.ImporterReader(options.directory, options.extensions);
@@ -573,17 +639,17 @@ class Database extends _events().EventEmitter {
573
639
  if (module.extend) {
574
640
  const collectionName = module.collectionOptions.name;
575
641
 
576
- const existCollection = _this7.getCollection(collectionName);
642
+ const existCollection = _this8.getCollection(collectionName);
577
643
 
578
644
  if (existCollection) {
579
645
  existCollection.updateOptions(module.collectionOptions, module.mergeOptions);
580
646
  } else {
581
- const existDelayExtends = _this7.delayCollectionExtend.get(collectionName) || [];
647
+ const existDelayExtends = _this8.delayCollectionExtend.get(collectionName) || [];
582
648
 
583
- _this7.delayCollectionExtend.set(collectionName, [...existDelayExtends, module]);
649
+ _this8.delayCollectionExtend.set(collectionName, [...existDelayExtends, module]);
584
650
  }
585
651
  } else {
586
- const collection = _this7.collection(module);
652
+ const collection = _this8.collection(module);
587
653
 
588
654
  result.set(collection.name, collection);
589
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.6",
3
+ "version": "0.7.2-alpha.2",
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.6",
15
+ "@nocobase/utils": "0.7.2-alpha.2",
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": "b6f96c97020d278ae597bf05553442d7516b2216"
33
+ "gitHead": "a0cc50154cc292248ef107c95a24bcc0c7a586fa"
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
  }