/**
  * 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;
 }
 /**
  * Explicitly invoke the process that performs the group
  * processing. Can be run either by a background task or a queuedjob.
  *
  * Does not commit changes to the index, so this must be controlled externally.
  *
  * @param LoggerInterface $logger
  * @param SolrIndex $indexInstance
  * @param array $state
  * @param string $class
  * @param int $groups
  * @param int $group
  */
 public function runGroup(LoggerInterface $logger, SolrIndex $indexInstance, $state, $class, $groups, $group)
 {
     // Set time limit and state
     increase_time_limit_to();
     SearchVariant::activate_state($state);
     $logger->info("Adding {$class}");
     // Prior to adding these records to solr, delete existing solr records
     $this->clearRecords($indexInstance, $class, $groups, $group);
     // Process selected records in this class
     $items = $this->getRecordsInGroup($indexInstance, $class, $groups, $group);
     $processed = array();
     foreach ($items as $item) {
         $processed[] = $item->ID;
         // By this point, obsolete classes/states have been removed in processVariant
         // and obsolete records have been removed in clearRecords
         $indexInstance->add($item);
         $item->destroy();
     }
     $logger->info("Updated " . implode(',', $processed));
     // 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');
     $logger->info("Done");
 }
 /**
  * @deprecated since version 2.0.0
  */
 protected function runFrom($index, $class, $start, $variantstate)
 {
     DeprecationTest_Deprecation::notice('2.0.0', 'Solr_Reindex now uses a new grouping mechanism');
     // Set time limit and state
     increase_time_limit_to();
     SearchVariant::activate_state($variantstate);
     // Generate filtered list
     $items = DataList::create($class)->limit($this->config()->recordsPerRequest, $start);
     // Add child filter
     $classes = $index->getClasses();
     $options = $classes[$class];
     if (!$options['include_children']) {
         $items = $items->filter('ClassName', $class);
     }
     // Process selected records in this class
     $this->getLogger()->info("Adding {$class}");
     foreach ($items->sort("ID") as $item) {
         $this->getLogger()->debug($item->ID);
         // See SearchUpdater_ObjectHandler::triggerReindex
         $item->triggerReindex();
         $item->destroy();
     }
     $this->getLogger()->info("Done");
 }
 /**
  * Generates the list of indexes to process for the dirty items
  * 
  * @return array
  */
 protected function prepareIndexes()
 {
     $originalState = SearchVariant::current_state();
     $dirtyIndexes = array();
     $dirty = $this->getSource();
     $indexes = FullTextSearch::get_indexes();
     foreach ($dirty as $base => $statefulids) {
         if (!$statefulids) {
             continue;
         }
         foreach ($statefulids as $statefulid) {
             $state = $statefulid['state'];
             $ids = $statefulid['ids'];
             SearchVariant::activate_state($state);
             // Ensure that indexes for all new / updated objects are included
             $objs = DataObject::get($base)->byIDs(array_keys($ids));
             foreach ($objs as $obj) {
                 foreach ($ids[$obj->ID] as $index) {
                     if (!$indexes[$index]->variantStateExcluded($state)) {
                         $indexes[$index]->add($obj);
                         $dirtyIndexes[$index] = $indexes[$index];
                     }
                 }
                 unset($ids[$obj->ID]);
             }
             // Generate list of records that do not exist and should be removed
             foreach ($ids as $id => $fromindexes) {
                 foreach ($fromindexes as $index) {
                     if (!$indexes[$index]->variantStateExcluded($state)) {
                         $indexes[$index]->delete($base, $id, $state);
                         $dirtyIndexes[$index] = $indexes[$index];
                     }
                 }
             }
         }
     }
     SearchVariant::activate_state($originalState);
     return $dirtyIndexes;
 }
 protected function runFrom($index, $class, $start, $variantstate)
 {
     $classes = $index->getClasses();
     $options = $classes[$class];
     $verbose = isset($_GET['verbose']);
     SearchVariant::activate_state($variantstate);
     $includeSubclasses = $options['include_children'];
     $filter = $includeSubclasses ? "" : '"ClassName" = \'' . $class . "'";
     $items = DataList::create($class)->where($filter)->limit($this->stat('recordsPerRequest'), $start);
     if ($verbose) {
         echo "Adding {$class}";
     }
     foreach ($items as $item) {
         if ($verbose) {
             echo $item->ID . ' ';
         }
         // See SearchUpdater_ObjectHandler::triggerReindex
         $item->triggerReindex();
         $item->destroy();
     }
     if ($verbose) {
         echo "Done ";
     }
 }