Skip to content

Commit

Permalink
fix: Refactor neo4j queries
Browse files Browse the repository at this point in the history
  • Loading branch information
yaraslau-kavaliou committed Oct 25, 2023
1 parent 497bff6 commit d3fc50a
Show file tree
Hide file tree
Showing 4 changed files with 461 additions and 88 deletions.
94 changes: 62 additions & 32 deletions core/kernel/persistence/starsql/class.Class.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
use function WikibaseSolutions\CypherDSL\parameter;
use function WikibaseSolutions\CypherDSL\procedure;
use function WikibaseSolutions\CypherDSL\query;
use function WikibaseSolutions\CypherDSL\relationshipTo;
use function WikibaseSolutions\CypherDSL\variable;

class core_kernel_persistence_starsql_Class extends core_kernel_persistence_starsql_Resource implements
Expand All @@ -46,22 +47,29 @@ public function getSubClasses(core_kernel_classes_Class $resource, $recursive =
{
$uri = $resource->getUri();
$relationship = OntologyRdfs::RDFS_SUBCLASSOF;

$uriParameter = parameter();
$startNodeFilteringUri = node()
->withLabels(['Resource'])
->withVariable("startNode")
->withProperties(["uri" => $uriParameter]);
$startNode = node()
->withVariable("startNode");
$descendantNode = node()
->withVariable("descendantNode");
$descendantRelationship = relationshipTo()
->addType($relationship);
if (!empty($recursive)) {
$query = <<<CYPHER
MATCH (startNode:Resource {uri: \$uri})
MATCH (descendantNode)-[:`{$relationship}`*]->(startNode)
RETURN descendantNode.uri
CYPHER;
} else {
$query = <<<CYPHER
MATCH (startNode:Resource {uri: \$uri})
MATCH (descendantNode)-[:`{$relationship}`]->(startNode)
RETURN descendantNode.uri
CYPHER;
$descendantRelationship ->withArbitraryHops();
}

// \common_Logger::i('getSubClasses(): ' . var_export($query, true));
$results = $this->getPersistence()->run($query, ['uri' => $uri]);
$query = query()
->match($startNodeFilteringUri)
->match($descendantNode->relationship($descendantRelationship, $startNode))
->returning([$descendantNode->property('uri')])
->build();

$results = $this->getPersistence()->run($query, [$uriParameter->getParameter() => $uri]);
$returnValue = [];
foreach ($results as $result) {
$uri = $result->current();
Expand Down Expand Up @@ -92,21 +100,29 @@ public function getParentClasses(core_kernel_classes_Class $resource, $recursive
{
$uri = $resource->getUri();
$relationship = OntologyRdfs::RDFS_SUBCLASSOF;

$uriParameter = parameter();
$startNodeFilteringUri = node()
->withLabels(['Resource'])
->withVariable("startNode")
->withProperties(["uri" => $uriParameter]);
$startNode = node()
->withVariable("startNode");
$ancestorNode = node()
->withVariable("ancestorNode");
$descendantRelationship = relationshipTo()
->addType($relationship);
if (!empty($recursive)) {
$query = <<<CYPHER
MATCH (startNode:Resource {uri: \$uri})
MATCH (startNode)-[:`{$relationship}`*]->(ancestorNode)
RETURN ancestorNode.uri
CYPHER;
} else {
$query = <<<CYPHER
MATCH (startNode:Resource {uri: \$uri})
MATCH (startNode)-[:`{$relationship}`]->(ancestorNode)
RETURN ancestorNode.uri
CYPHER;
$descendantRelationship ->withArbitraryHops();
}

$results = $this->getPersistence()->run($query, ['uri' => $uri]);
$query = query()
->match($startNodeFilteringUri)
->match($startNode->relationship($descendantRelationship, $ancestorNode))
->returning([$ancestorNode->property('uri')])
->build();

$results = $this->getPersistence()->run($query, [$uriParameter->getParameter() => $uri]);
$returnValue = [];
foreach ($results as $result) {
$uri = $result->current();
Expand All @@ -121,12 +137,26 @@ public function getProperties(core_kernel_classes_Class $resource, $recursive =
{
$uri = $resource->getUri();
$relationship = OntologyRdfs::RDFS_DOMAIN;
$query = <<<CYPHER
MATCH (startNode:Resource {uri: \$uri})
MATCH (descendantNode)-[:`{$relationship}`]->(startNode)
RETURN descendantNode.uri
CYPHER;
$results = $this->getPersistence()->run($query, ['uri' => $uri]);

$uriParameter = parameter();
$startNodeFilteringUri = node()
->withLabels(['Resource'])
->withVariable('startNode')
->withProperties(["uri" => $uriParameter]);
$startNode = node()
->withVariable("startNode");
$descendantNode = node()
->withVariable('descendantNode');
$descendantRelationship = relationshipTo()
->addType($relationship);

$query = query()
->match($startNodeFilteringUri)
->match($descendantNode->relationship($descendantRelationship, $startNode))
->returning([$descendantNode->property('uri')])
->build();

$results = $this->getPersistence()->run($query, [$uriParameter->getParameter() => $uri]);
$returnValue = [];
foreach ($results as $result) {
$uri = $result->current();
Expand All @@ -137,7 +167,7 @@ public function getProperties(core_kernel_classes_Class $resource, $recursive =
$returnValue[$property->getUri()] = $property;
}

if ($recursive == true) {
if ($recursive) {
$parentClasses = $this->getParentClasses($resource, true);
foreach ($parentClasses as $parent) {
if ($parent->getUri() != OntologyRdfs::RDFS_CLASS) {
Expand Down
163 changes: 107 additions & 56 deletions core/kernel/persistence/starsql/class.Resource.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@
use oat\generis\model\OntologyRdf;
use oat\oatbox\session\SessionService;
use oat\oatbox\user\UserLanguageServiceInterface;
use WikibaseSolutions\CypherDSL\Clauses\WhereClause;
use WikibaseSolutions\CypherDSL\Query;
use WikibaseSolutions\CypherDSL\Clauses\SetClause;
use Zend\ServiceManager\ServiceLocatorInterface;

use function WikibaseSolutions\CypherDSL\literal;
use function WikibaseSolutions\CypherDSL\node;
use function WikibaseSolutions\CypherDSL\parameter;
use function WikibaseSolutions\CypherDSL\procedure;
Expand Down Expand Up @@ -179,26 +182,47 @@ public function setPropertyValue(
}
}
}
$uriParam = parameter();
$objectParam = parameter();
if ($property->isRelationship()) {
$query = <<<CYPHER
MATCH
(a:Resource), (b:Resource)
WHERE a.uri = \$uri AND b.uri = \$object
CREATE (a)-[r:`{$propertyUri}`]->(b)
RETURN type(r)
CYPHER;
$uriParam = parameter();
$firstResource = node('Resource')->withVariable("a");
$secondResource = node('Resource')->withVariable("b");
$firstNode = node()->withVariable("a");
$secondNode = node()->withVariable("b");
$relationship = relationshipTo()->withVariable("r");
$rURI = relationshipTo()->withTypes([$propertyUri])->withVariable("r");

$query = query()
->match([$firstResource, $secondResource])
->where([
$firstNode->property('uri')->equals($uriParam),
$secondNode->property('uri')->equals($objectParam)
])
->create($firstNode->relationship($rURI, $secondNode))
->returning(procedure()::raw('type', $relationship))
->build();
} elseif ($property->isLgDependent()) {
$query = <<<CYPHER
MATCH (n:Resource {uri: \$uri})
SET n.`{$propertyUri}` = coalesce(n.`{$propertyUri}`, []) + \$object
CYPHER;
$node = node('Resource')->withVariable('n')->withProperties(['uri' => $uriParam]);
$procedure = procedure()::raw('coalesce', [$node->property($propertyUri), []]);
$expression = Query::rawExpression(
sprintf('%s+ $%s', $procedure->toQuery(), $objectParam->getParameter())
);
$query = query()
->match($node)
->set($node->property($propertyUri)->replaceWith($expression))
->build();
} else {
$query = <<<CYPHER
MATCH (n:Resource {uri: \$uri})
SET n.`{$propertyUri}` = \$object
CYPHER;
$node = node('Resource')->withVariable('n')->withProperties(['uri' => $uriParam]);
$query = query()
->match($node)
->set($node->property($propertyUri)->replaceWith($objectParam))->build();
}
$this->getPersistence()->run($query, ['uri' => $uri, 'object' => $object]);

$this->getPersistence()->run($query, [
$uriParam->getParameter() => $uri,
$objectParam->getParameter() => $object
]);

return true;
}
Expand Down Expand Up @@ -320,60 +344,74 @@ public function removePropertyValues(
): ?bool {
$uri = $resource->getUri();
$propertyUri = $property->getUri();
$conditions = [];
$pattern = $options['pattern'] ?? null;
$isLike = !empty($options['like']);
$whereClause = new WhereClause();

if (!empty($pattern)) {
if (!is_array($pattern)) {
$pattern = [$pattern];
}

$multiCondition = "( ";
foreach ($pattern as $index => $token) {
$clause = null;
foreach ($pattern as $token) {
if (empty($token)) {
continue;
}
if ($index > 0) {
$multiCondition .= ' OR ';
}
if ($isLike) {
$multiCondition .= "n.`{$propertyUri}` =~ '" . str_replace('*', '.*', $token) . "'";
} else {
$multiCondition .= "n.`{$propertyUri}` = '$token'";
}
}
$conditions[] = "{$multiCondition} ) ";
}
$queryCondition = node()
->withVariable("n")
->withProperties(["uri" => $uri])
->property($propertyUri)
->regex(
$isLike
? str_replace('*', '.*', $token)
: $token
);

$assembledConditions = '';
foreach ($conditions as $i => $additionalCondition) {
if (empty($assembledConditions)) {
$assembledConditions .= " WHERE ( {$additionalCondition} ) ";
} else {
$assembledConditions .= " AND ( {$additionalCondition} ) ";
$clause = ($clause === null)
? $queryCondition
: $clause->or($queryCondition);

if ($clause != null) {
$whereClause->addExpression($clause);
}
}
}

$uriParameter = parameter();
if (!$property->isRelationship()) {
$query = <<<CYPHER
MATCH (n:Resource {uri: "{$uri}"})
{$assembledConditions}
REMOVE n.`{$propertyUri}`
RETURN n
CYPHER;
$nResource = node()
->withLabels(['Resource'])
->withVariable('n')
->withProperties(['uri' => $uriParameter]);

$node = node()
->withVariable('n')
->withProperties(['uri' => $uriParameter]);

$query = query()
->match($nResource)
->addClause($whereClause)
->remove($node->property($propertyUri))
->returning($node)
->build();
} else {
$query = <<<CYPHER
MATCH (n:Resource {uri: "{$uri}"})-[p:`{$propertyUri}`]->()
{$assembledConditions}
DELETE p
RETURN n
CYPHER;
$nResource = node('Resource')
->withVariable('n')
->withProperties(['uri' => $uriParameter]);

$query = query()
->match($nResource->relationshipTo(node(), $propertyUri, null, 'p'))
->addClause($whereClause)
->delete(raw('p'))
->returning($nResource)
->build();
}

//@FIXME if value is array, then query should be for update. Try to deduce if $prop->isLgDependent or isMultiple
//@FIXME if property is represented as node relationship, query should remove that instead

$this->getPersistence()->run($query);
$this->getPersistence()->run($query, [$uriParameter->getParameter() => $uri]);

return true;
}
Expand Down Expand Up @@ -513,14 +551,27 @@ public function getPropertiesValues(core_kernel_classes_Resource $resource, $pro
return [];
}

$query = <<<CYPHER
MATCH (resource:Resource)-[relationshipTo]->(relatedResource:Resource)
WHERE resource.uri = \$uri
RETURN resource,
collect({relationship: type(relationshipTo), relatedResourceUri: relatedResource.uri}) AS relationships
CYPHER;
$uriParameter = parameter();

$relatedResource = node('Resource')->withVariable('relatedResource');
$queryResource = node()
->withLabels(['Resource'])
->withVariable('resource');

$params = literal()::map([
'relationship' => procedure()::raw('type', Query::variable('relationshipTo')),
'relatedResourceUri' => $relatedResource->property('uri')
]);

$procedure = procedure()::raw('collect', $params)->alias('relationships');
$query = query()
->match($queryResource->relationshipTo($relatedResource, null, null, "relationshipTo"))
->where($queryResource->property('uri')->equals($uriParameter))
->returning([$queryResource, $procedure])
->build();

$results = $this->getPersistence()->run($query, [$uriParameter->getParameter() => $resource->getUri()]);

$results = $this->getPersistence()->run($query, ['uri' => $resource->getUri()]);
if ($results->isEmpty()) {
return [];
}
Expand Down
22 changes: 22 additions & 0 deletions test/integration/model/persistence/starsql/ClassTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -764,4 +764,26 @@ public function testGetInstancesPropertyValuesWithResourceProperties()
);
}
}

public function testGetSubclassWithSubclassesWithoutRecursion()
{
$class = new core_kernel_classes_Class(WidgetRdf::CLASS_URI_WIDGET);
$subClasses = $class->getSubClasses(false);
$subClass = $class->createSubClass('subclasslabel', 'subclasscomment', 'subclassuri.com');
$subClassesAfter = $class->getSubClasses(false);
$subClass->delete();

$this->assertCount(count($subClasses) + 1, $subClassesAfter);
}

public function testGetSubclassWithSubclassesWithRecursion()
{
$class = new core_kernel_classes_Class(WidgetRdf::CLASS_URI_WIDGET);
$subClasses = $class->getSubClasses(true);
$subclass = $class->createSubClass('subclasslabel', 'subclasscomment', 'subclassuri.com');
$subClassesAfter = $class->getSubClasses(true);
$subclass->delete();

$this->assertCount(count($subClasses) + 1, $subClassesAfter);
}
}
Loading

0 comments on commit d3fc50a

Please sign in to comment.