mirror of
https://github.com/silverstripe/silverstripe-fulltextsearch
synced 2024-10-22 14:05:29 +02:00
Merge pull request #145 from dhensby/pull/144
Index now supports multiple relations with the same name.
This commit is contained in:
commit
02ef3af52f
@ -37,6 +37,27 @@ abstract class SearchIndex extends ViewableData
|
|||||||
*/
|
*/
|
||||||
private static $hide_ancestor;
|
private static $hide_ancestor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to separate class name and relation name in the sources array
|
||||||
|
* this string must not be present in class name
|
||||||
|
* @var string
|
||||||
|
* @config
|
||||||
|
*/
|
||||||
|
private static $class_delimiter = '_|_';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used to clean the source name from suffix
|
||||||
|
* suffixes are needed to support multiple relations with the same name on different page types
|
||||||
|
* @param string $source
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function getSourceName($source)
|
||||||
|
{
|
||||||
|
$source = explode(self::config()->get('class_delimiter'), $source);
|
||||||
|
|
||||||
|
return $source[0];
|
||||||
|
}
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
@ -57,6 +78,9 @@ abstract class SearchIndex extends ViewableData
|
|||||||
/**
|
/**
|
||||||
* Examines the classes this index is built on to try and find defined fields in the class hierarchy for those classes.
|
* Examines the classes this index is built on to try and find defined fields in the class hierarchy for those classes.
|
||||||
* Looks for db and viewable-data fields, although can't nessecarily find type for viewable-data fields.
|
* Looks for db and viewable-data fields, although can't nessecarily find type for viewable-data fields.
|
||||||
|
* If multiple classes have a relation with the same name all of these will be included in the search index
|
||||||
|
* Note that only classes that have the relations uninherited (defined in them) will be listed
|
||||||
|
* this is because inherited relations do not need to be processed by index explicitly
|
||||||
*/
|
*/
|
||||||
public function fieldData($field, $forceType = null, $extraOptions = array())
|
public function fieldData($field, $forceType = null, $extraOptions = array())
|
||||||
{
|
{
|
||||||
@ -77,19 +101,35 @@ abstract class SearchIndex extends ViewableData
|
|||||||
foreach ($lookups as $lookup) {
|
foreach ($lookups as $lookup) {
|
||||||
$next = array();
|
$next = array();
|
||||||
|
|
||||||
foreach ($sources as $source => $options) {
|
foreach ($sources as $source => $baseOptions) {
|
||||||
$class = null;
|
$source = $this->getSourceName($source);
|
||||||
|
|
||||||
foreach (SearchIntrospection::hierarchy($source, $options['include_children']) as $dataclass) {
|
foreach (SearchIntrospection::hierarchy($source, $baseOptions['include_children']) as $dataclass) {
|
||||||
|
$class = null;
|
||||||
|
$options = $baseOptions;
|
||||||
$singleton = singleton($dataclass);
|
$singleton = singleton($dataclass);
|
||||||
|
|
||||||
if ($hasOne = $singleton->has_one($lookup)) {
|
if ($hasOne = $singleton->has_one($lookup)) {
|
||||||
|
// we only want to include base class for relation, omit classes that inherited the relation
|
||||||
|
$relationList = Config::inst()->get($dataclass, 'has_one', Config::UNINHERITED);
|
||||||
|
$relationList = (!is_null($relationList)) ? $relationList : array();
|
||||||
|
if (!array_key_exists($lookup, $relationList)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$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->has_many($lookup)) {
|
||||||
|
// we only want to include base class for relation, omit classes that inherited the relation
|
||||||
|
$relationList = Config::inst()->get($dataclass, 'has_many', Config::UNINHERITED);
|
||||||
|
$relationList = (!is_null($relationList)) ? $relationList : array();
|
||||||
|
if (!array_key_exists($lookup, $relationList)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$class = $hasMany;
|
$class = $hasMany;
|
||||||
$options['multi_valued'] = true;
|
$options['multi_valued'] = true;
|
||||||
$options['lookup_chain'][] = array(
|
$options['lookup_chain'][] = array(
|
||||||
@ -97,6 +137,13 @@ abstract class SearchIndex extends ViewableData
|
|||||||
'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->many_many($lookup)) {
|
||||||
|
// we only want to include base class for relation, omit classes that inherited the relation
|
||||||
|
$relationList = Config::inst()->get($dataclass, 'many_many', Config::UNINHERITED);
|
||||||
|
$relationList = (!is_null($relationList)) ? $relationList : array();
|
||||||
|
if (!array_key_exists($lookup, $relationList)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$class = $manyMany[1];
|
$class = $manyMany[1];
|
||||||
$options['multi_valued'] = true;
|
$options['multi_valued'] = true;
|
||||||
$options['lookup_chain'][] = array(
|
$options['lookup_chain'][] = array(
|
||||||
@ -109,8 +156,10 @@ abstract class SearchIndex extends ViewableData
|
|||||||
if (!isset($options['origin'])) {
|
if (!isset($options['origin'])) {
|
||||||
$options['origin'] = $dataclass;
|
$options['origin'] = $dataclass;
|
||||||
}
|
}
|
||||||
$next[$class] = $options;
|
|
||||||
continue 2;
|
// we add suffix here to prevent the relation to be overwritten by other instances
|
||||||
|
// all sources lookups must clean the source name before reading it via getSourceName()
|
||||||
|
$next[$class . self::config()->get('class_delimiter') . $dataclass] = $options;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,6 +172,7 @@ abstract class SearchIndex extends ViewableData
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($sources as $class => $options) {
|
foreach ($sources as $class => $options) {
|
||||||
|
$class = $this->getSourceName($class);
|
||||||
$dataclasses = SearchIntrospection::hierarchy($class, $options['include_children']);
|
$dataclasses = SearchIntrospection::hierarchy($class, $options['include_children']);
|
||||||
|
|
||||||
while (count($dataclasses)) {
|
while (count($dataclasses)) {
|
||||||
@ -557,7 +607,7 @@ abstract class SearchIndex extends ViewableData
|
|||||||
|
|
||||||
$ids = $sql->execute()->column();
|
$ids = $sql->execute()->column();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($ids)) {
|
if (empty($ids)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,30 @@ class SearchUpdaterTest_Container extends DataObject
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to test ambiguous relationships.
|
||||||
|
*/
|
||||||
|
class SearchUpdaterTest_OtherContainer extends DataObject
|
||||||
|
{
|
||||||
|
private static $has_many = array(
|
||||||
|
'HasManyObjects' => 'SearchUpdaterTest_HasMany'
|
||||||
|
);
|
||||||
|
|
||||||
|
private static $many_many = array(
|
||||||
|
'ManyManyObjects' => 'SearchUpdaterTest_ManyMany'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to test inherited ambiguous relationships.
|
||||||
|
*/
|
||||||
|
class SearchUpdaterTest_ExtendedContainer extends SearchUpdaterTest_OtherContainer
|
||||||
|
{
|
||||||
|
private static $db = array(
|
||||||
|
'SomeField' => 'Varchar',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
class SearchUpdaterTest_HasOne extends DataObject
|
class SearchUpdaterTest_HasOne extends DataObject
|
||||||
{
|
{
|
||||||
private static $db = array(
|
private static $db = array(
|
||||||
@ -41,7 +65,8 @@ class SearchUpdaterTest_HasMany extends DataObject
|
|||||||
);
|
);
|
||||||
|
|
||||||
private static $has_one = array(
|
private static $has_one = array(
|
||||||
'HasManyContainer' => 'SearchUpdaterTest_Container'
|
'HasManyContainer' => 'SearchUpdaterTest_Container',
|
||||||
|
'HasManyOtherContainer' => 'SearchUpdaterTest_OtherContainer',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +78,8 @@ class SearchUpdaterTest_ManyMany extends DataObject
|
|||||||
);
|
);
|
||||||
|
|
||||||
private static $belongs_many_many = array(
|
private static $belongs_many_many = array(
|
||||||
'ManyManyContainer' => 'SearchUpdaterTest_Container'
|
'ManyManyContainer' => 'SearchUpdaterTest_Container',
|
||||||
|
'ManyManyOtherContainer' => 'SearchUpdaterTest_OtherContainer',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,6 +53,64 @@ class SolrIndexTest extends SapphireTest
|
|||||||
$this->assertEquals('SearchUpdaterTest_ManyMany', $data['class']);
|
$this->assertEquals('SearchUpdaterTest_ManyMany', $data['class']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testFieldDataAmbiguousHasMany()
|
||||||
|
{
|
||||||
|
$index = new SolrIndexTest_AmbiguousRelationIndex();
|
||||||
|
$data = $index->fieldData('HasManyObjects.Field1');
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('SearchUpdaterTest_Container_HasManyObjects_Field1', $data);
|
||||||
|
$this->assertArrayHasKey('SearchUpdaterTest_OtherContainer_HasManyObjects_Field1', $data);
|
||||||
|
|
||||||
|
$dataContainer = $data['SearchUpdaterTest_Container_HasManyObjects_Field1'];
|
||||||
|
$this->assertEquals($dataContainer['origin'], 'SearchUpdaterTest_Container');
|
||||||
|
$this->assertEquals($dataContainer['base'], 'SearchUpdaterTest_Container');
|
||||||
|
$this->assertEquals($dataContainer['class'], 'SearchUpdaterTest_HasMany');
|
||||||
|
|
||||||
|
$dataOtherContainer = $data['SearchUpdaterTest_OtherContainer_HasManyObjects_Field1'];
|
||||||
|
$this->assertEquals($dataOtherContainer['origin'], 'SearchUpdaterTest_OtherContainer');
|
||||||
|
$this->assertEquals($dataOtherContainer['base'], 'SearchUpdaterTest_OtherContainer');
|
||||||
|
$this->assertEquals($dataOtherContainer['class'], 'SearchUpdaterTest_HasMany');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFieldDataAmbiguousManyMany()
|
||||||
|
{
|
||||||
|
$index = new SolrIndexTest_AmbiguousRelationIndex();
|
||||||
|
$data = $index->fieldData('ManyManyObjects.Field1');
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('SearchUpdaterTest_Container_ManyManyObjects_Field1', $data);
|
||||||
|
$this->assertArrayHasKey('SearchUpdaterTest_OtherContainer_ManyManyObjects_Field1', $data);
|
||||||
|
|
||||||
|
$dataContainer = $data['SearchUpdaterTest_Container_ManyManyObjects_Field1'];
|
||||||
|
$this->assertEquals($dataContainer['origin'], 'SearchUpdaterTest_Container');
|
||||||
|
$this->assertEquals($dataContainer['base'], 'SearchUpdaterTest_Container');
|
||||||
|
$this->assertEquals($dataContainer['class'], 'SearchUpdaterTest_ManyMany');
|
||||||
|
|
||||||
|
$dataOtherContainer = $data['SearchUpdaterTest_OtherContainer_ManyManyObjects_Field1'];
|
||||||
|
$this->assertEquals($dataOtherContainer['origin'], 'SearchUpdaterTest_OtherContainer');
|
||||||
|
$this->assertEquals($dataOtherContainer['base'], 'SearchUpdaterTest_OtherContainer');
|
||||||
|
$this->assertEquals($dataOtherContainer['class'], 'SearchUpdaterTest_ManyMany');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFieldDataAmbiguousManyManyInherited()
|
||||||
|
{
|
||||||
|
$index = new SolrIndexTest_AmbiguousRelationInheritedIndex();
|
||||||
|
$data = $index->fieldData('ManyManyObjects.Field1');
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('SearchUpdaterTest_Container_ManyManyObjects_Field1', $data);
|
||||||
|
$this->assertArrayHasKey('SearchUpdaterTest_OtherContainer_ManyManyObjects_Field1', $data);
|
||||||
|
$this->assertArrayNotHasKey('SearchUpdaterTest_ExtendedContainer_ManyManyObjects_Field1', $data);
|
||||||
|
|
||||||
|
$dataContainer = $data['SearchUpdaterTest_Container_ManyManyObjects_Field1'];
|
||||||
|
$this->assertEquals($dataContainer['origin'], 'SearchUpdaterTest_Container');
|
||||||
|
$this->assertEquals($dataContainer['base'], 'SearchUpdaterTest_Container');
|
||||||
|
$this->assertEquals($dataContainer['class'], 'SearchUpdaterTest_ManyMany');
|
||||||
|
|
||||||
|
$dataOtherContainer = $data['SearchUpdaterTest_OtherContainer_ManyManyObjects_Field1'];
|
||||||
|
$this->assertEquals($dataOtherContainer['origin'], 'SearchUpdaterTest_OtherContainer');
|
||||||
|
$this->assertEquals($dataOtherContainer['base'], 'SearchUpdaterTest_OtherContainer');
|
||||||
|
$this->assertEquals($dataOtherContainer['class'], 'SearchUpdaterTest_ManyMany');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test boosting on SearchQuery
|
* Test boosting on SearchQuery
|
||||||
*/
|
*/
|
||||||
@ -359,3 +417,43 @@ class SolrIndexTest_BoostedIndex extends SolrIndex
|
|||||||
$this->addBoostedField('Field2', null, array(), 2.1);
|
$this->addBoostedField('Field2', null, array(), 2.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SolrIndexTest_AmbiguousRelationIndex extends SolrIndex
|
||||||
|
{
|
||||||
|
protected function getStoredDefault()
|
||||||
|
{
|
||||||
|
// Override isDev defaulting to stored
|
||||||
|
return 'false';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
$this->addClass('SearchUpdaterTest_Container');
|
||||||
|
$this->addClass('SearchUpdaterTest_OtherContainer');
|
||||||
|
|
||||||
|
// These relationships exist on both classes
|
||||||
|
$this->addFilterField('HasManyObjects.Field1');
|
||||||
|
$this->addFilterField('ManyManyObjects.Field1');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SolrIndexTest_AmbiguousRelationInheritedIndex extends SolrIndex
|
||||||
|
{
|
||||||
|
protected function getStoredDefault()
|
||||||
|
{
|
||||||
|
// Override isDev defaulting to stored
|
||||||
|
return 'false';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
$this->addClass('SearchUpdaterTest_Container');
|
||||||
|
// this one has not the relation defined in it's class but is rather inherited from parent
|
||||||
|
// note that even if we do not include it's parent class the fields will be properly added
|
||||||
|
$this->addClass('SearchUpdaterTest_ExtendedContainer');
|
||||||
|
|
||||||
|
// These relationships exist on both classes
|
||||||
|
$this->addFilterField('HasManyObjects.Field1');
|
||||||
|
$this->addFilterField('ManyManyObjects.Field1');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user