mirror of
https://github.com/silverstripe/silverstripe-fulltextsearch
synced 2024-10-22 14:05:29 +02:00
Merge branch '3'
This commit is contained in:
commit
c0b818fc49
12
.travis.yml
12
.travis.yml
@ -8,15 +8,15 @@ env:
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- php: 5.6
|
||||
- php: 7.1
|
||||
env: DB=MYSQL INSTALLER_VERSION=4.2.x-dev PHPCS_TEST=1 PHPUNIT_TEST=1
|
||||
- php: 7.0
|
||||
- php: 7.1
|
||||
env: DB=PGSQL INSTALLER_VERSION=4.3.x-dev PHPUNIT_TEST=1
|
||||
- php: 7.1
|
||||
env: DB=MYSQL INSTALLER_VERSION=4.3.x-dev PHPUNIT_COVERAGE_TEST=1
|
||||
- php: 7.1
|
||||
env: DB=MYSQL INSTALLER_VERSION=4.4.x-dev PHPUNIT_TEST=1 SUBSITES=1
|
||||
- php: 7.2
|
||||
env: DB=MYSQL INSTALLER_VERSION=4.3.x-dev PHPUNIT_COVERAGE_TEST=1
|
||||
- php: 7.3
|
||||
env: DB=MYSQL INSTALLER_VERSION=4.4.x-dev PHPUNIT_TEST=1 SUBSITES=1
|
||||
- php: 7.3
|
||||
env: DB=MYSQL INSTALLER_VERSION=4.x-dev PHPUNIT_TEST=1
|
||||
|
||||
before_script:
|
||||
|
@ -21,6 +21,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"silverstripe/framework": "^4.0",
|
||||
"monolog/monolog": "~1.15",
|
||||
"ptcinc/solr-php-client": "^1.0",
|
||||
|
@ -147,7 +147,7 @@ to render properly in the search results:
|
||||
* `Link()` needs to return the URL to follow from the search results to actually view the object.
|
||||
* `Name` (as a DB field) will be used as the result title.
|
||||
* `Abstract` (as a DB field) will show under the search result title.
|
||||
* `getShowInSearch()` is required to get the record to show in search, since all results are filtered by `ShowInSearch`.
|
||||
* `ShowInSearch` (as a DB field) or `getShowInSearch()` is recommended to allow the optional exclusion of DataObjects from being added to the search index. If omitted, then all DataObjects of this type will be added to the search index.
|
||||
|
||||
So with that, you can add your class to your index:
|
||||
|
||||
@ -172,6 +172,28 @@ you've just created, this will add `SearchableDataObject` and the text fields it
|
||||
on the site using `MySolrSearchIndex->search()`, the `SearchableDataObject` results will show alongside normal `Page`
|
||||
results.
|
||||
|
||||
### ShowInSearch and getShowInSearch() filtering
|
||||
|
||||
The fulltextsearch module checks the value of `ShowInSearch` on each object it operates against, and if this evaluates
|
||||
to `false`, the object is excluded from the index / results. You can implement a `getShowInSearch` method on your
|
||||
DataObject to control the way this is computed. This check happens in two places:
|
||||
|
||||
a) When attempting to add the object to the search index (or update it)
|
||||
b) Before returning results from the search index. Note: this only applies to Solr 4 implementations.
|
||||
|
||||
The second check is an additional layer to ensure that a result is excluded if the evaluated response changes between
|
||||
index and query time. For example, a getShowInSearch() implementation that filters out objects after a certain date
|
||||
might return `true` when the object is added to the index, but `false` when a user later performs a search.
|
||||
|
||||
This filtering is applied to all Page (SiteTree) and File records since they have a ShowInSearch database column.
|
||||
This will also be applied to any DataObjects that have a ShowInSearch database column or a getShowInSearch() function.
|
||||
|
||||
This is a compulsory check and there is no opt-out available.
|
||||
|
||||
Note: If you implement a custom getShowInSearch() method on a Page, the database column 'ShowInSearch' will not be used
|
||||
and the 'Show In Search?' settings in the CMS admin found under Page > Settings will no longer work. Either incorporate
|
||||
the ShowInSearch column in your getShowInSearch() logic, or remove the field from the CMS to minimise confusion.
|
||||
|
||||
## Solr dev tasks
|
||||
|
||||
There are two dev/tasks that are central to the operation of the module - `Solr_Configure` and `Solr_Reindex`. You can access these through the web, or via CLI. Running via the web will return "quiet" output by default, but you can increase verbosity by adding `?verbose=1` to the `dev/tasks` URL; CLI will return verbose output by default.
|
||||
|
@ -619,19 +619,14 @@ abstract class SearchIndex extends ViewableData
|
||||
$tableName = DataObject::getSchema()->tableName($step['class']);
|
||||
|
||||
if ($step['through'] == 'has_one') {
|
||||
$sql = new SQLSelect('"ID"', '"' . $tableName . '"', '"' . $step['foreignkey'] . '" IN (' . implode(',', $ids) . ')');
|
||||
singleton($step['class'])->extend('augmentSQL', $sql);
|
||||
|
||||
$ids = $sql->execute()->column();
|
||||
$ids = DataObject::get($step['class'])
|
||||
->filter($step['foreignkey'], $ids)
|
||||
->column('ID');
|
||||
} elseif ($step['through'] == 'has_many') {
|
||||
// Use TableName for queries
|
||||
$otherTableName = DataObject::getSchema()->tableName($step['otherclass']);
|
||||
|
||||
$sql = new SQLSelect('"' . $tableName . '"."ID"', '"' . $tableName . '"', '"' . $otherTableName . '"."ID" IN (' . implode(',', $ids) . ')');
|
||||
$sql->addInnerJoin($otherTableName, '"' . $tableName . '"."ID" = "' . $otherTableName . '"."' . $step['foreignkey'] . '"');
|
||||
singleton($step['class'])->extend('augmentSQL', $sql);
|
||||
|
||||
$ids = $sql->execute()->column();
|
||||
// foreignkey identifies a has_one column on the model linked via the has_many relation
|
||||
$ids = DataObject::get($step['otherclass'])
|
||||
->filter('ID', $ids)
|
||||
->column($step['foreignkey']);
|
||||
}
|
||||
|
||||
if (empty($ids)) {
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace SilverStripe\FullTextSearch\Search\Processors;
|
||||
|
||||
use SilverStripe\FullTextSearch\Search\Services\IndexableService;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\FullTextSearch\Search\Variants\SearchVariant;
|
||||
use SilverStripe\FullTextSearch\Search\FullTextSearch;
|
||||
@ -71,6 +72,7 @@ abstract class SearchUpdateProcessor
|
||||
$dirtyIndexes = array();
|
||||
$dirty = $this->getSource();
|
||||
$indexes = FullTextSearch::get_indexes();
|
||||
$indexableService = IndexableService::singleton();
|
||||
foreach ($dirty as $base => $statefulids) {
|
||||
if (!$statefulids) {
|
||||
continue;
|
||||
@ -87,7 +89,12 @@ abstract class SearchUpdateProcessor
|
||||
foreach ($objs as $obj) {
|
||||
foreach ($ids[$obj->ID] as $index) {
|
||||
if (!$indexes[$index]->variantStateExcluded($state)) {
|
||||
$indexes[$index]->add($obj);
|
||||
// Remove any existing records from index if ShowInSearch is changed to false
|
||||
if (!$indexableService->isIndexable($obj)) {
|
||||
$indexes[$index]->delete($base, $obj->ID, $state);
|
||||
} else {
|
||||
$indexes[$index]->add($obj);
|
||||
}
|
||||
$dirtyIndexes[$index] = $indexes[$index];
|
||||
}
|
||||
}
|
||||
|
58
src/Search/Services/IndexableService.php
Normal file
58
src/Search/Services/IndexableService.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\FullTextSearch\Search\Services;
|
||||
|
||||
use SilverStripe\Core\Extensible;
|
||||
use SilverStripe\Core\Injector\Injectable;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\ORM\Tests\MySQLDatabaseTest\Data;
|
||||
|
||||
/**
|
||||
* Checks if a DataObject is publically viewable thus able to be added or retrieved from a publically searchable index
|
||||
* Caching results because these checks may be done multiple times as there a few different code paths that search
|
||||
* results might follow in real-world search implementations
|
||||
*/
|
||||
class IndexableService
|
||||
{
|
||||
|
||||
use Injectable;
|
||||
use Extensible;
|
||||
|
||||
protected $cache = [];
|
||||
|
||||
public function clearCache(): void
|
||||
{
|
||||
$this->cache = [];
|
||||
}
|
||||
|
||||
public function isIndexable(DataObject $obj): bool
|
||||
{
|
||||
// check if is a valid DataObject that has been persisted to the database
|
||||
if (is_null($obj) || !$obj->ID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$key = $this->getCacheKey($obj);
|
||||
if (isset($this->cache[$key])) {
|
||||
return $this->cache[$key];
|
||||
}
|
||||
|
||||
$value = true;
|
||||
|
||||
// This will also call $obj->getShowInSearch() if it exists
|
||||
if (isset($obj->ShowInSearch) && !$obj->ShowInSearch) {
|
||||
$value = false;
|
||||
}
|
||||
|
||||
$this->extend('updateIsIndexable', $obj, $value);
|
||||
$this->cache[$key] = $value;
|
||||
return $value;
|
||||
}
|
||||
|
||||
protected function getCacheKey(DataObject $obj): string
|
||||
{
|
||||
$key = $obj->ClassName . '_' . $obj->ID;
|
||||
$this->extend('updateCacheKey', $obj, $key);
|
||||
return $key;
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ use SilverStripe\Forms\FormAction;
|
||||
use SilverStripe\Forms\TextField;
|
||||
use SilverStripe\FullTextSearch\Search\FullTextSearch;
|
||||
use SilverStripe\FullTextSearch\Search\Queries\SearchQuery;
|
||||
use SilverStripe\FullTextSearch\Search\Services\IndexableService;
|
||||
use SilverStripe\FullTextSearch\Solr\SolrIndex;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\View\ArrayData;
|
||||
@ -82,11 +83,13 @@ class SearchForm extends Form
|
||||
$index = $indexClass::singleton();
|
||||
$results = $index->search($query, -1, -1, $params);
|
||||
|
||||
$indexableService = IndexableService::singleton();
|
||||
|
||||
// filter by permission
|
||||
if ($results) {
|
||||
foreach ($results->Matches as $match) {
|
||||
/** @var DataObject $match */
|
||||
if (!$match->canView()) {
|
||||
if (!$indexableService->isIndexable($match)) {
|
||||
$results->Matches->remove($match);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ namespace SilverStripe\FullTextSearch\Solr\Reindex\Handlers;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use SilverStripe\Core\Environment;
|
||||
use SilverStripe\FullTextSearch\Search\Services\IndexableService;
|
||||
use SilverStripe\FullTextSearch\Solr\Solr;
|
||||
use SilverStripe\FullTextSearch\Solr\SolrIndex;
|
||||
use SilverStripe\FullTextSearch\Search\Variants\SearchVariant;
|
||||
@ -220,6 +221,7 @@ abstract class SolrReindexBase implements SolrReindexHandler
|
||||
{
|
||||
// Generate filtered list of local records
|
||||
$baseClass = DataObject::getSchema()->baseDataClass($class);
|
||||
/** @var DataList $items */
|
||||
$items = DataList::create($class)
|
||||
->where(sprintf(
|
||||
'"%s"."ID" %% \'%d\' = \'%d\'',
|
||||
@ -236,6 +238,20 @@ abstract class SolrReindexBase implements SolrReindexHandler
|
||||
$items = $items->filter('ClassName', $class);
|
||||
}
|
||||
|
||||
$indexableService = IndexableService::singleton();
|
||||
|
||||
// ShowInSearch filter
|
||||
// we cannot use $items->remove($item), as that deletes the record from the database
|
||||
$idsToRemove = [];
|
||||
foreach ($items as $item) {
|
||||
if (!$indexableService->isIndexable($item)) {
|
||||
$idsToRemove[] = $item->ID;
|
||||
}
|
||||
}
|
||||
if (!empty($idsToRemove)) {
|
||||
sort($idsToRemove);
|
||||
$items = $items->exclude(['ID' => $idsToRemove]);
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ use SilverStripe\FullTextSearch\Search\Indexes\SearchIndex;
|
||||
use SilverStripe\FullTextSearch\Search\Queries\SearchQuery;
|
||||
use SilverStripe\FullTextSearch\Search\Queries\SearchQuery_Range;
|
||||
use SilverStripe\FullTextSearch\Search\SearchIntrospection;
|
||||
use SilverStripe\FullTextSearch\Search\Services\IndexableService;
|
||||
use SilverStripe\FullTextSearch\Search\Variants\SearchVariant;
|
||||
use SilverStripe\FullTextSearch\Search\Variants\SearchVariant_Caller;
|
||||
use SilverStripe\FullTextSearch\Solr\Services\SolrService;
|
||||
@ -777,11 +778,18 @@ abstract class SolrIndex extends SearchIndex
|
||||
\Apache_Solr_Service::METHOD_POST
|
||||
);
|
||||
|
||||
$indexableService = IndexableService::singleton();
|
||||
|
||||
$results = new ArrayList();
|
||||
if ($res->getHttpStatus() >= 200 && $res->getHttpStatus() < 300) {
|
||||
foreach ($res->response->docs as $doc) {
|
||||
$result = DataObject::get_by_id($doc->ClassName, $doc->ID);
|
||||
if ($result) {
|
||||
// Filter out any results previously added to the solr index where ShowInSearch == false
|
||||
if (!$indexableService->isIndexable($result)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$results->push($result);
|
||||
|
||||
// Add highlighting (optional)
|
||||
|
55
tests/IndexableServiceTest.php
Normal file
55
tests/IndexableServiceTest.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\FullTextSearch\Tests;
|
||||
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\FullTextSearch\Search\Services\IndexableService;
|
||||
|
||||
class IndexableServiceTest extends SapphireTest
|
||||
{
|
||||
protected $usesDatabase = true;
|
||||
|
||||
public function setup()
|
||||
{
|
||||
parent::setup();
|
||||
IndexableService::singleton()->clearCache();
|
||||
}
|
||||
|
||||
public function testIsIndexable()
|
||||
{
|
||||
$indexableService = IndexableService::singleton();
|
||||
|
||||
$page = SiteTree::create();
|
||||
$page->CanViewType = 'Anyone';
|
||||
$page->ShowInSearch = 1;
|
||||
$page->write();
|
||||
$this->assertTrue($indexableService->isIndexable($page));
|
||||
|
||||
$page = SiteTree::create();
|
||||
$page->CanViewType = 'Anyone';
|
||||
$page->ShowInSearch = 0;
|
||||
$page->write();
|
||||
$this->assertFalse($indexableService->isIndexable($page));
|
||||
}
|
||||
|
||||
public function testClearCache()
|
||||
{
|
||||
$indexableService = IndexableService::singleton();
|
||||
|
||||
$page = SiteTree::create();
|
||||
$page->CanViewType = 'Anyone';
|
||||
$page->ShowInSearch = 0;
|
||||
$page->write();
|
||||
$this->assertFalse($indexableService->isIndexable($page));
|
||||
|
||||
// test the results are cached (expect stale result)
|
||||
$page->ShowInSearch = 1;
|
||||
$page->write();
|
||||
$this->assertFalse($indexableService->isIndexable($page));
|
||||
|
||||
// after clearing cache, expect fresh result
|
||||
$indexableService->clearCache();
|
||||
$this->assertTrue($indexableService->isIndexable($page));
|
||||
}
|
||||
}
|
@ -2,15 +2,24 @@
|
||||
|
||||
namespace SilverStripe\FullTextSearch\Tests;
|
||||
|
||||
use Apache_Solr_Document;
|
||||
use Page;
|
||||
use SebastianBergmann\Version;
|
||||
use SilverStripe\Assets\File;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Core\Environment;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Core\Kernel;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\FullTextSearch\Search\FullTextSearch;
|
||||
use SilverStripe\FullTextSearch\Search\Queries\SearchQuery;
|
||||
use SilverStripe\FullTextSearch\Search\Services\IndexableService;
|
||||
use SilverStripe\FullTextSearch\Search\Updaters\SearchUpdater;
|
||||
use SilverStripe\FullTextSearch\Search\Variants\SearchVariantSubsites;
|
||||
use SilverStripe\FullTextSearch\Solr\SolrIndex;
|
||||
use SilverStripe\FullTextSearch\Solr\Services\Solr3Service;
|
||||
use SilverStripe\FullTextSearch\Solr\Services\Solr4Service;
|
||||
use SilverStripe\FullTextSearch\Tests\SearchUpdaterTest\SearchUpdaterTest_Container;
|
||||
use SilverStripe\FullTextSearch\Tests\SearchUpdaterTest\SearchUpdaterTest_HasOne;
|
||||
use SilverStripe\FullTextSearch\Tests\SearchUpdaterTest\SearchUpdaterTest_HasMany;
|
||||
@ -21,10 +30,25 @@ use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_AmbiguousRelat
|
||||
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_BoostedIndex;
|
||||
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_FakeIndex;
|
||||
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_FakeIndex2;
|
||||
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_ShowInSearchIndex;
|
||||
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_MyPage;
|
||||
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_MyDataObjectOne;
|
||||
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_MyDataObjectTwo;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
use SilverStripe\Versioned\Versioned;
|
||||
|
||||
class SolrIndexTest extends SapphireTest
|
||||
{
|
||||
|
||||
protected $usesDatabase = true;
|
||||
|
||||
protected static $extra_dataobjects = [
|
||||
SolrIndexTest_MyPage::class,
|
||||
SolrIndexTest_MyDataObjectOne::class,
|
||||
SolrIndexTest_MyDataObjectTwo::class,
|
||||
];
|
||||
|
||||
public function testFieldDataHasOne()
|
||||
{
|
||||
$index = new SolrIndexTest_FakeIndex();
|
||||
@ -376,6 +400,125 @@ class SolrIndexTest extends SapphireTest
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that ShowInSearch and getShowInSearch() exclude DataObjects from being added to the index
|
||||
*
|
||||
* Note: this code path that really being tested here is SearchUpdateProcessor->prepareIndexes()
|
||||
* This code path is used for 'inlet' filtering on CMS->save()
|
||||
* The results of this will show-up in SolrIndex->_addAs()
|
||||
*/
|
||||
public function testShowInSearch()
|
||||
{
|
||||
$defaultMode = Versioned::get_reading_mode();
|
||||
Versioned::set_reading_mode('Stage.' . Versioned::DRAFT);
|
||||
|
||||
$serviceMock = $this->getMockBuilder(Solr4Service::class)
|
||||
->setMethods(['addDocument', 'deleteById'])
|
||||
->getMock();
|
||||
|
||||
$index = new SolrIndexTest_ShowInSearchIndex();
|
||||
$index->setService($serviceMock);
|
||||
FullTextSearch::force_index_list($index);
|
||||
|
||||
// will get added
|
||||
$pageA = new Page();
|
||||
$pageA->Title = 'Test Page true';
|
||||
$pageA->ShowInSearch = true;
|
||||
$pageA->write();
|
||||
|
||||
// will get filtered out
|
||||
$page = new Page();
|
||||
$page->Title = 'Test Page false';
|
||||
$page->ShowInSearch = false;
|
||||
$page->write();
|
||||
|
||||
// will get added
|
||||
$fileA = new File();
|
||||
$fileA->Title = 'Test File true';
|
||||
$fileA->ShowInSearch = true;
|
||||
$fileA->write();
|
||||
|
||||
// will get filtered out
|
||||
$file = new File();
|
||||
$file->Title = 'Test File false';
|
||||
$file->ShowInSearch = false;
|
||||
$file->write();
|
||||
|
||||
// will get added
|
||||
$objOneA = new SolrIndexTest_MyDataObjectOne();
|
||||
$objOneA->Title = 'Test MyDataObjectOne true';
|
||||
$objOneA->ShowInSearch = true;
|
||||
$objOneA->write();
|
||||
|
||||
// will get filtered out
|
||||
$objOne = new SolrIndexTest_MyDataObjectOne();
|
||||
$objOne->Title = 'Test MyDataObjectOne false';
|
||||
$objOne->ShowInSearch = false;
|
||||
$objOne->write();
|
||||
|
||||
// will get added
|
||||
// this class has a getShowInSearch() == true, which will override $mypage->ShowInSearch = false
|
||||
$objTwoA = new SolrIndexTest_MyDataObjectTwo();
|
||||
$objTwoA->Title = 'Test MyDataObjectTwo false';
|
||||
$objTwoA->ShowInSearch = false;
|
||||
$objTwoA->write();
|
||||
|
||||
// will get added
|
||||
// this class has a getShowInSearch() == true, which will override $mypage->ShowInSearch = false
|
||||
$myPageA = new SolrIndexTest_MyPage();
|
||||
$myPageA->Title = 'Test MyPage false';
|
||||
$myPageA->ShowInSearch = false;
|
||||
$myPageA->write();
|
||||
|
||||
$callback = function (Apache_Solr_Document $doc) use ($pageA, $myPageA, $fileA, $objOneA, $objTwoA): bool {
|
||||
$validKeys = [
|
||||
Page::class . $pageA->ID,
|
||||
SolrIndexTest_MyPage::class . $myPageA->ID,
|
||||
File::class . $fileA->ID,
|
||||
SolrIndexTest_MyDataObjectOne::class . $objOneA->ID,
|
||||
SolrIndexTest_MyDataObjectTwo::class . $objTwoA->ID
|
||||
];
|
||||
return in_array($this->createSolrDocKey($doc), $validKeys);
|
||||
};
|
||||
|
||||
$serviceMock
|
||||
->expects($this->exactly(5))
|
||||
->method('addDocument')
|
||||
->withConsecutive(
|
||||
[$this->callback($callback)],
|
||||
[$this->callback($callback)],
|
||||
[$this->callback($callback)],
|
||||
[$this->callback($callback)],
|
||||
[$this->callback($callback)]
|
||||
);
|
||||
|
||||
// This is what actually triggers all the solr stuff
|
||||
SearchUpdater::flush_dirty_indexes();
|
||||
|
||||
// delete a solr doc by setting ShowInSearch to false
|
||||
$pageA->ShowInSearch = false;
|
||||
$pageA->write();
|
||||
|
||||
$serviceMock
|
||||
->expects($this->exactly(1))
|
||||
->method('deleteById')
|
||||
->withConsecutive(
|
||||
[$this->callback(function (string $docID) use ($pageA): bool {
|
||||
return strpos($docID, $pageA->ID . '-' . SiteTree::class) !== false;
|
||||
})]
|
||||
);
|
||||
|
||||
IndexableService::singleton()->clearCache();
|
||||
SearchUpdater::flush_dirty_indexes();
|
||||
|
||||
Versioned::set_reading_mode($defaultMode);
|
||||
}
|
||||
|
||||
protected function createSolrDocKey(Apache_Solr_Document $doc)
|
||||
{
|
||||
return $doc->getField('ClassName')['value'] . $doc->getField('ID')['value'];
|
||||
}
|
||||
|
||||
protected function getFakeRawSolrResponse()
|
||||
{
|
||||
return new \Apache_Solr_Response(
|
||||
|
16
tests/SolrIndexTest/SolrIndexTest_MyDataObjectOne.php
Normal file
16
tests/SolrIndexTest/SolrIndexTest_MyDataObjectOne.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\FullTextSearch\Tests\SolrIndexTest;
|
||||
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
|
||||
class SolrIndexTest_MyDataObjectOne extends DataObject implements TestOnly
|
||||
{
|
||||
private static $db = [
|
||||
'Title' => 'Varchar(255)',
|
||||
'ShowInSearch' => 'Boolean'
|
||||
];
|
||||
|
||||
private static $table_name = 'SolrIndexTestMyDataObjectOne';
|
||||
}
|
16
tests/SolrIndexTest/SolrIndexTest_MyDataObjectTwo.php
Normal file
16
tests/SolrIndexTest/SolrIndexTest_MyDataObjectTwo.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\FullTextSearch\Tests\SolrIndexTest;
|
||||
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
|
||||
class SolrIndexTest_MyDataObjectTwo extends SolrIndexTest_MyDataObjectOne implements TestOnly
|
||||
{
|
||||
private static $table_name = 'SolrIndexTestMyDataObjectTwo';
|
||||
|
||||
public function getShowInSearch()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
14
tests/SolrIndexTest/SolrIndexTest_MyPage.php
Normal file
14
tests/SolrIndexTest/SolrIndexTest_MyPage.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\FullTextSearch\Tests\SolrIndexTest;
|
||||
|
||||
use Page;
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
|
||||
class SolrIndexTest_MyPage extends Page implements TestOnly
|
||||
{
|
||||
public function getShowInSearch()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
20
tests/SolrIndexTest/SolrIndexTest_ShowInSearchIndex.php
Normal file
20
tests/SolrIndexTest/SolrIndexTest_ShowInSearchIndex.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\FullTextSearch\Tests\SolrIndexTest;
|
||||
|
||||
use SilverStripe\Assets\File;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
use SilverStripe\FullTextSearch\Solr\SolrIndex;
|
||||
|
||||
class SolrIndexTest_ShowInSearchIndex extends SolrIndex implements TestOnly
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
// adding a class here includes will include all subclasses, e.g. SolrIndexTest_MyDataObjectTwo
|
||||
$this->addClass(SolrIndexTest_MyDataObjectOne::class);
|
||||
$this->addClass(SiteTree::class);
|
||||
$this->addClass(File::class);
|
||||
$this->addFilterField('ShowInSearch');
|
||||
}
|
||||
}
|
@ -2,27 +2,43 @@
|
||||
|
||||
namespace SilverStripe\FullTextSearch\Tests;
|
||||
|
||||
use Apache_Solr_Document;
|
||||
use Page;
|
||||
use SilverStripe\Assets\File;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\FullTextSearch\Search\FullTextSearch;
|
||||
use SilverStripe\FullTextSearch\Search\Updaters\SearchUpdater;
|
||||
use SilverStripe\FullTextSearch\Search\Variants\SearchVariant;
|
||||
use SilverStripe\FullTextSearch\Search\Variants\SearchVariantVersioned;
|
||||
use SilverStripe\FullTextSearch\Solr\Reindex\Handlers\SolrReindexHandler;
|
||||
use SilverStripe\FullTextSearch\Solr\Reindex\Handlers\SolrReindexImmediateHandler;
|
||||
use SilverStripe\FullTextSearch\Solr\Services\Solr4Service;
|
||||
use SilverStripe\FullTextSearch\Solr\Services\SolrService;
|
||||
use SilverStripe\FullTextSearch\Solr\Tasks\Solr_Reindex;
|
||||
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_MyDataObjectOne;
|
||||
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_MyDataObjectTwo;
|
||||
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_MyPage;
|
||||
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_ShowInSearchIndex;
|
||||
use SilverStripe\FullTextSearch\Tests\SolrReindexTest\SolrReindexTest_Index;
|
||||
use SilverStripe\FullTextSearch\Tests\SolrReindexTest\SolrReindexTest_Item;
|
||||
use SilverStripe\FullTextSearch\Tests\SolrReindexTest\SolrReindexTest_RecordingLogger;
|
||||
use SilverStripe\FullTextSearch\Tests\SolrReindexTest\SolrReindexTest_TestHandler;
|
||||
use SilverStripe\FullTextSearch\Tests\SolrReindexTest\SolrReindexTest_Variant;
|
||||
use SilverStripe\Versioned\Versioned;
|
||||
|
||||
class SolrReindexTest extends SapphireTest
|
||||
{
|
||||
|
||||
protected $usesDatabase = true;
|
||||
|
||||
protected static $extra_dataobjects = array(
|
||||
SolrReindexTest_Item::class
|
||||
SolrReindexTest_Item::class,
|
||||
SolrIndexTest_MyPage::class,
|
||||
SolrIndexTest_MyDataObjectOne::class,
|
||||
SolrIndexTest_MyDataObjectTwo::class,
|
||||
);
|
||||
|
||||
/**
|
||||
@ -153,7 +169,6 @@ class SolrReindexTest extends SapphireTest
|
||||
$this->assertEquals(240, SolrReindexTest_Item::get()->count());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Given the invocation of a new re-index with a given set of data, ensure that the necessary
|
||||
* list of groups are created and segmented for each state
|
||||
@ -284,4 +299,107 @@ class SolrReindexTest extends SapphireTest
|
||||
// Check ids
|
||||
$this->assertEquals(120, count($ids));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that ShowInSearch filtering is working correctly
|
||||
*/
|
||||
public function testShowInSearch()
|
||||
{
|
||||
$defaultMode = Versioned::get_reading_mode();
|
||||
Versioned::set_reading_mode('Stage.' . Versioned::DRAFT);
|
||||
|
||||
// will get added
|
||||
$pageA = new Page();
|
||||
$pageA->Title = 'Test Page true';
|
||||
$pageA->ShowInSearch = true;
|
||||
$pageA->write();
|
||||
|
||||
// will get filtered out
|
||||
$page = new Page();
|
||||
$page->Title = 'Test Page false';
|
||||
$page->ShowInSearch = false;
|
||||
$page->write();
|
||||
|
||||
// will get added
|
||||
$fileA = new File();
|
||||
$fileA->Title = 'Test File true';
|
||||
$fileA->ShowInSearch = true;
|
||||
$fileA->write();
|
||||
|
||||
// will get filtered out
|
||||
$file = new File();
|
||||
$file->Title = 'Test File false';
|
||||
$file->ShowInSearch = false;
|
||||
$file->write();
|
||||
|
||||
// will get added
|
||||
$objOneA = new SolrIndexTest_MyDataObjectOne();
|
||||
$objOneA->Title = 'Test MyDataObjectOne true';
|
||||
$objOneA->ShowInSearch = true;
|
||||
$objOneA->write();
|
||||
|
||||
// will get filtered out
|
||||
$objOne = new SolrIndexTest_MyDataObjectOne();
|
||||
$objOne->Title = 'Test MyDataObjectOne false';
|
||||
$objOne->ShowInSearch = false;
|
||||
$objOne->write();
|
||||
|
||||
// will get added
|
||||
// this class has a getShowInSearch() == true, which will override $mypage->ShowInSearch = false
|
||||
$objTwoA = new SolrIndexTest_MyDataObjectTwo();
|
||||
$objTwoA->Title = 'Test MyDataObjectTwo false';
|
||||
$objTwoA->ShowInSearch = false;
|
||||
$objTwoA->write();
|
||||
|
||||
// will get added
|
||||
// this class has a getShowInSearch() == true, which will override $mypage->ShowInSearch = false
|
||||
$myPageA = new SolrIndexTest_MyPage();
|
||||
$myPageA->Title = 'Test MyPage false';
|
||||
$myPageA->ShowInSearch = false;
|
||||
$myPageA->write();
|
||||
|
||||
$serviceMock = $this->getMockBuilder(Solr4Service::class)
|
||||
->setMethods(['addDocument', 'deleteByQuery'])
|
||||
->getMock();
|
||||
|
||||
$index = new SolrIndexTest_ShowInSearchIndex();
|
||||
$index->setService($serviceMock);
|
||||
FullTextSearch::force_index_list($index);
|
||||
|
||||
$callback = function (Apache_Solr_Document $doc) use ($pageA, $myPageA, $fileA, $objOneA, $objTwoA): bool {
|
||||
$validKeys = [
|
||||
Page::class . $pageA->ID,
|
||||
SolrIndexTest_MyPage::class . $myPageA->ID,
|
||||
File::class . $fileA->ID,
|
||||
SolrIndexTest_MyDataObjectOne::class . $objOneA->ID,
|
||||
SolrIndexTest_MyDataObjectTwo::class . $objTwoA->ID
|
||||
];
|
||||
return in_array($this->createSolrDocKey($doc), $validKeys);
|
||||
};
|
||||
|
||||
$serviceMock
|
||||
->expects($this->exactly(5))
|
||||
->method('addDocument')
|
||||
->withConsecutive(
|
||||
[$this->callback($callback)],
|
||||
[$this->callback($callback)],
|
||||
[$this->callback($callback)],
|
||||
[$this->callback($callback)],
|
||||
[$this->callback($callback)]
|
||||
);
|
||||
|
||||
$logger = new SolrReindexTest_RecordingLogger();
|
||||
$state = [SearchVariantVersioned::class => Versioned::DRAFT];
|
||||
$handler = Injector::inst()->get(SolrReindexImmediateHandler::class);
|
||||
$handler->runGroup($logger, $index, $state, SiteTree::class, 1, 0);
|
||||
$handler->runGroup($logger, $index, $state, File::class, 1, 0);
|
||||
$handler->runGroup($logger, $index, $state, SolrIndexTest_MyDataObjectOne::class, 1, 0);
|
||||
|
||||
Versioned::set_reading_mode($defaultMode);
|
||||
}
|
||||
|
||||
protected function createSolrDocKey(Apache_Solr_Document $doc)
|
||||
{
|
||||
return $doc->getField('ClassName')['value'] . $doc->getField('ID')['value'];
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user