diff --git a/.travis.yml b/.travis.yml index 303e94c..29a5d37 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,6 @@ language: php sudo: false php: - - 5.4 - 5.5 - 5.6 @@ -14,8 +13,6 @@ env: matrix: include: - - php: 5.3 - env: DB=PGSQL CORE_RELEASE=3.1 - php: 5.6 env: DB=MYSQL CORE_RELEASE=3.2 - php: 5.6 diff --git a/_config.php b/_config.php index c9e422a..a1de033 100644 --- a/_config.php +++ b/_config.php @@ -1,6 +1,4 @@ $class) { - if (!DataObject::has_own_table($class)) { - unset($classes[$i]); - } - } + $idx = array_search('SilverStripe\Core\Object', $classes); + if ($idx !== false) { + array_splice($classes, 0, $idx+1); } +//@todo find another way to determine if a dataobject does not have a table +// if ($dataOnly) { +// foreach ($classes as $i => $class) { +// if (!DataObject::has_own_table($class)) { +// unset($classes[$i]); +// } +// } +// } self::$hierarchy[$key] = $classes; } diff --git a/code/search/SearchIndex.php b/code/search/indexes/SearchIndex.php similarity index 90% rename from code/search/SearchIndex.php rename to code/search/indexes/SearchIndex.php index 7e4ab9a..2e1750d 100644 --- a/code/search/SearchIndex.php +++ b/code/search/indexes/SearchIndex.php @@ -1,5 +1,15 @@ has_one($lookup)) { + if ($hasOne = $singleton->hasOne($lookup)) { $class = $hasOne; $options['lookup_chain'][] = array( 'call' => 'method', 'method' => $lookup, 'through' => 'has_one', 'class' => $dataclass, 'otherclass' => $class, 'foreignkey' => "{$lookup}ID" ); - } elseif ($hasMany = $singleton->has_many($lookup)) { + } elseif ($hasMany = $singleton->hasMany($lookup)) { $class = $hasMany; $options['multi_valued'] = true; $options['lookup_chain'][] = array( 'call' => 'method', 'method' => $lookup, 'through' => 'has_many', 'class' => $dataclass, 'otherclass' => $class, 'foreignkey' => $singleton->getRemoteJoinField($lookup, 'has_many') ); - } elseif ($manyMany = $singleton->many_many($lookup)) { + } elseif ($manyMany = $singleton->manyMany($lookup)) { $class = $manyMany[1]; $options['multi_valued'] = true; $options['lookup_chain'][] = array( @@ -105,7 +115,7 @@ abstract class SearchIndex extends ViewableData ); } - if ($class) { + if (is_string($class) && $class) { if (!isset($options['origin'])) { $options['origin'] = $dataclass; } @@ -130,7 +140,7 @@ abstract class SearchIndex extends ViewableData $type = null; $fieldoptions = $options; - $fields = DataObject::database_fields($dataclass); + $fields = DataObject::getSchema()->databaseFields($class); if (isset($fields[$field])) { $type = $fields[$field]; @@ -209,7 +219,7 @@ abstract class SearchIndex extends ViewableData throw new Exception('Can\'t add class to Index after fields have already been added'); } - if (!DataObject::has_own_table($class)) { + if (!DataObject::getSchema()->classHasTable($class)) { throw new InvalidArgumentException('Can\'t add classes which don\'t have data tables (no $db or $has_one set on the class)'); } @@ -286,7 +296,7 @@ abstract class SearchIndex extends ViewableData { foreach ($this->getClasses() as $class => $options) { foreach (SearchIntrospection::hierarchy($class, $includeSubclasses, true) as $dataclass) { - $fields = DataObject::database_fields($dataclass); + $fields = DataObject::getSchema()->databaseFields($class); foreach ($fields as $field => $type) { if (preg_match('/^(\w+)\(/', $type, $match)) { @@ -599,95 +609,3 @@ abstract class SearchIndex extends ViewableData */ abstract public function init(); } - -/** - * A search index that does nothing. Useful for testing - */ -abstract class SearchIndex_Null extends SearchIndex -{ - public function add($object) - { - } - - public function delete($base, $id, $state) - { - } - - public function commit() - { - } -} - -/** - * A search index that just records actions. Useful for testing - */ -abstract class SearchIndex_Recording extends SearchIndex -{ - public $added = array(); - public $deleted = array(); - public $committed = false; - - public function reset() - { - $this->added = array(); - $this->deleted = array(); - $this->committed = false; - } - - public function add($object) - { - $res = array(); - - $res['ID'] = $object->ID; - - foreach ($this->getFieldsIterator() as $name => $field) { - $val = $this->_getFieldValue($object, $field); - $res[$name] = $val; - } - - $this->added[] = $res; - } - - public function getAdded($fields = array()) - { - $res = array(); - - foreach ($this->added as $added) { - $filtered = array(); - foreach ($fields as $field) { - if (isset($added[$field])) { - $filtered[$field] = $added[$field]; - } - } - $res[] = $filtered; - } - - return $res; - } - - public function delete($base, $id, $state) - { - $this->deleted[] = array('base' => $base, 'id' => $id, 'state' => $state); - } - - public function commit() - { - $this->committed = true; - } - - public function getIndexName() - { - return get_class($this); - } - - public function getIsCommitted() - { - return $this->committed; - } - - public function getService() - { - // Causes commits to the service to be redirected back to the same object - return $this; - } -} diff --git a/code/search/indexes/SearchIndex_Null.php b/code/search/indexes/SearchIndex_Null.php new file mode 100644 index 0000000..b2a2896 --- /dev/null +++ b/code/search/indexes/SearchIndex_Null.php @@ -0,0 +1,23 @@ +added = array(); + $this->deleted = array(); + $this->committed = false; + } + + public function add($object) + { + $res = array(); + + $res['ID'] = $object->ID; + + foreach ($this->getFieldsIterator() as $name => $field) { + $val = $this->_getFieldValue($object, $field); + $res[$name] = $val; + } + + $this->added[] = $res; + } + + public function getAdded($fields = array()) + { + $res = array(); + + foreach ($this->added as $added) { + $filtered = array(); + foreach ($fields as $field) { + if (isset($added[$field])) { + $filtered[$field] = $added[$field]; + } + } + $res[] = $filtered; + } + + return $res; + } + + public function delete($base, $id, $state) + { + $this->deleted[] = array('base' => $base, 'id' => $id, 'state' => $state); + } + + public function commit() + { + $this->committed = true; + } + + public function getIndexName() + { + return get_class($this); + } + + public function getIsCommitted() + { + return $this->committed; + } + + public function getService() + { + // Causes commits to the service to be redirected back to the same object + return $this; + } +} diff --git a/code/search/processors/SearchUpdateBatchedProcessor.php b/code/search/processors/SearchUpdateBatchedProcessor.php index b684939..eaf2fd4 100644 --- a/code/search/processors/SearchUpdateBatchedProcessor.php +++ b/code/search/processors/SearchUpdateBatchedProcessor.php @@ -1,5 +1,5 @@ start = $start; - $this->end = $end; - } - - public function start($start) - { - $this->start = $start; - } - - public function end($end) - { - $this->end = $end; - } - - public function isfiltered() - { - return $this->start !== null || $this->end !== null; - } -} diff --git a/code/search/queries/SearchQuery_Range.php b/code/search/queries/SearchQuery_Range.php new file mode 100644 index 0000000..33b35e5 --- /dev/null +++ b/code/search/queries/SearchQuery_Range.php @@ -0,0 +1,34 @@ +start = $start; + $this->end = $end; + } + + public function start($start) + { + $this->start = $start; + } + + public function end($end) + { + $this->end = $end; + } + + public function isfiltered() + { + return $this->start !== null || $this->end !== null; + } +} \ No newline at end of file diff --git a/code/search/SearchUpdater.php b/code/search/updaters/SearchUpdater.php similarity index 78% rename from code/search/SearchUpdater.php rename to code/search/updaters/SearchUpdater.php index 0c836f0..b4460c3 100644 --- a/code/search/SearchUpdater.php +++ b/code/search/updaters/SearchUpdater.php @@ -1,5 +1,8 @@ owner->ID) { - return; - } - - // Force SearchUpdater to mark this record as dirty - $manipulation = array( - $this->owner->ClassName => array( - 'fields' => array(), - 'id' => $this->owner->ID, - 'command' => 'update' - ) - ); - $this->owner->extend('augmentWrite', $manipulation); - SearchUpdater::handle_manipulation($manipulation); - } - - /** - * Forces this object to trigger a re-index in the current state - */ - public function triggerReindex() - { - if (!$this->owner->ID) { - return; - } - - $id = $this->owner->ID; - $class = $this->owner->ClassName; - $state = SearchVariant::current_state($class); - $base = ClassInfo::baseDataClass($class); - $key = "$id:$base:".serialize($state); - - $statefulids = array(array( - 'id' => $id, - 'state' => $state - )); - - $writes = array( - $key => array( - 'base' => $base, - 'class' => $class, - 'id' => $id, - 'statefulids' => $statefulids, - 'fields' => array() - ) - ); - - SearchUpdater::process_writes($writes); - } -} diff --git a/code/search/updaters/SearchUpdater_BindManipulationCaptureFilter.php b/code/search/updaters/SearchUpdater_BindManipulationCaptureFilter.php new file mode 100644 index 0000000..e6b4e05 --- /dev/null +++ b/code/search/updaters/SearchUpdater_BindManipulationCaptureFilter.php @@ -0,0 +1,23 @@ +owner->ID) { + return; + } + + // Force SearchUpdater to mark this record as dirty + $manipulation = array( + $this->owner->ClassName => array( + 'fields' => array(), + 'id' => $this->owner->ID, + 'command' => 'update' + ) + ); + $this->owner->extend('augmentWrite', $manipulation); + SearchUpdater::handle_manipulation($manipulation); + } + + /** + * Forces this object to trigger a re-index in the current state + */ + public function triggerReindex() + { + if (!$this->owner->ID) { + return; + } + + $id = $this->owner->ID; + $class = $this->owner->ClassName; + $state = SearchVariant::current_state($class); + $base = ClassInfo::baseDataClass($class); + $key = "$id:$base:".serialize($state); + + $statefulids = array(array( + 'id' => $id, + 'state' => $state + )); + + $writes = array( + $key => array( + 'base' => $base, + 'class' => $class, + 'id' => $id, + 'statefulids' => $statefulids, + 'fields' => array() + ) + ); + + SearchUpdater::process_writes($writes); + } +} \ No newline at end of file diff --git a/code/search/SearchVariant.php b/code/search/variants/SearchVariant.php similarity index 92% rename from code/search/SearchVariant.php rename to code/search/variants/SearchVariant.php index df6e187..57f6479 100644 --- a/code/search/SearchVariant.php +++ b/code/search/variants/SearchVariant.php @@ -1,5 +1,6 @@ variants = $variants; - } - - public function call($method, &$a1=null, &$a2=null, &$a3=null, &$a4=null, &$a5=null, &$a6=null, &$a7=null) - { - $values = array(); - - foreach ($this->variants as $variant) { - if (method_exists($variant, $method)) { - $value = $variant->$method($a1, $a2, $a3, $a4, $a5, $a6, $a7); - if ($value !== null) { - $values[] = $value; - } - } - } - - return $values; - } -} diff --git a/code/search/SearchVariantSiteTreeSubsitesPolyhome.php b/code/search/variants/SearchVariantSiteTreeSubsitesPolyhome.php similarity index 98% rename from code/search/SearchVariantSiteTreeSubsitesPolyhome.php rename to code/search/variants/SearchVariantSiteTreeSubsitesPolyhome.php index 52fc83d..5101dd1 100644 --- a/code/search/SearchVariantSiteTreeSubsitesPolyhome.php +++ b/code/search/variants/SearchVariantSiteTreeSubsitesPolyhome.php @@ -1,4 +1,5 @@ variants = $variants; + } + + public function call($method, &$a1=null, &$a2=null, &$a3=null, &$a4=null, &$a5=null, &$a6=null, &$a7=null) + { + $values = array(); + + foreach ($this->variants as $variant) { + if (method_exists($variant, $method)) { + $value = $variant->$method($a1, $a2, $a3, $a4, $a5, $a6, $a7); + if ($value !== null) { + $values[] = $value; + } + } + } + + return $values; + } +} \ No newline at end of file diff --git a/code/solr/Solr.php b/code/solr/Solr.php index fb460fa..98d3972 100644 --- a/code/solr/Solr.php +++ b/code/solr/Solr.php @@ -1,9 +1,8 @@ logger; - } - - /** - * Assign a new logger - * - * @param LoggerInterface $logger - */ - public function setLogger(LoggerInterface $logger) - { - $this->logger = $logger; - } - - /** - * @return SearchLogFactory - */ - protected function getLoggerFactory() - { - return Injector::inst()->get('SearchLogFactory'); - } - - /** - * Setup task - * - * @param SS_HTTPReqest $request - */ - public function run($request) - { - $name = get_class($this); - $verbose = $request->getVar('verbose'); - - // Set new logger - $logger = $this - ->getLoggerFactory() - ->getOutputLogger($name, $verbose); - $this->setLogger($logger); - } -} - - -class Solr_Configure extends Solr_BuildTask -{ - protected $enabled = true; - - public function run($request) - { - parent::run($request); - - // Find the IndexStore handler, which will handle uploading config files to Solr - $store = $this->getSolrConfigStore(); - $indexes = Solr::get_indexes(); - foreach ($indexes as $instance) { - try { - $this->updateIndex($instance, $store); - } catch (Exception $e) { - // We got an exception. Warn, but continue to next index. - $this - ->getLogger() - ->error("Failure: " . $e->getMessage()); - } - } - } - - /** - * Update the index on the given store - * - * @param SolrIndex $instance Instance - * @param SolrConfigStore $store - */ - protected function updateIndex($instance, $store) - { - $index = $instance->getIndexName(); - $this->getLogger()->info("Configuring $index."); - - // Upload the config files for this index - $this->getLogger()->info("Uploading configuration ..."); - $instance->uploadConfig($store); - - // Then tell Solr to use those config files - $service = Solr::service(); - if ($service->coreIsActive($index)) { - $this->getLogger()->info("Reloading core ..."); - $service->coreReload($index); - } else { - $this->getLogger()->info("Creating core ..."); - $service->coreCreate($index, $store->instanceDir($index)); - } - - $this->getLogger()->info("Done"); - } - - /** - * Get config store - * - * @return SolrConfigStore - */ - protected function getSolrConfigStore() - { - $options = Solr::solr_options(); - - if (!isset($options['indexstore']) || !($indexstore = $options['indexstore'])) { - user_error('No index configuration for Solr provided', E_USER_ERROR); - } - - // Find the IndexStore handler, which will handle uploading config files to Solr - $mode = $indexstore['mode']; - - if ($mode == 'file') { - return new SolrConfigStore_File($indexstore); - } elseif ($mode == 'webdav') { - return new SolrConfigStore_WebDAV($indexstore); - } elseif (ClassInfo::exists($mode) && ClassInfo::classImplements($mode, 'SolrConfigStore')) { - return new $mode($indexstore); - } else { - user_error('Unknown Solr index mode '.$indexstore['mode'], E_USER_ERROR); - } - } -} - -/** - * Task used for both initiating a new reindex, as well as for processing incremental batches - * within a reindex. - * - * When running a complete reindex you can provide any of the following - * - class (to limit to a single class) - * - verbose (optional) - * - * When running with a single batch, provide the following querystring arguments: - * - start - * - index - * - class - * - variantstate - * - verbose (optional) - */ -class Solr_Reindex extends Solr_BuildTask -{ - protected $enabled = true; - - /** - * Number of records to load and index per request - * - * @var int - * @config - */ - private static $recordsPerRequest = 200; - - /** - * Get the reindex handler - * - * @return SolrReindexHandler - */ - protected function getHandler() - { - return Injector::inst()->get('SolrReindexHandler'); - } - - /** - * @param SS_HTTPRequest $request - */ - public function run($request) - { - parent::run($request); - - // Reset state - $originalState = SearchVariant::current_state(); - $this->doReindex($request); - SearchVariant::activate_state($originalState); - } - - /** - * @param SS_HTTPRequest $request - */ - protected function doReindex($request) - { - $class = $request->getVar('class'); - - $index = $request->getVar('index'); - - //find the index classname by IndexName - // this is for when index names do not match the class name (this can be done by overloading getIndexName() on - // indexes - if ($index && !ClassInfo::exists($index)) { - foreach(ClassInfo::subclassesFor('SolrIndex') as $solrIndexClass) { - $reflection = new ReflectionClass($solrIndexClass); - //skip over abstract classes - if (!$reflection->isInstantiable()) { - continue; - } - //check the indexname matches the index passed to the request - if (!strcasecmp(singleton($solrIndexClass)->getIndexName(), $index)) { - //if we match, set the correct index name and move on - $index = $solrIndexClass; - break; - } - } - } - - // Deprecated reindex mechanism - $start = $request->getVar('start'); - if ($start !== null) { - // Run single batch directly - $indexInstance = singleton($index); - $state = json_decode($request->getVar('variantstate'), true); - $this->runFrom($indexInstance, $class, $start, $state); - return; - } - - // Check if we are re-indexing a single group - // If not using queuedjobs, we need to invoke Solr_Reindex as a separate process - // Otherwise each group is processed via a SolrReindexGroupJob - $groups = $request->getVar('groups'); - $handler = $this->getHandler(); - if ($groups) { - // Run grouped batches (id % groups = group) - $group = $request->getVar('group'); - $indexInstance = singleton($index); - $state = json_decode($request->getVar('variantstate'), true); - - $handler->runGroup($this->getLogger(), $indexInstance, $state, $class, $groups, $group); - return; - } - - // If run at the top level, delegate to appropriate handler - $self = get_class($this); - $handler->triggerReindex($this->getLogger(), $this->config()->recordsPerRequest, $self, $class); - } - - /** - * @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"); - } -} +} \ No newline at end of file diff --git a/code/solr/SolrConfigStore.php b/code/solr/SolrConfigStore.php deleted file mode 100644 index 0f16439..0000000 --- a/code/solr/SolrConfigStore.php +++ /dev/null @@ -1,134 +0,0 @@ -local = $config['path']; - $this->remote = isset($config['remotepath']) ? $config['remotepath'] : $config['path']; - } - - public function getTargetDir($index) - { - $targetDir = "{$this->local}/{$index}/conf"; - - if (!is_dir($targetDir)) { - $worked = @mkdir($targetDir, 0770, true); - - if (!$worked) { - throw new RuntimeException( - sprintf('Failed creating target directory %s, please check permissions', $targetDir) - ); - } - } - - return $targetDir; - } - - public function uploadFile($index, $file) - { - $targetDir = $this->getTargetDir($index); - copy($file, $targetDir.'/'.basename($file)); - } - - public function uploadString($index, $filename, $string) - { - $targetDir = $this->getTargetDir($index); - file_put_contents("$targetDir/$filename", $string); - } - - public function instanceDir($index) - { - return $this->remote.'/'.$index; - } -} - -/** - * Class SolrConfigStore_WebDAV - * - * A ConfigStore that uploads files to a Solr instance via a WebDAV server - */ -class SolrConfigStore_WebDAV implements SolrConfigStore -{ - public function __construct($config) - { - $options = Solr::solr_options(); - - $this->url = implode('', array( - 'http://', - isset($config['auth']) ? $config['auth'].'@' : '', - $options['host'].':'.(isset($config['port']) ? $config['port'] : $options['port']), - $config['path'] - )); - $this->remote = $config['remotepath']; - } - - public function getTargetDir($index) - { - $indexdir = "{$this->url}/$index"; - if (!WebDAV::exists($indexdir)) { - WebDAV::mkdir($indexdir); - } - - $targetDir = "{$this->url}/$index/conf"; - if (!WebDAV::exists($targetDir)) { - WebDAV::mkdir($targetDir); - } - - return $targetDir; - } - - public function uploadFile($index, $file) - { - $targetDir = $this->getTargetDir($index); - WebDAV::upload_from_file($file, $targetDir.'/'.basename($file)); - } - - public function uploadString($index, $filename, $string) - { - $targetDir = $this->getTargetDir($index); - WebDAV::upload_from_string($string, "$targetDir/$filename"); - } - - public function instanceDir($index) - { - return $this->remote ? "{$this->remote}/$index" : $index; - } -} diff --git a/code/solr/SolrIndex.php b/code/solr/SolrIndex.php index 49e1dc8..76842a2 100644 --- a/code/solr/SolrIndex.php +++ b/code/solr/SolrIndex.php @@ -1,7 +1,10 @@ '; return $this->_sendRawPost($this->_updateUrl, $rawPost, $timeout); } - + /** - * @inheritdoc + * @inheritdoc * @see Solr4Service_Core::addDocuments */ - public function addDocument(Apache_Solr_Document $document, $allowDups = false, + public function addDocument(\Apache_Solr_Document $document, $allowDups = false, $overwritePending = true, $overwriteCommitted = true, $commitWithin = 0 ) { return $this->addDocuments(array($document), $allowDups, $overwritePending, $overwriteCommitted, $commitWithin); @@ -51,8 +53,3 @@ class Solr4Service_Core extends SolrService_Core return $this->add($rawPost); } } - -class Solr4Service extends SolrService -{ - private static $core_class = 'Solr4Service_Core'; -} diff --git a/code/solr/SolrService.php b/code/solr/services/SolrService.php similarity index 94% rename from code/solr/SolrService.php rename to code/solr/services/SolrService.php index 7b8c448..6aae39b 100644 --- a/code/solr/SolrService.php +++ b/code/solr/services/SolrService.php @@ -1,14 +1,8 @@ local = $config['path']; + $this->remote = isset($config['remotepath']) ? $config['remotepath'] : $config['path']; + } + + public function getTargetDir($index) + { + $targetDir = "{$this->local}/{$index}/conf"; + + if (!is_dir($targetDir)) { + $worked = @mkdir($targetDir, 0770, true); + + if (!$worked) { + throw new RuntimeException( + sprintf('Failed creating target directory %s, please check permissions', $targetDir) + ); + } + } + + return $targetDir; + } + + public function uploadFile($index, $file) + { + $targetDir = $this->getTargetDir($index); + copy($file, $targetDir.'/'.basename($file)); + } + + public function uploadString($index, $filename, $string) + { + $targetDir = $this->getTargetDir($index); + file_put_contents("$targetDir/$filename", $string); + } + + public function instanceDir($index) + { + return $this->remote.'/'.$index; + } +} \ No newline at end of file diff --git a/code/solr/stores/SolrConfigStore_WebDAV.php b/code/solr/stores/SolrConfigStore_WebDAV.php new file mode 100644 index 0000000..96259cb --- /dev/null +++ b/code/solr/stores/SolrConfigStore_WebDAV.php @@ -0,0 +1,56 @@ +url = implode('', array( + 'http://', + isset($config['auth']) ? $config['auth'].'@' : '', + $options['host'].':'.(isset($config['port']) ? $config['port'] : $options['port']), + $config['path'] + )); + $this->remote = $config['remotepath']; + } + + public function getTargetDir($index) + { + $indexdir = "{$this->url}/$index"; + if (!WebDAV::exists($indexdir)) { + WebDAV::mkdir($indexdir); + } + + $targetDir = "{$this->url}/$index/conf"; + if (!WebDAV::exists($targetDir)) { + WebDAV::mkdir($targetDir); + } + + return $targetDir; + } + + public function uploadFile($index, $file) + { + $targetDir = $this->getTargetDir($index); + WebDAV::upload_from_file($file, $targetDir.'/'.basename($file)); + } + + public function uploadString($index, $filename, $string) + { + $targetDir = $this->getTargetDir($index); + WebDAV::upload_from_string($string, "$targetDir/$filename"); + } + + public function instanceDir($index) + { + return $this->remote ? "{$this->remote}/$index" : $index; + } +} \ No newline at end of file diff --git a/code/solr/tasks/Solr_BuildTask.php b/code/solr/tasks/Solr_BuildTask.php new file mode 100644 index 0000000..d26f647 --- /dev/null +++ b/code/solr/tasks/Solr_BuildTask.php @@ -0,0 +1,63 @@ +get('Logger'); + } + + /** + * Assign a new logger + * + * @param LoggerInterface $logger + */ + public function setLogger(LoggerInterface $logger) + { + $this->logger = $logger; + } + + /** + * @return SearchLogFactory + */ + protected function getLoggerFactory() + { +// return Injector::inst()->get('SearchLogFactory'); + } + + /** + * Setup task + * + * @param SS_HTTPReqest $request + */ + public function run($request) + { + $name = get_class($this); + $verbose = $request->getVar('verbose'); + + // Set new logger + $logger = $this + ->getLoggerFactory(); +//@todo: Cannot instantiate interface SearchLogFactory +// ->getOutputLogger($name, $verbose); + $this->setLogger($logger); + } +} diff --git a/code/solr/tasks/Solr_Configure.php b/code/solr/tasks/Solr_Configure.php new file mode 100644 index 0000000..aec6356 --- /dev/null +++ b/code/solr/tasks/Solr_Configure.php @@ -0,0 +1,83 @@ +getSolrConfigStore(); + + $indexes = Solr::get_indexes(); + foreach ($indexes as $instance) { + try { + $this->updateIndex($instance, $store); + } catch (Exception $e) { + // We got an exception. Warn, but continue to next index. + $this + ->getLogger() + ->error("Failure: " . $e->getMessage()); + } + } + } + + /** + * Update the index on the given store + * + * @param SolrIndex $instance Instance + * @param SolrConfigStore $store + */ + protected function updateIndex($instance, $store) + { + $index = $instance->getIndexName(); + $this->getLogger()->addInfo("Configuring $index."); + + // Upload the config files for this index + $this->getLogger()->addInfo("Uploading configuration ..."); + $instance->uploadConfig($store); + + // Then tell Solr to use those config files + $service = Solr::service(); + if ($service->coreIsActive($index)) { + $this->getLogger()->addInfo("Reloading core ..."); + $service->coreReload($index); + } else { + $this->getLogger()->addInfo("Creating core ..."); + $service->coreCreate($index, $store->instanceDir($index)); + } + + $this->getLogger()->addInfo("Done"); + } + + /** + * Get config store + * + * @return SolrConfigStore + */ + protected function getSolrConfigStore() + { + $options = Solr::solr_options(); + + if (!isset($options['indexstore']) || !($indexstore = $options['indexstore'])) { + throw new Exception('No index configuration for Solr provided', E_USER_ERROR); + } + + // Find the IndexStore handler, which will handle uploading config files to Solr + $mode = $indexstore['mode']; + + if ($mode == 'file') { + return new SolrConfigStore_File($indexstore); + } elseif ($mode == 'webdav') { + return new SolrConfigStore_WebDAV($indexstore); + } elseif (ClassInfo::exists($mode) && ClassInfo::classImplements($mode, 'SolrConfigStore')) { + return new $mode($indexstore); + } else { + user_error('Unknown Solr index mode '.$indexstore['mode'], E_USER_ERROR); + } + } +} \ No newline at end of file diff --git a/code/solr/tasks/Solr_Reindex.php b/code/solr/tasks/Solr_Reindex.php new file mode 100644 index 0000000..0adb70a --- /dev/null +++ b/code/solr/tasks/Solr_Reindex.php @@ -0,0 +1,150 @@ +get('SolrReindexImmediateHandler'); + } + + /** + * @param SS_HTTPRequest $request + */ + public function run($request) + { + parent::run($request); + + // Reset state + $originalState = SearchVariant::current_state(); + $this->doReindex($request); + SearchVariant::activate_state($originalState); + } + + /** + * @param SS_HTTPRequest $request + */ + protected function doReindex($request) + { + $class = $request->getVar('class'); + + $index = $request->getVar('index'); + + //find the index classname by IndexName + // this is for when index names do not match the class name (this can be done by overloading getIndexName() on + // indexes + if ($index && !ClassInfo::exists($index)) { + foreach(ClassInfo::subclassesFor('SolrIndex') as $solrIndexClass) { + $reflection = new ReflectionClass($solrIndexClass); + //skip over abstract classes + if (!$reflection->isInstantiable()) { + continue; + } + //check the indexname matches the index passed to the request + if (!strcasecmp(singleton($solrIndexClass)->getIndexName(), $index)) { + //if we match, set the correct index name and move on + $index = $solrIndexClass; + break; + } + } + } + + // Deprecated reindex mechanism + $start = $request->getVar('start'); + if ($start !== null) { + // Run single batch directly + $indexInstance = singleton($index); + $state = json_decode($request->getVar('variantstate'), true); + $this->runFrom($indexInstance, $class, $start, $state); + return; + } + + // Check if we are re-indexing a single group + // If not using queuedjobs, we need to invoke Solr_Reindex as a separate process + // Otherwise each group is processed via a SolrReindexGroupJob + $groups = $request->getVar('groups'); + + $handler = $this->getHandler(); + if ($groups) { + // Run grouped batches (id % groups = group) + $group = $request->getVar('group'); + $indexInstance = singleton($index); + $state = json_decode($request->getVar('variantstate'), true); + + $handler->runGroup($this->getLogger(), $indexInstance, $state, $class, $groups, $group); + return; + } + + // If run at the top level, delegate to appropriate handler + $self = get_class($this); + $handler->triggerReindex($this->getLogger(), $this->config()->recordsPerRequest, $self, $class); + } + + /** + * @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"); + } +} diff --git a/code/utils/CombinationsArrayIterator.php b/code/utils/CombinationsArrayIterator.php index 69e5727..0e873a8 100644 --- a/code/utils/CombinationsArrayIterator.php +++ b/code/utils/CombinationsArrayIterator.php @@ -1,5 +1,6 @@ 'Varchar' - ); -} - -class BatchedProcessorTest_Index extends SearchIndex_Recording implements TestOnly -{ - public function init() - { - $this->addClass('BatchedProcessorTest_Object'); - $this->addFilterField('TestText'); - } -} - -class BatchedProcessor_QueuedJobService -{ - protected $jobs = array(); - - public function queueJob(QueuedJob $job, $startAfter = null, $userId = null, $queueName = null) - { - $this->jobs[] = array( - 'job' => $job, - 'startAfter' => $startAfter - ); - return $job; - } - - public function getJobs() - { - return $this->jobs; - } -} +use SilverStripe\Dev\SapphireTest; +use SilverStripe\FullTextSearch\Search\FullTextSearch; /** * Tests {@see SearchUpdateQueuedJobProcessor} diff --git a/tests/BatchedProcessorTest/BatchedProcessorTest_Index.php b/tests/BatchedProcessorTest/BatchedProcessorTest_Index.php new file mode 100644 index 0000000..7116af1 --- /dev/null +++ b/tests/BatchedProcessorTest/BatchedProcessorTest_Index.php @@ -0,0 +1,15 @@ +addClass('BatchedProcessorTest_Object'); + $this->addFilterField('TestText'); + } +} diff --git a/tests/BatchedProcessorTest/BatchedProcessorTest_Object.php b/tests/BatchedProcessorTest/BatchedProcessorTest_Object.php new file mode 100644 index 0000000..b596a3a --- /dev/null +++ b/tests/BatchedProcessorTest/BatchedProcessorTest_Object.php @@ -0,0 +1,13 @@ + 'Varchar' + ); +} diff --git a/tests/BatchedProcessorTest/BatchedProcessor_QueuedJobService.php b/tests/BatchedProcessorTest/BatchedProcessor_QueuedJobService.php new file mode 100644 index 0000000..1cf5253 --- /dev/null +++ b/tests/BatchedProcessorTest/BatchedProcessor_QueuedJobService.php @@ -0,0 +1,22 @@ +jobs[] = array( + 'job' => $job, + 'startAfter' => $startAfter + ); + return $job; + } + + public function getJobs() + { + return $this->jobs; + } +} diff --git a/tests/SearchUpdaterTest.php b/tests/SearchUpdaterTest.php index d2211c0..ee2df21 100644 --- a/tests/SearchUpdaterTest.php +++ b/tests/SearchUpdaterTest.php @@ -1,5 +1,10 @@ setField('id', $id); $document->setField('title', "Item $id"); return $document; @@ -58,16 +63,3 @@ class Solr4ServiceTest extends SapphireTest ); } } - -class Solr4ServiceTest_RecordingService extends Solr4Service_Core -{ - protected function _sendRawPost($url, $rawPost, $timeout = false, $contentType = 'text/xml; charset=UTF-8') - { - return $rawPost; - } - - protected function _sendRawGet($url, $timeout = false) - { - return $url; - } -} diff --git a/tests/Solr4ServiceTest/Solr4ServiceTest_RecordingService.php b/tests/Solr4ServiceTest/Solr4ServiceTest_RecordingService.php new file mode 100644 index 0000000..480ab1b --- /dev/null +++ b/tests/Solr4ServiceTest/Solr4ServiceTest_RecordingService.php @@ -0,0 +1,19 @@ +addClass('File'); - $this->addClass('SiteTree'); - $this->addAllFulltextFields(); - } -} diff --git a/tests/SolrIndexSubsitesTest.yml b/tests/SolrIndexSubsitesTest/SolrIndexSubsitesTest.yml similarity index 100% rename from tests/SolrIndexSubsitesTest.yml rename to tests/SolrIndexSubsitesTest/SolrIndexSubsitesTest.yml diff --git a/tests/SolrIndexSubsitesTest/SolrIndexSubsitesTest_Index.php b/tests/SolrIndexSubsitesTest/SolrIndexSubsitesTest_Index.php new file mode 100644 index 0000000..97e9dfb --- /dev/null +++ b/tests/SolrIndexSubsitesTest/SolrIndexSubsitesTest_Index.php @@ -0,0 +1,15 @@ +addClass('File'); + $this->addClass('SiteTree'); + $this->addAllFulltextFields(); + } +} diff --git a/tests/SolrIndexTest.php b/tests/SolrIndexTest.php index c710113..0a6b82f 100644 --- a/tests/SolrIndexTest.php +++ b/tests/SolrIndexTest.php @@ -1,4 +1,11 @@ addClass('SearchUpdaterTest_Container'); - - $this->addFilterField('Field1'); - $this->addFilterField('MyDate', 'Date'); - $this->addFilterField('HasOneObject.Field1'); - $this->addFilterField('HasManyObjects.Field1'); - $this->addFilterField('ManyManyObjects.Field1'); - } -} - - -class SolrIndexTest_FakeIndex2 extends SolrIndex -{ - protected function getStoredDefault() - { - // Override isDev defaulting to stored - return 'false'; - } - - public function init() - { - $this->addClass('SearchUpdaterTest_Container'); - $this->addFilterField('MyDate', 'Date'); - $this->addFilterField('HasOneObject.Field1'); - $this->addFilterField('HasManyObjects.Field1'); - $this->addFilterField('ManyManyObjects.Field1'); - } -} - - -class SolrIndexTest_BoostedIndex extends SolrIndex -{ - protected function getStoredDefault() - { - // Override isDev defaulting to stored - return 'false'; - } - - public function init() - { - $this->addClass('SearchUpdaterTest_Container'); - $this->addAllFulltextFields(); - $this->setFieldBoosting('SearchUpdaterTest_Container_Field1', 1.5); - $this->addBoostedField('Field2', null, array(), 2.1); - } -} diff --git a/tests/SolrIndexTest/SolrIndexTest_BoostedIndex.php b/tests/SolrIndexTest/SolrIndexTest_BoostedIndex.php new file mode 100644 index 0000000..65dae12 --- /dev/null +++ b/tests/SolrIndexTest/SolrIndexTest_BoostedIndex.php @@ -0,0 +1,22 @@ +addClass('SearchUpdaterTest_Container'); + $this->addAllFulltextFields(); + $this->setFieldBoosting('SearchUpdaterTest_Container_Field1', 1.5); + $this->addBoostedField('Field2', null, array(), 2.1); + } +} diff --git a/tests/SolrIndexTest/SolrIndexTest_FakeIndex.php b/tests/SolrIndexTest/SolrIndexTest_FakeIndex.php new file mode 100644 index 0000000..b04e6ce --- /dev/null +++ b/tests/SolrIndexTest/SolrIndexTest_FakeIndex.php @@ -0,0 +1,19 @@ +addClass('SearchUpdaterTest_Container'); + + $this->addFilterField('Field1'); + $this->addFilterField('MyDate', 'Date'); + $this->addFilterField('HasOneObject.Field1'); + $this->addFilterField('HasManyObjects.Field1'); + $this->addFilterField('ManyManyObjects.Field1'); + } +} diff --git a/tests/SolrIndexTest/SolrIndexTest_FakeIndex2.php b/tests/SolrIndexTest/SolrIndexTest_FakeIndex2.php new file mode 100644 index 0000000..a9b58ac --- /dev/null +++ b/tests/SolrIndexTest/SolrIndexTest_FakeIndex2.php @@ -0,0 +1,23 @@ +addClass('SearchUpdaterTest_Container'); + $this->addFilterField('MyDate', 'Date'); + $this->addFilterField('HasOneObject.Field1'); + $this->addFilterField('HasManyObjects.Field1'); + $this->addFilterField('ManyManyObjects.Field1'); + } +} diff --git a/tests/SolrIndexVersionedTest.php b/tests/SolrIndexVersionedTest.php index f9df86f..4533576 100644 --- a/tests/SolrIndexVersionedTest.php +++ b/tests/SolrIndexVersionedTest.php @@ -1,5 +1,11 @@ deleteById($this->getExpectedDocumentId($id, 'Live')); } } - - -class SolrVersionedTest_Index extends SolrIndex -{ - public function init() - { - $this->addClass('SearchVariantVersionedTest_Item'); - $this->addClass('SolrIndexVersionedTest_Object'); - $this->addFilterField('TestText'); - $this->addFulltextField('Content'); - } -} - -/** - * Non-sitetree versioned dataobject - */ -class SolrIndexVersionedTest_Object extends DataObject implements TestOnly { - - private static $extensions = array( - 'Versioned' - ); - - private static $db = array( - 'Title' => 'Varchar', - 'Content' => 'Text', - 'TestText' => 'Varchar', - ); -} - -if (!class_exists('Phockito')) { - return; -} - -class SolrDocumentMatcher extends Hamcrest_BaseMatcher -{ - protected $properties; - - public function __construct($properties) - { - $this->properties = $properties; - } - - public function describeTo(\Hamcrest_Description $description) - { - $description->appendText('Apache_Solr_Document with properties '.var_export($this->properties, true)); - } - - public function matches($item) - { - if (! ($item instanceof Apache_Solr_Document)) { - return false; - } - - foreach ($this->properties as $key => $value) { - if ($item->{$key} != $value) { - return false; - } - } - - return true; - } -} diff --git a/tests/SolrIndexVersionedTest/SolrDocumentMatcher.php b/tests/SolrIndexVersionedTest/SolrDocumentMatcher.php new file mode 100644 index 0000000..5c9298e --- /dev/null +++ b/tests/SolrIndexVersionedTest/SolrDocumentMatcher.php @@ -0,0 +1,39 @@ +properties = $properties; + } + + public function describeTo(\Hamcrest_Description $description) + { + $description->appendText('Apache_Solr_Document with properties '.var_export($this->properties, true)); + } + + public function matches($item) + { + if (! ($item instanceof Apache_Solr_Document)) { + return false; + } + + foreach ($this->properties as $key => $value) { + if ($item->{$key} != $value) { + return false; + } + } + + return true; + } +} diff --git a/tests/SolrIndexVersionedTest/SolrIndexVersionedTest_Object.php b/tests/SolrIndexVersionedTest/SolrIndexVersionedTest_Object.php new file mode 100644 index 0000000..62059b2 --- /dev/null +++ b/tests/SolrIndexVersionedTest/SolrIndexVersionedTest_Object.php @@ -0,0 +1,23 @@ + 'Varchar', + 'Content' => 'Text', + 'TestText' => 'Varchar', + ]; +} diff --git a/tests/SolrIndexVersionedTest/SolrVersionedTest_Index.php b/tests/SolrIndexVersionedTest/SolrVersionedTest_Index.php new file mode 100644 index 0000000..7ac3a5b --- /dev/null +++ b/tests/SolrIndexVersionedTest/SolrVersionedTest_Index.php @@ -0,0 +1,16 @@ +addClass('SearchVariantVersionedTest_Item'); + $this->addClass('SolrIndexVersionedTest_Object'); + $this->addFilterField('TestText'); + $this->addFulltextField('Content'); + } +} diff --git a/tests/SolrReindexQueuedTest.php b/tests/SolrReindexQueuedTest.php index f3339f2..ed7aa0c 100644 --- a/tests/SolrReindexQueuedTest.php +++ b/tests/SolrReindexQueuedTest.php @@ -1,5 +1,10 @@ getIndexName(); - $stateName = json_encode($state); - $logger->info("Called processGroup with {$indexName}, {$stateName}, {$class}, group {$group} of {$groups}"); - } - - public function triggerReindex(LoggerInterface $logger, $batchSize, $taskName, $classes = null) - { - $logger->info("Called triggerReindex"); - } -} - - -class SolrReindexTest_Index extends SolrIndex implements TestOnly -{ - public function init() - { - $this->addClass('SolrReindexTest_Item'); - $this->addAllFulltextFields(); - } -} - -/** - * Does not have any variant extensions - */ -class SolrReindexTest_Item extends DataObject implements TestOnly -{ - private static $extensions = array( - 'SolrReindexTest_ItemExtension' - ); - - private static $db = array( - 'Title' => 'Varchar(255)', - 'Variant' => 'Int(0)' - ); -} - -/** - * Select only records in the current variant - */ -class SolrReindexTest_ItemExtension extends DataExtension implements TestOnly -{ - /** - * Filter records on the current variant - * - * @param SQLQuery $query - * @param DataQuery $dataQuery - */ - public function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null) - { - $variant = SolrReindexTest_Variant::get_current(); - if ($variant !== null && !$query->filtersOnID()) { - $sqlVariant = Convert::raw2sql($variant); - $query->addWhere("\"Variant\" = '{$sqlVariant}'"); - } - } -} - - -/** - * Dummy variant that selects items with field Varient matching the current value - * - * Variant states are 0 and 1, or null if disabled - */ -class SolrReindexTest_Variant extends SearchVariant implements TestOnly -{ - /** - * Value of this variant (either null, 0, or 1) - * - * @var int|null - */ - protected static $current = null; - - /** - * Activate this variant - */ - public static function enable() - { - self::disable(); - - self::$current = 0; - self::$variants = array( - 'SolrReindexTest_Variant' => singleton('SolrReindexTest_Variant') - ); - } - - /** - * Disable this variant and reset - */ - public static function disable() - { - self::$current = null; - self::$variants = null; - self::$class_variants = array(); - self::$call_instances = array(); - } - - public function activateState($state) - { - self::set_current($state); - } - - /** - * Set the current variant to the given state - * - * @param int $current 0, 1, 2, or null (disabled) - */ - public static function set_current($current) - { - self::$current = $current; - } - - /** - * Get the current state - * - * @return string|null - */ - public static function get_current() - { - // Always use string values for states for consistent json_encode value - if (isset(self::$current)) { - return (string)self::$current; - } - } - - public function alterDefinition($class, $index) - { - $self = get_class($this); - - $this->addFilterField($index, '_testvariant', array( - 'name' => '_testvariant', - 'field' => '_testvariant', - 'fullfield' => '_testvariant', - 'base' => ClassInfo::baseDataClass($class), - 'origin' => $class, - 'type' => 'Int', - 'lookup_chain' => array(array('call' => 'variant', 'variant' => $self, 'method' => 'currentState')) - )); - } - - public function alterQuery($query, $index) - { - // I guess just calling it _testvariant is ok? - $query->filter('_testvariant', $this->currentState()); - } - - public function appliesTo($class, $includeSubclasses) - { - return $class === 'SolrReindexTest_Item' || - ($includeSubclasses && is_subclass_of($class, 'SolrReindexTest_Item', true)); - } - - public function appliesToEnvironment() - { - // Set to null to disable - return self::$current !== null; - } - - public function currentState() - { - return self::get_current(); - } - - public function reindexStates() - { - // Always use string values for states for consistent json_encode value - return array('0', '1', '2'); - } -} - -/** - * Test logger for recording messages - */ -class SolrReindexTest_RecordingLogger extends Logger implements TestOnly -{ - /** - * @var SolrReindexTest_Handler - */ - protected $testHandler = null; - - public function __construct($name = 'testlogger', array $handlers = array(), array $processors = array()) - { - parent::__construct($name, $handlers, $processors); - - $this->testHandler = new SolrReindexTest_Handler(); - $this->pushHandler($this->testHandler); - } - - /** - * @return array - */ - public function getMessages() - { - return $this->testHandler->getMessages(); - } - - /** - * Clear all messages - */ - public function clear() - { - $this->testHandler->clear(); - } - - /** - * Get messages with the given filter - * - * @param string $containing - * @return array Filtered array - */ - public function filterMessages($containing) - { - return array_values(array_filter( - $this->getMessages(), - function ($content) use ($containing) { - return stripos($content, $containing) !== false; - } - )); - } - - /** - * Count all messages containing the given substring - * - * @param string $containing Message to filter by - * @return int - */ - public function countMessages($containing = null) - { - if ($containing) { - $messages = $this->filterMessages($containing); - } else { - $messages = $this->getMessages(); - } - return count($messages); - } -} - -/** - * Logger for recording messages for later retrieval - */ -class SolrReindexTest_Handler extends AbstractProcessingHandler implements TestOnly -{ - /** - * Messages - * - * @var array - */ - protected $messages = array(); - - /** - * Get all messages - * - * @return array - */ - public function getMessages() - { - return $this->messages; - } - - public function clear() - { - $this->messages = array(); - } - - protected function write(array $record) - { - $this->messages[] = $record['message']; - } -} diff --git a/tests/SolrReindexTest/SolrReindexTest_Handler.php b/tests/SolrReindexTest/SolrReindexTest_Handler.php new file mode 100644 index 0000000..ea29bcd --- /dev/null +++ b/tests/SolrReindexTest/SolrReindexTest_Handler.php @@ -0,0 +1,39 @@ +messages; + } + + public function clear() + { + $this->messages = array(); + } + + protected function write(array $record) + { + $this->messages[] = $record['message']; + } +} diff --git a/tests/SolrReindexTest/SolrReindexTest_Index.php b/tests/SolrReindexTest/SolrReindexTest_Index.php new file mode 100644 index 0000000..64e85b2 --- /dev/null +++ b/tests/SolrReindexTest/SolrReindexTest_Index.php @@ -0,0 +1,15 @@ +addClass('SolrReindexTest_Item'); + $this->addAllFulltextFields(); + } +} diff --git a/tests/SolrReindexTest/SolrReindexTest_Item.php b/tests/SolrReindexTest/SolrReindexTest_Item.php new file mode 100644 index 0000000..6a08387 --- /dev/null +++ b/tests/SolrReindexTest/SolrReindexTest_Item.php @@ -0,0 +1,22 @@ + 'Varchar(255)', + 'Variant' => 'Int(0)' + ); +} diff --git a/tests/SolrReindexTest/SolrReindexTest_ItemExtension.php b/tests/SolrReindexTest/SolrReindexTest_ItemExtension.php new file mode 100644 index 0000000..9981932 --- /dev/null +++ b/tests/SolrReindexTest/SolrReindexTest_ItemExtension.php @@ -0,0 +1,31 @@ +filtersOnID()) { + $sqlVariant = Convert::raw2sql($variant); + $query->addWhere("\"Variant\" = '{$sqlVariant}'"); + } + } +} diff --git a/tests/SolrReindexTest/SolrReindexTest_RecordingLogger.php b/tests/SolrReindexTest/SolrReindexTest_RecordingLogger.php new file mode 100644 index 0000000..3606e02 --- /dev/null +++ b/tests/SolrReindexTest/SolrReindexTest_RecordingLogger.php @@ -0,0 +1,74 @@ +testHandler = new SolrReindexTest_Handler(); + $this->pushHandler($this->testHandler); + } + + /** + * @return array + */ + public function getMessages() + { + return $this->testHandler->getMessages(); + } + + /** + * Clear all messages + */ + public function clear() + { + $this->testHandler->clear(); + } + + /** + * Get messages with the given filter + * + * @param string $containing + * @return array Filtered array + */ + public function filterMessages($containing) + { + return array_values(array_filter( + $this->getMessages(), + function ($content) use ($containing) { + return stripos($content, $containing) !== false; + } + )); + } + + /** + * Count all messages containing the given substring + * + * @param string $containing Message to filter by + * @return int + */ + public function countMessages($containing = null) + { + if ($containing) { + $messages = $this->filterMessages($containing); + } else { + $messages = $this->getMessages(); + } + return count($messages); + } +} diff --git a/tests/SolrReindexTest/SolrReindexTest_TestHandler.php b/tests/SolrReindexTest/SolrReindexTest_TestHandler.php new file mode 100644 index 0000000..fd86b88 --- /dev/null +++ b/tests/SolrReindexTest/SolrReindexTest_TestHandler.php @@ -0,0 +1,26 @@ +getIndexName(); + $stateName = json_encode($state); + $logger->info("Called processGroup with {$indexName}, {$stateName}, {$class}, group {$group} of {$groups}"); + } + + public function triggerReindex(LoggerInterface $logger, $batchSize, $taskName, $classes = null) + { + $logger->info("Called triggerReindex"); + } +} diff --git a/tests/SolrReindexTest/SolrReindexTest_Variant.php b/tests/SolrReindexTest/SolrReindexTest_Variant.php new file mode 100644 index 0000000..dbb6115 --- /dev/null +++ b/tests/SolrReindexTest/SolrReindexTest_Variant.php @@ -0,0 +1,117 @@ + singleton('SolrReindexTest_Variant') + ); + } + + /** + * Disable this variant and reset + */ + public static function disable() + { + self::$current = null; + self::$variants = null; + self::$class_variants = array(); + self::$call_instances = array(); + } + + public function activateState($state) + { + self::set_current($state); + } + + /** + * Set the current variant to the given state + * + * @param int $current 0, 1, 2, or null (disabled) + */ + public static function set_current($current) + { + self::$current = $current; + } + + /** + * Get the current state + * + * @return string|null + */ + public static function get_current() + { + // Always use string values for states for consistent json_encode value + if (isset(self::$current)) { + return (string)self::$current; + } + } + + public function alterDefinition($class, $index) + { + $self = get_class($this); + + $this->addFilterField($index, '_testvariant', array( + 'name' => '_testvariant', + 'field' => '_testvariant', + 'fullfield' => '_testvariant', + 'base' => ClassInfo::baseDataClass($class), + 'origin' => $class, + 'type' => 'Int', + 'lookup_chain' => array(array('call' => 'variant', 'variant' => $self, 'method' => 'currentState')) + )); + } + + public function alterQuery($query, $index) + { + // I guess just calling it _testvariant is ok? + $query->filter('_testvariant', $this->currentState()); + } + + public function appliesTo($class, $includeSubclasses) + { + return $class === 'SolrReindexTest_Item' || + ($includeSubclasses && is_subclass_of($class, 'SolrReindexTest_Item', true)); + } + + public function appliesToEnvironment() + { + // Set to null to disable + return self::$current !== null; + } + + public function currentState() + { + return self::get_current(); + } + + public function reindexStates() + { + // Always use string values for states for consistent json_encode value + return array('0', '1', '2'); + } +}