tachybase_todo/docs/reference/database.md
2021-12-03 11:01:50 +08:00

9.8 KiB
Raw Blame History

toc
menu

Database

db.sequelize

Sequelize 实例

Definition
class Database {
  public sequelize: Sequelize;
}
Examples

直接调用 sequelize api

db.sequelize.close();

db.close()

断开数据库连接

Definition
class Database {
  close(): Promise<void>;
}
Examples
await db.close();

db.collection()

配置数据表、字段和索引等,更多字段配置查看 Field Types

Definition
class Database {
  collection(options: CollectionOptions) : Collection;
}

interface CollectionOptions {
  name: string;
  title?: string;
  // 自定义 model
  model?: string;
  // 自定义 repository
  repository?: string;
  // 字段配置
  fields?: FieldOptions;
}

// TODO需要提供完善的 types用于配置时提示
type FieldOptions;
Examples

配置 fields

const Post = db.collection({
  name: 'posts',
  fields: [
    { type: 'string', name: 'name' },
  ],
});

自定义 model 见 db.registerModels()

自定义 repository 见 db.registerRepositories()

db.constructor()

Definition
class Database {
  constructor (options: DatabaseOptions) => void;
}

type DatabaseOptions = Sequelize.Options | Sequelize;
Examples

配置 options 与 Sequelize.Options 一致,如:

const db = new Database({
  dialect: 'sqlite',
  storage: 'path/to/database.sqlite'
});

也可以直接传 sequelize 实例

const sequelize = new Sequelize('sqlite::memory:');
const db = new Database(sequelize);

注意new Sequelize() 会有针对参数的 try catch 处理Database 如果要捕获异常,也需要在 new 的时候 try catch

try {
  const db = new Database({
    dialect: 'sqlite',
    storage: 'path/to/database.sqlite'
  });
} catch(error) {

}

db.emit()

同步事件触发

Definition
Examples

db.emitAsync()

异步事件触发

Definition
Examples

db.getCollection() 待完善

Definition
class Database {
  getCollection(name: string): Collection;
}
Examples
const collection = db.getCollection('tests');

db.hasCollection() 待完善

Definition
class Database {
  hasCollection(name: string): boolean;
}
Examples
if (db.hasCollection('tests')) {

}

db.import() 待完善

Definition
class Database {
  import(options: ImportOptions): Map<string, Collection>;
}

interface ImportOptions {
  // 配置所在文件夹
  directory: string;
  // 配置后置
  // @default ['js', 'ts', 'json']
  extensions?: string[];
}
// 为了配合 db.import(),提供了一个 extend 方法,用于扩展已有 collection 配置
function extend(collectionOptions: CollectionOptions, mergeOptions?: MergeOptions) {}
Examples

导入某文件夹下的所有 Collection 配置

db.import({
  directory: '/path/to/collections',
  extensions: ['js', 'ts', 'json'],
});

db.import 的使用场景分析

// 假设在 A 插件里配置里 tests
db.collection({
  name: 'tests',
  { type: 'string', name: 'name1' },
  { type: 'string', name: 'name2' },
  { type: 'string', name: 'name3' },
});

// 在 B 插件里可能想给 tests 新增字段
const Test = db.getCollection('tests');
Test.addField('name4', {});
Test.addField('name5', {});
Test.addField('name6', {});

// 通过 db.import 的做法,文件不分先后,自动处理
// 文件1里
{
  name: 'tests',
  fields: [
    { type: 'string', name: 'name1' },
    { type: 'string', name: 'name2' },
    { type: 'string', name: 'name3' },
  ],
}

// 文件2里
extend({
  name: 'tests',
  fields: [
    { type: 'string', name: 'name4' },
  ],
});

extend 可以自定义 merge 规则(deepmerge),如:

extend({
  name: 'demos',
  actions: [
    {
      name: 'list',
    },
  ],
}, {
  arrayMerge: (t, s) => t.concat(s),
})

备注extend 的 fields、hooks 等 array 参数,默认都是 concat 规则string 参数是覆盖,如:

{
  name: 'tests',
  repository: 'TestRepository1',
  fields: [
    { type: 'string', name: 'name1' },
  ],
}

extend({
  name: 'tests',
  repository: 'TestRepository2',
  fields: [
    { type: 'string', name: 'name2' },
  ],
});

// 等同于
{
  name: 'tests',
  repository: 'TestRepository2',
  fields: [
    { type: 'string', name: 'name1' },
    { type: 'string', name: 'name2' },
  ],
}

db.on()

Definition

collection 的事件(都是同步的)

  • beforeDefineCollection
  • afterDefineCollection
  • beforeUpdateCollection
  • afterUpdateCollection
  • beforeRemoveCollection
  • afterRemoveCollection

model 的事件(异步的)

  • <modelHookType>
  • <modelName>.<modelHookType>
Examples

全局事件

db.on('beforeDefineCollection', (options: CollectionOptions) => {

});

db.on('afterDefineCollection', (collection: Collection) => {

});

