summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/OrderCondition.php
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/OrderCondition.php')
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/OrderCondition.php252
1 files changed, 252 insertions, 0 deletions
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/OrderCondition.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/OrderCondition.php
new file mode 100644
index 00000000..10e974a6
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/OrderCondition.php
@@ -0,0 +1,252 @@
+<?php
+
+namespace SMW\SQLStore\QueryEngine;
+
+use RuntimeException;
+use SMW\DataValueFactory;
+use SMW\DataValues\PropertyChainValue;
+use SMW\Query\DescriptionFactory;
+use SMW\Query\Language\Description;
+
+/**
+ * Modifies a given query object at $qid to account for all ordering conditions
+ * in the Query $query. It is always required that $qid is the id of a query
+ * that joins with the SMW ID_TABELE so that the field alias.smw_title is
+ * available for default sorting.
+ *
+ * @license GNU GPL v2+
+ * @since 2.5
+ *
+ * @author mwjames
+ * @author Markus Krötzsch
+ */
+class OrderCondition {
+
+ /**
+ * @var QuerySegmentListBuilder
+ */
+ private $querySegmentListBuilder;
+
+ /**
+ * @var DescriptionFactory
+ */
+ private $descriptionFactory;
+
+ /**
+ * Array of sorting requests ("Property_name" => "ASC"/"DESC"). Used during query
+ * processing (where these property names are searched while compiling the query
+ * conditions).
+ *
+ * @var string[]
+ */
+ private $sortKeys = [];
+
+ /**
+ * @var boolean
+ */
+ private $isSupported = true;
+
+ /**
+ * @var boolean
+ */
+ private $asUnconditional = false;
+
+ /**
+ * @since 2.5
+ *
+ * @param QuerySegmentListBuilder $querySegmentListBuilder
+ */
+ public function __construct( QuerySegmentListBuilder $querySegmentListBuilder ) {
+ $this->querySegmentListBuilder = $querySegmentListBuilder;
+ $this->descriptionFactory = new DescriptionFactory();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param array $sortKeys
+ */
+ public function setSortKeys( $sortKeys ) {
+ $this->sortKeys = $sortKeys;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return string[]
+ */
+ public function getSortKeys() {
+ return $this->sortKeys;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->querySegmentListBuilder->getErrors();
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param boolean $isSupported
+ */
+ public function isSupported( $isSupported ) {
+ $this->isSupported = $isSupported;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param boolean $asUnconditional
+ */
+ public function asUnconditional( $asUnconditional ) {
+ $this->asUnconditional = $asUnconditional;
+ }
+
+ /**
+ * @since 2.5
+ *
+ * @param integer $qid
+ *
+ * @return QuerySegment[]
+ */
+ public function apply( $qid ) {
+
+ if ( !$this->isSupported ) {
+ return $this->querySegmentListBuilder->getQuerySegmentList();
+ }
+
+ $querySegment = $this->querySegmentListBuilder->findQuerySegment(
+ $qid
+ );
+
+ $extraDescriptions = $this->collectExtraDescriptionsFromSortKeys(
+ $querySegment
+ );
+
+ if ( $extraDescriptions !== [] ) {
+ $this->addConjunctionFromExtraDescriptions( $querySegment, $extraDescriptions );
+ }
+
+ return $this->querySegmentListBuilder->getQuerySegmentList();
+ }
+
+ private function collectExtraDescriptionsFromSortKeys( $querySegment ) {
+
+ $extraDescriptions = [];
+
+ foreach ( $this->sortKeys as $label => $order ) {
+
+ if ( !is_string( $label ) ) {
+ throw new RuntimeException( "Expected a string value as sortkey" );
+ }
+
+ if ( ( $description = $this->findExtraDescriptionBy( $querySegment, $label, $order ) ) instanceof Description ) {
+ $extraDescriptions[] = $description;
+ }
+ }
+
+ return $extraDescriptions;
+ }
+
+ private function findExtraDescriptionBy( $querySegment, $label, $order ) {
+
+ $description = null;
+
+ // Is assigned, leave ...
+ if ( array_key_exists( $label, $querySegment->sortfields ) ) {
+ return $description;
+ }
+
+ // Find missing property to sort by.
+ if ( $label === '' ) { // Sort by first result column (page titles).
+ $querySegment->sortfields[$label] = "$querySegment->alias.smw_sort";
+ } elseif ( $label === '#' ) { // Sort by first result column (page titles).
+ // PHP7 showed a rather erratic behaviour where in cases
+ // the sortkey contains the same string for comparison, the
+ // result returned from the DB was mixed in order therefore
+ // using # as indicator to search for additional fields if
+ // no specific property is given (see test cases in #1534)
+ $querySegment->sortfields[$label] = "$querySegment->alias.smw_sort,$querySegment->alias.smw_title,$querySegment->alias.smw_subobject";
+ } elseif ( PropertyChainValue::isChained( $label ) ) { // Try to extend query.
+ $propertyChainValue = DataValueFactory::getInstance()->newDataValueByType( PropertyChainValue::TYPE_ID );
+ $propertyChainValue->setUserValue( $label );
+
+ if ( !$propertyChainValue->isValid() ) {
+ return $description;
+ }
+
+ $lastDataItem = $propertyChainValue->getLastPropertyChainValue()->getDataItem();
+
+ $description = $this->descriptionFactory->newSomeProperty(
+ $lastDataItem,
+ $this->descriptionFactory->newThingDescription()
+ );
+
+ // #2176, Set a different membership in case duplicate detection is
+ // enabled, the fingerprint will be distinguishable from a condition
+ // with another ThingDescription for the same property that would
+ // otherwise create a "Error: 1066 Not unique table/alias: 't3'"
+ $description->setMembership( $label );
+
+ foreach ( $propertyChainValue->getPropertyChainValues() as $val ) {
+ $description = $this->descriptionFactory->newSomeProperty(
+ $val->getDataItem(),
+ $description
+ );
+ }
+
+ // Add and replace Foo.Bar=asc with Bar=asc as we ultimately only
+ // order to the result of the last element
+ $this->sortKeys[$lastDataItem->getKey()] = $order;
+ unset( $this->sortKeys[$label] );
+ } else { // Try to extend query.
+ $sortprop = DataValueFactory::getInstance()->newPropertyValueByLabel( $label );
+
+ if ( $sortprop->isValid() ) {
+ $description = $this->descriptionFactory->newSomeProperty(
+ $sortprop->getDataItem(),
+ $this->descriptionFactory->newThingDescription()
+ );
+ }
+ }
+
+ return $description;
+ }
+
+ private function addConjunctionFromExtraDescriptions( $querySegment, array $extraDescriptions ) {
+
+ $this->querySegmentListBuilder->setSortKeys(
+ $this->sortKeys
+ );
+
+ $this->querySegmentListBuilder->getQuerySegmentFrom(
+ $this->descriptionFactory->newConjunction( $extraDescriptions )
+ );
+
+ // This is always an QuerySegment::Q_CONJUNCTION ...
+ $newQuerySegment = $this->querySegmentListBuilder->findQuerySegment(
+ $this->querySegmentListBuilder->getLastQuerySegmentId()
+ );
+
+ // ... so just re-wire its dependencies
+ foreach ( $newQuerySegment->components as $cid => $field ) {
+ $querySegment->components[$cid] = $querySegment->joinfield;
+
+ if ( $this->asUnconditional ) {
+ $this->querySegmentListBuilder->findQuerySegment( $cid )->joinType = 'LEFT OUTER';
+ }
+
+ $querySegment->sortfields = array_merge(
+ $querySegment->sortfields,
+ $this->querySegmentListBuilder->findQuerySegment( $cid )->sortfields
+ );
+ }
+
+ $this->querySegmentListBuilder->addQuerySegment( $querySegment );
+ }
+
+}