Skip to content
This repository has been archived by the owner on Jul 27, 2022. It is now read-only.

Commit

Permalink
ISAICP-6242: Use a newer version of the sparql patch.
Browse files Browse the repository at this point in the history
  • Loading branch information
idimopoulos committed Apr 6, 2021
1 parent 3c09621 commit 37851f3
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 3 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@
"Bundle classes support @see https://github.com/ec-europa/sparql_entity_storage/pull/11": "https://patch-diff.githubusercontent.com/raw/ec-europa/sparql_entity_storage/pull/11.diff",
"Allow routes to filter by bundle @see https://github.com/ec-europa/sparql_entity_storage/pull/32": "https://patch-diff.githubusercontent.com/raw/ec-europa/sparql_entity_storage/pull/32.diff",
"Allow to pass options to the serializer @see https://github.com/idimopoulos/sparql_entity_storage/pull/3": "https://patch-diff.githubusercontent.com/raw/idimopoulos/sparql_entity_storage/pull/3.diff",
"Support NOT EXISTS query @see https://github.com/idimopoulos/sparql_entity_storage/pull/4": "https://patch-diff.githubusercontent.com/raw/idimopoulos/sparql_entity_storage/pull/4.diff"
"Support NOT EXISTS query @see https://github.com/idimopoulos/sparql_entity_storage/pull/4": "resources/patch/sparql_entity_storage_support_not_exists.patch"
},
"drupal/subpathauto": {
"Subpaths with redirect not resolved @see https://www.drupal.org/project/subpathauto/issues/3175637": "https://git.drupalcode.org/project/subpathauto/-/merge_requests/1/diffs.diff"
Expand Down
4 changes: 2 additions & 2 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

143 changes: 143 additions & 0 deletions resources/patch/sparql_entity_storage_support_not_exists.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
diff --git a/src/Entity/Query/Sparql/Query.php b/src/Entity/Query/Sparql/Query.php
index 9215946..3097e69 100644
--- a/src/Entity/Query/Sparql/Query.php
+++ b/src/Entity/Query/Sparql/Query.php
@@ -355,6 +355,16 @@ class Query extends QueryBase implements SparqlQueryInterface {
if (!in_array($direction, ['ASC', 'DESC'])) {
throw new \RuntimeException('Only "ASC" and "DESC" are allowed as sort order.');
}
+
+ // Unlike the normal SQL queries where a column not defined can be used for
+ // sorting if exists in the table, in SPARQL, the sort argument must be
+ // defined in the WHERE clause. Any sort property, therefore, must will be
+ // included with an EXISTS condition.
+ // Also, the $idKey and $bundleKey properties cannot be added as triples as
+ // they cannot be the object of the triple.
+ if (!in_array($field, [$this->idKey, $this->bundleKey])) {
+ $this->exists($field);
+ }
return parent::sort($field, $direction, $langcode);
}

diff --git a/src/Entity/Query/Sparql/SparqlCondition.php b/src/Entity/Query/Sparql/SparqlCondition.php
index 582416f..da6bee1 100644
--- a/src/Entity/Query/Sparql/SparqlCondition.php
+++ b/src/Entity/Query/Sparql/SparqlCondition.php
@@ -70,7 +70,7 @@ class SparqlCondition extends ConditionFundamentals implements SparqlConditionIn
'ENDS WITH' => ['prefix' => 'FILTER(STRENDS(', 'suffix' => '))'],
'LIKE' => ['prefix' => 'FILTER(CONTAINS(', 'suffix' => '))'],
'NOT LIKE' => ['prefix' => 'FILTER(!CONTAINS(', 'suffix' => '))'],
- 'EXISTS' => ['prefix' => 'FILTER EXISTS {', 'suffix' => '}'],
+ 'EXISTS' => ['prefix' => '', 'suffix' => ''],
'NOT EXISTS' => ['prefix' => 'FILTER NOT EXISTS {', 'suffix' => '}'],
'<' => ['prefix' => '', 'suffix' => ''],
'>' => ['prefix' => '', 'suffix' => ''],
@@ -271,10 +271,6 @@ class SparqlCondition extends ConditionFundamentals implements SparqlConditionIn
'lang' => $lang,
'column' => $column,
];
-
- if (!in_array($operator, ['EXISTS', 'NOT EXISTS'])) {
- $this->requiresDefaultPattern = FALSE;
- }
}

