r87813 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r87812‎ | r87813 | r87814 >
Date:09:20, 10 May 2011
Author:mkroetzsch
Status:deferred
Tags:
Comment:
support for orderings
Modified paths:
  • /trunk/extensions/SemanticMediaWiki/includes/storage/SMW_SparqlStoreQueryEngine.php (modified) (history)

Diff [purge]

Index: trunk/extensions/SemanticMediaWiki/includes/storage/SMW_SparqlStoreQueryEngine.php
@@ -32,7 +32,7 @@
3333 * typically for ordering. For instance, selecting the sortkey of a
3434 * page needs only be done once per query. The array is indexed by the
3535 * name of the (main) selected variable, e.g. "v42sortkey" to allow
36 - * elimination of duplicate weak conditions that aimm to introduce this
 36+ * elimination of duplicate weak conditions that aim to introduce this
3737 * variable.
3838 * @var array of format "condition identifier" => "condition"
3939 */
@@ -226,6 +226,9 @@
227227 */
228228 class SMWSparqlStoreQueryEngine {
229229
 230+ /// The name of the SPARQL variable that represents the query result.
 231+ const RESULT_VARIABLE = 'result';
 232+
230233 /**
231234 * Counter used to generate globally fresh variables.
232235 * @var integer
@@ -233,6 +236,21 @@
234237 protected $m_variableCounter = 0;
235238
236239 /**
 240+ * Array that relates sortkeys (given by the users, i.e. property
 241+ * names) to variable names in the generated SPARQL query.
 242+ * Format sortkey => variable name
 243+ * @var array
 244+ */
 245+ protected $m_orderVariables;
 246+
 247+ /**
 248+ * Copy of the SMWQuery sortkeys array to be used while building the
 249+ * SPARQL query conditions.
 250+ * @var array
 251+ */
 252+ protected $m_sortKeys;
 253+
 254+ /**
237255 * The store that we work for.
238256 * @var SMWStore
239257 */
@@ -243,6 +261,7 @@
244262 }
245263
246264 public function getInstanceQueryResult( SMWQuery $query ) {
 265+ $this->m_sortkeys = $query->sortkeys;
247266 $sparqlCondition = $this->getSparqlCondition( $query->getDescription() );
248267
249268 //debug_zval_dump($sparqlCondition);
@@ -250,12 +269,11 @@
251270 $condition = $sparqlCondition->getWeakConditionString();
252271 if ( ( $condition == '' ) && !$sparqlCondition->isSafe() ) {
253272 $swivtPageResource = SMWExporter::getSpecialNsResource( 'swivt', 'page' );
254 - $condition = '?result ' . $swivtPageResource->getQName() . " ?url .\n";
 273+ $condition = '?' . self::RESULT_VARIABLE . $swivtPageResource->getQName() . " ?url .\n";
255274 }
256275 $condition .= $sparqlCondition->getCondition();
257276
258277 if ( $sparqlCondition instanceof SMWSparqlSingletonCondition ) {
259 - // TODO use ASK to handle this
260278 $matchElement = $sparqlCondition->matchElement;
261279 if ( $sparqlCondition->condition == '' ) {
262280 $results = array( array ( $matchElement ) );
@@ -264,7 +282,7 @@
265283 if ( $matchElement instanceof SMWExpNsResource ) {
266284 $namespaces[$matchElement->getNamespaceId()] = $matchElement->getNamespace();
267285 }
268 - $condition = str_replace( '?result ', "$matchElementName ", $condition );
 286+ $condition = str_replace( '?' . self::RESULT_VARIABLE, "$matchElementName ", $condition );
269287 $askQueryResult = smwfGetSparqlDatabase()->ask( $condition, $namespaces );
270288 if ( $askQueryResult->isBooleanTrue() ) {
271289 $results = array( array ( $matchElement ) );
@@ -272,14 +290,14 @@
273291 $results = array();
274292 }
275293 }
276 - $sparqlResult = new SMWSparqlResultWrapper( array( 'result' => 0 ), $results ); //TODO
 294+ $sparqlResult = new SMWSparqlResultWrapper( array( self::RESULT_VARIABLE => 0 ), $results );
277295 } elseif ( $sparqlCondition instanceof SMWSparqlFalseCondition ) {
278 - $sparqlResult = new SMWSparqlResultWrapper( array( 'result' => 0 ), array() );
 296+ $sparqlResult = new SMWSparqlResultWrapper( array( self::RESULT_VARIABLE => 0 ), array() );
279297 } else {
280298 //debug_zval_dump( $condition );
281299 $options = $this->getSparqlOptions( $query );
282300 $options['DISTINCT'] = true;
283 - $sparqlResult = smwfGetSparqlDatabase()->select( '?result',
 301+ $sparqlResult = smwfGetSparqlDatabase()->select( '?' . self::RESULT_VARIABLE,
284302 $condition, $options, $namespaces );
285303 }
286304
@@ -308,9 +326,11 @@
309327 return $result;
310328 }
311329
312 - public function getSparqlCondition( SMWDescription $description ) {
 330+ protected function getSparqlCondition( SMWDescription $description ) {
313331 $this->m_variableCounter = 0;
314 - $sparqlCondition = $this->buildSparqlCondition( $description, 'result', null );
 332+ $this->m_orderVariables = array();
 333+ $sparqlCondition = $this->buildSparqlCondition( $description, self::RESULT_VARIABLE, null );
 334+ $this->addMissingOrderByConditions( $sparqlCondition );
315335 return $sparqlCondition;
316336 }
317337
@@ -333,15 +353,14 @@
334354 } elseif ( $description instanceof SMWConceptDescription ) {
335355 return new SMWSparqlTrueCondition(); ///TODO Implement concept queries
336356 } else { // (e.g. SMWThingDescription)
337 - return new SMWSparqlTrueCondition();
 357+ return $this->buildTrueCondition( $joinVariable, $orderByProperty );
338358 }
339359 }
340360
341361 protected function buildConjunctionCondition( SMWConjunction $description, $joinVariable, $orderByProperty ) {
342362 $subDescriptions = $description->getDescriptions();
343363 if ( count( $subDescriptions ) == 0 ) { // empty conjunction: true
344 - /// FIXME Ordering is missing
345 - return new SMWSparqlTrueCondition();
 364+ return $this->buildTrueCondition( $joinVariable, $orderByProperty );
346365 } elseif ( count( $subDescriptions ) == 1 ) { // conjunction with one element
347366 return $this->buildSparqlCondition( reset( $subDescriptions ), $joinVariable, $orderByProperty );
348367 }
@@ -399,13 +418,7 @@
400419
401420 $result->weakConditions = $weakConditions;
402421
403 - //*** Possibly add conditions for ordering ***//
404 - if ( $orderByProperty !== null ) {
405 - /// TODO Depends on datatype
406 -// $result->orderByVariable = $joinVariable . 'sk';
407 -// $skeyExpElement = SMWExporter::getSpecialPropertyResource( '_SKEY' );
408 -// $result->weakConditions = array( $result->orderByVariable => "?$joinVariable " . $skeyExpElement->getQName() . " ?{$result->orderByVariable} .\n" );
409 - }
 422+ $this->addOrderByDataForProperty( $result, $joinVariable, $orderByProperty );
410423
411424 return $result;
412425 }
@@ -427,8 +440,7 @@
428441 if ( $subCondition instanceof SMWSparqlFalseCondition ) {
429442 // empty parts in a disjunction can be ignored
430443 } elseif ( $subCondition instanceof SMWSparqlTrueCondition ) {
431 - /// FIXME Take ordering into account.
432 - return new SMWSparqlTrueCondition();
 444+ return $this->buildTrueCondition( $joinVariable, $orderByProperty );
433445 } elseif ( $subCondition instanceof SMWSparqlWhereCondition ) {
434446 $hasSafeSubconditions = $hasSafeSubconditions || $subCondition->isSafe();
435447 $unionCondition .= ( $unionCondition ? ' UNION ' : '' ) .
@@ -468,13 +480,7 @@
469481
470482 $result->weakConditions = $weakConditions;
471483
472 - //*** Possibly add conditions for ordering ***//
473 - if ( $orderByProperty !== null ) {
474 - /// TODO Depends on datatype
475 -// $result->orderByVariable = $joinVariable . 'sk';
476 -// $skeyExpElement = SMWExporter::getSpecialPropertyResource( '_SKEY' );
477 -// $result->weakConditions = array( $result->orderByVariable => "?$joinVariable " . $skeyExpElement->getQName() . " ?{$result->orderByVariable} .\n" );
478 - }
 484+ $this->addOrderByDataForProperty( $result, $joinVariable, $orderByProperty );
479485
480486 return $result;
481487
@@ -484,7 +490,11 @@
485491 $diProperty = $description->getProperty();
486492
487493 //*** Find out if we should order by the values of this property ***//
488 - $innerOrderByProperty = null; //TODO, also add to some global register if found below ...
 494+ if ( array_key_exists( $diProperty->getKey(), $this->m_sortkeys ) ) {
 495+ $innerOrderByProperty = $diProperty;
 496+ } else {
 497+ $innerOrderByProperty = null;
 498+ }
489499
490500 //*** Prepare inner condition ***//
491501 $innerJoinVariable = $this->getNextVariable();
@@ -503,6 +513,11 @@
504514 $objectName = '?' . $innerJoinVariable;
505515 }
506516
 517+ //*** Record inner ordering variable if found ***//
 518+ if ( ( $innerOrderByProperty !== null ) && ( $innerCondition->orderByVariable != '' ) ) {
 519+ $this->m_orderVariables[$diProperty->getKey()] = $innerCondition->orderByVariable;
 520+ }
 521+
