mirror of
https://github.com/silverstripe/silverstripe-fulltextsearch
synced 2024-10-22 12:05:29 +00:00
Merge branch 'compat4/btasker' into compat/4
This commit is contained in:
commit
63954f3a07
@ -5,7 +5,6 @@ language: php
|
||||
sudo: false
|
||||
|
||||
php:
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
- 7.1
|
||||
|
@ -1,6 +1,7 @@
|
||||
|
||||
# FullTextSearch module
|
||||
|
||||
[](http://travis-ci.org/silverstripe-labs/silverstripe-fulltextsearch)
|
||||
[](http://travis-ci.org/silverstripe/silverstripe-fulltextsearch)
|
||||
|
||||
Adds support for fulltext search engines like Sphinx and Solr to SilverStripe CMS.
|
||||
|
||||
|
@ -99,6 +99,7 @@ class SearchUpdater extends Object
|
||||
$id = $details['id'];
|
||||
$state = $details['state'];
|
||||
$class = $details['class'];
|
||||
$command = $details['command'];
|
||||
$fields = isset($details['fields']) ? $details['fields'] : array();
|
||||
|
||||
$base = DataObject::getSchema()->baseDataClass($class);
|
||||
@ -113,6 +114,7 @@ class SearchUpdater extends Object
|
||||
'class' => $class,
|
||||
'id' => $id,
|
||||
'statefulids' => $statefulids,
|
||||
'command' => $command,
|
||||
'fields' => array()
|
||||
);
|
||||
}
|
||||
@ -127,9 +129,9 @@ class SearchUpdater extends Object
|
||||
}
|
||||
}
|
||||
|
||||
// Trim records without fields
|
||||
foreach(array_keys($writes) as $key) {
|
||||
if(empty($writes[$key]['fields'])) {
|
||||
// Trim non-delete records without fields
|
||||
foreach (array_keys($writes) as $key) {
|
||||
if ($writes[$key]['command'] !== 'delete' && empty($writes[$key]['fields'])) {
|
||||
unset($writes[$key]);
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,9 @@ namespace SilverStripe\FullTextSearch\Search\Updaters;
|
||||
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
use SilverStripe\FullTextSearch\Search\Variants\SearchVariant;
|
||||
use SilverStripe\FullTextSearch\Search\Updaters\SearchUpdater;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\Core\ClassInfo;
|
||||
|
||||
/**
|
||||
* Delete operations do not use database manipulations.
|
||||
@ -23,14 +25,26 @@ class SearchUpdater_ObjectHandler extends DataExtension
|
||||
}
|
||||
|
||||
// Force SearchUpdater to mark this record as dirty
|
||||
$manipulation = array(
|
||||
$this->owner->ClassName => array(
|
||||
// Note: Some extensions require entire hierarchy passed to augmentWrite()
|
||||
$manipulation = array();
|
||||
foreach (ClassInfo::ancestry($this->owner) as $class) {
|
||||
if (!is_subclass_of($class, DataObject::class)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tableName = DataObject::getSchema()->tableName($class);
|
||||
$manipulation[$tableName] = array(
|
||||
'fields' => array(),
|
||||
'id' => $this->owner->ID,
|
||||
'command' => 'update'
|
||||
)
|
||||
);
|
||||
'class' => $class,
|
||||
// Note: 'delete' command not actually handled by manipulations,
|
||||
// but added so that SearchUpdater can detect the deletion
|
||||
'command' => 'delete'
|
||||
);
|
||||
}
|
||||
|
||||
$this->owner->extend('augmentWrite', $manipulation);
|
||||
|
||||
SearchUpdater::handle_manipulation($manipulation);
|
||||
}
|
||||
|
||||
|
@ -72,9 +72,21 @@ class SearchVariantSubsites extends SearchVariant
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This field has been altered to allow a user to obtain search results for a particular subsite
|
||||
* When attempting to do this in project code, SearchVariantSubsites kicks and overwrites any filter you've applied
|
||||
* This fix prevents the module from doing this if a filter is applied on the index or the query, or if a field is
|
||||
* being excluded specifically before being executed.
|
||||
*
|
||||
* A pull request has been raised for this issue. Once accepted this forked module can be deleted and the parent
|
||||
* project should be used instead.
|
||||
*/
|
||||
public function alterQuery($query, $index)
|
||||
{
|
||||
if ($this->isFieldFiltered('_subsite', $query)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subsite = Subsite::currentSubsiteID();
|
||||
$query->filter('_subsite', array($subsite, SearchQuery::$missing));
|
||||
}
|
||||
@ -121,4 +133,19 @@ class SearchVariantSubsites extends SearchVariant
|
||||
$writes[$key]['statefulids'] = $next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a field with a certain name is filtered by the search query or on the index
|
||||
* This is the equivalent of saying "show me the results that do ONLY contain this value"
|
||||
* @param $field string name of the field being filtered
|
||||
* @param $query SearchQuery currently being executed
|
||||
* @param $index SearchIndex which specifies a filter field
|
||||
* @return bool true if $field is being filtered, false if it is not being filtered
|
||||
*/
|
||||
protected function isFieldFiltered($field, $query)
|
||||
{
|
||||
$queryHasFilter = !empty($query->require[$field]);
|
||||
|
||||
return $queryHasFilter;
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,8 @@
|
||||
"monolog/monolog": "~1.15"
|
||||
},
|
||||
"require-dev": {
|
||||
"silverstripe/cms": "4.0.x-dev"
|
||||
"silverstripe/cms": "4.0.x-dev",
|
||||
"phpunit/phpunit": "*@stable"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
|
@ -191,6 +191,17 @@ from a new file `mysite/solr/templates/types.ss` instead:
|
||||
return $this->renderWith(Director::baseFolder() . '/mysite/solr/templates/types.ss');
|
||||
}
|
||||
}
|
||||
|
||||
#### Searching for words containing numbers
|
||||
|
||||
By default, the fulltextmodule is configured to split words containing numbers into multiple tokens. For example, the word “A1” would be interpreted as “A” “1”; since “a” is a common stopword, the term “A1” will be excluded from search.
|
||||
|
||||
To allow searches on words containing numeric tokens, you'll need to update your overloaded template to change the behaviour of the WordDelimiterFilterFactory. Each instance of `<filter class="solr.WordDelimiterFilterFactory">` needs to include the following attributes and values:
|
||||
|
||||
* add splitOnNumerics="0" on all WordDelimiterFilterFactory fields
|
||||
* change catenateOnNumbers="1" on all WordDelimiterFilterFactory fields
|
||||
|
||||
Update your index to point to your overloaded template using the method described above.
|
||||
|
||||
### Spell Checking ("Did you mean...")
|
||||
|
||||
|
92
tests/SearchVariantSubsitesTest.php
Normal file
92
tests/SearchVariantSubsitesTest.php
Normal file
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
|
||||
class SearchVariantSubsiteTest extends SapphireTest
|
||||
{
|
||||
|
||||
private static $index = null;
|
||||
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
// Check versioned available
|
||||
if (!class_exists('Subsite')) {
|
||||
return $this->markTestSkipped('The subsites module is not installed');
|
||||
}
|
||||
|
||||
if (self::$index === null) {
|
||||
self::$index = singleton('SearchVariantSubsiteTest');
|
||||
}
|
||||
|
||||
SearchUpdater::bind_manipulation_capture();
|
||||
|
||||
Config::inst()->update('Injector', 'SearchUpdateProcessor', array(
|
||||
'class' => 'SearchUpdateImmediateProcessor'
|
||||
));
|
||||
|
||||
FullTextSearch::force_index_list(self::$index);
|
||||
SearchUpdater::clear_dirty_indexes();
|
||||
}
|
||||
|
||||
public function testQueryIsAlteredWhenSubsiteNotSet()
|
||||
{
|
||||
$index = new SolrIndexTest_FakeIndex();
|
||||
$query = new SearchQuery();
|
||||
|
||||
//typical behaviour: nobody is explicitly filtering on subsite, so the search variant adds a filter to the query
|
||||
$this->assertArrayNotHasKey('_subsite', $query->require);
|
||||
$variant = new SearchVariantSubsites();
|
||||
$variant->alterDefinition('SearchUpdaterTest_Container', $index);
|
||||
$variant->alterQuery($query, $index);
|
||||
|
||||
//check that the "default" query has been put in place: it's not empty, and we're searching on Subsite ID:0 and
|
||||
// an object of SearchQuery::missing
|
||||
$this->assertNotEmpty($query->require['_subsite']);
|
||||
$this->assertEquals(0, $query->require['_subsite'][0]);
|
||||
|
||||
//check that SearchQuery::missing is set (by default, it is an object of stdClass)
|
||||
$this->assertInstanceOf('stdClass', $query->require['_subsite'][1]);
|
||||
}
|
||||
|
||||
|
||||
public function testQueryIsAlteredWhenSubsiteIsSet()
|
||||
{
|
||||
//now we want to test if somebody has already applied the _subsite filter to the query
|
||||
$index = new SolrIndexTest_FakeIndex();
|
||||
$query = new SearchQuery();
|
||||
|
||||
//check that _subsite is not applied yet
|
||||
//this key should not be exist until the SearchVariant applies it later
|
||||
$this->assertArrayNotHasKey('_subsite', $query->require);
|
||||
|
||||
//apply the subsite filter on the query (for example, if it's passed into a controller and set before searching)
|
||||
//we've chosen an arbirary value of 2 here, to check if it is changed later
|
||||
$query->filter('_subsite', 2);
|
||||
$this->assertNotEmpty($query->require['_subsite']);
|
||||
|
||||
//apply the search variant's definition and query
|
||||
$variant = new SearchVariantSubsites();
|
||||
$variant->alterDefinition('SearchUpdaterTest_Container', $index);
|
||||
|
||||
//the protected function isFieldFiltered is implicitly tested here
|
||||
$variant->alterQuery($query, $index);
|
||||
|
||||
//confirm that the query has been altered, but NOT with default values
|
||||
//first check that _subsite filter is not empty
|
||||
$this->assertNotEmpty($query->require['_subsite']);
|
||||
//subsite filter first value is not 0
|
||||
$this->assertNotEquals(0, $query->require['_subsite'][0]);
|
||||
|
||||
//subsite filter SearchQuery::missing should not be set so its expected location is empty
|
||||
$this->assertArrayNotHasKey(1, $query->require['_subsite']);
|
||||
|
||||
//subsite filter has been modified with our arbitrary test value. The second value is not set
|
||||
//this proves that the query has not been altered by the variant
|
||||
$this->assertEquals(2, $query->require['_subsite'][0]);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -2,8 +2,10 @@
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\FullTextSearch\Search\FullTextSearch;
|
||||
use SilverStripe\FullTextSearch\Search\Indexes\SearchIndex_Recording;
|
||||
use SilverStripe\FullTextSearch\Search\Variants\SearchVariantVersioned;
|
||||
use SilverStripe\FullTextSearch\Tests\SearchVariantVersionedTest\SearchVariantVersionedTest_Index;
|
||||
use SilverStripe\FullTextSearch\Tests\SearchVariantVersionedTest\SearchVariantVersionedTest_Item;
|
||||
use SilverStripe\FullTextSearch\Tests\SearchVariantVersionedTest\SearchVariantVersionedTest_IndexNoStage;
|
||||
@ -79,6 +81,28 @@ class SearchVariantVersionedTest extends SapphireTest
|
||||
));
|
||||
$added = self::$index->getAdded(array('ID', '_versionedstage'));
|
||||
$this->assertEquals($expected, $added);
|
||||
|
||||
// Test unpublish
|
||||
|
||||
self::$index->reset();
|
||||
|
||||
$item->deleteFromStage('Live');
|
||||
|
||||
SearchUpdater::flush_dirty_indexes();
|
||||
|
||||
$this->assertCount(1, self::$index->deleted);
|
||||
$this->assertEquals(
|
||||
SiteTree::class,
|
||||
self::$index->deleted[0]['base']
|
||||
);
|
||||
$this->assertEquals(
|
||||
$item->ID,
|
||||
self::$index->deleted[0]['id']
|
||||
);
|
||||
$this->assertEquals(
|
||||
'Live',
|
||||
self::$index->deleted[0]['state'][SearchVariantVersioned::class]
|
||||
);
|
||||
}
|
||||
|
||||
public function testExcludeVariantState()
|
||||
|
Loading…
x
Reference in New Issue
Block a user