return $this;
@@ -384,6 +380,7 @@ class SparqlCondition extends ConditionFundamentals implements SparqlConditionIn
}

$mappings = $this->fieldHandler->getFieldPredicates($entity_type_id, $field, $column);
+ $mappings = array_values(array_unique($mappings));
$field_name = $field . '__' . $column;
if (count($mappings) === 1) {
$this->fieldMappings[$field_name] = reset($mappings);
@@ -396,10 +393,10 @@ class SparqlCondition extends ConditionFundamentals implements SparqlConditionIn
// loaded by the database. There is no way that in a single request,
// the same predicate is found with a single and multiple mappings.
// There is no filter per bundle in the query.
- $this->fieldMappingConditions[] = [
+ $this->fieldMappingConditions[$field_name] = [
'field' => $field,
'column' => $column,
- 'value' => array_values(array_unique($mappings)),
+ 'value' => $mappings,
'operator' => 'IN',
];
}
@@ -504,7 +501,7 @@ class SparqlCondition extends ConditionFundamentals implements SparqlConditionIn

case 'EXISTS':
case 'NOT EXISTS':
- $this->addConditionFragment($this->compileExists($condition));
+ $this->compileExists($condition);
break;

case 'CONTAINS':
@@ -593,14 +590,31 @@ class SparqlCondition extends ConditionFundamentals implements SparqlConditionIn
*
* @param array $condition
* An array that contains the 'field', 'value', 'operator' values.
- *
- * @return string
- * A condition fragment string.
*/
- protected function compileExists(array $condition): string {
+ protected function compileExists(array $condition): void {
$prefix = self::$filterOperatorMap[$condition['operator']]['prefix'];
$suffix = self::$filterOperatorMap[$condition['operator']]['suffix'];
- return $prefix . self::ID_KEY . ' ' . $this->escapePredicate($this->fieldMappings[$condition['field']]) . ' ' . SparqlArg::toVar($condition['field']) . $suffix;
+
+ $field_predicate = $this->fieldMappings[$condition['field']];
+ $condition_string = self::ID_KEY . ' ' . $this->escapePredicate($field_predicate) . ' ' . SparqlArg::toVar($condition['field']);
+
+ if (isset($this->fieldMappingConditions[$condition['field']])) {
+ $mapping_condition = $this->fieldMappingConditions[$condition['field']];
+ $mapping_condition['value'] = SparqlArg::toResourceUris($mapping_condition['value']);
+ $mapping_condition['field'] = $field_predicate;
+ $condition_string .= ' . ' . $this->compileValuesFilter($mapping_condition);
+ }
+
+ $key = array_search($condition_string, $this->conditionFragments);
+ // Only add a condition if the mapping condition is not found. If found,
+ // replace it, in order to avoid creating an EXISTS and NOT EXISTS on the
+ // same property.
+ if ($key !== FALSE) {
+ $this->conditionFragments[$key] = $prefix . $this->conditionFragments[$key] . $suffix;
+ }
+ else {
+ $this->addConditionFragment($prefix . $condition_string . $suffix);
+ }
}

/**
diff --git a/tests/src/Kernel/SparqlEntityQueryTest.php b/tests/src/Kernel/SparqlEntityQueryTest.php
index a8fe4b7..ace3e25 100644
--- a/tests/src/Kernel/SparqlEntityQueryTest.php
+++ b/tests/src/Kernel/SparqlEntityQueryTest.php
@@ -448,6 +448,26 @@ class SparqlEntityQueryTest extends SparqlKernelTestBase {
];
}

+ /**
+ * Tests the NOT EXISTS operator.
+ */
+ public function testNotExists() {
+ $entity = SparqlTest::create([
+ 'id' => 'http://fruit.example.com/not_exists',
+ 'title' => 'fruit title not exists',
+ 'type' => 'fruit',
+ ]);
+ $entity->save();
+ $this->entities[] = $entity;
+
+ $results = $this->getQuery()
+ ->condition('type', 'fruit')
+ ->notExists('text')
+ ->execute();
+
+ $this->assertNotEmpty($results);
+ }
+
/**
* Asserts that arrays are identical.
*/

0 comments on commit 37851f3

Please sign in to comment.