Skip to content

Commit

Permalink
TASK: Normalize DimensionSpacePoints to separate table
Browse files Browse the repository at this point in the history
This should reduce stored data amounts, also make queries and workspace copies faster.
The hierarchy relations are now entirely limited size field types and should not
need external lookup like "text" fields do.
  • Loading branch information
kitsunet committed Mar 12, 2024
1 parent cadc549 commit 4274928
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\NodeRecord;
use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\NodeRelationAnchorPoint;
use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Repository\ContentGraph;
use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Repository\DimensionSpacePoints;
use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Repository\NodeFactory;
use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Repository\ProjectionContentGraph;
use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint;
Expand Down Expand Up @@ -83,6 +84,8 @@ final class DoctrineDbalContentGraphProjection implements ProjectionInterface, W

private DbalCheckpointStorage $checkpointStorage;

private DimensionSpacePoints $dimensionSpacePoints;

public function __construct(
private readonly DbalClientInterface $dbalClient,
private readonly NodeFactory $nodeFactory,
Expand All @@ -96,6 +99,8 @@ public function __construct(
$this->tableNamePrefix . '_checkpoint',
self::class
);

$this->dimensionSpacePoints = new DimensionSpacePoints($this->dbalClient->getConnection(), $this->tableNamePrefix);
}

protected function getProjectionContentGraph(): ProjectionContentGraph
Expand Down Expand Up @@ -175,6 +180,7 @@ private function truncateDatabaseTables(): void
$connection->executeQuery('TRUNCATE table ' . $this->tableNamePrefix . '_hierarchyrelation');
$connection->executeQuery('TRUNCATE table ' . $this->tableNamePrefix . '_referencerelation');
$connection->executeQuery('TRUNCATE table ' . $this->tableNamePrefix . '_restrictionrelation');
$connection->executeQuery('TRUNCATE table ' . $this->tableNamePrefix . '_dimensionspacepoints');
}

