diff --git a/packages/core/database/src/__tests__/eager-loading/eager-loading-tree.test.ts b/packages/core/database/src/__tests__/eager-loading/eager-loading-tree.test.ts index 6b070d4a1..69e492299 100644 --- a/packages/core/database/src/__tests__/eager-loading/eager-loading-tree.test.ts +++ b/packages/core/database/src/__tests__/eager-loading/eager-loading-tree.test.ts @@ -198,6 +198,7 @@ describe('Eager loading tree', () => { model: User.model, rootAttributes: findOptions.attributes, includeOption: findOptions.include, + db: db, }); await eagerLoadingTree.load(users.map((item) => item.id)); @@ -248,6 +249,7 @@ describe('Eager loading tree', () => { model: User.model, rootAttributes: findOptions.attributes, includeOption: findOptions.include, + db: db, }); await eagerLoadingTree.load([1, 2]); @@ -298,6 +300,7 @@ describe('Eager loading tree', () => { model: User.model, rootAttributes: findOptions.attributes, includeOption: findOptions.include, + db: db, }); await eagerLoadingTree.load(users.map((item) => item.id)); @@ -353,6 +356,7 @@ describe('Eager loading tree', () => { model: Post.model, rootAttributes: findOptions.attributes, includeOption: findOptions.include, + db: db, }); await eagerLoadingTree.load([1, 2]); @@ -415,6 +419,7 @@ describe('Eager loading tree', () => { model: Post.model, rootAttributes: findOptions.attributes, includeOption: findOptions.include, + db: db, }); await eagerLoadingTree.load([1, 2]); const root = eagerLoadingTree.root; @@ -517,6 +522,7 @@ describe('Eager loading tree', () => { model: User.model, rootAttributes: findOptions.attributes, includeOption: findOptions.include, + db: db, }); expect(eagerLoadingTree.root.children).toHaveLength(1); diff --git a/packages/core/database/src/__tests__/inhertits/collection-inherits.test.ts b/packages/core/database/src/__tests__/inhertits/collection-inherits.test.ts index d82220366..11f45bb71 100644 --- a/packages/core/database/src/__tests__/inhertits/collection-inherits.test.ts +++ b/packages/core/database/src/__tests__/inhertits/collection-inherits.test.ts @@ -70,6 +70,64 @@ pgOnly()('collection inherits', () => { expect(db.referenceMap.getReferences('root-target')).toHaveLength(1); }); + it('should append collection name in eager load', async () => { + const rootCollection = db.collection({ + name: 'assoc', + fields: [{ name: 'name', type: 'string' }], + }); + + const childCollection = db.collection({ + name: 'child', + inherits: ['assoc'], + }); + + const User = db.collection({ + name: 'users', + fields: [ + { + name: 'name', + type: 'string', + }, + { + name: 'assocs', + type: 'hasMany', + target: 'assoc', + }, + ], + }); + + await db.sync(); + + const child = await childCollection.repository.create({ + values: { + name: 'child1', + }, + }); + + await User.repository.create({ + values: { + name: 'user1', + assocs: [ + { + id: child.get('id'), + }, + ], + }, + }); + + const users = await User.repository.find({ + appends: ['assocs'], + }); + + const user = users[0]; + + const assoc = user.get('assocs')[0]; + expect(assoc.get('__tableName')).toEqual(childCollection.model.tableName); + expect(assoc.get('__schemaName')).toEqual(childCollection.collectionSchema()); + + expect(user.get('assocs')[0].get('__collection')).toBe('child'); + }); + it('should list data filtered by child type', async () => { const rootCollection = db.collection({ name: 'root', diff --git a/packages/core/database/src/eager-loading/eager-loading-tree.ts b/packages/core/database/src/eager-loading/eager-loading-tree.ts index 451a62ebb..57d27a436 100644 --- a/packages/core/database/src/eager-loading/eager-loading-tree.ts +++ b/packages/core/database/src/eager-loading/eager-loading-tree.ts @@ -1,5 +1,8 @@ import { Association, HasOne, Includeable, Model, ModelStatic, Transaction } from 'sequelize'; import lodash from 'lodash'; +import Database from '../database'; +import { OptionsParser } from '../options-parser'; +import { appendChildCollectionNameAfterRepositoryFind } from '../listeners/append-child-collection-name-after-repository-find'; interface EagerLoadingNode { model: ModelStatic; @@ -10,10 +13,29 @@ interface EagerLoadingNode { parent?: EagerLoadingNode; instances?: Array; order?: any; + inspectInheritAttribute?: boolean; } +const pushAttribute = (node, attribute) => { + if (lodash.isArray(node.attributes) && !node.attributes.includes(attribute)) { + node.attributes.push(attribute); + } +}; + +const EagerLoadingNodeProto = { + afterBuild(db: Database) { + const collection = db.modelCollection.get(this.model); + + if (collection && collection.isParent()) { + OptionsParser.appendInheritInspectAttribute(this.attributes.include, collection); + this.inspectInheritAttribute = true; + } + }, +}; + export class EagerLoadingTree { public root: EagerLoadingNode; + db: Database; constructor(root: EagerLoadingNode) { this.root = root; @@ -24,23 +46,24 @@ export class EagerLoadingTree { rootAttributes: Array; rootOrder?: any; includeOption: Includeable | Includeable[]; + db: Database; }): EagerLoadingTree { - const { model, rootAttributes, includeOption } = options; + const { model, rootAttributes, includeOption, db } = options; - const root = { + const buildNode = (node) => { + Object.setPrototypeOf(node, EagerLoadingNodeProto); + node.afterBuild(db); + return node; + }; + + const root = buildNode({ model, association: null, rawAttributes: lodash.cloneDeep(rootAttributes), attributes: lodash.cloneDeep(rootAttributes), order: options.rootOrder, children: [], - }; - - const pushAttribute = (node, attribute) => { - if (lodash.isArray(node.attributes) && !node.attributes.includes(attribute)) { - node.attributes.push(attribute); - } - }; + }); const traverseIncludeOption = (includeOption, eagerLoadingTreeParent) => { const includeOptions = lodash.castArray(includeOption); @@ -59,14 +82,14 @@ export class EagerLoadingTree { const association = eagerLoadingTreeParent.model.associations[include.association]; const associationType = association.associationType; - const child = { + const child = buildNode({ model: association.target, association, rawAttributes: lodash.cloneDeep(include.attributes), attributes: lodash.cloneDeep(include.attributes), parent: eagerLoadingTreeParent, children: [], - }; + }); if (associationType == 'HasOne' || associationType == 'HasMany') { const { sourceKey, foreignKey } = association; @@ -97,7 +120,9 @@ export class EagerLoadingTree { traverseIncludeOption(includeOption, root); - return new EagerLoadingTree(root); + const tree = new EagerLoadingTree(root); + tree.db = db; + return tree; } async load(pks: Array, transaction?: Transaction) { @@ -297,7 +322,17 @@ export class EagerLoadingTree { await loadRecursive(this.root, pks); + const appendChildCollectionName = appendChildCollectionNameAfterRepositoryFind(this.db); + const setInstanceAttributes = (node) => { + if (node.inspectInheritAttribute) { + appendChildCollectionName({ + findOptions: {}, + data: node.instances, + dataCollection: this.db.modelCollection.get(node.model), + }); + } + const nodeRawAttributes = node.rawAttributes; if (!lodash.isArray(nodeRawAttributes)) { diff --git a/packages/core/database/src/options-parser.ts b/packages/core/database/src/options-parser.ts index d67d74a87..c9657b7b8 100644 --- a/packages/core/database/src/options-parser.ts +++ b/packages/core/database/src/options-parser.ts @@ -35,6 +35,23 @@ export class OptionsParser { this.context = context; } + static appendInheritInspectAttribute(include, collection): any { + include.push([ + Sequelize.literal(`(select relname from pg_class where pg_class.oid = "${collection.name}".tableoid)`), + '__tableName', + ]); + + include.push([ + Sequelize.literal(` + (SELECT n.nspname + FROM pg_class c + JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE c.oid = "${collection.name}".tableoid) + `), + '__schemaName', + ]); + } + isAssociation(key: string) { return this.model.associations[key] !== undefined; } @@ -109,23 +126,6 @@ export class OptionsParser { return filterParams; } - protected inheritFromSubQuery(include): any { - include.push([ - Sequelize.literal(`(select relname from pg_class where pg_class.oid = "${this.collection.name}".tableoid)`), - '__tableName', - ]); - - include.push([ - Sequelize.literal(` - (SELECT n.nspname - FROM pg_class c - JOIN pg_namespace n ON n.oid = c.relnamespace - WHERE c.oid = "${this.collection.name}".tableoid) - `), - '__schemaName', - ]); - } - protected parseFields(filterParams: any) { const appends = this.options?.appends || []; const except = []; @@ -142,13 +142,13 @@ export class OptionsParser { }; // out put all fields by default if (this.collection.isParent()) { - this.inheritFromSubQuery(attributes.include); + OptionsParser.appendInheritInspectAttribute(attributes.include, this.collection); } if (this.options?.fields) { attributes = []; if (this.collection.isParent()) { - this.inheritFromSubQuery(attributes); + OptionsParser.appendInheritInspectAttribute(attributes, this.collection); } // 将fields拆分为 attributes 和 appends diff --git a/packages/core/database/src/relation-repository/multiple-relation-repository.ts b/packages/core/database/src/relation-repository/multiple-relation-repository.ts index a99813fb4..ed044bd3a 100644 --- a/packages/core/database/src/relation-repository/multiple-relation-repository.ts +++ b/packages/core/database/src/relation-repository/multiple-relation-repository.ts @@ -65,6 +65,7 @@ export abstract class MultipleRelationRepository extends RelationRepository { rootAttributes: findOptions.attributes, includeOption: findOptions.include, rootOrder: findOptions.order, + db: this.db, }); await eagerLoadingTree.load( diff --git a/packages/core/database/src/relation-repository/single-relation-repository.ts b/packages/core/database/src/relation-repository/single-relation-repository.ts index 8f24519d4..c484511c4 100644 --- a/packages/core/database/src/relation-repository/single-relation-repository.ts +++ b/packages/core/database/src/relation-repository/single-relation-repository.ts @@ -71,6 +71,7 @@ export abstract class SingleRelationRepository extends RelationRepository { model: this.targetModel, rootAttributes: findOptions.attributes, includeOption: findOptions.include, + db: this.db, }); await eagerLoadingTree.load([templateModel.get(this.targetModel.primaryKeyAttribute)], transaction); diff --git a/packages/core/database/src/repository.ts b/packages/core/database/src/repository.ts index 826a96007..b320c9230 100644 --- a/packages/core/database/src/repository.ts +++ b/packages/core/database/src/repository.ts @@ -367,6 +367,7 @@ export class Repository