/**
  * Process index for a single SolrIndex instance
  *
  * @param LoggerInterface $logger
  * @param SolrIndex $indexInstance
  * @param int $batchSize
  * @param string $taskName
  * @param string $classes
  */
 protected function processIndex(LoggerInterface $logger, SolrIndex $indexInstance, $batchSize, $taskName, $classes = null)
 {
     // Filter classes for this index
     $indexClasses = $this->getClassesForIndex($indexInstance, $classes);
     // Clear all records in this index which do not contain the given classes
     $logger->info("Clearing obsolete classes from " . $indexInstance->getIndexName());
     $indexInstance->clearObsoleteClasses($indexClasses);
     // Build queue for each class
     foreach ($indexClasses as $class => $options) {
         $includeSubclasses = $options['include_children'];
         foreach (SearchVariant::reindex_states($class, $includeSubclasses) as $state) {
             $this->processVariant($logger, $indexInstance, $state, $class, $includeSubclasses, $batchSize, $taskName);
         }
     }
 }
 /**
  * Given a class, object id, set of stateful ids and a list of changed fields (in a special format),
  * return what statefulids need updating in this index
  *
  * Internal function used by SearchUpdater.
  *
  * @param  $class
  * @param  $id
  * @param  $statefulids
  * @param  $fields
  * @return array
  */
 public function getDirtyIDs($class, $id, $statefulids, $fields)
 {
     $dirty = array();
     // First, if this object is directly contained in the index, add it
     foreach ($this->classes as $searchclass => $options) {
         if ($searchclass == $class || $options['include_children'] && is_subclass_of($class, $searchclass)) {
             $base = ClassInfo::baseDataClass($searchclass);
             $dirty[$base] = array();
             foreach ($statefulids as $statefulid) {
                 $key = serialize($statefulid);
                 $dirty[$base][$key] = $statefulid;
             }
         }
     }
     $current = SearchVariant::current_state();
     // Then, for every derived field
     foreach ($this->getDerivedFields() as $derivation) {
         // If the this object is a subclass of any of the classes we want a field from
         if (!SearchIntrospection::is_subclass_of($class, $derivation['classes'])) {
             continue;
         }
         if (!array_intersect_key($fields, $derivation['fields'])) {
             continue;
         }
         foreach (SearchVariant::reindex_states($class, false) as $state) {
             SearchVariant::activate_state($state);
             $ids = array($id);
             foreach ($derivation['chain'] as $step) {
                 if ($step['through'] == 'has_one') {
                     $sql = new SQLQuery('"ID"', '"' . $step['class'] . '"', '"' . $step['foreignkey'] . '" IN (' . implode(',', $ids) . ')');
                     singleton($step['class'])->extend('augmentSQL', $sql);
                     $ids = $sql->execute()->column();
                 } else {
                     if ($step['through'] == 'has_many') {
                         $sql = new SQLQuery('"' . $step['class'] . '"."ID"', '"' . $step['class'] . '"', '"' . $step['otherclass'] . '"."ID" IN (' . implode(',', $ids) . ')');
                         $sql->addInnerJoin($step['otherclass'], '"' . $step['class'] . '"."ID" = "' . $step['otherclass'] . '"."' . $step['foreignkey'] . '"');
                         singleton($step['class'])->extend('augmentSQL', $sql);
                         $ids = $sql->execute()->column();
                     }
                 }
             }
             SearchVariant::activate_state($current);
             if ($ids) {
                 $base = $derivation['base'];
                 if (!isset($dirty[$base])) {
                     $dirty[$base] = array();
                 }
                 foreach ($ids as $id) {
                     $statefulid = array('id' => $id, 'state' => $state);
                     $key = serialize($statefulid);
                     $dirty[$base][$key] = $statefulid;
                 }
             }
         }
     }
     return $dirty;
 }
 /**
  * Ensure the test variant is up and running properly
  */
 public function testVariant()
 {
     // State defaults to 0
     $variant = SearchVariant::current_state();
     $this->assertEquals(array("SolrReindexTest_Variant" => "0"), $variant);
     // All states enumerated
     $allStates = iterator_to_array(SearchVariant::reindex_states());
     $this->assertEquals(array(array("SolrReindexTest_Variant" => "0"), array("SolrReindexTest_Variant" => "1"), array("SolrReindexTest_Variant" => "2")), $allStates);
     // Check correct items created and that filtering on variant works
     $this->createDummyData(120);
     SolrReindexTest_Variant::set_current(2);
     $this->assertEquals(0, SolrReindexTest_Item::get()->count());
     SolrReindexTest_Variant::set_current(1);
     $this->assertEquals(120, SolrReindexTest_Item::get()->count());
     SolrReindexTest_Variant::set_current(0);
     $this->assertEquals(120, SolrReindexTest_Item::get()->count());
     SolrReindexTest_Variant::disable();
     $this->assertEquals(240, SolrReindexTest_Item::get()->count());
 }
 public function run($request)
 {
     increase_time_limit_to();
     $self = get_class($this);
     $verbose = isset($_GET['verbose']);
     $originalState = SearchVariant::current_state();
     if (isset($_GET['start'])) {
         $this->runFrom(singleton($_GET['index']), $_GET['class'], $_GET['start'], json_decode($_GET['variantstate'], true));
     } else {
         foreach (array('framework', 'sapphire') as $dirname) {
             $script = sprintf("%s%s{$dirname}%scli-script.php", BASE_PATH, DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR);
             if (file_exists($script)) {
                 break;
             }
         }
         $class = get_class($this);
         foreach (Solr::get_indexes() as $index => $instance) {
             echo "Rebuilding {$instance->getIndexName()}\n\n";
             $classes = $instance->getClasses();
             if ($request->getVar('class')) {
                 $limitClasses = explode(',', $request->getVar('class'));
                 $classes = array_intersect_key($classes, array_combine($limitClasses, $limitClasses));
             }
             if ($classes) {
                 Solr::service($index)->deleteByQuery('ClassHierarchy:(' . implode(' OR ', array_keys($classes)) . ')');
             }
             foreach ($classes as $class => $options) {
                 $includeSubclasses = $options['include_children'];
                 foreach (SearchVariant::reindex_states($class, $includeSubclasses) as $state) {
                     if ($instance->variantStateExcluded($state)) {
                         continue;
                     }
                     SearchVariant::activate_state($state);
                     $filter = $includeSubclasses ? "" : '"ClassName" = \'' . $class . "'";
                     $singleton = singleton($class);
                     $query = $singleton->get($class, $filter, null);
                     $dtaQuery = $query->dataQuery();
                     $sqlQuery = $dtaQuery->getFinalisedQuery();
                     $singleton->extend('augmentSQL', $sqlQuery, $dtaQuery);
                     $total = $query->count();
                     $statevar = json_encode($state);
                     echo "Class: {$class}, total: {$total}";
                     echo $statevar ? " in state {$statevar}\n" : "\n";
                     if (strpos(PHP_OS, "WIN") !== false) {
                         $statevar = '"' . str_replace('"', '\\"', $statevar) . '"';
                     } else {
                         $statevar = "'" . $statevar . "'";
                     }
                     for ($offset = 0; $offset < $total; $offset += $this->stat('recordsPerRequest')) {
                         echo "{$offset}..";
                         $cmd = "php {$script} dev/tasks/{$self} index={$index} class={$class} start={$offset} variantstate={$statevar}";
                         if ($verbose) {
                             echo "\n  Running '{$cmd}'\n";
                             $cmd .= " verbose=1 2>&1";
                         }
                         $res = $verbose ? passthru($cmd) : `{$cmd}`;
                         if ($verbose) {
                             echo "  " . preg_replace('/\\r\\n|\\n/', '$0  ', $res) . "\n";
                         }
                         // If we're in dev mode, commit more often for fun and profit
                         if (Director::isDev()) {
                             Solr::service($index)->commit();
                         }
                         // This will slow down things a tiny bit, but it is done so that we don't timeout to the database during a reindex
                         DB::query('SELECT 1');
                     }
                     echo "\n";
                 }
             }
             Solr::service($index)->commit();
         }
     }
     $originalState = SearchVariant::current_state();
 }