507522 //*** Exchange arguments when property is inverse ***//
508523 if ( $diProperty->isInverse() ) { // don't check if this really makes sense
509524 $subjectName = $objectName;
@@ -524,12 +539,7 @@
525540 }
526541 $result = new SMWSparqlWhereCondition( $condition, true, $namespaces );
527542
528 - //*** Possibly add conditions for ordering ***//
529 - if ( $orderByProperty !== null ) {
530 - $result->orderByVariable = $joinVariable . 'sk';
531 - $skeyExpElement = SMWExporter::getSpecialPropertyResource( '_SKEY' );
532 - $result->weakConditions = array( $result->orderByVariable => "?$joinVariable " . $skeyExpElement->getQName() . " ?{$result->orderByVariable} .\n" );
533 - }
 543+ $this->addOrderByDataForProperty( $result, $joinVariable, $orderByProperty, SMWDataItem::TYPE_WIKIPAGE );
534544
535545 return $result;
536546 }
@@ -556,11 +566,7 @@
557567
558568 $result = new SMWSparqlWhereCondition( $condition, true, $namespaces );
559569
560 - if ( $orderByProperty !== null ) {
561 - $result->orderByVariable = $joinVariable . 'sk';
562 - $skeyExpElement = SMWExporter::getSpecialPropertyResource( '_SKEY' );
563 - $result->weakConditions = array( $result->orderByVariable => "?$joinVariable " . $skeyExpElement->getQName() . " ?{$result->orderByVariable} .\n" );
564 - }
 570+ $this->addOrderByDataForProperty( $result, $joinVariable, $orderByProperty, SMWDataItem::TYPE_WIKIPAGE );
