Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BUGFIX: Improve performance on hierarchy relations #5273

Draft
wants to merge 4 commits into
base: 9.0
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -355,27 +355,34 @@ public function countAncestorNodes(NodeAggregateId $entryNodeAggregateId, CountA

public function findClosestNode(NodeAggregateId $entryNodeAggregateId, FindClosestNodeFilter $filter): ?Node
{
// 1) Fetch the hierarchy relation of the initial node aggregate, where this node is stored as child node.
// The parent node anchor points to the first parent node of the given node aggregate. This is also used for
// the recursive part, to determine its ancestors.
$queryBuilderInitial = $this->createQueryBuilder()
->select('n.*, ph.subtreetags, ph.parentnodeanchor')
->from($this->nodeQueryBuilder->tableNames->node(), 'n')
// we need to join with the hierarchy relation, because we need the node name.
->innerJoin('n', $this->nodeQueryBuilder->tableNames->hierarchyRelation(), 'ph', 'n.relationanchorpoint = ph.childnodeanchor')
->select('ph.subtreetags, ph.childnodeanchor, ph.parentnodeanchor')
->from($this->nodeQueryBuilder->tableNames->hierarchyRelation(), 'ph')
->innerJoin('ph', $this->nodeQueryBuilder->tableNames->node(), 'n', 'n.relationanchorpoint = ph.childnodeanchor')
->andWhere('ph.contentstreamid = :contentStreamId')
->andWhere('ph.dimensionspacepointhash = :dimensionSpacePointHash')
->andWhere('n.nodeaggregateid = :entryNodeAggregateId');
$this->addSubtreeTagConstraints($queryBuilderInitial, 'ph');

// 2) Fetch the parent hierarchy recursive, starting with the anchor point of the resulting parent as child
// anchor point for the next iteration.
$queryBuilderRecursive = $this->createQueryBuilder()
->select('pn.*, h.subtreetags, h.parentnodeanchor')
->select('h.subtreetags, h.childnodeanchor, h.parentnodeanchor')
->from('ancestry', 'cn')
->innerJoin('cn', $this->nodeQueryBuilder->tableNames->node(), 'pn', 'pn.relationanchorpoint = cn.parentnodeanchor')
->innerJoin('pn', $this->nodeQueryBuilder->tableNames->hierarchyRelation(), 'h', 'h.childnodeanchor = pn.relationanchorpoint')
->innerJoin('cn', $this->nodeQueryBuilder->tableNames->hierarchyRelation(), 'h', 'h.childnodeanchor = cn.parentnodeanchor')
->where('h.contentstreamid = :contentStreamId')
->andWhere('h.dimensionspacepointhash = :dimensionSpacePointHash');
$this->addSubtreeTagConstraints($queryBuilderRecursive);

$queryBuilderCte = $this->nodeQueryBuilder->buildBasicNodesCteQuery($entryNodeAggregateId, $this->contentStreamId, $this->dimensionSpacePoint);
$queryBuilderCte = $this->nodeQueryBuilder->buildBasicNodesCteQuery($entryNodeAggregateId, $this->contentStreamId, $this->dimensionSpacePoint, 'ancestry', 'a');

// 3) Finally we join the node table to all collected child node anchor as we also need the starting node included for finding the "closest" node.
$queryBuilderCte->innerJoin('a', $this->nodeQueryBuilder->tableNames->node(), 'pn', 'pn.relationanchorpoint = a.childnodeanchor');
$this->nodeQueryBuilder->addNodeTypeCriteria($queryBuilderCte, ExpandedNodeTypeCriteria::create($filter->nodeTypes, $this->nodeTypeManager), 'pn');

$nodeRows = $this->fetchCteResults(
$queryBuilderInitial,
$queryBuilderRecursive,
Expand Down Expand Up @@ -553,31 +560,32 @@ private function buildSiblingsQuery(bool $preceding, NodeAggregateId $siblingNod
*/
private function buildAncestorNodesQueries(NodeAggregateId $entryNodeAggregateId, FindAncestorNodesFilter|CountAncestorNodesFilter|FindClosestNodeFilter $filter): array
{
// 1) Fetch the hierarchy relation of the initial node aggregate, where this node is stored as child node.
// The parent node anchor points to the first parent node of the given node aggregate. This is also used for
// the recursive part, to determine its ancestors.
$queryBuilderInitial = $this->createQueryBuilder()
->select('n.*, ph.subtreetags, ph.parentnodeanchor')
->from($this->nodeQueryBuilder->tableNames->node(), 'n')
// we need to join with the hierarchy relation, because we need the node name.
->innerJoin('n', $this->nodeQueryBuilder->tableNames->hierarchyRelation(), 'ch', 'ch.parentnodeanchor = n.relationanchorpoint')
->innerJoin('ch', $this->nodeQueryBuilder->tableNames->node(), 'c', 'c.relationanchorpoint = ch.childnodeanchor')
->innerJoin('n', $this->nodeQueryBuilder->tableNames->hierarchyRelation(), 'ph', 'n.relationanchorpoint = ph.childnodeanchor')
->where('ch.contentstreamid = :contentStreamId')
->andWhere('ch.dimensionspacepointhash = :dimensionSpacePointHash')
->select('ph.subtreetags, ph.parentnodeanchor, ph.childnodeanchor')
->from($this->nodeQueryBuilder->tableNames->hierarchyRelation(), 'ph')
->innerJoin('ph', $this->nodeQueryBuilder->tableNames->node(), 'c', 'c.relationanchorpoint = ph.childnodeanchor')
->andWhere('ph.contentstreamid = :contentStreamId')
->andWhere('ph.dimensionspacepointhash = :dimensionSpacePointHash')
->andWhere('c.nodeaggregateid = :entryNodeAggregateId');
$this->addSubtreeTagConstraints($queryBuilderInitial, 'ph');
$this->addSubtreeTagConstraints($queryBuilderInitial, 'ch');

// 2) Fetch the parent hierarchy recursive, starting with the anchor point of the resulting parent as child anchor
// point for the next iteration.
$queryBuilderRecursive = $this->createQueryBuilder()
->select('pn.*, h.subtreetags, h.parentnodeanchor')
->select('h.subtreetags, h.parentnodeanchor, h.childnodeanchor')
->from('ancestry', 'cn')
->innerJoin('cn', $this->nodeQueryBuilder->tableNames->node(), 'pn', 'pn.relationanchorpoint = cn.parentnodeanchor')
->innerJoin('pn', $this->nodeQueryBuilder->tableNames->hierarchyRelation(), 'h', 'h.childnodeanchor = pn.relationanchorpoint')
->innerJoin('cn', $this->nodeQueryBuilder->tableNames->hierarchyRelation(), 'h', 'h.childnodeanchor = cn.parentnodeanchor')
->where('h.contentstreamid = :contentStreamId')
->andWhere('h.dimensionspacepointhash = :dimensionSpacePointHash');
$this->addSubtreeTagConstraints($queryBuilderRecursive);

$queryBuilderCte = $this->nodeQueryBuilder->buildBasicNodesCteQuery($entryNodeAggregateId, $this->contentStreamId, $this->dimensionSpacePoint);
$queryBuilderCte = $this->nodeQueryBuilder->buildBasicNodesCteQuery($entryNodeAggregateId, $this->contentStreamId, $this->dimensionSpacePoint, 'ancestry', 'a');
// 3) Finally we join the node table to all collected parent node anchor
$queryBuilderCte->innerJoin('a', $this->nodeQueryBuilder->tableNames->node(), 'pn', 'pn.relationanchorpoint = a.childnodeanchor AND pn.nodeaggregateid <> :entryNodeAggregateId');

if ($filter->nodeTypes !== null) {
$this->nodeQueryBuilder->addNodeTypeCriteria($queryBuilderCte, ExpandedNodeTypeCriteria::create($filter->nodeTypes, $this->nodeTypeManager), 'pn');
}
Expand Down
Loading