array( * '$State Key' => array( * 'state' => array( * 'key1' => 'value', * 'key2' => 'value' * ), * 'ids' => array( * '*id*' => array( * '*Index Name 1*', * '*Index Name 2*' * ) * ) * ) * ) * ) * * @var array */ protected $dirty; public function __construct() { $this->dirty = array(); } public function addDirtyIDs($class, $statefulids, $index) { $base = DataObject::getSchema()->baseDataClass($class); $forclass = isset($this->dirty[$base]) ? $this->dirty[$base] : array(); foreach ($statefulids as $statefulid) { $id = $statefulid['id']; $state = $statefulid['state']; $statekey = serialize($state); if (!isset($forclass[$statekey])) { $forclass[$statekey] = array('state' => $state, 'ids' => array($id => array($index))); } elseif (!isset($forclass[$statekey]['ids'][$id])) { $forclass[$statekey]['ids'][$id] = array($index); } elseif (array_search($index, $forclass[$statekey]['ids'][$id] ?? []) === false) { $forclass[$statekey]['ids'][$id][] = $index; // dirty count stays the same } } $this->dirty[$base] = $forclass; } /** * 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(); $searchableService = SearchableService::singleton(); 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 ?? [])); /** @var DataObject $obj */ foreach ($objs as $obj) { foreach ($ids[$obj->ID] as $index) { if (!$searchableService->variantStateExcluded($state) && !$indexes[$index]->variantStateExcluded($state) ) { // Remove any existing data from index if the object is no longer indexable if (!$searchableService->isIndexable($obj)) { $indexes[$index]->delete($base, $obj->ID, $state); } else { $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 (!$searchableService->variantStateExcluded($state) && !$indexes[$index]->variantStateExcluded($state) ) { $indexes[$index]->delete($base, $id, $state); $dirtyIndexes[$index] = $indexes[$index]; } } } } } SearchVariant::activate_state($originalState); return $dirtyIndexes; } /** * Commits the specified index to the Solr service * * @param SolrIndex $index Index object * @return bool Flag indicating success */ protected function commitIndex($index) { return $index->commit() !== false; } /** * Gets the record data source to process * * @return array */ protected function getSource() { return $this->dirty; } /** * Process all indexes, returning true if successful * * @return bool Flag indicating success */ public function process() { if (!DB::is_active()) { return false; } // Generate and commit all instances $indexes = $this->prepareIndexes(); foreach ($indexes as $index) { if (!$this->commitIndex($index)) { return false; } } return true; } abstract public function triggerProcessing(); }