public function testCanConstructSearchTableRebuilder() { $connection = $this->getMockBuilder('\\SMW\\MediaWiki\\Database')->disableOriginalConstructor()->getMock(); $this->store->expects($this->atLeastOnce())->method('getConnection')->will($this->returnValue($connection)); $instance = new FulltextSearchTableFactory(); $this->assertInstanceOf('\\SMW\\SQLStore\\QueryEngine\\Fulltext\\SearchTableRebuilder', $instance->newSearchTableRebuilder($this->store)); }
/** * @see Job::run * * @since 2.5 */ public function run() { $fulltextSearchTableFactory = new FulltextSearchTableFactory(); $textByChangeUpdater = $fulltextSearchTableFactory->newTextByChangeUpdater(ApplicationFactory::getInstance()->getStore('\\SMW\\SQLStore\\SQLStore')); $textByChangeUpdater->pushUpdatesFromJobParameters($this->params); Hooks::run('SMW::Job::AfterSearchTableUpdateComplete', array($this)); return true; }
/** * @see Maintenance::execute */ public function execute() { if (!defined('SMW_VERSION') || !$GLOBALS['smwgSemanticsEnabled']) { $this->output("You need to have SMW enabled in order to use this maintenance script!\n\n"); exit; } $applicationFactory = ApplicationFactory::getInstance(); $maintenanceFactory = $applicationFactory->newMaintenanceFactory(); $fulltextSearchTableFactory = new FulltextSearchTableFactory(); // Only the SQLStore is supported $searchTableRebuilder = $fulltextSearchTableFactory->newSearchTableRebuilder($applicationFactory->getStore('\\SMW\\SQLStore\\SQLStore')); $searchTableRebuilder->reportVerbose($this->hasOption('v')); $this->reportMessage("\nThe script rebuilds the search index from property tables that\n" . "support a fulltext search. Any change of the index rules (altered\n" . "stopwords, new stemmer etc.) and/or a newly added or altered table\n" . "requires to run this script again to ensure that the index complies\n" . "with the rules set forth by the DB or Sanitizer.\n\n"); $searchTable = $searchTableRebuilder->getSearchTable(); $textSanitizer = $fulltextSearchTableFactory->newTextSanitizer(); foreach ($textSanitizer->getVersions() as $key => $value) { $this->reportMessage("\r" . sprintf("%-35s%s", "- {$key}", $value) . "\n"); } $this->reportMessage("\nThe following properties are exempted from the fulltext search index.\n"); $exemptionList = ''; foreach ($searchTable->getPropertyExemptionList() as $prop) { $exemptionList .= ($exemptionList === '' ? '' : ', ') . $prop; if (strlen($exemptionList) > 60) { $this->reportMessage("\n- " . $exemptionList); $exemptionList = ''; } } $this->reportMessage("\n- " . $exemptionList . "\n\n"); $this->reportMessage("The entire index table is going to be purged first and \n" . "it may take a moment before the rebuild is completed due to\n" . "dependencies on table content and selected options.\n"); if (!$this->hasOption('quick')) { $this->reportMessage("\n" . 'Abort the rebuild with control-c in the next five seconds ... '); wfCountDown(5); } $maintenanceHelper = $maintenanceFactory->newMaintenanceHelper(); $maintenanceHelper->initRuntimeValues(); // Need to instantiate an extra object here since we cannot make this class itself // into a MessageReporter since the maintenance script does not load the interface in time. $reporter = MessageReporterFactory::getInstance()->newObservableMessageReporter(); $reporter->registerReporterCallback(array($this, 'reportMessage')); $searchTableRebuilder->setMessageReporter($reporter); $result = $searchTableRebuilder->run(); if ($result && $this->hasOption('report-runtime')) { $this->reportMessage("\n" . $maintenanceHelper->transformRuntimeValuesForOutput() . "\n"); } if ($this->hasOption('with-maintenance-log')) { $maintenanceLogger = $maintenanceFactory->newMaintenanceLogger('RebuildFulltextSearchTableLogger'); $maintenanceLogger->log($maintenanceHelper->transformRuntimeValuesForOutput()); } $maintenanceHelper->reset(); return $result; }
private function addFulltextSearchCondition($query, $comparator, &$value) { // Remove remaining ~ from the search string $value = str_replace('~', '', $value); $valueMatchConditionBuilder = $this->fulltextSearchTableFactory->newValueMatchConditionBuilderByType($this->querySegmentListBuilder->getStore()); if (!$valueMatchConditionBuilder->isEnabled() || !$valueMatchConditionBuilder->hasMinTokenLength($value)) { return false; } $query->joinTable = $valueMatchConditionBuilder->getTableName(); $query->joinfield = "{$query->alias}.s_id"; $query->components = array(); $query->where = $valueMatchConditionBuilder->getWhereCondition(new ValueDescription(new DIBlob($value), null, $comparator), $query->alias); return $query; }
/** * Given an Description that is just a conjunction or disjunction of * ValueDescription objects, create and return a plain WHERE condition * string for it. * * @param $query * @param ValueDescription $description * @param DataItemHandler $diHandler for that table * @param string $operator SQL operator "AND" or "OR" */ private function mapValueDescription($query, ValueDescription $description, DataItemHandler $diHandler, $operator) { $where = ''; $dataItem = $description->getDataItem(); $db = $this->querySegmentListBuilder->getStore()->getConnection('mw.db.queryengine'); $valueMatchConditionBuilder = $this->fulltextSearchTableFactory->newValueMatchConditionBuilderByType($this->querySegmentListBuilder->getStore()); // TODO Better get the handle from the property type // Some comparators (e.g. LIKE) could use DI values of // a different type; we care about the property table, not // about the value // Do not support smw_id joined data for now. $indexField = $diHandler->getIndexField(); //Hack to get to the field used as index $keys = $diHandler->getWhereConds($dataItem); $value = $keys[$indexField]; // See if the getSQLCondition method exists and call it if this is the case. // Invoked by SMAreaValueDescription, SMGeoCoordsValueDescription if (method_exists($description, 'getSQLCondition')) { $fields = $diHandler->getTableFields(); $where = $description->getSQLCondition($query->alias, array_keys($fields), $this->querySegmentListBuilder->getStore()->getConnection(DB_SLAVE)); } if ($where == '' && $valueMatchConditionBuilder->canApplyFulltextSearchMatchCondition($description)) { $query->joinTable = $valueMatchConditionBuilder->getTableName(); $query->sortIndexField = $valueMatchConditionBuilder->getSortIndexField($query->alias); $query->components = array(); $where = $valueMatchConditionBuilder->getWhereCondition($description, $query->alias); } elseif ($where == '') { $comparator = $this->comparatorMapper->mapComparator($description, $value); $where = "{$query->alias}.{$indexField}{$comparator}" . $db->addQuotes($value); } if ($where !== '') { if ($query->where && substr($query->where, -1) != '(') { $query->where .= " {$operator} "; } $query->where .= "({$where})"; } }
private function registerHooksForInternalUse(ApplicationFactory $applicationFactory, DeferredRequestDispatchManager $deferredRequestDispatchManager) { $queryDependencyLinksStoreFactory = new QueryDependencyLinksStoreFactory(); $queryDependencyLinksStore = $queryDependencyLinksStoreFactory->newQueryDependencyLinksStore($applicationFactory->getStore()); $transitionalDiffStore = $applicationFactory->singleton('TransitionalDiffStore'); /** * @see https://www.semantic-mediawiki.org/wiki/Hooks#SMW::SQLStore::AfterDataUpdateComplete */ $this->handlers['SMW::SQLStore::AfterDataUpdateComplete'] = function ($store, $semanticData, $compositePropertyTableDiffIterator) use($queryDependencyLinksStoreFactory, $queryDependencyLinksStore, $deferredRequestDispatchManager, $transitionalDiffStore) { // When in commandLine mode avoid deferred execution and run a process // within the same transaction $deferredRequestDispatchManager->isCommandLineMode($store->getOptions()->has('isCommandLineMode') ? $store->getOptions()->get('isCommandLineMode') : $GLOBALS['wgCommandLineMode']); $queryDependencyLinksStore->setStore($store); $subject = $semanticData->getSubject(); $slot = $transitionalDiffStore->createSlotFrom($compositePropertyTableDiffIterator); $queryDependencyLinksStore->pruneOutdatedTargetLinks($subject, $compositePropertyTableDiffIterator); $entityIdListRelevanceDetectionFilter = $queryDependencyLinksStoreFactory->newEntityIdListRelevanceDetectionFilter($store, $compositePropertyTableDiffIterator); $jobParameters = $queryDependencyLinksStore->buildParserCachePurgeJobParametersFrom($entityIdListRelevanceDetectionFilter); $deferredRequestDispatchManager->scheduleParserCachePurgeJobWith($subject->getTitle(), $jobParameters); $fulltextSearchTableFactory = new FulltextSearchTableFactory(); $textByChangeUpdater = $fulltextSearchTableFactory->newTextByChangeUpdater($store); $textByChangeUpdater->pushUpdates($compositePropertyTableDiffIterator, $deferredRequestDispatchManager, $slot); // Since we cannot predict as to when the slot is used and by whom, // schedule a job to ensure to be the last in-line to clean-up // any remaining slots for this transaction $deferredRequestDispatchManager->scheduleChronologyPurgeJobWith($subject->getTitle(), array('slot:id' => $slot)); return true; }; /** * @see https://www.semantic-mediawiki.org/wiki/Hooks#SMW::Store::AfterQueryResultLookupComplete */ $this->handlers['SMW::Store::AfterQueryResultLookupComplete'] = function ($store, &$result) use($queryDependencyLinksStore, $applicationFactory) { $queryDependencyLinksStore->setStore($store); $queryDependencyLinksStore->doUpdateDependenciesFrom($result); $applicationFactory->singleton('CachedQueryResultPrefetcher')->recordStats(); return true; }; }