db.on('afterCreate', async (model, options) => {

});

特定 model 事件

db.on('posts.afterCreate', async (model, options) => {

});

db.registerFieldTypes()

自定义字段存储类型,更多字段类型查看 Field Types

Definition
class Database {
  registerFieldTypes(types: RegisterFieldTypes): void;
}

interface RegisterFieldTypes {
  [key: string]: Field;
}
Examples
class CustomField extends Field {
  get dataType() {
    return DataTypes.STRING;
  }
}

db.registerFieldTypes({ custom: CustomField });

db.collection({
  name: 'tests',
  fields: [
    { type: 'custom', name: 'customName' },
  ],
});

db.registerModels() 待完善

自定义 Model

Definition
class Database {
  registerModels(models: RegisterModels): void;
}

interface RegisterModels {
  [key: string]: Sequelize.Model;
}
Examples
class CustomModel extends Model {
  customMethod() {
    console.log('custom method');
  }
}

db.registerModels({
  CustomModel,
});

const Test = db.collection({
  name: 'tests',
  model: 'CustomModel',
});

const test = Test.model<CustomModel>.create();
test.customMethod();

db.registerOperators() 待完善

自定义筛选条件

Definition
class Database {
  registerOperators(operators: RegisterOperators): any;
}

interface RegisterOperators {
  [key: string]: (value: any, ctx?: RegisterOperatorsContext) => any;
}

interface RegisterOperatorsContext {
  db?: Database;
  path?: string;
  field?: Field;
}
Examples

大部分自定义 Operator可以直接转换

db.registerOperators({
  includes: (value, ctx) => {
    const dialect = ctx.db.sequelize.getDialect();
    return {
      [dialect === 'postgres' ? Op.iLike : Op.like]: `%${value}%`,
    }
  },
});

repository.find({
  filter: {
    'attr.$includes': 'abc',
  },
});

但是也有少量 Operator 比较复杂

db.registerOperators({
  anyOf: (value: any[], ctx) => {
    if (!values) {
      return Sequelize.literal('');
    }
    values = Array.isArray(values) ? values : [values];
    if (values.length === 0) {
      return Sequelize.literal('');
    }
    const { path } = ctx;
    const column = path
      .split('.')
      .map((name) => `"${name}"`)
      .join('.');
    const sql = values
      .map((value) => `(${column})::jsonb @> '${JSON.stringify(value)}'`)
      .join(' OR ');
    return Sequelize.literal(sql);
  },
});

repository.find({
  filter: {
    'attr.$anyOf': ['val1', 'val2'],
  },
});

更多例子:

db.collection({
  name: 'users',
  fields: [
    { type: 'date', name: 'birthday' },
  ],
});

db.collection({
  name: 'posts',
  fields: [
    { type: 'belongsTo', name: 'user' },
  ],
});

repository.find({
  filter: {
    'birthday.$dateOn': '1999-01-02',
  },
});

db.registerOperators({
  dateOn: (value, ctx) => {
    console.log(value) // 1999-01-02
    console.log(ctx.path) // birthday
    console.log(ctx.field) // ctx.field instanceof DateField
  }
});

repository.find({
  filter: {
    $and: [
      { 'birthday.$dateOn': '1999-01-02' },
    ]
  },
});

db.registerOperators({
  dateOn: (value, ctx) => {
    console.log(value) // 1999-01-02
    console.log(ctx.path) // birthday
    console.log(ctx.field) // ctx.field instanceof DateField
  },
});

repository.find({
  filter: {
    $and: [
      { 'user.birthday.$dateOn': '1999-01-02' },
    ]
  },
});

db.registerOperators({
  dateOn: (value, ctx) => {
    console.log(value) // 1999-01-02
    console.log(ctx.path) // user.birthday
    console.log(ctx.field) // ctx.field instanceof DateField
  },
});

repository.find({
  filter: {
    $or: [
      {
        $and: [
          {'user.birthday.$dateOn': '1999-01-02'}
        ],
      },
    ],
  },
});

db.registerOperators({
  dateOn: (value, ctx) => {
    console.log(value) // 1999-01-02
    console.log(ctx.path) // user.birthday
    console.log(ctx.field) // ctx.field instanceof DateField
  }
});

db.registerRepositories() 待完善

自定义 Repository

Examples
class CustomRepository extends Repository {
  customMethod() {
    console.log('custom method');
  }
}

db.registerModels({
  CustomRepository,
});

const Test = db.collection({
  name: 'tests',
  repository: 'CustomRepository',
});

Test.repository<CustomRepository>.customMethod();

db.removeCollection() 待完善

移除 collection

Definition
class Database {
  removeCollection(name: string): Collection;
}
Examples
db.removeCollection('tests');

db.sync()

将所有定义的 Collections 同步给数据库。

Definition
class Database {
  sync(options?: Sequelize.SyncOptions): Promise<Database>;
}
Examples
await db.sync();
await db.sync({force: true});