2011-05-02 06:33:05 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
class Solr {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Configuration on where to find the solr server and how to get new index configurations into it.
|
|
|
|
*
|
|
|
|
* Required fields:
|
|
|
|
* host (default: localhost) - The host or IP Solr is listening on
|
|
|
|
* port (default: 8983) - The port Solr is listening on
|
|
|
|
* path (default: /solr) - The suburl the solr service is available on
|
|
|
|
*
|
2012-09-05 18:09:45 +02:00
|
|
|
* Optional fields:
|
|
|
|
* extraspath (default: <basefolder>/fulltextsearch/conf/extras/) - Absolute path to
|
|
|
|
* the folder containing templates which are used for generating the schema and field definitions.
|
|
|
|
* templates (default: <basefolder>/fulltextsearch/conf/templates/) - Absolute path to
|
|
|
|
* the configuration default files, e.g. solrconfig.xml.
|
|
|
|
*
|
2011-05-02 06:33:05 +02:00
|
|
|
* indexstore => an array with
|
|
|
|
*
|
2013-08-29 00:26:55 +02:00
|
|
|
* mode - a classname which implements SolrConfigStore, or 'file' or 'webdav'
|
2011-05-02 06:33:05 +02:00
|
|
|
*
|
2013-08-29 00:26:55 +02:00
|
|
|
* When mode == SolrConfigStore_File or file (indexes should be written on a local filesystem)
|
2011-05-02 06:33:05 +02:00
|
|
|
* path - The (locally accessible) path to write the index configurations to.
|
|
|
|
* remotepath (default: the same as indexpath) - The path that the Solr server will read the index configurations from
|
|
|
|
*
|
2013-08-29 00:26:55 +02:00
|
|
|
* When mode == SolrConfigStore_WebDAV or webdav (indexes should stored on a remote Solr server via webdav)
|
2011-05-02 06:33:05 +02:00
|
|
|
* auth (default: none) - A username:password pair string to use to auth against the webdav server
|
|
|
|
* path (default: /solrindex) - The suburl on the solr host that is set up to accept index configurations via webdav
|
|
|
|
* remotepath - The path that the Solr server will read the index configurations from
|
|
|
|
*/
|
|
|
|
static $solr_options = array();
|
|
|
|
|
|
|
|
static function configure_server($options = array()) {
|
|
|
|
self::$solr_options = array_merge(array(
|
|
|
|
'host' => 'localhost',
|
|
|
|
'port' => 8983,
|
2012-09-05 18:09:45 +02:00
|
|
|
'path' => '/solr',
|
|
|
|
'extraspath' => Director::baseFolder().'/fulltextsearch/conf/extras/',
|
|
|
|
'templatespath' => Director::baseFolder().'/fulltextsearch/conf/templates/',
|
2011-05-02 06:33:05 +02:00
|
|
|
), self::$solr_options, $options);
|
|
|
|
}
|
|
|
|
|
|
|
|
static protected $service_class = 'SolrService';
|
|
|
|
|
|
|
|
static function set_service_class($class) {
|
|
|
|
self::$service_class = $class;
|
|
|
|
self::$service = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
static protected $service = null;
|
|
|
|
|
|
|
|
static function service($core = null) {
|
|
|
|
if (!self::$service) {
|
|
|
|
if (!self::$solr_options) user_error('No configuration for Solr server provided', E_USER_ERROR);
|
|
|
|
|
|
|
|
$class = self::$service_class;
|
|
|
|
self::$service = new $class(self::$solr_options['host'], self::$solr_options['port'], self::$solr_options['path']);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $core ? self::$service->serviceForCore($core) : self::$service;
|
|
|
|
}
|
|
|
|
|
|
|
|
static function get_indexes() {
|
|
|
|
return FullTextSearch::get_indexes('SolrIndex');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Include the thirdparty Solr client api library. Done this way to avoid issues where code is called in mysite/_config
|
2012-07-18 17:54:48 +02:00
|
|
|
* before fulltextsearch/_config has a change to update the include path.
|
2011-05-02 06:33:05 +02:00
|
|
|
*/
|
|
|
|
static function include_client_api() {
|
|
|
|
static $included = false;
|
|
|
|
|
|
|
|
if (!$included) {
|
2012-07-19 00:15:57 +02:00
|
|
|
set_include_path(get_include_path() . PATH_SEPARATOR . Director::baseFolder() . '/fulltextsearch/thirdparty/solr-php-client');
|
2011-05-02 06:33:05 +02:00
|
|
|
require_once('Apache/Solr/Service.php');
|
|
|
|
require_once('Apache/Solr/Document.php');
|
|
|
|
|
|
|
|
$included = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-29 00:26:55 +02:00
|
|
|
|
2011-05-02 06:33:05 +02:00
|
|
|
class Solr_Configure extends BuildTask {
|
|
|
|
|
|
|
|
public function run($request) {
|
|
|
|
$service = Solr::service();
|
2013-02-12 22:15:14 +01:00
|
|
|
$indexes = Solr::get_indexes();
|
2011-05-02 06:33:05 +02:00
|
|
|
|
2013-08-29 00:26:55 +02:00
|
|
|
if (!isset(Solr::$solr_options['indexstore']) || !($indexstore = Solr::$solr_options['indexstore'])) {
|
2011-05-02 06:33:05 +02:00
|
|
|
user_error('No index configuration for Solr provided', E_USER_ERROR);
|
|
|
|
}
|
|
|
|
|
2013-08-29 00:26:55 +02:00
|
|
|
// Find the IndexStore handler, which will handle uploading config files to Solr
|
|
|
|
$mode = $indexstore['mode'];
|
|
|
|
|
|
|
|
if ($mode == 'file') {
|
|
|
|
$store = new SolrConfigStore_File($indexstore);
|
|
|
|
} elseif ($mode == 'webdav') {
|
|
|
|
$store = new SolrConfigStore_WebDAV($indexstore);
|
|
|
|
} elseif (ClassInfo::exists($mode) && ClassInfo::classImplements($mode, 'SolrConfigStore')) {
|
|
|
|
$store = new $mode($indexstore);
|
|
|
|
} else {
|
|
|
|
user_error('Unknown Solr index mode '.$indexstore['mode'], E_USER_ERROR);
|
|
|
|
}
|
2011-05-02 06:33:05 +02:00
|
|
|
|
2013-08-29 00:26:55 +02:00
|
|
|
foreach ($indexes as $instance) {
|
|
|
|
$index = $instance->getIndexName();
|
|
|
|
echo "Configuring $index. "; flush();
|
2011-05-02 06:33:05 +02:00
|
|
|
|
2013-08-29 00:26:55 +02:00
|
|
|
try {
|
|
|
|
// Upload the config files for this index
|
|
|
|
echo "Uploading configuration ... "; flush();
|
2011-05-02 06:33:05 +02:00
|
|
|
|
2013-08-29 00:26:55 +02:00
|
|
|
$store->uploadString($index, 'schema.xml', (string)$instance->generateSchema());
|
2011-05-02 06:33:05 +02:00
|
|
|
|
2013-08-29 00:26:55 +02:00
|
|
|
foreach (glob($instance->getExtrasPath().'/*') as $file) {
|
|
|
|
if (is_file($file)) $store->uploadFile($index, $file);
|
2011-05-02 06:33:05 +02:00
|
|
|
}
|
|
|
|
|
2013-08-29 00:26:55 +02:00
|
|
|
// Then tell Solr to use those config files
|
|
|
|
if ($service->coreIsActive($index)) {
|
|
|
|
echo "Reloading core ... ";
|
|
|
|
$service->coreReload($index);
|
|
|
|
} else {
|
|
|
|
echo "Creating core ... ";
|
|
|
|
$service->coreCreate($index, $store->instanceDir($index));
|
2013-02-12 22:15:14 +01:00
|
|
|
}
|
2013-08-29 00:26:55 +02:00
|
|
|
|
|
|
|
// And done
|
|
|
|
echo "Done\n";
|
|
|
|
|
|
|
|
} catch(Exception $e) {
|
|
|
|
// We got an exception. Warn, but continue to next index.
|
|
|
|
echo "Failure: " . $e->getMessage() . "\n"; flush();
|
2013-02-12 21:56:07 +01:00
|
|
|
}
|
2012-07-19 17:16:50 +02:00
|
|
|
}
|
2011-05-02 06:33:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-29 00:26:55 +02:00
|
|
|
|
2011-05-02 06:33:05 +02:00
|
|
|
class Solr_Reindex extends BuildTask {
|
|
|
|
static $recordsPerRequest = 200;
|
|
|
|
|
|
|
|
public function run($request) {
|
|
|
|
increase_time_limit_to();
|
|
|
|
$self = get_class($this);
|
2012-07-19 17:16:50 +02:00
|
|
|
$verbose = isset($_GET['verbose']);
|
2011-05-02 06:33:05 +02:00
|
|
|
|
|
|
|
$originalState = SearchVariant::current_state();
|
|
|
|
|
|
|
|
if (isset($_GET['start'])) {
|
|
|
|
$this->runFrom(singleton($_GET['index']), $_GET['class'], $_GET['start'], json_decode($_GET['variantstate'], true));
|
|
|
|
}
|
|
|
|
else {
|
2012-05-14 02:29:41 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2011-05-02 06:33:05 +02:00
|
|
|
$class = get_class($this);
|
|
|
|
|
|
|
|
foreach (Solr::get_indexes() as $index => $instance) {
|
|
|
|
echo "Rebuilding {$instance->getIndexName()}\n\n";
|
|
|
|
|
2012-08-22 19:36:31 +02:00
|
|
|
$classes = $instance->getClasses();
|
|
|
|
if($request->getVar('class')) {
|
|
|
|
$limitClasses = explode(',', $request->getVar('class'));
|
|
|
|
$classes = array_intersect_key($classes, array_combine($limitClasses, $limitClasses));
|
|
|
|
}
|
|
|
|
|
|
|
|
Solr::service($index)->deleteByQuery('ClassHierarchy:(' . implode(' OR ', array_keys($classes)) . ')');
|
2011-05-02 06:33:05 +02:00
|
|
|
|
2012-08-22 19:36:31 +02:00
|
|
|
foreach ($classes as $class => $options) {
|
2011-05-02 06:33:05 +02:00
|
|
|
$includeSubclasses = $options['include_children'];
|
|
|
|
|
|
|
|
foreach (SearchVariant::reindex_states($class, $includeSubclasses) as $state) {
|
2012-08-28 23:21:51 +02:00
|
|
|
if ($instance->variantStateExcluded($state)) continue;
|
|
|
|
|
2011-05-02 06:33:05 +02:00
|
|
|
SearchVariant::activate_state($state);
|
|
|
|
|
2012-07-19 03:13:52 +02:00
|
|
|
$filter = $includeSubclasses ? "" : '"ClassName" = \''.$class."'";
|
2011-05-02 06:33:05 +02:00
|
|
|
$singleton = singleton($class);
|
2012-07-19 03:13:52 +02:00
|
|
|
$query = $singleton->get($class,$filter,null);
|
|
|
|
$dtaQuery = $query->dataQuery();
|
|
|
|
$sqlQuery = $dtaQuery->getFinalisedQuery();
|
|
|
|
$singleton->extend('augmentSQL',$sqlQuery,$dtaQuery);
|
|
|
|
$total = $query->count();
|
2011-05-02 06:33:05 +02:00
|
|
|
|
|
|
|
$statevar = json_encode($state);
|
2012-07-19 17:16:50 +02:00
|
|
|
echo "Class: $class, total: $total";
|
|
|
|
echo ($statevar) ? " in state $statevar\n" : "\n";
|
2011-05-02 06:33:05 +02:00
|
|
|
|
|
|
|
if (strpos(PHP_OS, "WIN") !== false) $statevar = '"'.str_replace('"', '\\"', $statevar).'"';
|
|
|
|
else $statevar = "'".$statevar."'";
|
|
|
|
|
|
|
|
for ($offset = 0; $offset < $total; $offset += $this->stat('recordsPerRequest')) {
|
|
|
|
echo "$offset..";
|
2013-08-29 00:26:55 +02:00
|
|
|
|
2012-07-19 16:35:26 +02:00
|
|
|
$cmd = "php $script dev/tasks/$self index=$index class=$class start=$offset variantstate=$statevar";
|
2012-07-19 17:16:50 +02:00
|
|
|
if($verbose) echo "\n Running '$cmd'\n";
|
|
|
|
$res = $verbose ? passthru($cmd) : `$cmd`;
|
|
|
|
if($verbose) echo " ".preg_replace('/\r\n|\n/', '$0 ', $res)."\n";
|
2011-05-02 06:33:05 +02:00
|
|
|
|
|
|
|
// If we're in dev mode, commit more often for fun and profit
|
|
|
|
if (Director::isDev()) Solr::service($index)->commit();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Solr::service($index)->commit();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$originalState = SearchVariant::current_state();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function runFrom($index, $class, $start, $variantstate) {
|
|
|
|
$classes = $index->getClasses();
|
|
|
|
$options = $classes[$class];
|
|
|
|
|
|
|
|
SearchVariant::activate_state($variantstate);
|
|
|
|
|
|
|
|
$includeSubclasses = $options['include_children'];
|
|
|
|
$filter = $includeSubclasses ? "" : '"ClassName" = \''.$class."'";
|
|
|
|
|
2012-07-19 03:33:18 +02:00
|
|
|
$items = DataList::create($class)->where($filter)->limit($this->stat('recordsPerRequest'), $start);
|
2011-05-02 06:33:05 +02:00
|
|
|
foreach ($items as $item) { $index->add($item); $item->destroy(); }
|
|
|
|
}
|
|
|
|
|
2013-02-12 21:56:07 +01:00
|
|
|
}
|