feat(database): append child collection name after eager load (#1978)
* test: eager load with inherit collection name * feat: append child collection name after eager load * chore: call build eager loading tree
This commit is contained in:
parent
bc00bd161a
commit
57d47371da
@ -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);
|
||||
|
@ -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',
|
||||
|
@ -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<any>;
|
||||
@ -10,10 +13,29 @@ interface EagerLoadingNode {
|
||||
parent?: EagerLoadingNode;
|
||||
instances?: Array<Model>;
|
||||
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<string>;
|
||||
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<string | number>, 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)) {
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
|
@ -367,6 +367,7 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
||||
rootAttributes: opts.attributes,
|
||||
includeOption: opts.include,
|
||||
rootOrder: opts.order,
|
||||
db: this.database,
|
||||
});
|
||||
|
||||
await eagerLoadingTree.load(
|
||||
|
Loading…
Reference in New Issue
Block a user