Skip to content

Commit

Permalink
Merge pull request #4755 from mhsdesign/task/renameToSubgraph.findChi…
Browse files Browse the repository at this point in the history
…ldNodeByNodeName

!!! TASK: Substitute `findChildNodeConnectedThroughEdgeName` with `findNodeByPath `
  • Loading branch information
mhsdesign authored Nov 26, 2023
2 parents a8f20b4 + 05967a6 commit 6fb7940
Show file tree
Hide file tree
Showing 18 changed files with 103 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,10 @@ public function findParentNode(NodeAggregateId $childNodeAggregateId): ?Node
return $this->fetchNode($queryBuilder);
}

public function findNodeByPath(NodePath $path, NodeAggregateId $startingNodeAggregateId): ?Node
public function findNodeByPath(NodePath|NodeName $path, NodeAggregateId $startingNodeAggregateId): ?Node
{
$path = $path instanceof NodeName ? NodePath::fromNodeNames($path) : $path;

$startingNode = $this->findNodeById($startingNodeAggregateId);

return $startingNode
Expand All @@ -229,7 +231,12 @@ public function findNodeByAbsolutePath(AbsoluteNodePath $path): ?Node
: null;
}

public function findChildNodeConnectedThroughEdgeName(NodeAggregateId $parentNodeAggregateId, NodeName $edgeName): ?Node
/**
* Find a single child node by its name
*
* @return Node|null the node that is connected to its parent with the specified $nodeName, or NULL if no matching node exists or the parent node is not accessible
*/
private function findChildNodeConnectedThroughEdgeName(NodeAggregateId $parentNodeAggregateId, NodeName $nodeName): ?Node
{
$queryBuilder = $this->createQueryBuilder()
->select('cn.*, h.name, h.contentstreamid')
Expand All @@ -239,7 +246,7 @@ public function findChildNodeConnectedThroughEdgeName(NodeAggregateId $parentNod
->where('pn.nodeaggregateid = :parentNodeAggregateId')->setParameter('parentNodeAggregateId', $parentNodeAggregateId->value)
->andWhere('h.contentstreamid = :contentStreamId')->setParameter('contentStreamId', $this->contentStreamId->value)
->andWhere('h.dimensionspacepointhash = :dimensionSpacePointHash')->setParameter('dimensionSpacePointHash', $this->dimensionSpacePoint->hash)
->andWhere('h.name = :edgeName')->setParameter('edgeName', $edgeName->value);
->andWhere('h.name = :edgeName')->setParameter('edgeName', $nodeName->value);
$this->addRestrictionRelationConstraints($queryBuilder, 'cn');
return $this->fetchNode($queryBuilder);
}
Expand Down Expand Up @@ -472,7 +479,6 @@ private function findNodeByPathFromStartingNode(NodePath $path, Node $startingNo
$currentNode = $startingNode;

foreach ($path->getParts() as $edgeName) {
// id exists here :)
$currentNode = $this->findChildNodeConnectedThroughEdgeName($currentNode->nodeAggregateId, $edgeName);
if ($currentNode === null) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,10 @@ public function findParentNode(NodeAggregateId $childNodeAggregateId): ?Node
) : null;
}

public function findNodeByPath(NodePath $path, NodeAggregateId $startingNodeAggregateId): ?Node
public function findNodeByPath(NodePath|NodeName $path, NodeAggregateId $startingNodeAggregateId): ?Node
{
$path = $path instanceof NodeName ? NodePath::fromNodeNames($path) : $path;

$startingNode = $this->findNodeById($startingNodeAggregateId);

return $startingNode
Expand All @@ -287,9 +289,9 @@ public function findNodeByAbsolutePath(AbsoluteNodePath $path): ?Node
: null;
}

public function findChildNodeConnectedThroughEdgeName(
private function findChildNodeConnectedThroughEdgeName(
NodeAggregateId $parentNodeAggregateId,
NodeName $edgeName
NodeName $nodeName
): ?Node {
$query = HypergraphChildQuery::create(
$this->contentStreamId,
Expand All @@ -298,7 +300,7 @@ public function findChildNodeConnectedThroughEdgeName(
);
$query = $query->withDimensionSpacePoint($this->dimensionSpacePoint)
->withRestriction($this->visibilityConstraints)
->withChildNodeName($edgeName);
->withChildNodeName($nodeName);

$nodeRow = $query->execute($this->getDatabaseConnection())->fetchAssociative();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@contentrepository @adapters=DoctrineDBAL,Postgres
Feature: Find nodes using the findChildNodeConnectedThroughEdgeName query
Feature: Find nodes using the findNodeByPath query with node name as path argument

Background:
Given using the following content dimensions:
Expand Down Expand Up @@ -88,15 +88,15 @@ Feature: Find nodes using the findChildNodeConnectedThroughEdgeName query
And the graph projection is fully up to date

Scenario:
# findChildNodeConnectedThroughEdgeName queries without results
When I execute the findChildNodeConnectedThroughEdgeName query for parent node aggregate id "non-existing" and edge name "non-existing" I expect no node to be returned
When I execute the findChildNodeConnectedThroughEdgeName query for parent node aggregate id "home" and edge name "non-existing" I expect no node to be returned
When I execute the findChildNodeConnectedThroughEdgeName query for parent node aggregate id "non-existing" and edge name "home" I expect no node to be returned
# findNodeByPath queries without results
When I execute the findNodeByPath query for parent node aggregate id "non-existing" and node name "non-existing" as path I expect no node to be returned
When I execute the findNodeByPath query for parent node aggregate id "home" and node name "non-existing" as path I expect no node to be returned
When I execute the findNodeByPath query for parent node aggregate id "non-existing" and node name "home" as path I expect no node to be returned
# node "a2a2" is disabled and should not be returned
When I execute the findChildNodeConnectedThroughEdgeName query for parent node aggregate id "a2a" and edge name "a2a2" I expect no node to be returned
When I execute the findNodeByPath query for parent node aggregate id "a2a" and node name "a2a2" as path I expect no node to be returned
# node "a2a2" is disabled and should not lead to results if specified as parent node id
When I execute the findChildNodeConnectedThroughEdgeName query for parent node aggregate id "a2a2" and edge name "a2a2a" I expect no node to be returned
When I execute the findNodeByPath query for parent node aggregate id "a2a2" and node name "a2a2a" as path I expect no node to be returned

# findChildNodeConnectedThroughEdgeName queries with results
When I execute the findChildNodeConnectedThroughEdgeName query for parent node aggregate id "home" and edge name "contact" I expect the node "contact" to be returned
When I execute the findChildNodeConnectedThroughEdgeName query for parent node aggregate id "a2a" and edge name "a2a1" I expect the node "a2a1" to be returned
# findNodeByPath queries with results
When I execute the findNodeByPath query for parent node aggregate id "home" and node name "contact" as path I expect the node "contact" to be returned
When I execute the findNodeByPath query for parent node aggregate id "a2a" and node name "a2a1" as path I expect the node "a2a1" to be returned
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public function withInitialPropertyValues(PropertyValuesToWrite $newInitialPrope
* a tethered node aggregate id, you need to generate the child node aggregate ids in advance.
*
* _Alternatively you would need to fetch the created tethered node first from the subgraph.
* {@see ContentSubgraphInterface::findChildNodeConnectedThroughEdgeName()}_
* {@see ContentSubgraphInterface::findNodeByPath()}_
*
* The helper method {@see NodeAggregateIdsByNodePaths::createForNodeType()} will generate recursively
* node aggregate ids for every tethered child node:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,9 @@ private function handleChangeNodeAggregateType(
$node->originDimensionSpacePoint->toDimensionSpacePoint(),
VisibilityConstraints::withoutRestrictions()
);
$tetheredNode = $subgraph->findChildNodeConnectedThroughEdgeName(
$node->nodeAggregateId,
$tetheredNodeName
$tetheredNode = $subgraph->findNodeByPath(
$tetheredNodeName,
$node->nodeAggregateId
);
if ($tetheredNode === null) {
$tetheredNodeAggregateId = $command->tetheredDescendantNodeAggregateIds
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public static function create(ContentStreamId $contentStreamId, NodeAggregateId
* a tethered node aggregate id, you need to generate the child node aggregate ids in advance.
*
* _Alternatively you would need to fetch the created tethered node first from the subgraph.
* {@see ContentSubgraphInterface::findChildNodeConnectedThroughEdgeName()}_
* {@see ContentSubgraphInterface::findNodeByPath()}_
*
* The helper method {@see NodeAggregateIdsByNodePaths::createForNodeType()} will generate recursively
* node aggregate ids for every tethered child node:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,19 @@
* Example:
* root path: '/<Neos.ContentRepository:Root>' results in
* ~ {"rootNodeTypeName": "Neos.ContentRepository:Root", "path": []}
* non-root path: '/<Neos.ContentRepository:Root>/my/site' results in
* ~ {"rootNodeTypeName": "Neos.ContentRepository:Root", "path": ["my","site"]}
* non-root path: '/<Neos.ContentRepository:Root>/my-site/main' results in
* ~ {"rootNodeTypeName": "Neos.ContentRepository:Root", "path": ["my-site", "main"]}
*
* It describes the hierarchy path of a node to and including its root node in a subgraph.
*
* To fetch a node via an absolute path use the subgraph: {@see ContentSubgraphInterface::findNodeByAbsolutePath()}
*
* ```php
* $subgraph->findNodeByAbsolutePath(
* AbsoluteNodePath::fromString("/<Neos.Neos:Sites>/my-site/main")
* )
* ```
*
* @api
*/
final class AbsoluteNodePath implements \JsonSerializable
Expand All @@ -49,8 +58,14 @@ public static function fromRootNodeTypeNameAndRelativePath(
}

/**
* The ancestors must be ordered with the root node first, so if you call this using
* {@see ContentSubgraphInterface::findAncestorNodes()}, you need to call ${@see Nodes::reverse()} first
* The ancestors must be ordered with the root node first.
*
* If you want to retrieve the path of a node using {@see ContentSubgraphInterface::findAncestorNodes()}, you need to reverse the order first {@see Nodes::reverse()}
*
* ```php
* $ancestors = $this->findAncestorNodes($leafNode->nodeAggregateId, FindAncestorNodesFilter::create())->reverse();
* $absoluteNodePath = AbsoluteNodePath::fromLeafNodeAndAncestors($leafNode, $ancestors);
* ```
*/
public static function fromLeafNodeAndAncestors(Node $leafNode, Nodes $ancestors): self
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,33 +163,18 @@ public function findParentNode(NodeAggregateId $childNodeAggregateId): ?Node
return $parentNode;
}

public function findNodeByPath(NodePath $path, NodeAggregateId $startingNodeAggregateId): ?Node
public function findNodeByPath(NodePath|NodeName $path, NodeAggregateId $startingNodeAggregateId): ?Node
{
// TODO implement runtime caches
// TODO: implement runtime caches
return $this->wrappedContentSubgraph->findNodeByPath($path, $startingNodeAggregateId);
}

public function findNodeByAbsolutePath(AbsoluteNodePath $path): ?Node
{
// TODO implement runtime caches
// TODO: implement runtime caches
return $this->wrappedContentSubgraph->findNodeByAbsolutePath($path);
}

public function findChildNodeConnectedThroughEdgeName(NodeAggregateId $parentNodeAggregateId, NodeName $edgeName): ?Node
{
$namedChildNodeCache = $this->inMemoryCache->getNamedChildNodeByNodeIdCache();
if ($namedChildNodeCache->contains($parentNodeAggregateId, $edgeName)) {
return $namedChildNodeCache->get($parentNodeAggregateId, $edgeName);
}
$node = $this->wrappedContentSubgraph->findChildNodeConnectedThroughEdgeName($parentNodeAggregateId, $edgeName);
if ($node === null) {
return null;
}
$namedChildNodeCache->add($parentNodeAggregateId, $edgeName, $node);
$this->inMemoryCache->getNodeByNodeAggregateIdCache()->add($node->nodeAggregateId, $node);
return $node;
}

public function findSucceedingSiblingNodes(NodeAggregateId $siblingNodeAggregateId, FindSucceedingSiblingNodesFilter $filter): Nodes
{
// TODO implement runtime caches
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,6 @@ public function findSucceedingSiblingNodes(NodeAggregateId $siblingNodeAggregate
*/
public function findPrecedingSiblingNodes(NodeAggregateId $siblingNodeAggregateId, Filter\FindPrecedingSiblingNodesFilter $filter): Nodes;

/**
* Find a single child node by its name
*
* @return Node|null the node that is connected to its parent with the specified $edgeName, or NULL if no matching node exists or the parent node is not accessible
*/
public function findChildNodeConnectedThroughEdgeName(NodeAggregateId $parentNodeAggregateId, NodeName $edgeName): ?Node;

/**
* Recursively find all nodes above the $entryNodeAggregateId that match the specified $filter and return them as a flat list
*/
Expand Down Expand Up @@ -181,10 +174,12 @@ public function countBackReferences(NodeAggregateId $nodeAggregateId, Filter\Cou
/**
* Find a single node underneath $startingNodeAggregateId that matches the specified $path
*
* If a node name as $path is given it will be treated as path with a single segment.
*
* NOTE: This operation is most likely to be deprecated since the concept of node paths is not really used in the core, and it has some logical issues
* @return Node|null the node that matches the given $path, or NULL if no node on that path is accessible
*/
public function findNodeByPath(NodePath $path, NodeAggregateId $startingNodeAggregateId): ?Node;
public function findNodeByPath(NodePath|NodeName $path, NodeAggregateId $startingNodeAggregateId): ?Node;

/**
* Find a single node underneath that matches the specified absolute $path
Expand All @@ -197,7 +192,7 @@ public function findNodeByAbsolutePath(AbsoluteNodePath $path): ?Node;
/**
* Determine the absolute path of a node
*
* @deprecated use ${@see self::findAncestorNodes()} instead
* @deprecated use {@see self::findAncestorNodes()} in combination with {@see AbsoluteNodePath::fromLeafNodeAndAncestors()} instead
* @throws \InvalidArgumentException if the node path could not be retrieved because it is inaccessible or contains no valid path. The latter can happen if any node in the hierarchy has no name
*/
public function retrieveNodePath(NodeAggregateId $nodeAggregateId): AbsoluteNodePath;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
* @param NodeTypeName $nodeTypeName The node's node type name; always set, even if unknown to the NodeTypeManager
* @param NodeType|null $nodeType The node's node type, null if unknown to the NodeTypeManager - @deprecated Don't rely on this too much, as the capabilities of the NodeType here will probably change a lot; Ask the {@see NodeTypeManager} instead
* @param PropertyCollection $properties All properties of this node. References are NOT part of this API; To access references, {@see ContentSubgraphInterface::findReferences()} can be used; To read the serialized properties use {@see PropertyCollection::serialized()}.
* @param NodeName|null $nodeName The optional name of this node {@see ContentSubgraphInterface::findChildNodeConnectedThroughEdgeName()}
* @param NodeName|null $nodeName The optionally named hierarchy relation to the node's parent.
* @param Timestamps $timestamps Creation and modification timestamps of this node
*/
private function __construct(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,23 @@
use Neos\ContentRepository\Core\SharedModel\Node\NodeName;

/**
* The relative node path is a collection of NodeNames. If it contains no elements, it is considered root.
* The relative node path is a collection of node names {@see NodeName}. If it contains no elements, it is considered root.
*
* Example:
* root path: '' is resolved to []
* non-root path: 'my/site' is resolved to ~ ['my', 'site']
* non-root path: 'my-document/main' is resolved to ~ ['my-document', 'main']
*
* It describes the hierarchy path of a node to an ancestor node in a subgraph.
*
* To fetch a node on a path use the subgraph: {@see ContentSubgraphInterface::findNodeByPath()}
*
* ```php
* $subgraph->findNodeByPath(
* NodePath::fromString("my-document/main"),
* $siteNodeAggregateId
* )
* ```
*
* @api
*/
final class NodePath implements \JsonSerializable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,25 @@
namespace Neos\ContentRepository\Core\SharedModel\Node;

use Behat\Transliterator\Transliterator;
use Neos\ContentRepository\Core\Projection\ContentGraph\NodePath;

/**
* The Node name is the "path part" of the node; i.e. when accessing the node "/foo" via path,
* The Node name is the "path part" of the node; i.e. when accessing the node "/foo" via path {@see NodePath},
* the node name is "foo".
*
* Semantically it describes the hierarchical relation of a node to its parent, e.g. "main" denotes the main child node.
*
* Multiple node names describe a node path {@see NodePath}
*
* To fetch the child node that is connected with the parent via the name "main" use the subgraph's: {@see ContentSubgraphInterface::findNodeByPath()}
*
* ```php
* $subgraph->findNodeByPath(
* NodeName::fromString("main"),
* $parentNodeAggregateId
* )
* ```
*
* @api
*/
final class NodeName implements \JsonSerializable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/

use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindChildNodesFilter;
use Neos\ContentRepository\Core\Projection\ContentGraph\NodePath;
use Neos\ContentRepository\Core\SharedModel\Node\NodeName;
use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\NodeType\NodeTypeCriteria;
use Neos\ContentRepository\Core\NodeType\NodeTypeNames;
Expand Down Expand Up @@ -136,18 +137,13 @@ protected function earlyOptimizationOfFilters(FlowQuery $flowQuery, array $parse
// Optimize property name filter if present
if (isset($filter['PropertyNameFilter']) || isset($filter['PathFilter'])) {
$nodePath = $filter['PropertyNameFilter'] ?? $filter['PathFilter'];
$nodePathSegments = explode('/', $nodePath);
/** @var Node $contextNode */
foreach ($flowQuery->getContext() as $contextNode) {
$currentPathSegments = $nodePathSegments;
$resolvedNode = $contextNode;
while (($nodePathSegment = array_shift($currentPathSegments)) && !is_null($resolvedNode)) {
$resolvedNode = $this->contentRepositoryRegistry->subgraphForNode($resolvedNode)
->findChildNodeConnectedThroughEdgeName(
$resolvedNode->nodeAggregateId,
NodeName::fromString($nodePathSegment)
$resolvedNode = $this->contentRepositoryRegistry->subgraphForNode($contextNode)
->findNodeByPath(
NodePath::fromString($nodePath),
$contextNode->nodeAggregateId,
);
}

if (!is_null($resolvedNode) && !isset($filteredOutputNodeIdentifiers[
$resolvedNode->nodeAggregateId->value
Expand Down
Loading

0 comments on commit 6fb7940

Please sign in to comment.