Index: trunk/extensions/SemanticMediaWiki/includes/sparql/SMW_SparqlDatabase.php |
— | — | @@ -243,6 +243,34 @@ |
244 | 244 | } |
245 | 245 | |
246 | 246 | /** |
| 247 | + * SELECT wrapper for counting results. |
| 248 | + * The function declares the standard namespaces wiki, swivt, rdf, owl, |
| 249 | + * rdfs, property, xsd, so these do not have to be included in |
| 250 | + * $extraNamespaces. |
| 251 | + * |
| 252 | + * @param $variable string variable name or '*' |
| 253 | + * @param $where string WHERE part of the query, without surrounding { } |
| 254 | + * @param $options array (associative) of options, e.g. array('LIMIT' => '10') |
| 255 | + * @param $extraNamespaces array (associative) of namespaceId => namespaceUri |
| 256 | + * @return SMWSparqlResultWrapper |
| 257 | + */ |
| 258 | + public function selectCount( $variable, $where, $options = array(), $extraNamespaces = array() ) { |
| 259 | + $sparql = self::getPrefixString( $extraNamespaces ) . 'SELECT (COUNT('; |
| 260 | + if ( array_key_exists( 'DISTINCT', $options ) ) { |
| 261 | + $sparql .= 'DISTINCT '; |
| 262 | + } |
| 263 | + $sparql .= $variable . ") AS ?count) WHERE {\n" . $where . "\n}"; |
| 264 | + if ( array_key_exists( 'OFFSET', $options ) ) { |
| 265 | + $sparql .= "\nOFFSET " . $options['OFFSET']; |
| 266 | + } |
| 267 | + if ( array_key_exists( 'LIMIT', $options ) ) { |
| 268 | + $sparql .= "\nLIMIT " . $options['LIMIT']; |
| 269 | + } |
| 270 | + |
| 271 | + return $this->doQuery( $sparql ); |
| 272 | + } |
| 273 | + |
| 274 | + /** |
247 | 275 | * DELETE wrapper. |
248 | 276 | * The function declares the standard namespaces wiki, swivt, rdf, owl, |
249 | 277 | * rdfs, property, xsd, so these do not have to be included in |
Index: trunk/extensions/SemanticMediaWiki/includes/sparql/SMW_SparqlResultWrapper.php |
— | — | @@ -97,6 +97,26 @@ |
98 | 98 | } |
99 | 99 | |
100 | 100 | /** |
| 101 | + * Check if the result is what one would get for a SPARQL SELECT COUNT |
| 102 | + * query, and return the corresponding integer value. Returns 0 in all |
| 103 | + * other cases (including the case that the results do not look at all |
| 104 | + * like the result of a SELECT COUNT query). |
| 105 | + * |
| 106 | + * @return integer |
| 107 | + */ |
| 108 | + public function getNumericValue() { |
| 109 | + if ( count( $this->m_data ) == 1 ) { |
| 110 | + $row = reset( $this->m_data ); |
| 111 | + $expElement = reset( $row ); |
| 112 | + if ( ( count( $row ) == 1 ) && ( $expElement instanceof SMWExpLiteral ) && |
| 113 | + ( $expElement->getDatatype() == 'http://www.w3.org/2001/XMLSchema#integer' ) ) { |
| 114 | + return (int)$expElement->getLexicalForm(); |
| 115 | + } |
| 116 | + } |
| 117 | + return 0; |
| 118 | + } |
| 119 | + |
| 120 | + /** |
101 | 121 | * Reset iterator to position 0. Standard method of Iterator. |
102 | 122 | */ |
103 | 123 | public function rewind() { |
Index: trunk/extensions/SemanticMediaWiki/includes/storage/SMW_SparqlStoreQueryEngine.php |
— | — | @@ -260,50 +260,114 @@ |
261 | 261 | $this->m_store = $store; |
262 | 262 | } |
263 | 263 | |
| 264 | + public function getCountQueryResult( SMWQuery $query ) { |
| 265 | + $this->m_sortkeys = array(); // ignore sorting, just count |
| 266 | + $sparqlCondition = $this->getSparqlCondition( $query->getDescription() ); |
| 267 | + |
| 268 | + if ( $sparqlCondition instanceof SMWSparqlSingletonCondition ) { |
| 269 | + $matchElement = $sparqlCondition->matchElement; |
| 270 | + if ( $sparqlCondition->condition == '' ) { // all URIs exist, no querying |
| 271 | + return 1; |
| 272 | + } else { |
| 273 | + $condition = $this->getSparqlConditionString( $sparqlCondition ); |
| 274 | + $namespaces = $sparqlCondition->namespaces; |
| 275 | + $askQueryResult = smwfGetSparqlDatabase()->ask( $condition, $namespaces ); |
| 276 | + return $askQueryResult->isBooleanTrue() ? 1 : 0; |
| 277 | + } |
| 278 | + } elseif ( $sparqlCondition instanceof SMWSparqlFalseCondition ) { |
| 279 | + return 0; |
| 280 | + } else { |
| 281 | + //debug_zval_dump( $condition ); |
| 282 | + $condition = $this->getSparqlConditionString( $sparqlCondition ); |
| 283 | + $namespaces = $sparqlCondition->namespaces; |
| 284 | + $options = $this->getSparqlOptions( $query ); |
| 285 | + $options['DISTINCT'] = true; |
| 286 | + $sparqlResultWrapper = smwfGetSparqlDatabase()->selectCount( '?' . self::RESULT_VARIABLE, |
| 287 | + $condition, $options, $namespaces ); |
| 288 | + if ( $sparqlResultWrapper->getErrorCode() == SMWSparqlResultWrapper::ERROR_NOERROR ) { |
| 289 | + return (int)$sparqlResultWrapper->getNumericValue(); |
| 290 | + } else { |
| 291 | + ///@todo Implement error reporting for counting queries. |
| 292 | +// smwfLoadExtensionMessages( 'SemanticMediaWiki' ); |
| 293 | +// $result->addErrors( array( wfMsgForContent( 'smw_db_sparqlqueryproblem' ) ) ); |
| 294 | + } |
| 295 | + } |
| 296 | + } |
| 297 | + |
264 | 298 | public function getInstanceQueryResult( SMWQuery $query ) { |
265 | 299 | $this->m_sortkeys = $query->sortkeys; |
266 | 300 | $sparqlCondition = $this->getSparqlCondition( $query->getDescription() ); |
267 | | - |
268 | 301 | //debug_zval_dump($sparqlCondition); |
269 | | - $namespaces = $sparqlCondition->namespaces; |
270 | | - $condition = $sparqlCondition->getWeakConditionString(); |
271 | | - if ( ( $condition == '' ) && !$sparqlCondition->isSafe() ) { |
272 | | - $swivtPageResource = SMWExporter::getSpecialNsResource( 'swivt', 'page' ); |
273 | | - $condition = '?' . self::RESULT_VARIABLE . $swivtPageResource->getQName() . " ?url .\n"; |
274 | | - } |
275 | | - $condition .= $sparqlCondition->getCondition(); |
276 | 302 | |
277 | 303 | if ( $sparqlCondition instanceof SMWSparqlSingletonCondition ) { |
278 | 304 | $matchElement = $sparqlCondition->matchElement; |
279 | | - if ( $sparqlCondition->condition == '' ) { |
| 305 | + if ( $sparqlCondition->condition == '' ) { // all URIs exist, no querying |
280 | 306 | $results = array( array ( $matchElement ) ); |
281 | 307 | } else { |
282 | | - $matchElementName = SMWTurtleSerializer::getTurtleNameForExpElement( $matchElement ); |
283 | | - if ( $matchElement instanceof SMWExpNsResource ) { |
284 | | - $namespaces[$matchElement->getNamespaceId()] = $matchElement->getNamespace(); |
285 | | - } |
286 | | - $condition = str_replace( '?' . self::RESULT_VARIABLE, "$matchElementName ", $condition ); |
| 308 | + $condition = $this->getSparqlConditionString( $sparqlCondition ); |
| 309 | + $namespaces = $sparqlCondition->namespaces; |
287 | 310 | $askQueryResult = smwfGetSparqlDatabase()->ask( $condition, $namespaces ); |
288 | | - if ( $askQueryResult->isBooleanTrue() ) { |
289 | | - $results = array( array ( $matchElement ) ); |
290 | | - } else { |
291 | | - $results = array(); |
292 | | - } |
| 311 | + $results = $askQueryResult->isBooleanTrue() ? array( array ( $matchElement ) ) : array(); |
293 | 312 | } |
294 | | - $sparqlResult = new SMWSparqlResultWrapper( array( self::RESULT_VARIABLE => 0 ), $results ); |
| 313 | + $sparqlResultWrapper = new SMWSparqlResultWrapper( array( self::RESULT_VARIABLE => 0 ), $results ); |
295 | 314 | } elseif ( $sparqlCondition instanceof SMWSparqlFalseCondition ) { |
296 | | - $sparqlResult = new SMWSparqlResultWrapper( array( self::RESULT_VARIABLE => 0 ), array() ); |
| 315 | + $sparqlResultWrapper = new SMWSparqlResultWrapper( array( self::RESULT_VARIABLE => 0 ), array() ); |
297 | 316 | } else { |
298 | 317 | //debug_zval_dump( $condition ); |
| 318 | + $condition = $this->getSparqlConditionString( $sparqlCondition ); |
| 319 | + $namespaces = $sparqlCondition->namespaces; |
299 | 320 | $options = $this->getSparqlOptions( $query ); |
300 | 321 | $options['DISTINCT'] = true; |
301 | | - $sparqlResult = smwfGetSparqlDatabase()->select( '?' . self::RESULT_VARIABLE, |
302 | | - $condition, $options, $namespaces ); |
| 322 | + $sparqlResultWrapper = smwfGetSparqlDatabase()->select( '?' . self::RESULT_VARIABLE, |
| 323 | + $condition, $options, $namespaces ); |
303 | 324 | } |
304 | 325 | |
305 | | - //debug_zval_dump( $sparqlResult ); |
| 326 | + //debug_zval_dump( $sparqlResultWrapper ); |
| 327 | + return $this->getQueryResultFromSparqlResult( $sparqlResultWrapper, $query ); |
| 328 | + } |
| 329 | + |
| 330 | + /** |
| 331 | + * Build the condition (WHERE) string for a given SMWSparqlCondition. |
| 332 | + * The function also expresses the single value of |
| 333 | + * SMWSparqlSingletonCondition objects in the condition, which may |
| 334 | + * lead to additional namespaces for serializing its URI. |
| 335 | + * |
| 336 | + * @param SMWSparqlCondition $sparqlCondition |
| 337 | + * @return string |
| 338 | + */ |
| 339 | + protected function getSparqlConditionString( SMWSparqlCondition &$sparqlCondition ) { |
| 340 | + $condition = $sparqlCondition->getWeakConditionString(); |
| 341 | + if ( ( $condition == '' ) && !$sparqlCondition->isSafe() ) { |
| 342 | + $swivtPageResource = SMWExporter::getSpecialNsResource( 'swivt', 'page' ); |
| 343 | + $condition = '?' . self::RESULT_VARIABLE . ' ' . $swivtPageResource->getQName() . " ?url .\n"; |
| 344 | + } |
| 345 | + $condition .= $sparqlCondition->getCondition(); |
| 346 | + |
| 347 | + if ( $sparqlCondition instanceof SMWSparqlSingletonCondition ) { // prepare for ASK, maybe rather use BIND? |
| 348 | + $matchElement = $sparqlCondition->matchElement; |
| 349 | + $matchElementName = SMWTurtleSerializer::getTurtleNameForExpElement( $matchElement ); |
| 350 | + if ( $matchElement instanceof SMWExpNsResource ) { |
| 351 | + $sparqlCondition->namespaces[$matchElement->getNamespaceId()] = $matchElement->getNamespace(); |
| 352 | + } |
| 353 | + $condition = str_replace( '?' . self::RESULT_VARIABLE, "$matchElementName ", $condition ); |
| 354 | + } |
| 355 | + |
| 356 | + return $condition; |
| 357 | + } |
| 358 | + |
| 359 | + /** |
| 360 | + * Build an SMWQueryResult object from a SMWSparqlResultWrapper. This |
| 361 | + * function is used to generate instance query results, and the given |
| 362 | + * result wrapper must have an according format (one result column that |
| 363 | + * contains URIs of wiki pages). |
| 364 | + * |
| 365 | + * @param SMWSparqlResultWrapper $sparqlResultWrapper |
| 366 | + * @param SMWQuery $query SMWQueryResults hold a reference to original query |
| 367 | + * @return SMWQueryResult |
| 368 | + */ |
| 369 | + protected function getQueryResultFromSparqlResult( SMWSparqlResultWrapper $sparqlResultWrapper, SMWQuery $query ) { |
306 | 370 | $resultDataItems = array(); |
307 | | - foreach ( $sparqlResult as $resultRow ) { |
| 371 | + foreach ( $sparqlResultWrapper as $resultRow ) { |
308 | 372 | if ( count( $resultRow ) > 0 ) { |
309 | 373 | $dataItem = SMWExporter::findDataItemForExpElement( $resultRow[0] ); |
310 | 374 | if ( $dataItem !== null ) { |
— | — | @@ -311,6 +375,7 @@ |
312 | 376 | } |
313 | 377 | } |
314 | 378 | } |
| 379 | + |
315 | 380 | if ( count( $resultDataItems ) > $query->getLimit() ) { |
316 | 381 | array_pop( $resultDataItems ); |
317 | 382 | $hasFurtherResults = true; |
— | — | @@ -319,10 +384,12 @@ |
320 | 385 | } |
321 | 386 | |
322 | 387 | $result = new SMWQueryResult( $query->getDescription()->getPrintrequests(), $query, $resultDataItems, $this->m_store, $hasFurtherResults ); |
323 | | - if ( $sparqlResult->getErrorCode() != SMWSparqlResultWrapper::ERROR_NOERROR ) { |
| 388 | + |
| 389 | + if ( $sparqlResultWrapper->getErrorCode() != SMWSparqlResultWrapper::ERROR_NOERROR ) { |
324 | 390 | smwfLoadExtensionMessages( 'SemanticMediaWiki' ); |
325 | 391 | $result->addErrors( array( wfMsgForContent( 'smw_db_sparqlqueryproblem' ) ) ); |
326 | 392 | } |
| 393 | + |
327 | 394 | return $result; |
328 | 395 | } |
329 | 396 | |
Index: trunk/extensions/SemanticMediaWiki/includes/storage/SMW_SparqlStore.php |
— | — | @@ -177,13 +177,14 @@ |
178 | 178 | |
179 | 179 | if ( $query->querymode == SMWQuery::MODE_NONE ) { // don't query, but return something to printer |
180 | 180 | return new SMWQueryResult( $query->getDescription()->getPrintrequests(), $query, array(), $this, true ); |
181 | | - } elseif ( $query->querymode == SMWQuery::MODE_DEBUG || $query->querymode == SMWQuery::MODE_COUNT ) { |
| 181 | + } elseif ( $query->querymode == SMWQuery::MODE_DEBUG ) { |
182 | 182 | return "Not implemented.\n"; |
| 183 | + } elseif ( $query->querymode == SMWQuery::MODE_COUNT ) { |
| 184 | + $queryEngine = new SMWSparqlStoreQueryEngine( $this ); |
| 185 | + return $queryEngine->getCountQueryResult( $query ); |
183 | 186 | } else { |
184 | 187 | $queryEngine = new SMWSparqlStoreQueryEngine( $this ); |
185 | | - $queryResult = $queryEngine->getInstanceQueryResult( $query ); |
186 | | -// debug_zval_dump( $queryResult ); |
187 | | - return $queryResult; |
| 188 | + return $queryEngine->getInstanceQueryResult( $query ); |
188 | 189 | } |
189 | 190 | } |
190 | 191 | |