NocoBase 数据源管理核心接口 ICollection 详解:从接口定义到模型实现的完整指南
【免费下载链接】nocobaseNocoBase is an open-source AI + no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase
ICollection是 NocoBase 数据源管理(Data Source Manager)模块中对数据模型的抽象接口,它统一描述了数据集合的名称、字段、关联关系以及数据读写入口(repository)。无论底层是 Sequelize 数据库还是其他数据源,上层代码都通过ICollection与模型打交道。读完本文,你将理解ICollection的完整接口契约、每个方法的签名与底层实现,并掌握如何通过CollectionManager获取、扩展集合以及操作字段的实战方法。
ICollection 在数据源管理架构中的位置
NocoBase 支持接入多种数据源(主数据库、外部数据库、API 数据源等),为此在 packages/core/data-source-manager 中抽象出一套面向数据源的统一接口层。从源码结构看,其核心调用链为:
DataSourceManager(管理多个 DataSource) └─ DataSource(数据源实例,持有 CollectionManager) └─ CollectionManager(实现 ICollectionManager) ├─ defineCollection() / getCollection() 返回 ICollection └─ ICollection(数据模型抽象) └─ IRepository(数据读写入口)其中DataSourceManager(见>export interface ICollection { repository: IRepository; updateOptions(options: CollectionOptions, mergeOptions?: MergeOptions): void; setField(name: string, options: any): IField; removeField(name: string): void; getFields(): Array<IField>; getField(name: string): IField; getFieldByField(field: string): IField; [key: string]: any; unavailableActions?: () => string[]; availableActions?: () => string[]; }
可以看到,接口包含三部分内容:
- 数据入口:
repository属性,类型为IRepository,负责对该集合数据的增删改查; - 模型元数据操作:
updateOptions()、setField()、removeField()、getFields()、getField(),管理集合的属性与字段; - 扩展点:索引签名
[key: string]: any允许实现类携带任意扩展属性;availableActions/unavailableActions用于声明集合可用的操作。
与接口密切相关的还有两个类型(同样定义在 types.ts):
export interface CollectionOptions { name: string; // 集合唯一名称 schema?: string; // 数据库 schema tableName: string; // 物理表名 title?: string; // 显示标题 template?: string; // 模板名 timestamps?: boolean; // 是否自动维护 createdAt / updatedAt filterTargetKey?: string | Array<string>; // 过滤主键 fields: FieldOptions[]; // 字段定义数组 autoGenId?: boolean; // 是否自动生成主键 view?: boolean; // 是否为视图集合 unsupportedFields?: UnsupportedFieldOptions[]; // 不支持的字段(逆向表时出现) [key: string]: any; } export interface FieldOptions { name: string; // 字段名 field: string; // 物理列名 rawType: string; // 原始数据库类型 type: string; // 字段类型(如 string、integer) description?: string; interface?: string; // 界面接口类型 uiSchema?: any; // UI Schema possibleTypes?: string[]; defaultValue?: any; primaryKey?: boolean; unique?: boolean; allowNull?: boolean; autoIncrement?: boolean; [key: string]: any; }成员详解:repository —— 集合的数据读写入口
repository是ICollection上唯一以属性形式暴露的核心成员,类型为IRepository。IRepository的契约定义于 types.ts:
export interface IRepository { find(options?: FindOptions): Promise<IModel[]>; findOne(options?: any): Promise<IModel>; count(options?: any): Promise<Number>; findAndCount(options?: any): Promise<[IModel[], Number]>; create(options: any): any; update(options: any): any; destroy(options: any): any; [key: string]: any; }从源码结构看,repository的注入由集合实现类完成:在 collection.ts 中,setRepository()通过CollectionManager.getRegisteredRepository()从注册表中取出 Repository 类并实例化:
protected setRepository(repository: any) { const RepositoryClass = this.collectionManager.getRegisteredRepository(repository || 'Repository'); this.repository = new RepositoryClass(this); }也就是说,一个ICollection实例被创建时,其repository即被绑定为该集合专属的数据操作对象;你也可以通过updateOptions({ repository })在运行时替换。默认的 Repository 基类位于 repository.ts,实现了IRepository的全部方法;而 Sequelize 场景下,实际的 Repository 由 @nocobase/database 提供(getRepository()见 sequelize-collection-manager.ts)。
API 逐个解析:五个方法签名与底层实现
updateOptions(options): 更新集合属性
签名:updateOptions(options: CollectionOptions, mergeOptions?: MergeOptions): void
用于更新Collection的属性(名称、表名、标题、时间戳等配置)。Collection实现类的逻辑见 collection.ts:
updateOptions(options: CollectionOptions, mergeOptions?: any) { const newOptions = { ...this.options, ...lodash.cloneDeep(options), }; this.options = newOptions; this.setFields(newOptions.fields || []); if (options.repository) { this.setRepository(options.repository); } return this; }注意三个关键行为:
- 新配置通过浅合并 + 深拷贝方式覆盖旧配置;
- 更新配置时会同步重建全部字段(调用
setFields),因此传入的fields若缺失则清空字段,更新时务必带上完整字段列表; - 若传入
repository,会同时替换数据入口。
在数据源管理中,extendCollection()正是基于此方法实现集合扩展(见 collection-manager.ts)。
setField(name, options): 设置字段
签名:setField(name: string, options: any): IField
向集合新增或覆盖一个字段,并返回字段实例。Collection实现类将其包装为CollectionField后存入fieldsMap(collection.ts):
setField(name: string, options: any) { const field = new CollectionField(options); this.fields.set(name, field); return field; }CollectionField(见 collection-field.ts)实现了IField接口,内部持有options: FieldOptions,并对外提供isRelationField()判断是否为关联字段——这是后续判断字段是否触发关联关系(belongsTo / hasMany 等)的重要依据。在数据库实现中,setField还会同步构建 Sequelize 模型属性。
removeField(name): 移除字段
签名:removeField(name: string): void
按名称删除字段,实现即从fieldsMap 中删除对应键(collection.ts):
removeField(name: string) { this.fields.delete(name); }getFields(): 获取全部字段
签名:getFields(): Array<IField>
返回该集合全部字段实例的数组:
getFields() { return [...this.fields.values()]; }返回值可直接用于遍历,例如批量读取每个字段的options.name、options.type做校验或生成 UI Schema。
getField(name): 按名称获取字段
签名:getField(name: string): IField
按字段名取单个字段,不存在时返回undefined。除文档列出的getField外,源码中的Collection还额外实现了getFieldByField(field)(collection.ts),它按物理列名(field)而非字段名查找,在数据库逆向(introspection)场景中非常实用:
getFieldByField(field: string): IField { for (const item of this.fields.values()) { if (item.options.field === field) { return item; } } return null; }从接口到实现:两种 CollectionManager 的差异化落地
ICollection是纯接口,具体实现取决于CollectionManager的实现类型。在 packages/core/data-source-manager/src 中存在两条实现路径:
1. 内存版Collection(通用/自定义数据源)
collection.ts 中的Collection类直接实现ICollection,以Map<string, IField>在内存中维护字段,构造时自动完成setRepository与字段初始化:
export class Collection implements ICollection { repository: IRepository; fields: Map<string, IField> = new Map<string, IField>(); constructor( protected options: CollectionOptions, public collectionManager: ICollectionManager, ) { this.setRepository(options.repository); if (options.fields) { this.setFields(options.fields); } } }它由CollectionManager(collection-manager.ts)通过defineCollection()、getCollection()等统一创建与管理。
2. Sequelize 版(主数据库 / 关系型数据库)
sequelize-collection-manager.ts 中的SequelizeCollectionManager则将ICollection的实现委托给 @nocobase/database 的Collection类(见 collection.ts)。该实现持有真实的 Sequelizemodel、repository,并在构造时完成modelInit()、字段注册与表名映射:
// packages/core/database/src/collection.ts export class Collection<...> extends EventEmitter { options: CollectionOptions; fields: Map<string, any>; model: ModelStatic<Model>; repository: Repository<...>; constructor(options: CollectionOptions, context: CollectionContext) { super(); this.context = context; this.options = options; this.checkOptions(options); this.bindFieldEventListener(); this.modelInit(); // ... 表名 / 模型映射注册 this.setFields(options.fields); this.setRepository(options.repository); this.setSortable(options.sortable); } }这也解释了为什么业务代码只依赖ICollection:无论底层是内存 Map 还是 Sequelize 模型,上层拿到的都是同一套字段管理与数据读写接口。
实战:如何获取与操作一个 ICollection
在实际开发中,通常通过CollectionManager(ICollectionManager)拿到ICollection实例:
// 1. 定义一个新集合,返回 ICollection const collection: ICollection = collectionManager.defineCollection({ name: 'posts', tableName: 'posts', fields: [ { name: 'id', type: 'integer', primaryKey: true, autoIncrement: true }, { name: 'title', type: 'string' }, ], }); // 2. 查询集合与字段 if (collectionManager.hasCollection('posts')) { const posts = collectionManager.getCollection('posts'); posts.getFields().forEach((f) => console.log(f.options.name, f.options.type)); const titleField = posts.getField('title'); } // 3. 运行时扩展集合(合并字段) collectionManager.extendCollection({ name: 'posts', fields: [ { name: 'authorId', type: 'integer' }, ], }); // 4. 通过 repository 读写数据 const rows = await collectionManager.getRepository('posts').find({ filter: { title: { $like: '%NocoBase%' } } }); const one = await collectionManager.getRepository('posts').create({ values: { title: 'Hello' } });几点实战注意:
defineCollection()后集合即注册到管理器;extendCollection()本质是getCollection(name).updateOptions(...)(collection-manager.ts),因此扩展时fields需提供完整字段列表,否则会清空原字段;- 字段类型、主键、唯一约束等通过
FieldOptions声明,具体取值范围与底层数据源类型映射有关,Sequelize 场景下可参考 packages/core/database 的字段类型注册; - 在多数据源应用中,
DataSourceManager会依据请求头x-data-source选择对应数据源(data-source-manager.ts),默认回落到main数据源,因此同一套ICollection抽象可平滑切换不同数据源。
小结
ICollection是 NocoBase 数据源管理模块中"数据模型"的统一抽象:repository打通数据读写,setField/removeField/getFields/getField完成字段的增删查,updateOptions支持运行时调整集合配置。理解它,是掌握 NocoBase 数据源架构、编写自定义数据源插件或深入CollectionManager二次开发的前提。接口契约见 types.ts,默认实现见 collection.ts,Sequelize 落地路径可继续阅读 sequelize-collection-manager.ts 与 packages/core/database/src/collection.ts。
【免费下载链接】nocobaseNocoBase is an open-source AI + no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考