mirror of
https://github.com/silverstripe/silverstripe-fulltextsearch
synced 2024-10-22 12:05:29 +00:00
WIP: Silverstripe 4 compatibility
Thanks to Marco Hermo and Brett Tasker for helping with this * Bump framework/cms to ^4.0@dev * WIP Silverstripe 4 compatibility fixes * more replacements and patches to migrate this module to 4.0 * Update composer.json * remove php <5.5 from travis.yml * WIP more SS4 compatibility fixes * WIP fix solr path to use DIR, avoid hardcoded module name * WIP respect current include path * WIP Namespacing and use on SearchIndex class * Namespacing for tests * WIP add namespaces to all classes * Second push of Test changes + namespacing * WIP split Solr files with multiple classes into single file / single class. Adjust namespaces * Fix PHP errors in test * break out search components with multiple classes into individual files and change namespaces * Update namespacing for Search indexes and variants in tests * Batch fixes for tests #2 * Update _config.php to use namespace * Use root namespace in referencing Apache_Solr_Document * Migrate task names so that the name is not fully qualified
This commit is contained in:
parent
176014afaa
commit
1728a62af5
@ -5,7 +5,6 @@ language: php
|
|||||||
sudo: false
|
sudo: false
|
||||||
|
|
||||||
php:
|
php:
|
||||||
- 5.4
|
|
||||||
- 5.5
|
- 5.5
|
||||||
- 5.6
|
- 5.6
|
||||||
|
|
||||||
@ -14,8 +13,6 @@ env:
|
|||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- php: 5.3
|
|
||||||
env: DB=PGSQL CORE_RELEASE=3.1
|
|
||||||
- php: 5.6
|
- php: 5.6
|
||||||
env: DB=MYSQL CORE_RELEASE=3.2
|
env: DB=MYSQL CORE_RELEASE=3.2
|
||||||
- php: 5.6
|
- php: 5.6
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
<?php
|
<?php
|
||||||
|
use SilverStripe\FullTextSearch\Search\Updaters\SearchUpdater;
|
||||||
global $databaseConfig;
|
global $databaseConfig;
|
||||||
if (isset($databaseConfig['type'])) SearchUpdater::bind_manipulation_capture();
|
if (isset($databaseConfig['type'])) SearchUpdater::bind_manipulation_capture();
|
||||||
|
|
||||||
Deprecation::notification_version('1.0.0', 'fulltextsearch');
|
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Search;
|
||||||
|
|
||||||
|
use SilverStripe\Core\Config\Config;
|
||||||
|
use SilverStripe\Core\ClassInfo;
|
||||||
/**
|
/**
|
||||||
* Base class to manage active search indexes.
|
* Base class to manage active search indexes.
|
||||||
*/
|
*/
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Search;
|
||||||
|
use SilverStripe\Core\ClassInfo;
|
||||||
|
use SilverStripe\ORM\DataObject;
|
||||||
/**
|
/**
|
||||||
* Some additional introspection tools that are used often by the fulltext search code
|
* Some additional introspection tools that are used often by the fulltext search code
|
||||||
*/
|
*/
|
||||||
@ -41,18 +43,23 @@ class SearchIntrospection
|
|||||||
$classes = array_unique(array_merge($classes, array_values(ClassInfo::subclassesFor($class))));
|
$classes = array_unique(array_merge($classes, array_values(ClassInfo::subclassesFor($class))));
|
||||||
}
|
}
|
||||||
|
|
||||||
$idx = array_search('DataObject', $classes);
|
$idx = array_search('SilverStripe\View\ViewableData', $classes);
|
||||||
if ($idx !== false) {
|
if ($idx !== false) {
|
||||||
array_splice($classes, 0, $idx+1);
|
array_splice($classes, 0, $idx+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($dataOnly) {
|
$idx = array_search('SilverStripe\Core\Object', $classes);
|
||||||
foreach ($classes as $i => $class) {
|
if ($idx !== false) {
|
||||||
if (!DataObject::has_own_table($class)) {
|
array_splice($classes, 0, $idx+1);
|
||||||
unset($classes[$i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
//@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;
|
self::$hierarchy[$key] = $classes;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Search\Indexes;
|
||||||
|
|
||||||
|
use SilverStripe\View\ViewableData;
|
||||||
|
use SilverStripe\ORM\DataObject;
|
||||||
|
use SilverStripe\ORM\DataObjectSchema;
|
||||||
|
use SilverStripe\Core\Object;
|
||||||
|
use SilverStripe\Core\ClassInfo;
|
||||||
|
use SilverStripe\FullTextSearch\Search\SearchIntrospection;
|
||||||
|
use SilverStripe\FullTextSearch\Search\Variants\SearchVariant;
|
||||||
|
use SilverStripe\FullTextSearch\Utils\MultipleArrayIterator;
|
||||||
/**
|
/**
|
||||||
* SearchIndex is the base index class. Each connector will provide a subclass of this that
|
* SearchIndex is the base index class. Each connector will provide a subclass of this that
|
||||||
* provides search engine specific behavior.
|
* provides search engine specific behavior.
|
||||||
@ -83,20 +93,20 @@ abstract class SearchIndex extends ViewableData
|
|||||||
foreach (SearchIntrospection::hierarchy($source, $options['include_children']) as $dataclass) {
|
foreach (SearchIntrospection::hierarchy($source, $options['include_children']) as $dataclass) {
|
||||||
$singleton = singleton($dataclass);
|
$singleton = singleton($dataclass);
|
||||||
|
|
||||||
if ($hasOne = $singleton->has_one($lookup)) {
|
if ($hasOne = $singleton->hasOne($lookup)) {
|
||||||
$class = $hasOne;
|
$class = $hasOne;
|
||||||
$options['lookup_chain'][] = array(
|
$options['lookup_chain'][] = array(
|
||||||
'call' => 'method', 'method' => $lookup,
|
'call' => 'method', 'method' => $lookup,
|
||||||
'through' => 'has_one', 'class' => $dataclass, 'otherclass' => $class, 'foreignkey' => "{$lookup}ID"
|
'through' => 'has_one', 'class' => $dataclass, 'otherclass' => $class, 'foreignkey' => "{$lookup}ID"
|
||||||
);
|
);
|
||||||
} elseif ($hasMany = $singleton->has_many($lookup)) {
|
} elseif ($hasMany = $singleton->hasMany($lookup)) {
|
||||||
$class = $hasMany;
|
$class = $hasMany;
|
||||||
$options['multi_valued'] = true;
|
$options['multi_valued'] = true;
|
||||||
$options['lookup_chain'][] = array(
|
$options['lookup_chain'][] = array(
|
||||||
'call' => 'method', 'method' => $lookup,
|
'call' => 'method', 'method' => $lookup,
|
||||||
'through' => 'has_many', 'class' => $dataclass, 'otherclass' => $class, 'foreignkey' => $singleton->getRemoteJoinField($lookup, 'has_many')
|
'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];
|
$class = $manyMany[1];
|
||||||
$options['multi_valued'] = true;
|
$options['multi_valued'] = true;
|
||||||
$options['lookup_chain'][] = array(
|
$options['lookup_chain'][] = array(
|
||||||
@ -105,7 +115,7 @@ abstract class SearchIndex extends ViewableData
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($class) {
|
if (is_string($class) && $class) {
|
||||||
if (!isset($options['origin'])) {
|
if (!isset($options['origin'])) {
|
||||||
$options['origin'] = $dataclass;
|
$options['origin'] = $dataclass;
|
||||||
}
|
}
|
||||||
@ -130,7 +140,7 @@ abstract class SearchIndex extends ViewableData
|
|||||||
$type = null;
|
$type = null;
|
||||||
$fieldoptions = $options;
|
$fieldoptions = $options;
|
||||||
|
|
||||||
$fields = DataObject::database_fields($dataclass);
|
$fields = DataObject::getSchema()->databaseFields($class);
|
||||||
|
|
||||||
if (isset($fields[$field])) {
|
if (isset($fields[$field])) {
|
||||||
$type = $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');
|
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)');
|
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 ($this->getClasses() as $class => $options) {
|
||||||
foreach (SearchIntrospection::hierarchy($class, $includeSubclasses, true) as $dataclass) {
|
foreach (SearchIntrospection::hierarchy($class, $includeSubclasses, true) as $dataclass) {
|
||||||
$fields = DataObject::database_fields($dataclass);
|
$fields = DataObject::getSchema()->databaseFields($class);
|
||||||
|
|
||||||
foreach ($fields as $field => $type) {
|
foreach ($fields as $field => $type) {
|
||||||
if (preg_match('/^(\w+)\(/', $type, $match)) {
|
if (preg_match('/^(\w+)\(/', $type, $match)) {
|
||||||
@ -599,95 +609,3 @@ abstract class SearchIndex extends ViewableData
|
|||||||
*/
|
*/
|
||||||
abstract public function init();
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
23
code/search/indexes/SearchIndex_Null.php
Normal file
23
code/search/indexes/SearchIndex_Null.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Search\Indexes;
|
||||||
|
|
||||||
|
use SilverStripe\FullTextSearch\Search\Indexes\SearchIndex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
79
code/search/indexes/SearchIndex_Recording.php
Normal file
79
code/search/indexes/SearchIndex_Recording.php
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Search\Indexes;
|
||||||
|
|
||||||
|
use SilverStripe\FullTextSearch\Search\Indexes\SearchIndex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Search\Processors;
|
||||||
/**
|
/**
|
||||||
* Provides batching of search updates
|
* Provides batching of search updates
|
||||||
*/
|
*/
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Search\Processors;
|
||||||
if (!interface_exists('QueuedJob')) {
|
if (!interface_exists('QueuedJob')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Search\Processors;
|
||||||
class SearchUpdateImmediateProcessor extends SearchUpdateProcessor
|
class SearchUpdateImmediateProcessor extends SearchUpdateProcessor
|
||||||
{
|
{
|
||||||
public function triggerProcessing()
|
public function triggerProcessing()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Search\Processors;
|
||||||
class SearchUpdateMessageQueueProcessor extends SearchUpdateProcessor
|
class SearchUpdateMessageQueueProcessor extends SearchUpdateProcessor
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Search\Processors;
|
||||||
abstract class SearchUpdateProcessor
|
abstract class SearchUpdateProcessor
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Search\Processors;
|
||||||
if (!interface_exists('QueuedJob')) {
|
if (!interface_exists('QueuedJob')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Search\Indexes;
|
||||||
/**
|
/**
|
||||||
* Represents a search query
|
* Represents a search query
|
||||||
*
|
*
|
||||||
* API very much still in flux.
|
* API very much still in flux.
|
||||||
*/
|
*/
|
||||||
|
use SilverStripe\View\ViewableData;
|
||||||
class SearchQuery extends ViewableData
|
class SearchQuery extends ViewableData
|
||||||
{
|
{
|
||||||
public static $missing = null;
|
public static $missing = null;
|
||||||
@ -120,33 +121,4 @@ class SearchQuery extends ViewableData
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create one of these and pass as one of the values in filter or exclude to filter or exclude by a (possibly
|
|
||||||
* open ended) range
|
|
||||||
*/
|
|
||||||
class SearchQuery_Range
|
|
||||||
{
|
|
||||||
public $start = null;
|
|
||||||
public $end = null;
|
|
||||||
|
|
||||||
public function __construct($start = null, $end = null)
|
|
||||||
{
|
|
||||||
$this->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;
|
|
||||||
}
|
|
||||||
}
|
|
34
code/search/queries/SearchQuery_Range.php
Normal file
34
code/search/queries/SearchQuery_Range.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Search\Queries;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create one of these and pass as one of the values in filter or exclude to filter or exclude by a (possibly
|
||||||
|
* open ended) range
|
||||||
|
*/
|
||||||
|
class SearchQuery_Range
|
||||||
|
{
|
||||||
|
public $start = null;
|
||||||
|
public $end = null;
|
||||||
|
|
||||||
|
public function __construct($start = null, $end = null)
|
||||||
|
{
|
||||||
|
$this->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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Search\Updaters;
|
||||||
|
|
||||||
|
use SilverStripe\ORM\DB;
|
||||||
/**
|
/**
|
||||||
* This class is responsible for capturing changes to DataObjects and triggering index updates of the resulting dirty index
|
* This class is responsible for capturing changes to DataObjects and triggering index updates of the resulting dirty index
|
||||||
* items.
|
* items.
|
||||||
@ -12,6 +15,9 @@
|
|||||||
*
|
*
|
||||||
* TODO: The way we bind in is awful hacky.
|
* TODO: The way we bind in is awful hacky.
|
||||||
*/
|
*/
|
||||||
|
use SilverStripe\Core\Object;
|
||||||
|
use SilverStripe\ORM\DataExtension;
|
||||||
|
|
||||||
class SearchUpdater extends Object
|
class SearchUpdater extends Object
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@ -204,77 +210,5 @@ class SearchUpdater extends Object
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SearchUpdater_BindManipulationCaptureFilter implements RequestFilter
|
|
||||||
{
|
|
||||||
public function preRequest(SS_HTTPRequest $request, Session $session, DataModel $model)
|
|
||||||
{
|
|
||||||
SearchUpdater::bind_manipulation_capture();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function postRequest(SS_HTTPRequest $request, SS_HTTPResponse $response, DataModel $model)
|
|
||||||
{
|
|
||||||
/* NOP */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete operations do not use database manipulations.
|
|
||||||
*
|
|
||||||
* If a delete has been requested, force a write on objects that should be
|
|
||||||
* indexed. This causes the object to be marked for deletion from the index.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class SearchUpdater_ObjectHandler extends DataExtension
|
|
||||||
{
|
|
||||||
public function onAfterDelete()
|
|
||||||
{
|
|
||||||
// Calling delete() on empty objects does nothing
|
|
||||||
if (!$this->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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Search\Updaters;
|
||||||
|
|
||||||
|
use SilverStripe\Control\RequestFilter;
|
||||||
|
use SilverStripe\Control\HTTPRequest;
|
||||||
|
use SilverStripe\Control\Session;
|
||||||
|
use SilverStripe\ORM\DataModel;
|
||||||
|
use SilverStripe\Control\HTTPResponse;
|
||||||
|
|
||||||
|
class SearchUpdater_BindManipulationCaptureFilter implements RequestFilter
|
||||||
|
{
|
||||||
|
|
||||||
|
public function preRequest(HTTPRequest $request, Session $session, DataModel $model)
|
||||||
|
{
|
||||||
|
SearchUpdater::bind_manipulation_capture();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function postRequest(HTTPRequest $request, HTTPResponse $response, DataModel $model)
|
||||||
|
{
|
||||||
|
/* NOP */
|
||||||
|
}
|
||||||
|
}
|
66
code/search/updaters/SearchUpdater_ObjectHandler.php
Normal file
66
code/search/updaters/SearchUpdater_ObjectHandler.php
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Search\Updaters;
|
||||||
|
use SilverStripe\ORM\DataExtension;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete operations do not use database manipulations.
|
||||||
|
*
|
||||||
|
* If a delete has been requested, force a write on objects that should be
|
||||||
|
* indexed. This causes the object to be marked for deletion from the index.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class SearchUpdater_ObjectHandler extends DataExtension
|
||||||
|
{
|
||||||
|
public function onAfterDelete()
|
||||||
|
{
|
||||||
|
// Calling delete() on empty objects does nothing
|
||||||
|
if (!$this->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);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Search\Variants;
|
||||||
|
use SilverStripe\Core\ClassInfo;
|
||||||
/**
|
/**
|
||||||
* A Search Variant handles decorators and other situations where the items to reindex or search through are modified
|
* A Search Variant handles decorators and other situations where the items to reindex or search through are modified
|
||||||
* from the default state - for instance, dealing with Versioned or Subsite
|
* from the default state - for instance, dealing with Versioned or Subsite
|
||||||
@ -252,31 +253,3 @@ abstract class SearchVariant
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal utility class used to hold the state of the SearchVariant::with call
|
|
||||||
*/
|
|
||||||
class SearchVariant_Caller
|
|
||||||
{
|
|
||||||
protected $variants = null;
|
|
||||||
|
|
||||||
public function __construct($variants)
|
|
||||||
{
|
|
||||||
$this->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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Search\Variants;
|
||||||
|
|
||||||
class SearchVariantSiteTreeSubsitesPolyhome extends SearchVariant
|
class SearchVariantSiteTreeSubsitesPolyhome extends SearchVariant
|
||||||
{
|
{
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Search\Variants;
|
||||||
class SearchVariantSubsites extends SearchVariant
|
class SearchVariantSubsites extends SearchVariant
|
||||||
{
|
{
|
||||||
public function appliesToEnvironment()
|
public function appliesToEnvironment()
|
@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Search\Variants;
|
||||||
|
|
||||||
class SearchVariantVersioned extends SearchVariant
|
class SearchVariantVersioned extends SearchVariant
|
||||||
{
|
{
|
38
code/search/variants/SearchVariant_Caller.php
Normal file
38
code/search/variants/SearchVariant_Caller.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Created by PhpStorm.
|
||||||
|
* User: elliot
|
||||||
|
* Date: 21/04/17
|
||||||
|
* Time: 1:13 PM
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Search\Variants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal utility class used to hold the state of the SearchVariant::with call
|
||||||
|
*/
|
||||||
|
class SearchVariant_Caller
|
||||||
|
{
|
||||||
|
protected $variants = null;
|
||||||
|
|
||||||
|
public function __construct($variants)
|
||||||
|
{
|
||||||
|
$this->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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Solr;
|
||||||
|
|
||||||
use Monolog\Formatter\LineFormatter;
|
use SilverStripe\Control\Director;
|
||||||
use Monolog\Handler\StreamHandler;
|
use SilverStripe\Core\Object;
|
||||||
use Monolog\Logger;
|
|
||||||
use Psr\Log\LoggerInterface;
|
|
||||||
|
|
||||||
class Solr
|
class Solr
|
||||||
{
|
{
|
||||||
@ -149,295 +148,12 @@ class Solr
|
|||||||
static $included = false;
|
static $included = false;
|
||||||
|
|
||||||
if (!$included) {
|
if (!$included) {
|
||||||
set_include_path(get_include_path() . PATH_SEPARATOR . Director::baseFolder() . '/fulltextsearch/thirdparty/solr-php-client');
|
$solr_php_path = __DIR__. '/../..' . '/thirdparty/solr-php-client/';
|
||||||
require_once('Apache/Solr/Service.php');
|
set_include_path(get_include_path() . PATH_SEPARATOR . $solr_php_path);
|
||||||
require_once('Apache/Solr/Document.php');
|
require_once($solr_php_path . 'Apache/Solr/Service.php');
|
||||||
|
require_once($solr_php_path . 'Apache/Solr/Document.php');
|
||||||
|
|
||||||
$included = true;
|
$included = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract class for build tasks
|
|
||||||
*/
|
|
||||||
class Solr_BuildTask extends BuildTask
|
|
||||||
{
|
|
||||||
protected $enabled = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Logger
|
|
||||||
*
|
|
||||||
* @var LoggerInterface
|
|
||||||
*/
|
|
||||||
protected $logger = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current logger
|
|
||||||
*
|
|
||||||
* @return LoggerInterface
|
|
||||||
*/
|
|
||||||
public function getLogger()
|
|
||||||
{
|
|
||||||
return $this->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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,134 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class SolrConfigStore
|
|
||||||
*
|
|
||||||
* The interface Solr_Configure uses to upload configuration files to Solr
|
|
||||||
*/
|
|
||||||
interface SolrConfigStore
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Upload a file to Solr for index $index
|
|
||||||
* @param $index string - The name of an index (which is also used as the name of the Solr core for the index)
|
|
||||||
* @param $file string - A path to a file to upload. The base name of the file will be used on the remote side
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
public function uploadFile($index, $file);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Upload a file to Solr from a string for index $index
|
|
||||||
* @param $index string - The name of an index (which is also used as the name of the Solr core for the index)
|
|
||||||
* @param $filename string - The base name of the file to use on the remote side
|
|
||||||
* @param $strong string - The contents of the file
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
public function uploadString($index, $filename, $string);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the instanceDir to tell Solr to use for index $index
|
|
||||||
* @param $index string - The name of an index (which is also used as the name of the Solr core for the index)
|
|
||||||
*/
|
|
||||||
public function instanceDir($index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class SolrConfigStore_File
|
|
||||||
*
|
|
||||||
* A ConfigStore that uploads files to a Solr instance on a locally accessible filesystem
|
|
||||||
* by just using file copies
|
|
||||||
*/
|
|
||||||
class SolrConfigStore_File implements SolrConfigStore
|
|
||||||
{
|
|
||||||
public function __construct($config)
|
|
||||||
{
|
|
||||||
$this->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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Solr;
|
||||||
Solr::include_client_api();
|
Solr::include_client_api();
|
||||||
|
|
||||||
|
use SilverStripe\Control\Director;
|
||||||
|
use SilverStripe\FulltextSearch\Search\Indexes\SearchIndex;
|
||||||
|
|
||||||
abstract class SolrIndex extends SearchIndex
|
abstract class SolrIndex extends SearchIndex
|
||||||
{
|
{
|
||||||
public static $fulltextTypeMap = array(
|
public static $fulltextTypeMap = array(
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Solr\Reindex\Handlers;
|
||||||
|
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
|
use SilverStripe\FullTextSearch\Solr\Solr;
|
||||||
|
use SilverStripe\FullTextSearch\Solr\SolrIndex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for re-indexing of solr content
|
* Base class for re-indexing of solr content
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Solr\Reindex\Handlers;
|
||||||
|
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
|
use SilverStripe\FullTextSearch\Solr\SolrIndex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides interface for queueing a solr reindex
|
* Provides interface for queueing a solr reindex
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Solr\Reindex\Handlers;
|
||||||
|
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
|
use SilverStripe\FullTextSearch\Solr\SolrIndex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invokes an immediate reindex
|
* Invokes an immediate reindex
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Solr\Reindex\Handlers;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
if (!class_exists('MessageQueue')) {
|
if (!class_exists('MessageQueue')) {
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Solr\Reindex\Handlers;
|
||||||
|
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
|
use SilverStripe\FullTextSearch\Solr\SolrIndex;
|
||||||
|
|
||||||
if (!interface_exists('QueuedJob')) {
|
if (!interface_exists('QueuedJob')) {
|
||||||
return;
|
return;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Solr\Reindex\Jobs;
|
||||||
if (!interface_exists('QueuedJob')) {
|
if (!interface_exists('QueuedJob')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Solr\Reindex\Jobs;
|
||||||
if (!interface_exists('QueuedJob')) {
|
if (!interface_exists('QueuedJob')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Solr\Reindex\Jobs;
|
||||||
use Monolog\Logger;
|
use Monolog\Logger;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Solr\Services;
|
||||||
class Solr3Service_Core extends SolrService_Core
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
class Solr3Service extends SolrService
|
class Solr3Service extends SolrService
|
||||||
{
|
{
|
8
code/solr/services/Solr3Service_Core.php
Normal file
8
code/solr/services/Solr3Service_Core.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Solr\Services;
|
||||||
|
|
||||||
|
|
||||||
|
class Solr3Service_Core extends SolrService_Core
|
||||||
|
{
|
||||||
|
}
|
7
code/solr/services/Solr4Service.php
Normal file
7
code/solr/services/Solr4Service.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Solr\Services;
|
||||||
|
|
||||||
|
class Solr4Service extends SolrService
|
||||||
|
{
|
||||||
|
private static $core_class = 'Solr4Service_Core';
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Solr\Services;
|
||||||
|
|
||||||
class Solr4Service_Core extends SolrService_Core
|
class Solr4Service_Core extends SolrService_Core
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@ -23,7 +25,7 @@ class Solr4Service_Core extends SolrService_Core
|
|||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
* @see Solr4Service_Core::addDocuments
|
* @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
|
$overwritePending = true, $overwriteCommitted = true, $commitWithin = 0
|
||||||
) {
|
) {
|
||||||
return $this->addDocuments(array($document), $allowDups, $overwritePending, $overwriteCommitted, $commitWithin);
|
return $this->addDocuments(array($document), $allowDups, $overwritePending, $overwriteCommitted, $commitWithin);
|
||||||
@ -51,8 +53,3 @@ class Solr4Service_Core extends SolrService_Core
|
|||||||
return $this->add($rawPost);
|
return $this->add($rawPost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Solr4Service extends SolrService
|
|
||||||
{
|
|
||||||
private static $core_class = 'Solr4Service_Core';
|
|
||||||
}
|
|
@ -1,14 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Solr\Services;
|
||||||
|
use SilverStripe\Core\Config\Config;
|
||||||
|
use SilverStripe\FullTextSearch\Solr\Solr;
|
||||||
Solr::include_client_api();
|
Solr::include_client_api();
|
||||||
|
|
||||||
/**
|
|
||||||
* The API for accessing a specific core of a Solr server. Exactly the same as Apache_Solr_Service for now.
|
|
||||||
*/
|
|
||||||
class SolrService_Core extends Apache_Solr_Service
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The API for accessing the primary Solr installation, which includes both SolrService_Core,
|
* The API for accessing the primary Solr installation, which includes both SolrService_Core,
|
||||||
* plus extra methods for interrogating, creating, reloading and getting SolrService_Core instances
|
* plus extra methods for interrogating, creating, reloading and getting SolrService_Core instances
|
18
code/solr/services/SolrService_Core.php
Normal file
18
code/solr/services/SolrService_Core.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Created by PhpStorm.
|
||||||
|
* User: elliot
|
||||||
|
* Date: 21/04/17
|
||||||
|
* Time: 12:45 PM
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Solr\Services;
|
||||||
|
|
||||||
|
use \Apache_Solr_Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The API for accessing a specific core of a Solr server. Exactly the same as Apache_Solr_Service for now.
|
||||||
|
*/
|
||||||
|
class SolrService_Core extends Apache_Solr_Service
|
||||||
|
{
|
||||||
|
}
|
36
code/solr/stores/SolrConfigStore.php
Normal file
36
code/solr/stores/SolrConfigStore.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Solr\Stores;
|
||||||
|
/**
|
||||||
|
* Class SolrConfigStore
|
||||||
|
*
|
||||||
|
* The interface Solr_Configure uses to upload configuration files to Solr
|
||||||
|
*/
|
||||||
|
interface SolrConfigStore
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Upload a file to Solr for index $index
|
||||||
|
* @param $index string - The name of an index (which is also used as the name of the Solr core for the index)
|
||||||
|
* @param $file string - A path to a file to upload. The base name of the file will be used on the remote side
|
||||||
|
* @return null
|
||||||
|
*/
|
||||||
|
public function uploadFile($index, $file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload a file to Solr from a string for index $index
|
||||||
|
* @param $index string - The name of an index (which is also used as the name of the Solr core for the index)
|
||||||
|
* @param $filename string - The base name of the file to use on the remote side
|
||||||
|
* @param $strong string - The contents of the file
|
||||||
|
* @return null
|
||||||
|
*/
|
||||||
|
public function uploadString($index, $filename, $string);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the instanceDir to tell Solr to use for index $index
|
||||||
|
* @param $index string - The name of an index (which is also used as the name of the Solr core for the index)
|
||||||
|
*/
|
||||||
|
public function instanceDir($index);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
53
code/solr/stores/SolrConfigStore_File.php
Normal file
53
code/solr/stores/SolrConfigStore_File.php
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Solr\Stores;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class SolrConfigStore_File
|
||||||
|
*
|
||||||
|
* A ConfigStore that uploads files to a Solr instance on a locally accessible filesystem
|
||||||
|
* by just using file copies
|
||||||
|
*/
|
||||||
|
class SolrConfigStore_File implements SolrConfigStore
|
||||||
|
{
|
||||||
|
public function __construct($config)
|
||||||
|
{
|
||||||
|
$this->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;
|
||||||
|
}
|
||||||
|
}
|
56
code/solr/stores/SolrConfigStore_WebDAV.php
Normal file
56
code/solr/stores/SolrConfigStore_WebDAV.php
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Solr\Stores;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
63
code/solr/tasks/Solr_BuildTask.php
Normal file
63
code/solr/tasks/Solr_BuildTask.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Solr\Tasks;
|
||||||
|
use SilverStripe\Dev\BuildTask;
|
||||||
|
/**
|
||||||
|
* Abstract class for build tasks
|
||||||
|
*/
|
||||||
|
class Solr_BuildTask extends BuildTask
|
||||||
|
{
|
||||||
|
protected $enabled = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logger
|
||||||
|
*
|
||||||
|
* @var LoggerInterface
|
||||||
|
*/
|
||||||
|
protected $logger = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current logger
|
||||||
|
*
|
||||||
|
* @return LoggerInterface
|
||||||
|
*/
|
||||||
|
public function getLogger()
|
||||||
|
{
|
||||||
|
return Injector::inst()->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);
|
||||||
|
}
|
||||||
|
}
|
83
code/solr/tasks/Solr_Configure.php
Normal file
83
code/solr/tasks/Solr_Configure.php
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Solr\Tasks;
|
||||||
|
class Solr_Configure extends Solr_BuildTask
|
||||||
|
{
|
||||||
|
private static $segment = 'Solr_Configure';
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
150
code/solr/tasks/Solr_Reindex.php
Normal file
150
code/solr/tasks/Solr_Reindex.php
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Solr\Tasks;
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
{
|
||||||
|
private static $segment = 'Solr_Reindex';
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Utils;
|
||||||
|
use Iterator;
|
||||||
class CombinationsArrayIterator implements Iterator
|
class CombinationsArrayIterator implements Iterator
|
||||||
{
|
{
|
||||||
protected $arrays;
|
protected $arrays;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Utils;
|
||||||
|
use Iterator;
|
||||||
class MultipleArrayIterator implements Iterator
|
class MultipleArrayIterator implements Iterator
|
||||||
{
|
{
|
||||||
protected $arrays;
|
protected $arrays;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Utils;
|
||||||
class WebDAV
|
class WebDAV
|
||||||
{
|
{
|
||||||
public static function curl_init($url, $method)
|
public static function curl_init($url, $method)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Utils\Logging;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\Formatter\LineFormatter;
|
use Monolog\Formatter\LineFormatter;
|
||||||
use Monolog\Handler\HandlerInterface;
|
use Monolog\Handler\HandlerInterface;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Utils\Logging;
|
||||||
use Monolog\Handler\AbstractProcessingHandler;
|
use Monolog\Handler\AbstractProcessingHandler;
|
||||||
use Monolog\Logger;
|
use Monolog\Logger;
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
namespace SilverStripe\FullTextSearch\Utils\Logging;
|
||||||
use Psr\Log;
|
use Psr\Log;
|
||||||
|
|
||||||
interface SearchLogFactory
|
interface SearchLogFactory
|
||||||
|
@ -20,11 +20,11 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"silverstripe/framework": "~3.1",
|
"silverstripe/framework": "4.0.x-dev",
|
||||||
"monolog/monolog": "~1.15"
|
"monolog/monolog": "~1.15"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"silverstripe/cms": "~3.1",
|
"silverstripe/cms": "4.0.x-dev",
|
||||||
"hafriedlander/silverstripe-phockito": "*"
|
"hafriedlander/silverstripe-phockito": "*"
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
|
@ -1,40 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Tests;
|
||||||
|
|
||||||
class BatchedProcessorTest_Object extends SiteTree implements TestOnly
|
use SilverStripe\Dev\SapphireTest;
|
||||||
{
|
use SilverStripe\FullTextSearch\Search\FullTextSearch;
|
||||||
private static $db = array(
|
|
||||||
'TestText' => '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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@see SearchUpdateQueuedJobProcessor}
|
* Tests {@see SearchUpdateQueuedJobProcessor}
|
||||||
|
15
tests/BatchedProcessorTest/BatchedProcessorTest_Index.php
Normal file
15
tests/BatchedProcessorTest/BatchedProcessorTest_Index.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Tests\BatchedProcessorTest;
|
||||||
|
|
||||||
|
use SilverStripe\Dev\TestOnly;
|
||||||
|
use SilverStripe\FullTextSearch\Search\Indexes\SearchIndex_Recording;
|
||||||
|
|
||||||
|
class BatchedProcessorTest_Index extends SearchIndex_Recording implements TestOnly
|
||||||
|
{
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
$this->addClass('BatchedProcessorTest_Object');
|
||||||
|
$this->addFilterField('TestText');
|
||||||
|
}
|
||||||
|
}
|
13
tests/BatchedProcessorTest/BatchedProcessorTest_Object.php
Normal file
13
tests/BatchedProcessorTest/BatchedProcessorTest_Object.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Tests\BatchedProcessorTest;
|
||||||
|
|
||||||
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
use SilverStripe\Dev\TestOnly;
|
||||||
|
|
||||||
|
class BatchedProcessorTest_Object extends SiteTree implements TestOnly
|
||||||
|
{
|
||||||
|
private static $db = array(
|
||||||
|
'TestText' => 'Varchar'
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Tests\BatchedProcessorTest;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use SilverStripe\ORM\DataObject;
|
||||||
|
use SilverStripe\FullTextSearch\Search\Indexes\SearchIndex_Recording;
|
||||||
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
use SilverStripe\FullTextSearch\Search\Updaters\SearchUpdater;
|
||||||
|
|
||||||
class SearchUpdaterTest_Container extends DataObject
|
class SearchUpdaterTest_Container extends DataObject
|
||||||
{
|
{
|
||||||
private static $db = array(
|
private static $db = array(
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
use SilverStripe\FullTextSearch\Search\Indexes\SearchIndex_Recording;
|
||||||
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
|
||||||
class SearchVariantSiteTreeSubsitesPolyhomeTest_Item extends SiteTree
|
class SearchVariantSiteTreeSubsitesPolyhomeTest_Item extends SiteTree
|
||||||
{
|
{
|
||||||
// TODO: Currently theres a failure if you addClass a non-table class
|
// TODO: Currently theres a failure if you addClass a non-table class
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
use SilverStripe\Dev\TestOnly;
|
||||||
|
use SilverStripe\FullTextSearch\Search\Indexes\SearchIndex_Recording;
|
||||||
|
|
||||||
class SearchVariantVersionedTest extends SapphireTest
|
class SearchVariantVersionedTest extends SapphireTest
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Tests;
|
||||||
|
|
||||||
|
use SilverStripe\FullTextSearch\Tests\Solr4ServiceTest\Solr4ServiceTest_RecordingService;
|
||||||
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test solr 4.0 compatibility
|
* Test solr 4.0 compatibility
|
||||||
*/
|
*/
|
||||||
@ -16,7 +21,7 @@ class Solr4ServiceTest extends SapphireTest
|
|||||||
|
|
||||||
protected function getMockDocument($id)
|
protected function getMockDocument($id)
|
||||||
{
|
{
|
||||||
$document = new Apache_Solr_Document();
|
$document = new \Apache_Solr_Document();
|
||||||
$document->setField('id', $id);
|
$document->setField('id', $id);
|
||||||
$document->setField('title', "Item $id");
|
$document->setField('title', "Item $id");
|
||||||
return $document;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
19
tests/Solr4ServiceTest/Solr4ServiceTest_RecordingService.php
Normal file
19
tests/Solr4ServiceTest/Solr4ServiceTest_RecordingService.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Tests\Solr4ServiceTest;
|
||||||
|
|
||||||
|
use SilverStripe\FullTextSearch\Solr\Services\Solr4Service_Core;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
use SilverStripe\FullTextSearch\Tests\SolrIndexSubsitesTest\SolrIndexSubsitesTest_Index;
|
||||||
|
|
||||||
if (class_exists('Phockito')) {
|
if (class_exists('Phockito')) {
|
||||||
Phockito::include_hamcrest(false);
|
Phockito::include_hamcrest(false);
|
||||||
}
|
}
|
||||||
@ -9,7 +12,7 @@ if (class_exists('Phockito')) {
|
|||||||
*/
|
*/
|
||||||
class SolrIndexSubsitesTest extends SapphireTest {
|
class SolrIndexSubsitesTest extends SapphireTest {
|
||||||
|
|
||||||
public static $fixture_file = 'SolrIndexSubsitesTest.yml';
|
public static $fixture_file = 'SolrIndexSubsitesTest/SolrIndexSubsitesTest.yml';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var SolrIndexSubsitesTest_Index
|
* @var SolrIndexSubsitesTest_Index
|
||||||
@ -243,13 +246,3 @@ class SolrIndexSubsitesTest extends SapphireTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SolrIndexSubsitesTest_Index extends SolrIndex
|
|
||||||
{
|
|
||||||
public function init()
|
|
||||||
{
|
|
||||||
$this->addClass('File');
|
|
||||||
$this->addClass('SiteTree');
|
|
||||||
$this->addAllFulltextFields();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
15
tests/SolrIndexSubsitesTest/SolrIndexSubsitesTest_Index.php
Normal file
15
tests/SolrIndexSubsitesTest/SolrIndexSubsitesTest_Index.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Tests\SolrIndexSubsitesTest;
|
||||||
|
|
||||||
|
use SilverStripe\FullTextSearch\Solr\SolrIndex;
|
||||||
|
|
||||||
|
class SolrIndexSubsitesTest_Index extends SolrIndex
|
||||||
|
{
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
$this->addClass('File');
|
||||||
|
$this->addClass('SiteTree');
|
||||||
|
$this->addAllFulltextFields();
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_FakeIndex;
|
||||||
|
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_FakeIndex2;
|
||||||
|
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_BoostedIndex;
|
||||||
|
|
||||||
|
|
||||||
class SolrIndexTest extends SapphireTest
|
class SolrIndexTest extends SapphireTest
|
||||||
{
|
{
|
||||||
public function setUpOnce()
|
public function setUpOnce()
|
||||||
@ -308,54 +315,3 @@ class SolrIndexTest extends SapphireTest
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SolrIndexTest_FakeIndex extends SolrIndex
|
|
||||||
{
|
|
||||||
public function init()
|
|
||||||
{
|
|
||||||
$this->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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
22
tests/SolrIndexTest/SolrIndexTest_BoostedIndex.php
Normal file
22
tests/SolrIndexTest/SolrIndexTest_BoostedIndex.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Tests\SolrIndexTest;
|
||||||
|
|
||||||
|
use SilverStripe\FullTextSearch\Solr\SolrIndex;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
19
tests/SolrIndexTest/SolrIndexTest_FakeIndex.php
Normal file
19
tests/SolrIndexTest/SolrIndexTest_FakeIndex.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Tests\SolrIndexTest;
|
||||||
|
|
||||||
|
use SilverStripe\FullTextSearch\Solr\SolrIndex;
|
||||||
|
|
||||||
|
class SolrIndexTest_FakeIndex extends SolrIndex
|
||||||
|
{
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
$this->addClass('SearchUpdaterTest_Container');
|
||||||
|
|
||||||
|
$this->addFilterField('Field1');
|
||||||
|
$this->addFilterField('MyDate', 'Date');
|
||||||
|
$this->addFilterField('HasOneObject.Field1');
|
||||||
|
$this->addFilterField('HasManyObjects.Field1');
|
||||||
|
$this->addFilterField('ManyManyObjects.Field1');
|
||||||
|
}
|
||||||
|
}
|
23
tests/SolrIndexTest/SolrIndexTest_FakeIndex2.php
Normal file
23
tests/SolrIndexTest/SolrIndexTest_FakeIndex2.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Tests\SolrIndexTest;
|
||||||
|
|
||||||
|
use SilverStripe\FullTextSearch\Solr\SolrIndex;
|
||||||
|
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
use SilverStripe\FullTextSearch\Tests\SolrVersionedTest\SolrDocumentMatcher;
|
||||||
|
use SilverStripe\FullTextSearch\Tests\SolrVersionedTest\SolrIndexVersionedTest_Object;
|
||||||
|
use SilverStripe\FullTextSearch\Tests\SolrVersionedTest\SolrVersionedTest_Index;
|
||||||
|
use SilverStripe\Versioned\Versioned;
|
||||||
|
|
||||||
if (class_exists('Phockito')) {
|
if (class_exists('Phockito')) {
|
||||||
Phockito::include_hamcrest(false);
|
Phockito::include_hamcrest(false);
|
||||||
}
|
}
|
||||||
@ -169,65 +175,3 @@ class SolrIndexVersionedTest extends SapphireTest
|
|||||||
->deleteById($this->getExpectedDocumentId($id, 'Live'));
|
->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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
39
tests/SolrIndexVersionedTest/SolrDocumentMatcher.php
Normal file
39
tests/SolrIndexVersionedTest/SolrDocumentMatcher.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Tests\SolrVersionedTest;
|
||||||
|
|
||||||
|
if (!class_exists('Phockito')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Phockito::include_hamcrest(false);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Tests\SolrVersionedTest;
|
||||||
|
|
||||||
|
use SilverStripe\ORM\DataObject;
|
||||||
|
use SilverStripe\Dev\TestOnly;
|
||||||
|
use SilverStripe\Versioned\Versioned;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Non-sitetree versioned dataobject
|
||||||
|
*/
|
||||||
|
class SolrIndexVersionedTest_Object extends DataObject implements TestOnly {
|
||||||
|
|
||||||
|
private static $extensions = [
|
||||||
|
Versioned::class
|
||||||
|
];
|
||||||
|
|
||||||
|
private static $db = [
|
||||||
|
'Title' => 'Varchar',
|
||||||
|
'Content' => 'Text',
|
||||||
|
'TestText' => 'Varchar',
|
||||||
|
];
|
||||||
|
}
|
16
tests/SolrIndexVersionedTest/SolrVersionedTest_Index.php
Normal file
16
tests/SolrIndexVersionedTest/SolrVersionedTest_Index.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Tests\SolrVersionedTest;
|
||||||
|
|
||||||
|
use SilverStripe\FullTextSearch\Solr\SolrIndex;
|
||||||
|
|
||||||
|
class SolrVersionedTest_Index extends SolrIndex
|
||||||
|
{
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
$this->addClass('SearchVariantVersionedTest_Item');
|
||||||
|
$this->addClass('SolrIndexVersionedTest_Object');
|
||||||
|
$this->addFilterField('TestText');
|
||||||
|
$this->addFulltextField('Content');
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
|
||||||
|
use SilverStripe\FullTextSearch\Search\FullTextSearch;
|
||||||
|
use SilverStripe\FullTextSearch\Tests\SolrReindexTest\SolrReindexTest_Variant;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Additional tests of solr reindexing processes when run with queuedjobs
|
* Additional tests of solr reindexing processes when run with queuedjobs
|
||||||
*/
|
*/
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Monolog\Handler\AbstractProcessingHandler;
|
use SilverStripe\Dev\SapphireTest;
|
||||||
use Monolog\Handler\HandlerInterface;
|
|
||||||
use Monolog\Logger;
|
use SilverStripe\FullTextSearch\Search\FullTextSearch;
|
||||||
use Psr\Log\LoggerInterface;
|
use SilverStripe\FullTextSearch\Tests\SolrReindexTest\SolrReindexTest_Variant;
|
||||||
|
|
||||||
if (class_exists('Phockito')) {
|
if (class_exists('Phockito')) {
|
||||||
Phockito::include_hamcrest(false);
|
Phockito::include_hamcrest(false);
|
||||||
@ -283,280 +283,3 @@ class SolrReindexTest extends SapphireTest
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides a wrapper for testing SolrReindexBase
|
|
||||||
*/
|
|
||||||
class SolrReindexTest_TestHandler extends SolrReindexBase
|
|
||||||
{
|
|
||||||
public function processGroup(
|
|
||||||
LoggerInterface $logger, SolrIndex $indexInstance, $state, $class, $groups, $group, $taskName
|
|
||||||
) {
|
|
||||||
$indexName = $indexInstance->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'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
39
tests/SolrReindexTest/SolrReindexTest_Handler.php
Normal file
39
tests/SolrReindexTest/SolrReindexTest_Handler.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Tests\SolrReindexTest;
|
||||||
|
|
||||||
|
use Monolog\Handler\AbstractProcessingHandler;
|
||||||
|
use SilverStripe\Dev\TestOnly;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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'];
|
||||||
|
}
|
||||||
|
}
|
15
tests/SolrReindexTest/SolrReindexTest_Index.php
Normal file
15
tests/SolrReindexTest/SolrReindexTest_Index.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Tests\SolrReindexTest;
|
||||||
|
|
||||||
|
use SilverStripe\Dev\TestOnly;
|
||||||
|
use SilverStripe\FullTextSearch\Solr\SolrIndex;
|
||||||
|
|
||||||
|
class SolrReindexTest_Index extends SolrIndex implements TestOnly
|
||||||
|
{
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
$this->addClass('SolrReindexTest_Item');
|
||||||
|
$this->addAllFulltextFields();
|
||||||
|
}
|
||||||
|
}
|
22
tests/SolrReindexTest/SolrReindexTest_Item.php
Normal file
22
tests/SolrReindexTest/SolrReindexTest_Item.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Tests\SolrReindexTest;
|
||||||
|
|
||||||
|
use SilverStripe\Dev\TestOnly;
|
||||||
|
use SilverStripe\ORM\DataObject;
|
||||||
|
use SilverStripe\FullTextSearch\Tests\SolrReindexTest\SolrReindexTest_ItemExtension;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does not have any variant extensions
|
||||||
|
*/
|
||||||
|
class SolrReindexTest_Item extends DataObject implements TestOnly
|
||||||
|
{
|
||||||
|
private static $extensions = [
|
||||||
|
SolrReindexTest_ItemExtension::class
|
||||||
|
];
|
||||||
|
|
||||||
|
private static $db = array(
|
||||||
|
'Title' => 'Varchar(255)',
|
||||||
|
'Variant' => 'Int(0)'
|
||||||
|
);
|
||||||
|
}
|
31
tests/SolrReindexTest/SolrReindexTest_ItemExtension.php
Normal file
31
tests/SolrReindexTest/SolrReindexTest_ItemExtension.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Tests\SolrReindexTest;
|
||||||
|
|
||||||
|
use SilverStripe\ORM\DataExtension;
|
||||||
|
use SilverStripe\Dev\TestOnly;
|
||||||
|
use SilverStripe\ORM\Queries\SQLSelect;
|
||||||
|
use SilverStripe\ORM\DataQuery;
|
||||||
|
use SilverStripe\FullTextSearch\Tests\SolrReindexTest\SolrReindexTest_Variant;
|
||||||
|
use SilverStripe\Core\Convert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(SQLSelect $query, DataQuery $dataQuery = NULL)
|
||||||
|
{
|
||||||
|
$variant = SolrReindexTest_Variant::get_current();
|
||||||
|
if ($variant !== null && !$query->filtersOnID()) {
|
||||||
|
$sqlVariant = Convert::raw2sql($variant);
|
||||||
|
$query->addWhere("\"Variant\" = '{$sqlVariant}'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
74
tests/SolrReindexTest/SolrReindexTest_RecordingLogger.php
Normal file
74
tests/SolrReindexTest/SolrReindexTest_RecordingLogger.php
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Tests\SolrReindexTest;
|
||||||
|
|
||||||
|
use Monolog\Logger;
|
||||||
|
use SilverStripe\Dev\TestOnly;
|
||||||
|
use SilverStripe\FullTextSearch\Tests\SolrReindexTest\SolrReindexTest_Handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
26
tests/SolrReindexTest/SolrReindexTest_TestHandler.php
Normal file
26
tests/SolrReindexTest/SolrReindexTest_TestHandler.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Tests\SolrReindexTest;
|
||||||
|
|
||||||
|
use SilverStripe\FullTextSearch\Solr\Reindex\Handlers\SolrReindexBase;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use SilverStripe\FullTextSearch\Solr\SolrIndex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a wrapper for testing SolrReindexBase
|
||||||
|
*/
|
||||||
|
class SolrReindexTest_TestHandler extends SolrReindexBase
|
||||||
|
{
|
||||||
|
public function processGroup(
|
||||||
|
LoggerInterface $logger, SolrIndex $indexInstance, $state, $class, $groups, $group, $taskName
|
||||||
|
) {
|
||||||
|
$indexName = $indexInstance->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");
|
||||||
|
}
|
||||||
|
}
|
117
tests/SolrReindexTest/SolrReindexTest_Variant.php
Normal file
117
tests/SolrReindexTest/SolrReindexTest_Variant.php
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\FullTextSearch\Tests\SolrReindexTest;
|
||||||
|
|
||||||
|
use SilverStripe\Dev\TestOnly;
|
||||||
|
use SilverStripe\FullTextSearch\Search\Variants\SearchVariant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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');
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user