This commit is contained in:
GitHub Merge Button 2012-05-20 18:32:21 -07:00
commit b0dab15c65
17 changed files with 170 additions and 105 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
MyIndex

View File

@ -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

View File

@ -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;

View File

@ -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() { }
}

View File

@ -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) {

View File

@ -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;
}

View File

@ -32,7 +32,7 @@ class SearchVariantSiteTreeSubsitesPolyhome extends SearchVariant {
function alterDefinition($base, $index) {
$self = get_class($this);
$index->filterFields['_subsite'] = array(
'name' => '_subsite',
'field' => '_subsite',

View File

@ -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';

View File

@ -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(); }
}

View File

@ -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();
}
}

View File

@ -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.

View File

@ -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.

View File

@ -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();

View File

@ -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')
));
}
}

BIN
thirdparty/solr/server/post.jar vendored Normal file

Binary file not shown.

View File

@ -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">&lt;p style="font-size: 13.625px; line-height: 31.475px;"&gt;Welcome to SilverStripe! This is the default homepage. You can edit this page by opening &lt;a style="font-size: 13.625px; line-height: 31.475px;" href="admin/"&gt;the CMS&lt;/a&gt;. You can now access the &lt;a style="font-size: 13.625px; line-height: 31.475px;" href="http://doc.silverstripe.org"&gt;developer documentation&lt;/a&gt;, or begin &lt;a style="font-size: 13.625px; line-height: 31.475px;" href="http://doc.silverstripe.org/doku.php?id=tutorials"&gt;the tutorials.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;therearetestypeopleintheworld&lt;/p&gt;
&lt;p style="font-size: 13.625px; line-height: 31.475px;"&gt; &lt;/p&gt;
&lt;p style="font-size: 13.625px; line-height: 31.475px;"&gt; &lt;/p&gt;
</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>

View File

@ -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>