diff --git a/code/solr/Solr.php b/code/solr/Solr.php index c9bc780..98d3972 100644 --- a/code/solr/Solr.php +++ b/code/solr/Solr.php @@ -1,13 +1,7 @@ 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); - } -} - - -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()->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); - } - } -} - -/** - * 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() - { - //@todo: this needs to determine the best class from a Factory implementation - //@todo: it was 'SolrReindexHandler' but that doesn't work on 4.0 - return Injector::inst()->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"); - } -} +} \ No newline at end of file diff --git a/code/solr/SolrConfigStore.php b/code/solr/SolrConfigStore.php deleted file mode 100644 index 5e856d3..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 157b53e..68e13b4 100644 --- a/code/solr/SolrIndex.php +++ b/code/solr/SolrIndex.php @@ -2,7 +2,6 @@ namespace SilverStripe\FullTextSearch\Solr; Solr::include_client_api(); -use SilverStripe\Dev\BuildTask; use SilverStripe\Control\Director; use SilverStripe\FulltextSearch\SearchIndex; diff --git a/code/solr/Solr3Service.php b/code/solr/services/Solr3Service.php similarity index 51% rename from code/solr/Solr3Service.php rename to code/solr/services/Solr3Service.php index 2bdcee5..f2546a6 100644 --- a/code/solr/Solr3Service.php +++ b/code/solr/services/Solr3Service.php @@ -1,8 +1,5 @@ '; return $this->_sendRawPost($this->_updateUrl, $rawPost, $timeout); } - + /** - * @inheritdoc + * @inheritdoc * @see Solr4Service_Core::addDocuments */ public function addDocument(Apache_Solr_Document $document, $allowDups = false, @@ -50,9 +52,4 @@ class Solr4Service_Core extends SolrService_Core return $this->add($rawPost); } -} - -class Solr4Service extends SolrService -{ - private static $core_class = 'Solr4Service_Core'; -} +} \ No newline at end of file diff --git a/code/solr/SolrService.php b/code/solr/services/SolrService.php similarity index 91% rename from code/solr/SolrService.php rename to code/solr/services/SolrService.php index f2766ac..6aae39b 100644 --- a/code/solr/SolrService.php +++ b/code/solr/services/SolrService.php @@ -1,16 +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..8989f9c --- /dev/null +++ b/code/solr/tasks/Solr_Configure.php @@ -0,0 +1,81 @@ +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..b4f614c --- /dev/null +++ b/code/solr/tasks/Solr_Reindex.php @@ -0,0 +1,148 @@ +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"); + } +}