diff options
Diffstat (limited to 'www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/SPARQLStore.php')
-rw-r--r-- | www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/SPARQLStore.php | 486 |
1 files changed, 486 insertions, 0 deletions
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/SPARQLStore.php b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/SPARQLStore.php new file mode 100644 index 00000000..d83d32c2 --- /dev/null +++ b/www/wiki/extensions/SemanticMediaWiki/src/SPARQLStore/SPARQLStore.php @@ -0,0 +1,486 @@ +<?php + +namespace SMW\SPARQLStore; + +use SMW\DIProperty; +use SMW\DIWikiPage; +use SMW\SemanticData; +use SMW\Store; +use SMWDataItem as DataItem; +use SMWExpNsResource as ExpNsResource; +use SMWExporter as Exporter; +use SMWQuery as Query; +use SMWTurtleSerializer as TurtleSerializer; +use Title; + +/** + * Storage and query access point for a SPARQL supported RepositoryConnector to + * enable SMW to communicate with a SPARQL endpoint. + * + * The store uses a base store to update certain aspects of the data that is not + * yet modelled and supported by a RepositoryConnector, which may become optional + * in future. + * + * @license GNU GPL v2+ + * @since 1.6 + * + * @author Markus Krötzsch + */ +class SPARQLStore extends Store { + + /** + * @var SPARQLStoreFactory + */ + private $factory; + + /** + * Class to be used as an underlying base store. This can be changed in + * LocalSettings.php (after enableSemantics()) to use another base + * store. + * + * @since 1.8 + * @var string + */ + static public $baseStoreClass = 'SMWSQLStore3'; + + /** + * Underlying store to use for basic read operations. + * + * @since 1.8 + * @var Store + */ + private $baseStore; + + /** + * @since 1.8 + * + * @param Store $baseStore + */ + public function __construct( Store $baseStore = null ) { + $this->factory = new SPARQLStoreFactory( $this ); + $this->baseStore = $baseStore; + + if ( $this->baseStore === null ) { + $this->baseStore = $this->factory->getBaseStore( self::$baseStoreClass ); + } + } + + /** + * @see Store::getSemanticData() + * @since 1.8 + */ + public function getSemanticData( DIWikiPage $subject, $filter = false ) { + return $this->baseStore->getSemanticData( $subject, $filter ); + } + + /** + * @see Store::getPropertyValues() + * @since 1.8 + */ + public function getPropertyValues( $subject, DIProperty $property, $requestoptions = null ) { + return $this->baseStore->getPropertyValues( $subject, $property, $requestoptions); + } + + /** + * @see Store::getPropertySubjects() + * @since 1.8 + */ + public function getPropertySubjects( DIProperty $property, $value, $requestoptions = null ) { + return $this->baseStore->getPropertySubjects( $property, $value, $requestoptions ); + } + + /** + * @see Store::getAllPropertySubjects() + * @since 1.8 + */ + public function getAllPropertySubjects( DIProperty $property, $requestoptions = null ) { + return $this->baseStore->getAllPropertySubjects( $property, $requestoptions ); + } + + /** + * @see Store::getProperties() + * @since 1.8 + */ + public function getProperties( DIWikiPage $subject, $requestoptions = null ) { + return $this->baseStore->getProperties( $subject, $requestoptions ); + } + + /** + * @see Store::getInProperties() + * @since 1.8 + */ + public function getInProperties( DataItem $object, $requestoptions = null ) { + return $this->baseStore->getInProperties( $object, $requestoptions ); + } + + /** + * @see Store::deleteSubject() + * @since 1.6 + */ + public function deleteSubject( Title $subject ) { + $this->doSparqlDataDelete( DIWikiPage::newFromTitle( $subject ) ); + $this->baseStore->deleteSubject( $subject ); + } + + /** + * @see Store::changeTitle() + * @since 1.6 + */ + public function changeTitle( Title $oldtitle, Title $newtitle, $pageid, $redirid = 0 ) { + + $oldWikiPage = DIWikiPage::newFromTitle( $oldtitle ); + $newWikiPage = DIWikiPage::newFromTitle( $newtitle ); + $oldExpResource = Exporter::getInstance()->getDataItemExpElement( $oldWikiPage ); + $newExpResource = Exporter::getInstance()->getDataItemExpElement( $newWikiPage ); + $namespaces = [ $oldExpResource->getNamespaceId() => $oldExpResource->getNamespace() ]; + $namespaces[$newExpResource->getNamespaceId()] = $newExpResource->getNamespace(); + $oldUri = TurtleSerializer::getTurtleNameForExpElement( $oldExpResource ); + $newUri = TurtleSerializer::getTurtleNameForExpElement( $newExpResource ); + + // do this only here, so Imported from is not moved too early + $this->baseStore->changeTitle( + $oldtitle, + $newtitle, + $pageid, + $redirid + ); + + $sparqlDatabase = $this->getConnection(); + $sparqlDatabase->insertDelete( "?s ?p $newUri", "?s ?p $oldUri", "?s ?p $oldUri", $namespaces ); + + if ( $oldtitle->getNamespace() === SMW_NS_PROPERTY ) { + $sparqlDatabase->insertDelete( "?s $newUri ?o", "?s $oldUri ?o", "?s $oldUri ?o", $namespaces ); + } + + /** + * @since 2.3 Moved UpdateJob to the base-store to ensurethat both stores + * operate similar when dealing with redirects + * + * @note Note that we cannot change oldUri to newUri in triple subjects, + * since some triples change due to the move. + */ + + // #566 $redirid == 0 indicates a `move` not a redirect action + if ( $redirid == 0 ) { + $this->doSparqlDataDelete( $oldWikiPage ); + } + } + + /** + * Update the Sparql back-end. + * + * This method can be called independently to force an update of the Sparql + * database. In general it is suggested to use updateData to carry out a + * synchronized update of the base and Sparql store. + * + * @since 2.0 + * + * @param SemanticData $semanticData + */ + public function doSparqlDataUpdate( SemanticData $semanticData ) { + + $replicationDataTruncator = $this->factory->newReplicationDataTruncator(); + $semanticData = $replicationDataTruncator->doTruncate( $semanticData ); + + $turtleTriplesBuilder = $this->factory->newTurtleTriplesBuilder(); + + $this->doSparqlFlatDataUpdate( $semanticData, $turtleTriplesBuilder ); + + foreach( $semanticData->getSubSemanticData() as $subSemanticData ) { + $subSemanticData = $replicationDataTruncator->doTruncate( $subSemanticData ); + $this->doSparqlFlatDataUpdate( $subSemanticData, $turtleTriplesBuilder ); + } + + //wfDebugLog( 'smw', ' InMemoryPoolCache: ' . json_encode( \SMW\InMemoryPoolCache::getInstance()->getStats() ) ); + + // Reset internal cache + $turtleTriplesBuilder->reset(); + } + + /** + * @param SemanticData $semanticData + * @param TurtleTriplesBuilder $turtleTriplesBuilder + */ + private function doSparqlFlatDataUpdate( SemanticData $semanticData, TurtleTriplesBuilder $turtleTriplesBuilder ) { + + $turtleTriplesBuilder->doBuildTriplesFrom( $semanticData ); + + if ( !$turtleTriplesBuilder->hasTriples() ) { + return; + } + + if ( $semanticData->getSubject()->getSubobjectName() === '' ) { + $this->doSparqlDataDelete( $semanticData->getSubject() ); + } + + foreach( $turtleTriplesBuilder->getChunkedTriples() as $chunkedTriples ) { + $this->getConnection()->insertData( + $chunkedTriples, + $turtleTriplesBuilder->getPrefixes() + ); + } + } + + /** + * @see Store::doDataUpdate() + * @since 1.6 + */ + protected function doDataUpdate( SemanticData $semanticData ) { + $this->baseStore->doDataUpdate( $semanticData ); + $this->doSparqlDataUpdate( $semanticData ); + } + + /** + * Delete a dataitem from the Sparql back-end together with all data that is + * associated resources + * + * @since 2.0 + * + * @param DataItem $dataItem + * + * @return boolean + */ + public function doSparqlDataDelete( DataItem $dataItem ) { + + $extraNamespaces = []; + + $expResource = Exporter::getInstance()->getDataItemExpElement( $dataItem ); + $resourceUri = TurtleSerializer::getTurtleNameForExpElement( $expResource ); + + if ( $expResource instanceof ExpNsResource ) { + $extraNamespaces = [ $expResource->getNamespaceId() => $expResource->getNamespace() ]; + } + + $masterPageProperty = Exporter::getInstance()->getSpecialNsResource( 'swivt', 'masterPage' ); + $masterPagePropertyUri = TurtleSerializer::getTurtleNameForExpElement( $masterPageProperty ); + + $success = $this->getConnection()->deleteContentByValue( $masterPagePropertyUri, $resourceUri, $extraNamespaces ); + + if ( $success ) { + return $this->getConnection()->delete( "$resourceUri ?p ?o", "$resourceUri ?p ?o", $extraNamespaces ); + } + + return false; + } + + /** + * @note Move hooks to the base class in 3.* + * + * @see Store::getQueryResult + * @since 1.6 + */ + public function getQueryResult( Query $query ) { + + // Use a fallback QueryEngine in case the QueryEndpoint is inaccessible + if ( !$this->hasQueryEndpoint() ) { + return $this->baseStore->getQueryResult( $query ); + } + + $result = null; + $start = microtime( true ); + + if ( \Hooks::run( 'SMW::Store::BeforeQueryResultLookupComplete', [ $this, $query, &$result, $this->factory->newMasterQueryEngine() ] ) ) { + $result = $this->fetchQueryResult( $query ); + } + + \Hooks::run( 'SMW::Store::AfterQueryResultLookupComplete', [ $this, &$result ] ); + + $query->setOption( Query::PROC_QUERY_TIME, microtime( true ) - $start ); + + return $result; + } + + protected function fetchQueryResult( Query $query ) { + return $this->factory->newMasterQueryEngine()->getQueryResult( $query ); + } + + /** + * @see Store::getPropertiesSpecial() + * @since 1.8 + */ + public function getPropertiesSpecial( $requestoptions = null ) { + return $this->baseStore->getPropertiesSpecial( $requestoptions ); + } + + /** + * @see Store::getUnusedPropertiesSpecial() + * @since 1.8 + */ + public function getUnusedPropertiesSpecial( $requestoptions = null ) { + return $this->baseStore->getUnusedPropertiesSpecial( $requestoptions ); + } + + /** + * @see Store::getWantedPropertiesSpecial() + * @since 1.8 + */ + public function getWantedPropertiesSpecial( $requestoptions = null ) { + return $this->baseStore->getWantedPropertiesSpecial( $requestoptions ); + } + + /** + * @see Store::getStatistics() + * @since 1.8 + */ + public function getStatistics() { + return $this->baseStore->getStatistics(); + } + + /** + * @see Store::refreshConceptCache() + * @since 1.8 + */ + public function refreshConceptCache( Title $concept ) { + return $this->baseStore->refreshConceptCache( $concept ); + } + + /** + * @see Store::deleteConceptCache() + * @since 1.8 + */ + public function deleteConceptCache( $concept ) { + return $this->baseStore->deleteConceptCache( $concept ); + } + + /** + * @see Store::getConceptCacheStatus() + * @since 1.8 + */ + public function getConceptCacheStatus( $concept ) { + return $this->baseStore->getConceptCacheStatus( $concept ); + } + + /** + * @see Store::service + * + * {@inheritDoc} + */ + public function service( $service, ...$args ) { + return $this->baseStore->service( $service, ...$args ); + } + + /** + * @see Store::setup() + * @since 1.8 + */ + public function setup( $verbose = true ) { + + // Only copy required options to the base store + $options = $this->getOptions()->filter( + [ + \SMW\SQLStore\Installer::OPT_TABLE_OPTIMIZE, + \SMW\SQLStore\Installer::OPT_IMPORT, + \SMW\SQLStore\Installer::OPT_SCHEMA_UPDATE, + \SMW\SQLStore\Installer::OPT_SUPPLEMENT_JOBS + ] + ); + + foreach ( $options as $key => $value ) { + $this->baseStore->setOption( $key, $value ); + } + + $this->baseStore->setMessageReporter( $this->messageReporter ); + $this->baseStore->setup( $verbose ); + } + + /** + * @see Store::drop() + * @since 1.6 + */ + public function drop( $verbose = true ) { + $this->baseStore->drop( $verbose ); + $this->getConnection()->deleteAll(); + } + + /** + * @see Store::refreshData() + * @since 1.8 + */ + public function refreshData( &$index, $count, $namespaces = false, $usejobs = true ) { + return $this->baseStore->refreshData( $index, $count, $namespaces, $usejobs ); + } + + /** + * @since 2.5 + * + * @return PropertyTableInfoFetcher + */ + public function getPropertyTableInfoFetcher() { + return $this->baseStore->getPropertyTableInfoFetcher(); + } + + /** + * @since 2.0 + */ + public function getPropertyTables() { + return $this->baseStore->getPropertyTables(); + } + + /** + * @since 2.3 + */ + public function getObjectIds() { + return $this->baseStore->getObjectIds(); + } + + /** + * @since 2.4 + */ + public function getPropertyTableIdReferenceFinder() { + return $this->baseStore->getPropertyTableIdReferenceFinder(); + } + + /** + * @since 1.9.2 + */ + public function clear() { + $this->baseStore->clear(); + } + + /** + * @since 3.0 + * + * @param string|null $type + * + * @return array + */ + public function getInfo( $type = null ) { + + $client = $this->getConnection( 'sparql' )->getRepositoryClient(); + + if ( $type === 'store' ) { + return [ 'SMWSPARQLStore', $client->getName() ]; + } + + $connection = $this->getConnection( 'mw.db' ); + + if ( $type === 'db' ) { + return $connection->getInfo(); + } + + return [ + 'SMWSPARQLStore' => $connection->getInfo() + [ $client->getName() => 'n/a' ] + ]; + } + + /** + * @since 2.1 + * + * @param string $type + * + * @return mixed + */ + public function getConnection( $type = 'sparql' ) { + + if ( $this->connectionManager === null ) { + $this->setConnectionManager( $this->factory->getConnectionManager() ); + } + + return parent::getConnection( $type ); + } + + private function hasQueryEndpoint() { + return $this->getConnection( 'sparql' )->getRepositoryClient()->getQueryEndpoint() !== false; + } + +} |