public function canHandle(EventInterface $event): bool
Expand Down Expand Up @@ -644,7 +650,6 @@ private function whenContentStreamWasForked(ContentStreamWasForked $event): void
childnodeanchor,
`name`,
position,
dimensionspacepoint,
dimensionspacepointhash,
contentstreamid
)
Expand All @@ -653,7 +658,6 @@ private function whenContentStreamWasForked(ContentStreamWasForked $event): void
h.childnodeanchor,
h.name,
h.position,
h.dimensionspacepoint,
h.dimensionspacepointhash,
"' . $event->newContentStreamId->value . '" AS contentstreamid
FROM
Expand Down Expand Up @@ -1111,6 +1115,7 @@ private function copyReferenceRelations(

private function whenDimensionSpacePointWasMoved(DimensionSpacePointWasMoved $event): void
{
$this->dimensionSpacePoints->insertDimensionSpacePoint($event->target);
$this->transactional(function () use ($event) {
// the ordering is important - we first update the OriginDimensionSpacePoints, as we need the
// hierarchy relations for this query. Then, we update the Hierarchy Relations.
Expand Down Expand Up @@ -1150,7 +1155,6 @@ function (NodeRecord $nodeRecord) use ($event) {
'
UPDATE ' . $this->tableNamePrefix . '_hierarchyrelation h
SET
h.dimensionspacepoint = :newDimensionSpacePoint,
h.dimensionspacepointhash = :newDimensionSpacePointHash
WHERE
h.dimensionspacepointhash = :originalDimensionSpacePointHash
Expand All @@ -1159,7 +1163,6 @@ function (NodeRecord $nodeRecord) use ($event) {
[
'originalDimensionSpacePointHash' => $event->source->hash,
'newDimensionSpacePointHash' => $event->target->hash,
'newDimensionSpacePoint' => $event->target->toJson(),
'contentStreamId' => $event->contentStreamId->value
]
);
Expand All @@ -1185,6 +1188,7 @@ function (NodeRecord $nodeRecord) use ($event) {

private function whenDimensionShineThroughWasAdded(DimensionShineThroughWasAdded $event): void
{
$this->dimensionSpacePoints->insertDimensionSpacePoint($event->target);
$this->transactional(function () use ($event) {
// 1) hierarchy relations
$this->getDatabaseConnection()->executeStatement(
Expand All @@ -1194,7 +1198,6 @@ private function whenDimensionShineThroughWasAdded(DimensionShineThroughWasAdded
childnodeanchor,
`name`,
position,
dimensionspacepoint,
dimensionspacepointhash,
contentstreamid
)
Expand All @@ -1203,7 +1206,6 @@ private function whenDimensionShineThroughWasAdded(DimensionShineThroughWasAdded
h.childnodeanchor,
h.name,
h.position,
:newDimensionSpacePoint AS dimensionspacepoint,
:newDimensionSpacePointHash AS dimensionspacepointhash,
h.contentstreamid
FROM
Expand All @@ -1214,7 +1216,6 @@ private function whenDimensionShineThroughWasAdded(DimensionShineThroughWasAdded
'contentStreamId' => $event->contentStreamId->value,
'sourceDimensionSpacePointHash' => $event->source->hash,
'newDimensionSpacePointHash' => $event->target->hash,
'newDimensionSpacePoint' => $event->target->toJson(),
]
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Neos\ContentGraph\DoctrineDbalAdapter;

use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Repository\DimensionSpacePoints;
use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Repository\NodeFactory;
use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Repository\ProjectionContentGraph;
use Neos\ContentRepository\Core\Factory\ContentRepositoryId;
Expand Down Expand Up @@ -40,13 +41,16 @@ public function build(
$projectionFactoryDependencies->contentRepositoryId
);



return new ContentGraphProjection(
new DoctrineDbalContentGraphProjection(
$this->dbalClient,
new NodeFactory(
$projectionFactoryDependencies->contentRepositoryId,
$projectionFactoryDependencies->nodeTypeManager,
$projectionFactoryDependencies->propertyConverter
$projectionFactoryDependencies->propertyConverter,
new DimensionSpacePoints($this->dbalClient->getConnection(), $tableNamePrefix)
),
$projectionFactoryDependencies->contentRepositoryId,
$projectionFactoryDependencies->nodeTypeManager,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\Index;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Types\Type;
Expand All @@ -29,7 +28,8 @@ public function buildSchema(AbstractSchemaManager $schemaManager): Schema
$this->createNodeTable(),
$this->createHierarchyRelationTable(),
$this->createReferenceRelationTable(),
$this->createRestrictionRelationTable()
$this->createRestrictionRelationTable(),
$this->createDimensionSpacePointsTable()
]);
}

Expand All @@ -38,7 +38,6 @@ private function createNodeTable(): Table
$table = new Table($this->tableNamePrefix . '_node', [
DbalSchemaFactory::columnForNodeAnchorPoint('relationanchorpoint')->setAutoincrement(true),
DbalSchemaFactory::columnForNodeAggregateId('nodeaggregateid')->setNotnull(false),
DbalSchemaFactory::columnForDimensionSpacePoint('origindimensionspacepoint')->setNotnull(false),
DbalSchemaFactory::columnForDimensionSpacePointHash('origindimensionspacepointhash')->setNotnull(false),
DbalSchemaFactory::columnForNodeTypeName('nodetypename'),
(new Column('properties', Type::getType(Types::TEXT)))->setNotnull(true)->setCustomSchemaOption('collation', self::DEFAULT_TEXT_COLLATION),
Expand All @@ -61,7 +60,6 @@ private function createHierarchyRelationTable(): Table
(new Column('name', Type::getType(Types::STRING)))->setLength(255)->setNotnull(false)->setCustomSchemaOption('collation', self::DEFAULT_TEXT_COLLATION),
(new Column('position', Type::getType(Types::INTEGER)))->setNotnull(true),
DbalSchemaFactory::columnForContentStreamId('contentstreamid')->setNotnull(true),
DbalSchemaFactory::columnForDimensionSpacePoint('dimensionspacepoint')->setNotnull(true),
DbalSchemaFactory::columnForDimensionSpacePointHash('dimensionspacepointhash')->setNotnull(true),
DbalSchemaFactory::columnForNodeAnchorPoint('parentnodeanchor'),
DbalSchemaFactory::columnForNodeAnchorPoint('childnodeanchor')
Expand All @@ -75,6 +73,17 @@ private function createHierarchyRelationTable(): Table
->addIndex(['contentstreamid', 'dimensionspacepointhash']);
}

private function createDimensionSpacePointsTable(): Table
{
$table = new Table($this->tableNamePrefix . '_dimensionspacepoints', [
DbalSchemaFactory::columnForDimensionSpacePointHash('hash')->setNotnull(true),
DbalSchemaFactory::columnForDimensionSpacePoint('dimensionspacepoint')->setNotnull(true)
]);

return $table
->setPrimaryKey(['hash']);
}

private function createReferenceRelationTable(): Table
{
$table = new Table($this->tableNamePrefix . '_referencerelation', [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
namespace Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection;

use Doctrine\DBAL\Connection;
use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Repository\DimensionSpacePoints;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint;
use Neos\ContentRepository\Core\SharedModel\Node\NodeName;
Expand Down Expand Up @@ -42,12 +43,14 @@ public function __construct(
*/
public function addToDatabase(Connection $databaseConnection, string $tableNamePrefix): void
{
$dimensionSpacePoints = new DimensionSpacePoints($databaseConnection, $tableNamePrefix);
$dimensionSpacePoints->insertDimensionSpacePoint($this->dimensionSpacePoint);

$databaseConnection->insert($tableNamePrefix . '_hierarchyrelation', [
'parentnodeanchor' => $this->parentNodeAnchor->value,
'childnodeanchor' => $this->childNodeAnchor->value,
'name' => $this->name?->value,
'contentstreamid' => $this->contentStreamId->value,
'dimensionspacepoint' => $this->dimensionSpacePoint->toJson(),
'dimensionspacepointhash' => $this->dimensionSpacePointHash,
'position' => $this->position
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
namespace Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Doctrine\DBAL\Types\Types;
use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Repository\DimensionSpacePoints;
use Neos\ContentRepository\Core\Feature\NodeModification\Dto\SerializedPropertyValues;
use Neos\ContentRepository\Core\Projection\ContentGraph\Timestamps;
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateClassification;
Expand Down Expand Up @@ -55,7 +57,6 @@ public function updateToDatabase(Connection $databaseConnection, string $tableNa
$tableNamePrefix . '_node',
[
'nodeaggregateid' => $this->nodeAggregateId->value,
'origindimensionspacepoint' => json_encode($this->originDimensionSpacePoint),
'origindimensionspacepointhash' => $this->originDimensionSpacePointHash,
'properties' => json_encode($this->properties),
'nodetypename' => $this->nodeTypeName->value,
Expand Down Expand Up @@ -138,9 +139,11 @@ public static function createNewInDatabase(
?NodeName $nodeName,
Timestamps $timestamps,
): self {
$dimensionSpacePoints = new DimensionSpacePoints($databaseConnection, $tableNamePrefix);
$dimensionSpacePoints->insertDimensionSpacePointByHashAndCoordinates($originDimensionSpacePointHash, $originDimensionSpacePoint);

$databaseConnection->insert($tableNamePrefix . '_node', [
'nodeaggregateid' => $nodeAggregateId->value,
'origindimensionspacepoint' => json_encode($originDimensionSpacePoint),
'origindimensionspacepointhash' => $originDimensionSpacePointHash,
'properties' => json_encode($properties),
'nodetypename' => $nodeTypeName->value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ public function hierarchyIntegrityIsProvided(): Result
}

$invalidlyHashedHierarchyRelationRecords = $this->client->getConnection()->executeQuery(
'SELECT * FROM ' . $this->tableNamePrefix . '_hierarchyrelation
WHERE dimensionspacepointhash != MD5(dimensionspacepoint)'
'SELECT * FROM ' . $this->tableNamePrefix . '_hierarchyrelation h LEFT JOIN ' . $this->tableNamePrefix . '_dimensionspacepoints dsp ON dsp.hash = h.dimensionspacepointhash
HAVING dsp.dimensionspacepoint IS NULL'
)->fetchAllAssociative();

foreach ($invalidlyHashedHierarchyRelationRecords as $record) {
Expand Down Expand Up @@ -179,7 +179,7 @@ public function restrictionsArePropagatedRecursively(): Result
{
$result = new Result();
$nodeRecordsWithMissingRestrictions = $this->client->getConnection()->executeQuery(
'SELECT c.nodeaggregateid, h.contentstreamid, h.dimensionspacepoint
'SELECT c.nodeaggregateid, h.contentstreamid, h.dimensionspacepointhash
FROM ' . $this->tableNamePrefix . '_hierarchyrelation h
INNER JOIN ' . $this->tableNamePrefix . '_node p
ON p.relationanchorpoint = h.parentnodeanchor
Expand All @@ -200,7 +200,7 @@ public function restrictionsArePropagatedRecursively(): Result
$result->addError(new Error(
'Node aggregate ' . $nodeRecord['nodeaggregateid']
. ' misses a restriction relation in content stream ' . $nodeRecord['contentstreamid']
. ' and dimension space point ' . $nodeRecord['dimensionspacepoint'],
. ' and dimension space point hash ' . $nodeRecord['dimensionspacepointhash'],
self::ERROR_CODE_NODE_HAS_MISSING_RESTRICTION
));
}
Expand Down Expand Up @@ -557,7 +557,7 @@ public function childNodeCoverageIsASubsetOfParentNodeCoverage(): Result
$result = new Result();
foreach ($this->findProjectedContentStreamIds() as $contentStreamId) {
$excessivelyCoveringNodeRecords = $this->client->getConnection()->executeQuery(
'SELECT n.nodeaggregateid, c.dimensionspacepoint
'SELECT n.nodeaggregateid, c.dimensionspacepointhash
FROM ' . $this->tableNamePrefix . '_hierarchyrelation c
INNER JOIN ' . $this->tableNamePrefix . '_node n
ON c.childnodeanchor = n.relationanchorpoint
Expand All @@ -576,7 +576,7 @@ public function childNodeCoverageIsASubsetOfParentNodeCoverage(): Result
$result->addError(new Error(
'Node aggregate ' . $excessivelyCoveringNodeRecord['nodeaggregateid']
. ' in content stream ' . $contentStreamId->value
. ' covers dimension space point ' . $excessivelyCoveringNodeRecord['dimensionspacepoint']
. ' covers dimension space point hash ' . $excessivelyCoveringNodeRecord['dimensionspacepointhash']
. ' but its parent does not.',
self::ERROR_CODE_CHILD_NODE_COVERAGE_IS_NO_SUBSET_OF_PARENT_NODE_COVERAGE
));
Expand All @@ -594,7 +594,7 @@ public function allNodesCoverTheirOrigin(): Result
$result = new Result();
foreach ($this->findProjectedContentStreamIds() as $contentStreamId) {
$nodeRecordsWithMissingOriginCoverage = $this->client->getConnection()->executeQuery(
'SELECT nodeaggregateid, origindimensionspacepoint
'SELECT nodeaggregateid, origindimensionspacepointhash
FROM ' . $this->tableNamePrefix . '_node n
INNER JOIN ' . $this->tableNamePrefix . '_hierarchyrelation h
ON h.childnodeanchor = n.relationanchorpoint
Expand All @@ -620,7 +620,7 @@ public function allNodesCoverTheirOrigin(): Result
$result->addError(new Error(
'Node aggregate ' . $nodeRecord['nodeaggregateid']
. ' in content stream ' . $contentStreamId->value
. ' does not cover its origin dimension space point ' . $nodeRecord['origindimensionspacepoint']
. ' does not cover its origin dimension space point hash ' . $nodeRecord['origindimensionspacepointhash']
. '.',
self::ERROR_CODE_NODE_DOES_NOT_COVER_ITS_ORIGIN
));
Expand Down Expand Up @@ -656,7 +656,7 @@ protected function findProjectedContentStreamIds(): iterable
protected function findProjectedDimensionSpacePoints(): DimensionSpacePointSet
{
$records = $this->client->getConnection()->executeQuery(
'SELECT DISTINCT dimensionspacepoint FROM ' . $this->tableNamePrefix . '_hierarchyrelation'
'SELECT dimensionspacepoint FROM ' . $this->tableNamePrefix . '_dimensionspacepoints'
)->fetchAllAssociative();

$records = array_map(function (array $record) {
Expand Down
Loading

0 comments on commit 4274928

Please sign in to comment.