-
-
Notifications
You must be signed in to change notification settings - Fork 6.5k
Description
Issue description
Fetching relations on TreeRepository's method findDescendants() uses JOIN strategy instead of QUERY strategy to load relations, even if DataSource's relationLoadStrategy is set to 'query'
Expected Behavior
This is a custom implementation where fetching a TreeRepository with nested relations is required. The expected query built from this method should be a series of different queries to gather all the required rows
Actual Behavior
The raw query results in a complex query with multiple LEFT JOINS to fetch all the corresponding relations. This is causing a memory heap allocation error on my application.
This is the resulting query:
web: query: SELECTtreeEntity.idAStreeEntity_id, treeEntity.deletedAtAStreeEntity_deletedAt, treeEntity.nameAStreeEntity_name, treeEntity.valueAStreeEntity_value, treeEntity.value_typeAStreeEntity_value_type, treeEntity.typeAStreeEntity_type, treeEntity.parentIdAStreeEntity_parentId, treeEntity.priorityAStreeEntity_priority, treeEntity.mpathAStreeEntity_mpath, treeEntity__rules.idAStreeEntity__rules_id, treeEntity__rules.deletedAtAStreeEntity__rules_deletedAt, treeEntity__rules.factIdAStreeEntity__rules_factId, treeEntity__rules.actionAStreeEntity__rules_action, treeEntity__rules.valueAStreeEntity__rules_value, treeEntity__rules.nodeIdAStreeEntity__rules_nodeId, treeEntity__rules__fact.idAStreeEntity__rules__fact_id, treeEntity__rules__fact.deletedAtAStreeEntity__rules__fact_deletedAt, treeEntity__rules__fact.nameAStreeEntity__rules__fact_name, treeEntity__rules__fact.keyAStreeEntity__rules__fact_key, treeEntity__rules__fact.hintAStreeEntity__rules__fact_hint, treeEntity__rules__fact.isActiveAStreeEntity__rules__fact_isActive, treeEntity__rules__fact.typeAStreeEntity__rules__fact_type, treeEntity__rules__fact.treeIdAStreeEntity__rules__fact_treeId, treeEntity__rules_fact.idAStreeEntity__rules_fact_id, treeEntity__rules_fact.deletedAtAStreeEntity__rules_fact_deletedAt, treeEntity__rules_fact.nameAStreeEntity__rules_fact_name, treeEntity__rules_fact.keyAStreeEntity__rules_fact_key, treeEntity__rules_fact.hintAStreeEntity__rules_fact_hint, treeEntity__rules_fact.isActiveAStreeEntity__rules_fact_isActive, treeEntity__rules_fact.typeAStreeEntity__rules_fact_type, treeEntity__rules_fact.treeIdAStreeEntity__rules_fact_treeId, treeEntity__evaluationNodes.idAStreeEntity__evaluationNodes_id, treeEntity__evaluationNodes.deletedAtAStreeEntity__evaluationNodes_deletedAt, treeEntity__evaluationNodes.processedAtAStreeEntity__evaluationNodes_processedAt, treeEntity__evaluationNodes.resultAStreeEntity__evaluationNodes_result, treeEntity__evaluationNodes.orderAStreeEntity__evaluationNodes_order, treeEntity__evaluationNodes.evaluationTypeAStreeEntity__evaluationNodes_evaluationType, treeEntity__evaluationNodes.nodeIdAStreeEntity__evaluationNodes_nodeId, treeEntity__evaluationNodes.evaluationIdAStreeEntity__evaluationNodes_evaluationId, treeEntity__evaluationNodes__evaluation.idAStreeEntity__evaluationNodes__evaluation_id, treeEntity__evaluationNodes__evaluation.deletedAtAStreeEntity__evaluationNodes__evaluation_deletedAt, treeEntity__evaluationNodes__evaluation.processedAtAStreeEntity__evaluationNodes__evaluation_processedAt, treeEntity__evaluationNodes__evaluation.simulatedAStreeEntity__evaluationNodes__evaluation_simulated, treeEntity__evaluationNodes__evaluation.payloadAStreeEntity__evaluationNodes__evaluation_payload, treeEntity__evaluationNodes__evaluation.resultAStreeEntity__evaluationNodes__evaluation_result, treeEntity__evaluationNodes__evaluation.referenceIdAStreeEntity__evaluationNodes__evaluation_referenceId, treeEntity__evaluationNodes__evaluation.referenceEntityAStreeEntity__evaluationNodes__evaluation_referenceEntity, treeEntity__evaluationNodes__evaluation.versionIdAStreeEntity__evaluationNodes__evaluation_versionIdFROMnode treeEntityLEFT JOINrule treeEntity__rulesONtreeEntity__rules.nodeId=treeEntity.id AND (treeEntity__rules.deletedAtIS NULL) LEFT JOINfact treeEntity__rules__factONtreeEntity__rules__fact.id=treeEntity__rules.factId AND (treeEntity__rules__fact.deletedAtIS NULL) LEFT JOINfact treeEntity__rules_factONtreeEntity__rules_fact.id=treeEntity__rules.factId AND (treeEntity__rules_fact.deletedAtIS NULL) LEFT JOINevaluationNode treeEntity__evaluationNodesONtreeEntity__evaluationNodes.nodeId=treeEntity.id AND (treeEntity__evaluationNodes.deletedAtIS NULL) LEFT JOINevaluation treeEntity__evaluationNodes__evaluationONtreeEntity__evaluationNodes__evaluation.id=treeEntity__evaluationNodes.evaluationId AND (treeEntity__evaluationNodes__evaluation.deletedAtIS NULL) WHERE (treeEntity.mpathLIKE CONCAT((SELECTNode.mpathASpathFROMnode NodeWHERE (Node.idIN (?) ) AND (Node.deletedAtIS NULL )), '%') ) AND (treeEntity.deletedAtIS NULL ) -- PARAMETERS: [19805]
Steps to reproduce
This is the implementation causing the problem.
let nodes = await this.dataSource.manager .getTreeRepository(Node) .findDescendants(node, { relations: [ 'rules', 'rules.fact', 'evaluationNodes', 'evaluationNodes.evaluation', ], });
Here are the entities involved (I'm just providing relevant cols)
`
@entity('rule')
export class Rule extends DefaultEntity {
@manytoone(() => Fact, (fact) => fact.rules, { eager: true })
fact?: Fact;
@Column()
factId?: number;
@ManyToOne(() => Node, (node) => node.rules)
node?: Node;
// Node.entity.ts
@entity('node')
@TreeDecorator('materialized-path')
export class Node extends DefaultEntity {
@TreeChildren({ cascade: true })
children?: Node[];
@Column()
parentId?: number;
@TreeParent()
parent?: Node;
@OneToMany(() => Rule, (rule) => rule.node, {
cascade: true,
onDelete: 'CASCADE',
})
rules?: Rule[];
@OneToMany(() => EvaluationNode, (evaluation) => evaluation.node)
evaluationNodes?: EvaluationNode[];
@OneToOne(() => Version, (version) => version.tree)
version?: Version;
}
`
`
@entity('evaluationNode')
export class EvaluationNode extends DefaultEntity {
@Column()
nodeId!: number;
@ManyToOne(() => Node, (node) => node.evaluationNodes)
node?: Node;
// PARENT
@ManyToOne(() => Evaluation, (evaluation) => evaluation.evaluationNodes)
evaluation?: Evaluation;
}
`
My Environment
| Dependency | Version |
|---|---|
| Operating System | macOS BigSur 11.3.1 |
| Node.js version | v14.18.0 |
| Typescript version | 4.5.5 |
| TypeORM version | 0.3.11 |
Additional Context
I know that a similar situation was addressed and resolved regarding the relationLoadStrategy for regular repositories. Maybe this change needs to be replicated into the TreeRepository? I could help writting the PR, but I just need a little insight on where to start
Relevant Database Driver(s)
- aurora-mysql
- aurora-postgres
- better-sqlite3
- cockroachdb
- cordova
- expo
- mongodb
- mysql
- nativescript
- oracle
- postgres
- react-native
- sap
- spanner
- sqlite
- sqlite-abstract
- sqljs
- sqlserver
Are you willing to resolve this issue by submitting a Pull Request?
Yes, I have the time, but I don't know how to start. I would need guidance.