Skip to content
Open
Show file tree
Hide file tree
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
12 changes: 6 additions & 6 deletions Classes/Domain/Projection/Feature/ContentStream.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ private function createContentStream(ContentStreamId $contentStreamId, ?ContentS
{
return $this->client->runStatement(
Statement::create(
'CREATE (contentStream:ContentStream {contentStreamId: $contentStreamId, version: 0, sourceContentStreamVersion: $sourceVersion, closed: 0, hasChanges: 0})
'CREATE (contentStream:ContentStream {contentStreamId: $contentStreamId, version: 0, closed: 0, hasChanges: 0})
WITH contentStream
WHERE $sourceContentStreamId IS NOT NULL
OPTIONAL MATCH (sourceContentStream:ContentStream {contentStreamId: $sourceContentStreamId})
MERGE (contentStream)-[:SOURCE_CONTENT_STREAM]->(sourceContentStream)
MERGE (contentStream)-[:SOURCE_CONTENT_STREAM {sourceContentStreamVersion: sourceContentStream.version}]->(sourceContentStream)
RETURN contentStream',
[
'contentStreamId' => $contentStreamId->value,
Expand All @@ -31,11 +31,11 @@ private function createContentStream(ContentStreamId $contentStreamId, ?ContentS
);
}

private function closeContentStream(ContentStreamId $contentStreamId): SummarizedResult
private function closeContentStream(ContentStreamId $contentStreamId): void
{
return $this->client->runStatement(
$this->client->runStatement(
Statement::create(
'MATCH (contentStream:ContentStream {contentStreamId: $contentStreamId}) SET contentStream.closed = 1 RETURN contentStream',
'MATCH (contentStream:ContentStream {contentStreamId: $contentStreamId}) SET contentStream.closed = 1',
['contentStreamId' => $contentStreamId->value]
)
);
Expand Down Expand Up @@ -65,7 +65,7 @@ private function updateContentStreamVersion(ContentStreamId $contentStreamId, Ve
{
$this->client->runStatement(
Statement::create(
'MATCH (contentStream:ContentStream {contentStreamId: $contentStreamId}) SET contentStream.version = $version, contentStream.hasChanges = $hasChanges',
'MATCH (contentStream:ContentStream {contentStreamId: $contentStreamId}) SET contentStream.version = $version, contentStream.hasChanges = CASE WHEN contentStream.hasChanges = 1 THEN 1 ELSE $hasChanges END',
[
'contentStreamId' => $contentStreamId->value,
'version' => $version->value,
Expand Down
100 changes: 82 additions & 18 deletions Classes/Domain/Projection/Feature/HierarchyRelation.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,36 @@ private function addParentHierarchyRelation(
ContentStreamId $contentStreamId,
DimensionSpacePoint $dimensionSpacePoint,
int $position,
\DateTimeImmutable $lastModified,
\DateTimeImmutable $originalLastModified
): void {
$this->client->runStatement(
Statement::create(
'MATCH (childNode:Node) WHERE ID(childNode) = $childNodeAggregateId
MATCH (parentNode:Node|Root {aggregateId: $parentNodeAggregateId})-[:IS_CHILD {contentStreamId: $contentStreamId, dimensionSpacePointHash: $dimensionSpacePointHash}]->()
MATCH (parentNode:Node|Root {aggregateId: $parentNodeAggregateId})-[parentRel:IS_CHILD {contentStreamId: $contentStreamId, dimensionSpacePointHash: $dimensionSpacePointHash}]->()

// Get parent subtree tags and convert them to inherited tags for the child
WITH childNode, parentNode, parentRel,
CASE WHEN parentRel.subtreeTags IS NOT NULL
THEN apoc.convert.fromJsonMap(parentRel.subtreeTags)
ELSE {} END as parentTags

// Convert parent tags to inherited tags for child (true -> inherit, inherit -> inherit)
WITH childNode, parentNode, parentRel, parentTags,
apoc.map.fromPairs([key in keys(parentTags) WHERE parentTags[key] IN [true, "inherit"] | [key, "inherit"]]) as inheritedTags

CREATE (childNode)-[:IS_CHILD {
contentStreamId: $contentStreamId,
dimensionSpacePointHash: $dimensionSpacePointHash,
position: $position
}]->(parentNode)
SET childNode.lastModified = $lastModified
SET childNode.originalLastModified = $originalLastModified',
position: $position,
subtreeTags: CASE WHEN size(keys(inheritedTags)) > 0
THEN apoc.convert.toJson(inheritedTags)
ELSE null END
}]->(parentNode)',
[
'childNodeAggregateId' => $childNode->getId(),
'parentNodeAggregateId' => $parentNodeAggregateId->value,
'contentStreamId' => $contentStreamId->value,
'dimensionSpacePointHash' => $dimensionSpacePoint->hash,
'position' => $position,
'lastModified' => $lastModified->format(\DateTimeInterface::ATOM),
'originalLastModified' => $originalLastModified->format(\DateTimeInterface::ATOM),
]
)
);
Expand Down Expand Up @@ -76,6 +84,7 @@ private function copyHierarchyRelation(
Node $newParentNode,
DimensionSpacePoint $dimensionSpacePoint,
int $position,
bool $copyDisabledState = true,
): void
{
$this->client->runStatement(
Expand All @@ -84,21 +93,45 @@ private function copyHierarchyRelation(
MATCH (newChildNode:Node)
MATCH (newParentNode:Node)
WHERE ID(newChildNode) = $newChildNodeId AND ID(newParentNode) = $newParentNodeId

// Find parent node\'s relationship for tag inheritance using the same contentStreamId and dimensionSpacePointHash
OPTIONAL MATCH (newParentNode)-[parentRel:IS_CHILD {contentStreamId: rOld.contentStreamId, dimensionSpacePointHash: $dimensionSpacePointHash}]->()

// Get parent subtree tags and convert them to inherited tags for the child
WITH rOld, newChildNode, newParentNode, parentRel,
CASE WHEN parentRel.subtreeTags IS NOT NULL
THEN apoc.convert.fromJsonMap(parentRel.subtreeTags)
ELSE {} END as parentTags,
CASE WHEN rOld.subtreeTags IS NOT NULL AND $copySubtreeTags = TRUE
THEN apoc.convert.fromJsonMap(rOld.subtreeTags)
ELSE {} END as oldTags

// Convert parent tags to inherited tags for child (true -> inherit, inherit -> inherit)
WITH rOld, newChildNode, newParentNode, parentTags, oldTags,
apoc.map.fromPairs([key in keys(parentTags) WHERE parentTags[key] IN [true, "inherit"] | [key, "inherit"]]) as inheritedTags

// Merge inherited tags with old tags, with old tags taking precedence
WITH rOld, newChildNode, newParentNode, inheritedTags, oldTags,
apoc.map.merge(inheritedTags, CASE WHEN $copyDisabledState = TRUE
THEN oldTags
ELSE apoc.map.removeKey(oldTags, "disabled") END) as finalTags

CREATE (newChildNode)-[rNew:IS_CHILD {
contentStreamId: rOld.contentStreamId,
dimensionSpacePointHash: $dimensionSpacePointHash,
position: $position
}]->(newParentNode)
FOREACH (_ IN CASE WHEN rOld.subtreeTags IS NOT NULL AND $copySubtreeTags = TRUE THEN [1] ELSE [] END |
SET rNew.subtreeTags = rOld.subtreeTags
)',
position: $position,
subtreeTags: CASE WHEN size(keys(finalTags)) > 0
THEN apoc.convert.toJson(finalTags)
ELSE null END
}]->(newParentNode)',
[
'relationshipId' => $relationship->getId(),
'newChildNodeId' => $newChildNode->getId(),
'newParentNodeId' => $newParentNode->getId(),
'position' => $position,
'dimensionSpacePointHash' => $dimensionSpacePoint->hash,
'copySubtreeTags' => false,
'copySubtreeTags' => true,
'copyDisabledState' => $copyDisabledState,
]
)
);
Expand All @@ -108,19 +141,47 @@ private function moveChildHierarchyRelation(
Node $newChildNode,
Relationship $relationship,
int $position,
bool $copyDisabledState = true,
): void
{
$this->client->runStatement(
Statement::create(
'MATCH (newChildNode:Node) WHERE ID(newChildNode) = $newChildNodeId
MATCH ()-[relationship]->() WHERE ID(relationship) = $relationshipId
MATCH ()-[relationship]->(parentNode) WHERE ID(relationship) = $relationshipId

// Find parent node\'s relationship for tag inheritance using the same contentStreamId and dimensionSpacePointHash
OPTIONAL MATCH (parentNode)-[parentRel:IS_CHILD {contentStreamId: relationship.contentStreamId, dimensionSpacePointHash: relationship.dimensionSpacePointHash}]->()

// Get parent subtree tags and existing relationship tags
WITH newChildNode, relationship, parentNode, parentRel,
CASE WHEN parentRel.subtreeTags IS NOT NULL
THEN apoc.convert.fromJsonMap(parentRel.subtreeTags)
ELSE {} END as parentTags,
CASE WHEN relationship.subtreeTags IS NOT NULL
THEN apoc.convert.fromJsonMap(relationship.subtreeTags)
ELSE {} END as existingTags

// Convert parent tags to inherited tags for child (true -> inherit, inherit -> inherit)
WITH newChildNode, relationship, parentNode, parentTags, existingTags,
apoc.map.fromPairs([key in keys(parentTags) WHERE parentTags[key] IN [true, "inherit"] | [key, "inherit"]]) as inheritedTags

// Merge inherited tags with existing tags, with existing tags taking precedence
WITH newChildNode, relationship, inheritedTags, existingTags,
apoc.map.merge(inheritedTags, CASE WHEN $copyDisabledState = TRUE
THEN existingTags
ELSE apoc.map.removeKey(existingTags, "disabled") END) as finalTags

CALL apoc.refactor.from(relationship, newChildNode)
YIELD output as newRelationship
SET newRelationship.position = $position',
SET newRelationship.position = $position,
newRelationship.subtreeTags = CASE WHEN size(keys(finalTags)) > 0
THEN apoc.convert.toJson(finalTags)
ELSE null END',
[
'newChildNodeId' => $newChildNode->getId(),
'relationshipId' => $relationship->getId(),
'position' => $position,
'copyDisabledState' => $copyDisabledState,
]
)
);
Expand Down Expand Up @@ -168,7 +229,10 @@ private function addRootRelation(
'childNodeAggregateId' => $childNode->getId(),
'contentStreamId' => $contentStreamId->value,
'dimensionSpacePointHash' => $dimensionSpacePoint->hash,
'position' => Neo4jContentGraphProjection::RELATION_DEFAULT_OFFSET,
'position' => $this->projectionContentGraph->determineRootNodePosition(
$contentStreamId,
$dimensionSpacePoint,
),
'lastModified' => $lastModified->format(\DateTimeInterface::ATOM),
'originalLastModified' => $originalLastModified->format(\DateTimeInterface::ATOM),
]
Expand Down
36 changes: 25 additions & 11 deletions Classes/Domain/Projection/Feature/ReferenceRelation.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint;
use Neos\ContentRepository\Core\Feature\NodeReferencing\Dto\SerializedNodeReferences;
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId;
use Neos\ContentRepository\Core\SharedModel\Node\ReferenceName;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;

trait ReferenceRelation
Expand All @@ -21,19 +22,22 @@ private function clearReferenceRelations(
DimensionSpacePoint $dimensionSpacePoint,
\DateTimeImmutable $lastModified,
\DateTimeImmutable $originalLastModified,
SerializedNodeReferences $newReferences,
): void
{
$this->client->runStatement(
Statement::create(
'MATCH (sourceNode:Node {aggregateId: $aggregateId})-[:IS_CHILD {contentStreamId: $contentStreamId, dimensionSpacePointHash: $dimensionSpacePointHash}]->()
OPTIONAL MATCH (sourceNode)-[rel:REFERENCE]->()
WHERE rel.referenceName IN $referenceNames
DELETE rel
SET sourceNode.lastModified = $lastModified
SET sourceNode.originalLastModified = $originalLastModified',
[
'aggregateId' => $sourceNodeAggregateId->value,
'contentStreamId' => $contentStreamId->value,
'dimensionSpacePointHash' => $dimensionSpacePoint->hash,
'referenceNames' => array_map(fn(ReferenceName $referenceName) => $referenceName->value, $newReferences->getReferenceNames()),
'lastModified' => $lastModified->format(\DateTimeInterface::ATOM),
'originalLastModified' => $originalLastModified->format(\DateTimeInterface::ATOM),
]
Expand All @@ -52,32 +56,42 @@ private function createReferenceRelations(
foreach ($references as $reference) {
$position = 0;
foreach ($reference->references as $nodeReference) {
$this->client->runStatement(Statement::create(
$result = $this->client->runStatement(Statement::create(
'MATCH (sourceNode:Node {aggregateId: $aggregateId})-[:IS_CHILD {contentStreamId: $contentStreamId, dimensionSpacePointHash: $dimensionSpacePointHash}]->()
MATCH (targetNode:Node {aggregateId: $referencedNodeAggregateId})-[:IS_CHILD {contentStreamId: $contentStreamId, dimensionSpacePointHash: $dimensionSpacePointHash}]->()
MERGE (sourceNode)-[:REFERENCE {referenceName: $referenceName, position: $position}]->(targetNode)
SET sourceNode.lastModified = $lastModified
SET sourceNode.originalLastModified = $originalLastModified',
MERGE (sourceNode)-[newRef:REFERENCE {referenceName: $referenceName, position: $position}]->(targetNode)
RETURN newRef',
[
'aggregateId' => $sourceNodeAggregateId->value,
'contentStreamId' => $contentStreamId->value,
'dimensionSpacePointHash' => $dimensionSpacePoint->hash,
'referenceName' => $reference->referenceName->value,
'position' => $position,
'referencedNodeAggregateId' => $nodeReference->targetNodeAggregateId->value,
'lastModified' => $lastModified->format(\DateTimeInterface::ATOM),
'originalLastModified' => $originalLastModified->format(\DateTimeInterface::ATOM),
]
],
));
if (empty($result) || !$result->hasKey(0) || !$result->getAsCypherMap(0)->hasKey('newRef')) {
continue;
}
$referenceResult = $result->getAsCypherMap(0)->getAsRelationship('newRef');;
if ($nodeReference->properties->count() > 0) {
$this->client->runStatement(
Statement::create(
'MATCH ()-[rel]->() WHERE ID(rel) = $relId
SET rel.properties = $properties',
[
'properties' => json_encode($nodeReference->properties),
'relId' => $referenceResult->getId()
]
)
);
}
$position++;
}
}
}

private function copyReferenceRelations(
Node $sourceNode,
Node $targetNode,
): void
private function copyReferenceRelations(Node $sourceNode, Node $targetNode): void
{
$this->client->runStatement(
Statement::create(
Expand Down
Loading