Merge f2d53a5cba
into 17be5a3e63
This commit is contained in:
commit
b0dab15c65
|
@ -0,0 +1 @@
|
|||
MyIndex
|
|
@ -10,7 +10,7 @@ An attempt to add stable support for Fulltext Search engines like Sphinx and Sol
|
|||
|
||||
## Requirements
|
||||
|
||||
* SilverStripe 2.4. Untested in 3, but probably won't work.
|
||||
* SilverStripe 2.4. Experimental support in 3.0.
|
||||
|
||||
## Documentation
|
||||
|
||||
|
@ -18,7 +18,7 @@ See docs/README.md
|
|||
|
||||
## TODO
|
||||
|
||||
* Get rid of includeSubclasses - isn't actually used in practise, makes the codebase uglier, and ClassHierarchy can be
|
||||
* Get rid of includeSubclasses - isn't actually used in practice, makes the codebase uglier, and ClassHierarchy can be
|
||||
used at query time for most of the same use cases
|
||||
|
||||
* Fix field referencing in queries. Should be able to do `$query->search('Text', 'Content')`, not
|
||||
|
|
|
@ -41,7 +41,7 @@ class FullTextSearch {
|
|||
|
||||
$valid = array();
|
||||
foreach ($all as $indexclass => $instance) {
|
||||
if (ClassInfo::is_subclass_of($indexclass, $class)) $valid[$indexclass] = $instance;
|
||||
if (is_subclass_of($indexclass, $class)) $valid[$indexclass] = $instance;
|
||||
}
|
||||
|
||||
self::$indexes_by_subclass[$class] = $valid;
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
* - Specifying which classes and fields this index contains
|
||||
*
|
||||
* - Specifying update rules that are not extractable from metadata (because the values come from functions for instance)
|
||||
*
|
||||
*
|
||||
*/
|
||||
abstract class SearchIndex extends ViewableData {
|
||||
|
||||
|
@ -242,7 +242,7 @@ abstract class SearchIndex extends ViewableData {
|
|||
|
||||
foreach ($fields as $field => $type) {
|
||||
if (preg_match('/^(\w+)\(/', $type, $match)) $type = $match[1];
|
||||
if (ClassInfo::is_subclass_of($type, 'StringField')) $this->addFulltextField($field);
|
||||
if (is_subclass_of($type, 'StringField')) $this->addFulltextField($field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -273,7 +273,7 @@ abstract class SearchIndex extends ViewableData {
|
|||
|
||||
/**
|
||||
* Returns an array where each member is all the fields and the classes that are at the end of some
|
||||
* specific lookup chain from one of the base classes
|
||||
* specific lookup chain from one of the base classes
|
||||
*/
|
||||
function getDerivedFields() {
|
||||
if ($this->derivedFields === null) {
|
||||
|
@ -308,7 +308,7 @@ abstract class SearchIndex extends ViewableData {
|
|||
|
||||
/**
|
||||
* Get the "document ID" (a database & variant unique id) given some "Base" class, DataObject ID and state array
|
||||
*
|
||||
*
|
||||
* @param String $base - The base class of the object
|
||||
* @param Integer $id - The ID of the object
|
||||
* @param Array $state - The variant state of the object
|
||||
|
@ -411,7 +411,7 @@ abstract class SearchIndex extends ViewableData {
|
|||
|
||||
// First, if this object is directly contained in the index, add it
|
||||
foreach ($this->classes as $searchclass => $options) {
|
||||
if ($searchclass == $class || ($options['include_children'] && ClassInfo::is_subclass_of($class, $searchclass))) {
|
||||
if ($searchclass == $class || ($options['include_children'] && is_subclass_of($searchclass, $class))) {
|
||||
|
||||
$dirty[$searchclass] = array();
|
||||
foreach ($statefulids as $statefulid) {
|
||||
|
@ -444,7 +444,7 @@ abstract class SearchIndex extends ViewableData {
|
|||
}
|
||||
else if ($step['through'] == 'has_many') {
|
||||
$sql = new SQLQuery('"'.$step['class'].'"."ID"', $step['class'], '"'.$step['otherclass'].'"."ID" IN ('.implode(',', $ids).')');
|
||||
$sql->innerJoin($step['otherclass'], '"'.$step['class'].'"."ID" = "'.$step['otherclass'].'"."'.$step['foreignkey'].'"');
|
||||
$sql->addInnerJoin($step['otherclass'], '"'.$step['class'].'"."ID" = "'.$step['otherclass'].'"."'.$step['foreignkey'].'"');
|
||||
singleton($step['class'])->extend('augmentSQL', $sql);
|
||||
|
||||
$ids = $sql->execute()->column();
|
||||
|
@ -516,7 +516,7 @@ abstract class SearchIndex_Recording extends SearchIndex {
|
|||
$res = array();
|
||||
|
||||
$res['ID'] = $object->ID;
|
||||
|
||||
|
||||
foreach ($this->getFieldsIterator() as $name => $field) {
|
||||
$val = $this->_getFieldValue($object, $field);
|
||||
$res[$name] = $val;
|
||||
|
@ -544,5 +544,5 @@ abstract class SearchIndex_Recording extends SearchIndex {
|
|||
}
|
||||
|
||||
function commit() { }
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ class SearchUpdater extends Object {
|
|||
*
|
||||
* If messagequeue module not installed, AUTO => IMMEDIATE and DEFERRED => DISABLED
|
||||
*/
|
||||
static $update_method = SearchUpdater::DEFERRED;
|
||||
static $update_method = SearchUpdater::AUTO;
|
||||
|
||||
// How many items can be dirty before we defer updates
|
||||
static $auto_threshold = 6;
|
||||
|
@ -93,7 +93,7 @@ class SearchUpdater extends Object {
|
|||
}
|
||||
|
||||
static $registered = false;
|
||||
|
||||
|
||||
/**
|
||||
* Called by the SearchManiplateCapture database adapter with every manipulation made against the database.
|
||||
*
|
||||
|
@ -116,7 +116,7 @@ class SearchUpdater extends Object {
|
|||
SearchVariant::call('extractManipulationState', $manipulation);
|
||||
|
||||
// Then combine the manipulation back into object field sets
|
||||
|
||||
|
||||
$writes = array();
|
||||
|
||||
foreach ($manipulation as $table => $details) {
|
||||
|
@ -131,11 +131,11 @@ class SearchUpdater extends Object {
|
|||
$key = "$id:$base:".serialize($state);
|
||||
|
||||
$statefulids = array(array('id' => $id, 'state' => $state));
|
||||
|
||||
|
||||
// Is this the first table for this particular object? Then add an item to $writes
|
||||
if (!isset($writes[$key])) $writes[$key] = array('base' => $base, 'class' => $class, 'id' => $id, 'statefulids' => $statefulids, 'fields' => array());
|
||||
// Otherwise update the class label if it's more specific than the currently recorded one
|
||||
else if (ClassInfo::is_subclass_of($class, $writes[$key]['class'])) $writes[$key]['class'] = $class;
|
||||
else if (is_subclass_of($class, $writes[$key]['class'])) $writes[$key]['class'] = $class;
|
||||
|
||||
// Update the fields
|
||||
foreach ($fields as $field => $value) {
|
||||
|
|
|
@ -33,6 +33,12 @@ abstract class SearchVariant {
|
|||
*/
|
||||
abstract function activateState($state);
|
||||
|
||||
/** Holds the class dependencies of each variant **/
|
||||
protected static $dependentClasses = array(
|
||||
'Subsite',
|
||||
'Versioned'
|
||||
);
|
||||
|
||||
/*** OVERRIDES end here*/
|
||||
|
||||
/** Holds a cache of all variants */
|
||||
|
@ -60,10 +66,14 @@ abstract class SearchVariant {
|
|||
|
||||
$concrete = array();
|
||||
foreach ($classes as $variantclass) {
|
||||
$ref = new ReflectionClass($variantclass);
|
||||
if ($ref->isInstantiable()) $concrete[$variantclass] = singleton($variantclass);
|
||||
foreach(self::$dependentClasses as $dependency) {
|
||||
// Rather relies on variants being named similarly to their dependencies
|
||||
if(preg_match("#$dependency#i",$variantclass) && class_exists($dependency)) {
|
||||
$ref = new ReflectionClass($variantclass);
|
||||
if ($ref->isInstantiable()) $concrete[$variantclass] = singleton($variantclass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self::$variants = $concrete;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ class SearchVariantSiteTreeSubsitesPolyhome extends SearchVariant {
|
|||
|
||||
function alterDefinition($base, $index) {
|
||||
$self = get_class($this);
|
||||
|
||||
|
||||
$index->filterFields['_subsite'] = array(
|
||||
'name' => '_subsite',
|
||||
'field' => '_subsite',
|
||||
|
|
|
@ -28,10 +28,10 @@ class SearchVariantVersioned extends SearchVariant {
|
|||
$stage = Versioned::current_stage();
|
||||
$query->filter('_versionedstage', array($stage, SearchQuery::$missing));
|
||||
}
|
||||
|
||||
|
||||
function extractManipulationState(&$manipulation) {
|
||||
$self = get_class($this);
|
||||
|
||||
|
||||
foreach ($manipulation as $table => $details) {
|
||||
$class = $details['class'];
|
||||
$stage = 'Stage';
|
||||
|
|
|
@ -90,7 +90,7 @@ class Solr_Configure extends BuildTask {
|
|||
case 'file':
|
||||
$local = $index['path'];
|
||||
$remote = isset($index['remotepath']) ? $index['remotepath'] : $local;
|
||||
|
||||
|
||||
foreach (Solr::get_indexes() as $index => $instance) {
|
||||
$confdir = "$local/$index/conf";
|
||||
if (!is_dir($confdir)) mkdir($confdir, 0770, true);
|
||||
|
@ -101,7 +101,7 @@ class Solr_Configure extends BuildTask {
|
|||
if (is_file($file)) copy($file, $confdir.'/'.basename($file));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
|
||||
case 'webdav':
|
||||
|
@ -111,7 +111,7 @@ class Solr_Configure extends BuildTask {
|
|||
Solr::$solr_options['host'] . ':' . Solr::$solr_options['port'],
|
||||
$index['path']
|
||||
));
|
||||
|
||||
|
||||
$remote = $index['remotepath'];
|
||||
|
||||
foreach (Solr::get_indexes() as $index => $instance) {
|
||||
|
@ -127,7 +127,7 @@ class Solr_Configure extends BuildTask {
|
|||
if (is_file($file)) WebDAV::upload_from_file($file, $confdir.'/'.basename($file));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -149,10 +149,15 @@ class Solr_Reindex extends BuildTask {
|
|||
$originalState = SearchVariant::current_state();
|
||||
|
||||
if (isset($_GET['start'])) {
|
||||
$this->runFrom(singleton($_GET['index']), $_GET['class'], $_GET['start'], json_decode($_GET['variantstate'], true));
|
||||
$this->runFrom(singleton($_GET['index']), $_GET['class'], $_GET['start'], json_decode($_GET['variantstate'],true));
|
||||
}
|
||||
else {
|
||||
$script = sprintf('%s%ssapphire%scli-script.php', BASE_PATH, DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR);
|
||||
foreach(array('framework','sapphire') as $dirname) {
|
||||
$script = sprintf("%s%s$dirname%scli-script.php", BASE_PATH, DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR);
|
||||
if(file_exists($script)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$class = get_class($this);
|
||||
|
||||
foreach (Solr::get_indexes() as $index => $instance) {
|
||||
|
@ -162,20 +167,17 @@ class Solr_Reindex extends BuildTask {
|
|||
|
||||
foreach ($instance->getClasses() as $class => $options) {
|
||||
$includeSubclasses = $options['include_children'];
|
||||
|
||||
|
||||
foreach (SearchVariant::reindex_states($class, $includeSubclasses) as $state) {
|
||||
SearchVariant::activate_state($state);
|
||||
|
||||
|
||||
$filter = $includeSubclasses ? "" : '"ClassName" = \''.$class."'";
|
||||
|
||||
$singleton = singleton($class);
|
||||
|
||||
$query = $singleton->buildSQL($filter);
|
||||
$query->select('COUNT("'.$class.'"."ID")');
|
||||
$query->orderby = null;
|
||||
$singleton->extend('augmentSQL', $query);
|
||||
|
||||
$total = $query->execute()->value();
|
||||
$query = $singleton->get($class,$filter,null);
|
||||
$dtaQuery = $query->dataQuery();
|
||||
$sqlQuery = $dtaQuery->getFinalisedQuery();
|
||||
$singleton->extend('augmentSQL',$sqlQuery,$dtaQuery);
|
||||
$total = $query->count();
|
||||
|
||||
$statevar = json_encode($state);
|
||||
echo "Class: $class, total: $total in state $statevar\n";
|
||||
|
@ -185,7 +187,7 @@ class Solr_Reindex extends BuildTask {
|
|||
|
||||
for ($offset = 0; $offset < $total; $offset += $this->stat('recordsPerRequest')) {
|
||||
echo "$offset..";
|
||||
|
||||
|
||||
$res = `php $script dev/tasks/$self index=$index class=$class start=$offset variantstate=$statevar`;
|
||||
if (isset($_GET['verbose'])) echo "\n ".preg_replace('/\r\n|\n/', '$0 ', $res)."\n";
|
||||
|
||||
|
@ -212,7 +214,7 @@ class Solr_Reindex extends BuildTask {
|
|||
$includeSubclasses = $options['include_children'];
|
||||
$filter = $includeSubclasses ? "" : '"ClassName" = \''.$class."'";
|
||||
|
||||
$items = DataObject::get($class, $filter, "", "", array('limit' => $this->stat('recordsPerRequest'), 'start' => $start));
|
||||
$items = DataList::create($class)->where($filter)->limit($this->stat('recordsPerRequest'), $start);
|
||||
foreach ($items as $item) { $index->add($item); $item->destroy(); }
|
||||
}
|
||||
|
||||
|
|
|
@ -70,13 +70,13 @@ abstract class SolrIndex extends SearchIndex {
|
|||
|
||||
foreach ($this->sortFields as $name => $field) {
|
||||
if ($field['fullfield'] == 'ID' || $field['fullfield'] == 'ClassName') continue;
|
||||
|
||||
|
||||
$multiValued = (isset($field['multi_valued']) && $field['multi_valued']) ? "multiValued='true'" : '';
|
||||
|
||||
$type = self::$sortTypeMap[$field['type']];
|
||||
$xml[] = "<field name='{$name}' type='{$type}' indexed='true' $stored $multiValued />";
|
||||
}
|
||||
|
||||
|
||||
return implode("\n\t\t", $xml);
|
||||
}
|
||||
|
||||
|
@ -92,7 +92,7 @@ abstract class SolrIndex extends SearchIndex {
|
|||
|
||||
protected function _addField($doc, $object, $field) {
|
||||
$class = get_class($object);
|
||||
if ($class != $field['origin'] && !ClassInfo::is_subclass_of($class, $field['origin'])) return;
|
||||
if ($class != $field['origin'] && !is_subclass_of($class, $field['origin'])) return;
|
||||
|
||||
$value = $this->_getFieldValue($object, $field);
|
||||
$type = isset(self::$filterTypeMap[$field['type']]) ? self::$filterTypeMap[$field['type']] : self::$filterTypeMap['*'];
|
||||
|
@ -102,7 +102,7 @@ abstract class SolrIndex extends SearchIndex {
|
|||
if ($type == 'tdate') $sub = gmdate('Y-m-d\TH:i:s\Z', strtotime($sub));
|
||||
/* Solr requires numbers to be valid if presented, not just empty */
|
||||
if (($type == 'tint' || $type == 'tfloat' || $type == 'tdouble') && !is_numeric($sub)) continue;
|
||||
|
||||
|
||||
$doc->addField($field['name'], $sub);
|
||||
}
|
||||
|
||||
|
@ -142,7 +142,7 @@ abstract class SolrIndex extends SearchIndex {
|
|||
$class = get_class($object);
|
||||
|
||||
foreach ($this->getClasses() as $searchclass => $options) {
|
||||
if ($searchclass == $class || ($options['include_children'] && ClassInfo::is_subclass_of($class, $searchclass))) {
|
||||
if ($searchclass == $class || ($options['include_children'] && is_subclass_of($class, $searchclass))) {
|
||||
$this->_addAs($object, $searchclass, $options);
|
||||
}
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ abstract class SolrIndex extends SearchIndex {
|
|||
|
||||
function canAdd($class) {
|
||||
foreach ($this->classes as $searchclass => $options) {
|
||||
if ($searchclass == $class || ($options['include_children'] && ClassInfo::is_subclass_of($class, $searchclass))) return true;
|
||||
if ($searchclass == $class || ($options['include_children'] && is_subclass_of($class, $searchclass))) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -264,7 +264,7 @@ abstract class SolrIndex extends SearchIndex {
|
|||
|
||||
$res = $service->search($q ? implode(' ', $q) : '*:*', $offset, $limit, array('fq' => implode(' ', $fq)), Apache_Solr_Service::METHOD_POST);
|
||||
|
||||
$results = array();
|
||||
$results = new ArrayList();
|
||||
|
||||
foreach ($res->response->docs as $doc) {
|
||||
$result = DataObject::get_by_id($doc->ClassName, $doc->ID);
|
||||
|
@ -272,9 +272,12 @@ abstract class SolrIndex extends SearchIndex {
|
|||
}
|
||||
|
||||
$ret = array();
|
||||
$ret['Matches'] = new DataObjectSet($results);
|
||||
$ret['Matches']->setPageLimits($offset, $limit, $res->numFound);
|
||||
$ret['Matches'] = new PaginatedList($results);
|
||||
$ret['Matches']->setTotalItems($res->numFound); // Tell PaginatedList how many results there are
|
||||
$ret['Matches']->setPageStart($offset); // Results for current page start at $offset
|
||||
$ret['Matches']->setPageLength($limit); // Results per page
|
||||
|
||||
return new ArrayData($ret);
|
||||
// Just return what's useful
|
||||
return $ret['Matches']->toArray();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,19 +2,21 @@
|
|||
|
||||
# FullTextSearch module
|
||||
|
||||
An attempt to add stable support for Fulltext Search engines like Sphinx and Solr to SilverStripe CMS
|
||||
|
||||
## Maintainer Contact
|
||||
|
||||
* Hamish Friedlander <hamish (at) silverstripe (dot) com>
|
||||
|
||||
## Requirements
|
||||
|
||||
* SilverStripe 2.4. Untested in 3 alpha, but probably won't work.
|
||||
* SilverStripe 2.4. Experimental support in 3.0.
|
||||
|
||||
## Introduction
|
||||
|
||||
This is a module aimed at adding support for standalone fulltext search engines to SilverStripe.
|
||||
|
||||
It contains several layers.
|
||||
It contains several layers:
|
||||
|
||||
* A fulltext API, ignoring the actual provision of fulltext searching
|
||||
|
||||
|
@ -32,22 +34,23 @@ fulltext searching as an extension of the object model. However the disconnect b
|
|||
design and the object model meant that searching was inefficient. The abstraction would also often break and it was
|
||||
hard to then figure out what was going on.
|
||||
|
||||
Instead this module provides the ability to define those indexes and queries in PHP. The indexes are defined as a mapping
|
||||
between the object model and the index model. This module then interogates model metadata to build the specific index
|
||||
definition. It also hooks into Sapphire in order to update the indexes when the models change.
|
||||
This module instead provides the ability to define those indexes and queries in PHP. The indexes are defined as a mapping
|
||||
between the SilverStripe object model and the connector-specific fulltext engine index model. This module then interrogates model metadata
|
||||
to build the specific index definition.
|
||||
|
||||
Connectors then convert those index and query definitions into fulltext engine specific code.
|
||||
It also hooks into Sapphire in order to update the indexes when the models change and connectors then convert those index and query definitions
|
||||
into fulltext engine specific code.
|
||||
|
||||
The intent of this module is not to make changing fulltext search engines seamless. Where possible this module provides
|
||||
common interfaces to fulltext engine functionality, abstracting out common behaviour. However, each connector also
|
||||
offers it's own extensions, and there is some behaviour (such as getting the fulltext search engines installed, configured
|
||||
and running) that each connector deals with itself, in a way best suited to the specific fulltext search engines design.
|
||||
offers its own extensions, and there is some behaviour (such as getting the fulltext search engines installed, configured
|
||||
and running) that each connector deals with itself, in a way best suited to that search engine's design.
|
||||
|
||||
## Basic usage
|
||||
|
||||
Basic usage is a four step process
|
||||
Basic usage is a four step process:
|
||||
|
||||
1) Define an index (Note the specific connector index instance - that's what defines which engine gets used)
|
||||
1). Define an index in SilverStripe (Note: The specific connector index instance - that's what defines which engine gets used)
|
||||
|
||||
```php
|
||||
mysite/code/MyIndex.php:
|
||||
|
@ -62,24 +65,24 @@ class MyIndex extends SolrIndex {
|
|||
}
|
||||
```
|
||||
|
||||
2) Add something to the index (adding existing objects to the index is connector specific)
|
||||
2). Add something to the index (Note: You can also just update an existing document in the CMS. but adding _existing_ objects to the index is connector specific)
|
||||
|
||||
```php
|
||||
$page = new Page(array('Contents' => 'Help me. My house is on fire. This is less than optimal.'));
|
||||
$page->write();
|
||||
```
|
||||
|
||||
3) Build a query
|
||||
3). Build a query
|
||||
|
||||
```php
|
||||
$query = new SearchQuery();
|
||||
$query->search('My house is on fire');
|
||||
```
|
||||
|
||||
4) Apply that query to an index
|
||||
4). Apply that query to an index
|
||||
|
||||
```php
|
||||
singleton('MyIndex')->search($query);
|
||||
$results = singleton($index)->search($query);
|
||||
```
|
||||
|
||||
Note that for most connectors, changes won't be searchable until _after_ the request that triggered the change.
|
||||
|
|
29
docs/Solr.md
29
docs/Solr.md
|
@ -1,12 +1,14 @@
|
|||
# Solr connector for SilverStripe fulltextsearch
|
||||
# Solr connector for SilverStripe fulltextsearch module
|
||||
|
||||
This module provides a fulltextsearch module connector to Solr.
|
||||
|
||||
It works with Solr in multi-core mode. It needs to be able to update Solr configuration files, and has modes for
|
||||
doing this by direct file access (when Solr shares a server with SilverStripe) and by WebDAV (when it's on a different server).
|
||||
|
||||
Since Solr is Java based, this module requires a Java runtime to be present on the server Solr is running on (not nessecarily
|
||||
the same server the SilverStripe server is on).
|
||||
Since Solr is Java based, this module requires a Java runtime to be present on the server Solr is running on (not necessarily
|
||||
the same physical machine the SilverStripe server is on).
|
||||
|
||||
* See the helpful [Solr Tutorial](http://lucene.apache.org/solr/api/doc-files/tutorial.html), for more on cores and querying.
|
||||
|
||||
## Getting started quickly (dev mode)
|
||||
|
||||
|
@ -41,18 +43,25 @@ class MyIndex extends SolrIndex {
|
|||
}
|
||||
```
|
||||
|
||||
Open a terminal, change to thirdparty/solr/server and start Solr by running `java -jar start.jar`
|
||||
* Open a terminal, change to thirdparty/solr/server and start Solr by running `java -jar start.jar`
|
||||
* In another terminal run the configure task `sake dev/tasks/Solr_configure`
|
||||
* Then run the configure task `sake dev/tasks/Solr_reindex`
|
||||
|
||||
In another terminal run the configure task `sake dev/tasks/Solr_configure`
|
||||
You can now visit http://localhost:8983/solr/MyIndex/admin/ to search the contents of the now created Solr index via the native SOLR UI
|
||||
|
||||
Tne run the configure task `sake dev/tasks/Solr_reindex`
|
||||
## Debugging
|
||||
|
||||
You can now visit http://localhost:8983/solr/MyIndex/admin/ to search the contents of the now created Solr index
|
||||
It is possible to manually replicate the data automatically sent to Solr when saving/publishing in SilverStripe,
|
||||
which is useful when debugging front-end queries, see: thirdparty/solr/server/silverstripe-solr-test.xml but roughly:
|
||||
|
||||
```
|
||||
#> java -Durl=http://localhost:8983/solr/MyIndex/update/ -Dtype=text/xml -jar post.jar silverstripe-solr-test.xml
|
||||
```
|
||||
|
||||
-----
|
||||
|
||||
These instructions will get you running quickly, but the Solr indexes will be stored in your project. You can also
|
||||
copy the thirdparty/solr directory somewhere else. The instructions are above still apply then - just set the path value
|
||||
in mysite/_config.php to point to this other location, and of course run `java -jar start.jar` from the new directory
|
||||
These instructions will get you running quickly, but the Solr indexes will be stored as binary files inside your SilverStripe project. You can also
|
||||
copy the thirdparty/solr directory somewhere else. The instructions above will still apply - just set the path value
|
||||
in mysite/_config.php to point to this other location, and of course run `java -jar start.jar` from the new directory,
|
||||
not the thirdparty one.
|
||||
|
||||
|
|
|
@ -21,8 +21,13 @@ class SearchVariantSiteTreeSubsitesPolyhomeTest extends SapphireTest {
|
|||
private static $subsite_a = null;
|
||||
private static $subsite_b = null;
|
||||
|
||||
function setUp() {
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
// Check subsites installed
|
||||
if(!class_exists('Subsite')) {
|
||||
user_error("Subsite class not available. This is probably because the Subsites module is not installed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (self::$index === null) self::$index = singleton('SearchVariantSiteTreeSubsitesPolyhomeTest_Index');
|
||||
|
||||
|
@ -35,7 +40,7 @@ class SearchVariantSiteTreeSubsitesPolyhomeTest extends SapphireTest {
|
|||
SearchUpdater::clear_dirty_indexes();
|
||||
}
|
||||
|
||||
function testSavingDirect() {
|
||||
function testSavingDirect() {
|
||||
// Initial add
|
||||
|
||||
$item = new SearchVariantSiteTreeSubsitesPolyhomeTest_Item();
|
||||
|
|
|
@ -20,6 +20,12 @@ class SearchVariantVersionedTest extends SapphireTest {
|
|||
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Check versioned installed (something is very wrong if it isn't)
|
||||
if(!class_exists('Versioned')) {
|
||||
user_error("Versioned class not available. Something is wrong with your SilverStripe install as \"Versioned\" is core sapphire functionality");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (self::$index === null) self::$index = singleton('SearchVariantVersionedTest_Index');
|
||||
|
||||
|
@ -58,7 +64,7 @@ class SearchVariantVersionedTest extends SapphireTest {
|
|||
|
||||
SearchUpdater::flush_dirty_indexes();
|
||||
$this->assertEquals(self::$index->getAdded(array('ID', '_versionedstage')), array(
|
||||
array('ID' => $item->ID, '_versionedstage' => 'Stage')
|
||||
array('ID' => $item->ID, '_versionedstage' => 'Stage3')
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,53 @@
|
|||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<!--
|
||||
|
||||
This XML is representative of the data posted to SOLR automatically by the fulltextsearch module.
|
||||
Use it to simulate CMS updates auto-posted to SOLR by posting manually to SOLR, bypassing the module & CMS.
|
||||
This is useful for debugging & testing expected output in any front-end development you do.
|
||||
|
||||
To post to SOLR:
|
||||
|
||||
#> java -Durl=http://localhost:8983/solr/MyIndex/update/ -Dtype=text/xml -jar post.jar silverstripe-solr-test.xml
|
||||
|
||||
-->
|
||||
|
||||
<add allowDups="false" overwritePending="true" overwriteCommitted="true">
|
||||
<doc>
|
||||
<field name="_documentid">187-HomePage-{"SearchVariantVersioned":"Live"}</field>
|
||||
<field name="ID">187</field>
|
||||
<field name="ClassName">HomePage</field>
|
||||
<field name="ClassHierarchy">SiteTree</field>
|
||||
<field name="ClassHierarchy">Page</field>
|
||||
<field name="ClassHierarchy">HomePage</field>
|
||||
<field name="SiteTree_URLSegment">home</field>
|
||||
<field name="SiteTree_Title">Home</field>
|
||||
<field name="SiteTree_MenuTitle">Home</field>
|
||||
<field name="SiteTree_Content"><p style="font-size: 13.625px; line-height: 31.475px;">Welcome to SilverStripe! This is the default homepage. You can edit this page by opening <a style="font-size: 13.625px; line-height: 31.475px;" href="admin/">the CMS</a>. You can now access the <a style="font-size: 13.625px; line-height: 31.475px;" href="http://doc.silverstripe.org">developer documentation</a>, or begin <a style="font-size: 13.625px; line-height: 31.475px;" href="http://doc.silverstripe.org/doku.php?id=tutorials">the tutorials.</a></p>
|
||||
<p>therearetestypeopleintheworld</p>
|
||||
<p style="font-size: 13.625px; line-height: 31.475px;"> </p>
|
||||
<p style="font-size: 13.625px; line-height: 31.475px;"> </p>
|
||||
</field>
|
||||
<field name="SiteTree_MetaTitle"></field>
|
||||
<field name="SiteTree_MetaDescription"></field>
|
||||
<field name="SiteTree_MetaKeywords"></field>
|
||||
<field name="SiteTree_ExtraMeta"></field>
|
||||
<field name="SiteTree_ReportClass"></field>
|
||||
<field name="_versionedstage">Live</field>
|
||||
</doc>
|
||||
</add>
|
|
@ -1,33 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<!--
|
||||
All (relative) paths are relative to the installation path
|
||||
|
||||
persistent: Save changes made via the API to this file
|
||||
sharedLib: path to a lib directory that will be shared across all cores
|
||||
-->
|
||||
<solr persistent="true">
|
||||
|
||||
<!--
|
||||
adminPath: RequestHandler path to manage cores.
|
||||
If 'null' (or absent), cores will not be manageable via request handler
|
||||
-->
|
||||
<cores adminPath="/admin/cores">
|
||||
<core name="MyIndex" instanceDir="/Users/rmichell/sites/ss3/solr/thirdparty/solr/server/solr/MyIndex/"/>
|
||||
</cores>
|
||||
</solr>
|
||||
|
|
Loading…
Reference in New Issue