565571
566572 return $result;
567573 }
@@ -595,22 +601,88 @@
596602 $result = new SMWSparqlFilterCondition( $filter, $namespaces );
597603 }
598604
599 - //*** Possibly add conditions for ordering ***//
600 - if ( $orderByProperty !== null ) {
601 - /// TODO Depends on datatype
602 -// $result->orderByVariable = $joinVariable . 'sk';
603 -// $skeyExpElement = SMWExporter::getSpecialPropertyResource( '_SKEY' );
604 -// $result->weakConditions = array( $result->orderByVariable => "?$joinVariable " . $skeyExpElement->getQName() . " ?{$result->orderByVariable} .\n" );
605 - }
 605+ $this->addOrderByDataForProperty( $result, $joinVariable, $orderByProperty, $dataItem->getDIType() );
606606
607607 return $result;
608608 }
609609
 610+ protected function buildTrueCondition( $joinVariable, $orderByProperty ) {
 611+ $result = new SMWSparqlTrueCondition();
 612+ $this->addOrderByDataForProperty( $result, $joinVariable, $orderByProperty );
 613+ return $result;
 614+ }
 615+
610616 protected function getNextVariable() {
611617 return 'v' . ( ++$this->m_variableCounter );
612618 }
613619
614620 /**
 621+ * Extend the given SPARQL condition by a suitable order by variable,
 622+ * if an order by property is set.
 623+ *
 624+ * @param SMWSparqlCondition $sparqlCondition condition to modify
 625+ * @param string $mainVariable the variable that represents the value to be ordered
 626+ * @param mixed $orderByProperty SMWDIProperty or null
 627+ * @param integer $diType DataItem type id if known, or SMWDataItem::TYPE_NOTYPE to determine it from the property
 628+ */
 629+ protected function addOrderByDataForProperty( SMWSparqlCondition &$sparqlCondition, $mainVariable, $orderByProperty, $diType = SMWDataItem::TYPE_NOTYPE ) {
 630+ if ( $orderByProperty === null ) {
 631+ return;
 632+ }
 633+
 634+ if ( $diType == SMWDataItem::TYPE_NOTYPE ) {
 635+ $typeId = $orderByProperty->findPropertyTypeID();
 636+ $diType = SMWDataValueFactory::getDataItemId( $typeId );
 637+ }
 638+
 639+ $this->addOrderByData( $sparqlCondition, $mainVariable, $diType );
 640+ }
 641+
 642+ /**
 643+ * Extend the given SPARQL condition by a suitable order by variable,
 644+ * possibly adding conditions if required for the type of data.
 645+ *
 646+ * @param SMWSparqlCondition $sparqlCondition condition to modify
 647+ * @param string $mainVariable the variable that represents the value to be ordered
 648+ * @param integer $diType DataItem type id
 649+ */
 650+ protected function addOrderByData( SMWSparqlCondition &$sparqlCondition, $mainVariable, $diType ) {
 651+ if ( $diType == SMWDataItem::TYPE_WIKIPAGE ) {
 652+ $sparqlCondition->orderByVariable = $mainVariable . 'sk';
 653+ $skeyExpElement = SMWExporter::getSpecialPropertyResource( '_SKEY' );
 654+ $sparqlCondition->weakConditions = array( $sparqlCondition->orderByVariable =>
 655+ "?$mainVariable " . $skeyExpElement->getQName() . " ?{$sparqlCondition->orderByVariable} .\n" );
 656+ } else {
 657+ $sparqlCondition->orderByVariable = $mainVariable;
 658+ }
 659+ }
 660+
 661+ /**
 662+ * Extend the given SMWSparqlCondition with additional conditions to
 663+ * ensure that it can be ordered by all requested properties. After
 664+ * this operation, every key in m_sortkeys is assigned to a query
 665+ * variable by m_orderVariables.
 666+ *
 667+ * @param SMWSparqlCondition $sparqlCondition condition to modify
 668+ */
 669+ protected function addMissingOrderByConditions( SMWSparqlCondition &$sparqlCondition ) {
 670+ foreach ( $this->m_sortkeys as $propkey => $order ) {
 671+ if ( !array_key_exists( $propkey, $this->m_orderVariables ) ) { // Find missing property to sort by.
 672+ if ( $propkey == '' ) { // order by result page sortkey
 673+ $this->addOrderByData( $sparqlCondition, self::RESULT_VARIABLE, SMWDataItem::TYPE_WIKIPAGE );
 674+ $this->m_orderVariables[$propkey] = $sparqlCondition->orderByVariable;
 675+ } else { // extend query to order by other property values
 676+ $diProperty = new SMWDIProperty( $propkey );
 677+ $auxDescription = new SMWSomeProperty( $diProperty, new SMWThingDescription() );
 678+ $auxSparqlCondition = $this->buildSparqlCondition( $auxDescription, self::RESULT_VARIABLE, null );
 679+ // m_orderVariables MUST be set for $propkey -- or there is a bug; let it show!
 680+ $sparqlCondition->weakConditions[$this->m_orderVariables[$propkey]] = $auxSparqlCondition->getWeakConditionString() . $auxSparqlCondition->getCondition();
 681+ }
 682+ }
 683+ }
 684+ }
 685+
 686+ /**
615687 * Get a SPARQL option array for the given query.
616688 *
617689 * @param SMWQuery $query
@@ -622,17 +694,19 @@
623695 $result = array( 'LIMIT' => $query->getLimit() + 1, 'OFFSET' => $query->getOffset() );
624696
625697 // Build ORDER BY options using discovered sorting fields.
626 -// if ( $smwgQSortingSupport ) {
627 -// $qobj = $this->m_queries[$rootid];
628 -//
629 -// foreach ( $this->m_sortkeys as $propkey => $order ) {
630 -// if ( ( $order != 'RANDOM' ) && array_key_exists( $propkey, $qobj->sortfields ) ) { // Field was successfully added.
631 -// $result['ORDER BY'] = ( array_key_exists( 'ORDER BY', $result ) ? $result['ORDER BY'] . ', ' : '' ) . $qobj->sortfields[$propkey] . " $order ";
632 -// } elseif ( ( $order == 'RANDOM' ) && $smwgQRandSortingSupport ) {
633 -// $result['ORDER BY'] = ( array_key_exists( 'ORDER BY', $result ) ? $result['ORDER BY'] . ', ' : '' ) . ' RAND() ';
634 -// }
635 -// }
636 -// }
 698+ if ( $smwgQSortingSupport ) {
 699+ $orderByString = '';
 700+ foreach ( $this->m_sortkeys as $propkey => $order ) {
 701+ if ( ( $order != 'RANDOM' ) && array_key_exists( $propkey, $this->m_orderVariables ) ) {
 702+ $orderByString .= "$order(?" . $this->m_orderVariables[$propkey] . ") ";
 703+ } elseif ( ( $order == 'RANDOM' ) && $smwgQRandSortingSupport ) {
 704+ // not supported in SPARQL; might be possible via function calls in some stores
 705+ }
 706+ }
 707+ if ( $orderByString != '' ) {
 708+ $result['ORDER BY'] = $orderByString;
 709+ }
 710+ }
637711 return $result;
638712 }
639713

Status & tagging log