summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/QueryEngine.php
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/QueryEngine.php')
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/QueryEngine.php272
1 files changed, 272 insertions, 0 deletions
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/QueryEngine.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/QueryEngine.php
new file mode 100644
index 00000000..046f4ce8
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/QueryEngine/QueryEngine.php
@@ -0,0 +1,272 @@
+<?php
+
+namespace SMW\SPARQLStore\QueryEngine;
+
+use RuntimeException;
+use SMW\Exporter\Element;
+use SMW\Query\DebugFormatter;
+use SMW\Query\Language\ThingDescription;
+use SMW\QueryEngine as QueryEngineInterface;
+use SMW\SPARQLStore\QueryEngine\Condition\Condition;
+use SMW\SPARQLStore\QueryEngine\Condition\FalseCondition;
+use SMW\SPARQLStore\QueryEngine\Condition\SingletonCondition;
+use SMW\SPARQLStore\RepositoryConnection;
+use SMWQuery as Query;
+use SMWQueryResult as QueryResult;
+
+/**
+ * Class mapping SMWQuery objects to SPARQL, and for controlling the execution
+ * of these queries to obtain suitable QueryResult objects.
+ *
+ * @license GNU GPL v2+
+ * @since 2.0
+ *
+ * @author Markus Krötzsch
+ * @author mwjames
+ */
+class QueryEngine implements QueryEngineInterface {
+
+ /**
+ * The name of the SPARQL variable that represents the query result.
+ */
+ const RESULT_VARIABLE = 'result';
+
+ /**
+ * @var RepositoryConnection
+ */
+ private $connection;
+
+ /**
+ * @var ConditionBuilder
+ */
+ private $conditionBuilder;
+
+ /**
+ * @var QueryResultFactory
+ */
+ private $queryResultFactory;
+
+ /**
+ * @var EngineOptions
+ */
+ private $engineOptions;
+
+ /**
+ * @var array
+ */
+ private $sortKeys = [];
+
+ /**
+ * @since 2.0
+ *
+ * @param RepositoryConnection $connection
+ * @param ConditionBuilder $conditionBuilder
+ * @param QueryResultFactory $queryResultFactory
+ * @param EngineOptions|null $EngineOptions
+ */
+ // @codingStandardsIgnoreStart phpcs, ignore --sniffs=Generic.Files.LineLength
+ public function __construct( RepositoryConnection $connection, ConditionBuilder $conditionBuilder, QueryResultFactory $queryResultFactory, EngineOptions $engineOptions = null ) {
+ // @codingStandardsIgnoreEnd
+ $this->connection = $connection;
+ $this->conditionBuilder = $conditionBuilder;
+ $this->queryResultFactory = $queryResultFactory;
+ $this->engineOptions = $engineOptions;
+
+ if ( $this->engineOptions === null ) {
+ $this->engineOptions = new EngineOptions();
+ }
+
+ $this->conditionBuilder->setResultVariable( self::RESULT_VARIABLE );
+ }
+
+ /**
+ * @since 2.0
+ * @param Query $query
+ *
+ * @return QueryResult|string
+ */
+ public function getQueryResult( Query $query ) {
+
+ if ( ( !$this->engineOptions->get( 'smwgIgnoreQueryErrors' ) || $query->getDescription() instanceof ThingDescription ) &&
+ $query->querymode != Query::MODE_DEBUG &&
+ count( $query->getErrors() ) > 0 ) {
+ return $this->queryResultFactory->newEmptyQueryResult( $query, false );
+ }
+
+ // don't query, but return something to the printer
+ if ( $query->querymode == Query::MODE_NONE || $query->getLimit() < 1 ) {
+ return $this->queryResultFactory->newEmptyQueryResult( $query, true );
+ }
+
+ $this->sortKeys = $query->sortkeys;
+ $this->conditionBuilder->setSortKeys( $this->sortKeys );
+
+ $compoundCondition = $this->conditionBuilder->getConditionFrom(
+ $query->getDescription()
+ );
+
+ $query->addErrors(
+ $this->conditionBuilder->getErrors()
+ );
+
+ if ( $query->querymode == Query::MODE_DEBUG ) {
+ return $this->getDebugQueryResult( $query, $compoundCondition );
+ } elseif ( $query->querymode == Query::MODE_COUNT ) {
+ return $this->getCountQueryResult( $query, $compoundCondition );
+ }
+
+ return $this->getInstanceQueryResult( $query, $compoundCondition );
+ }
+
+ private function getCountQueryResult( Query $query, Condition $compoundCondition ) {
+
+ if ( $this->isSingletonConditionWithElementMatch( $compoundCondition ) ) {
+ if ( $compoundCondition->condition === '' ) { // all URIs exist, no querying
+ return 1;
+ } else {
+ $condition = $this->conditionBuilder->convertConditionToString( $compoundCondition );
+ $namespaces = $compoundCondition->namespaces;
+ $askQueryResult = $this->connection->ask( $condition, $namespaces );
+
+ return $askQueryResult->isBooleanTrue() ? 1 : 0;
+ }
+ } elseif ( $compoundCondition instanceof FalseCondition ) {
+ return 0;
+ }
+
+ $condition = $this->conditionBuilder->convertConditionToString( $compoundCondition );
+ $namespaces = $compoundCondition->namespaces;
+ $this->sortKeys = $this->conditionBuilder->getSortKeys();
+
+ $options = $this->getOptions( $query, $compoundCondition );
+ $options['DISTINCT'] = true;
+
+ $repositoryResult = $this->connection->selectCount(
+ '?' . self::RESULT_VARIABLE,
+ $condition,
+ $options,
+ $namespaces
+ );
+
+ return $this->queryResultFactory->newQueryResult( $repositoryResult, $query );
+ }
+
+ private function getInstanceQueryResult( Query $query, Condition $compoundCondition ) {
+
+ if ( $this->isSingletonConditionWithElementMatch( $compoundCondition ) ) {
+ $matchElement = $compoundCondition->matchElement;
+
+ if ( $compoundCondition->condition === '' ) { // all URIs exist, no querying
+ $results = [ [ $matchElement ] ];
+ } else {
+ $condition = $this->conditionBuilder->convertConditionToString( $compoundCondition );
+ $namespaces = $compoundCondition->namespaces;
+ $askQueryResult = $this->connection->ask( $condition, $namespaces );
+ $results = $askQueryResult->isBooleanTrue() ? [ [ $matchElement ] ] : [];
+ }
+
+ $repositoryResult = new RepositoryResult( [ self::RESULT_VARIABLE => 0 ], $results );
+
+ } elseif ( $compoundCondition instanceof FalseCondition ) {
+ $repositoryResult = new RepositoryResult( [ self::RESULT_VARIABLE => 0 ], [] );
+ } else {
+ $condition = $this->conditionBuilder->convertConditionToString( $compoundCondition );
+ $namespaces = $compoundCondition->namespaces;
+ $this->sortKeys = $this->conditionBuilder->getSortKeys();
+
+ $options = $this->getOptions( $query, $compoundCondition );
+ $options['DISTINCT'] = true;
+
+ $repositoryResult = $this->connection->select(
+ '?' . self::RESULT_VARIABLE,
+ $condition,
+ $options,
+ $namespaces
+ );
+ }
+
+ return $this->queryResultFactory->newQueryResult( $repositoryResult, $query );
+ }
+
+ private function getDebugQueryResult( Query $query, Condition $compoundCondition ) {
+
+ $entries = [];
+
+ if ( $this->isSingletonConditionWithElementMatch( $compoundCondition ) ) {
+ if ( $compoundCondition->condition === '' ) { // all URIs exist, no querying
+ $sparql = 'None (no conditions).';
+ } else {
+ $condition = $this->conditionBuilder->convertConditionToString( $compoundCondition );
+ $namespaces = $compoundCondition->namespaces;
+ $sparql = $this->connection->getSparqlForAsk( $condition, $namespaces );
+ }
+ } elseif ( $compoundCondition instanceof FalseCondition ) {
+ $sparql = 'None (conditions can not be satisfied by anything).';
+ } else {
+ $condition = $this->conditionBuilder->convertConditionToString( $compoundCondition );
+ $namespaces = $compoundCondition->namespaces;
+ $this->sortKeys = $this->conditionBuilder->getSortKeys();
+
+ $options = $this->getOptions( $query, $compoundCondition );
+ $options['DISTINCT'] = true;
+
+ $sparql = $this->connection->getSparqlForSelect(
+ '?' . self::RESULT_VARIABLE,
+ $condition,
+ $options,
+ $namespaces
+ );
+ }
+
+ $entries['SPARQL Query'] = DebugFormatter::prettifySparql( $sparql );
+
+ return DebugFormatter::getStringFrom( 'SPARQLStore', $entries, $query );
+ }
+
+ private function isSingletonConditionWithElementMatch( $condition ) {
+ return $condition instanceof SingletonCondition && $condition->matchElement instanceof Element;
+ }
+
+ /**
+ * Get a SPARQL option array for the given query.
+ *
+ * @param Query $query
+ * @param Condition $compoundCondition (storing order by variable names)
+ *
+ * @return array
+ */
+ protected function getOptions( Query $query, Condition $compoundCondition ) {
+
+ $options = [
+ 'LIMIT' => $query->getLimit() + 1,
+ 'OFFSET' => $query->getOffset()
+ ];
+
+ // Build ORDER BY options using discovered sorting fields.
+ if ( !$this->engineOptions->isFlagSet( 'smwgQSortFeatures', SMW_QSORT ) || !is_array( $this->sortKeys ) ) {
+ return $options;
+ }
+
+ $orderByString = '';
+
+ foreach ( $this->sortKeys as $propkey => $order ) {
+
+ if ( !is_string( $propkey ) ) {
+ throw new RuntimeException( "Expected a string value as sortkey" );
+ }
+
+ if ( ( $order != 'RANDOM' ) && array_key_exists( $propkey, $compoundCondition->orderVariables ) ) {
+ $orderByString .= "$order(?" . $compoundCondition->orderVariables[$propkey] . ") ";
+ } elseif ( ( $order == 'RANDOM' ) && $this->engineOptions->isFlagSet( 'smwgQSortFeatures', SMW_QSORT_RANDOM ) ) {
+ // not supported in SPARQL; might be possible via function calls in some stores
+ }
+ }
+
+ if ( $orderByString !== '' ) {
+ $options['ORDER BY'] = $orderByString;
+ }
+
+ return $options;
+ }
+
+}