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;
     };
 }