diff --git a/code/search/SearchIndex.php b/code/search/SearchIndex.php index 81f5993..d62f84a 100644 --- a/code/search/SearchIndex.php +++ b/code/search/SearchIndex.php @@ -169,6 +169,8 @@ abstract class SearchIndex extends ViewableData { protected $sortFields = array(); + protected $excludedVariantStates = array(); + /** * Add a DataObject subclass whose instances should be included in this index * @@ -258,6 +260,23 @@ abstract class SearchIndex extends ViewableData { return new MultipleArrayIterator($this->fulltextFields, $this->filterFields, $this->sortFields); } + public function excludeVariantState($state) { + $this->excludedVariantStates[] = $state; + } + + /** Returns true if some variant state should be ignored */ + public function variantStateExcluded($state) { + foreach ($this->excludedVariantStates as $excludedstate) { + $matches = true; + + foreach ($excludedstate as $variant => $variantstate) { + if (!isset($state[$variant]) || $state[$variant] != $variantstate) { $matches = false; break; } + } + + if ($matches) return true; + } + } + public $dependancyList = array(); function buildDependancyList() { diff --git a/code/search/SearchUpdater.php b/code/search/SearchUpdater.php index c9de1f4..c3ff67d 100644 --- a/code/search/SearchUpdater.php +++ b/code/search/SearchUpdater.php @@ -250,12 +250,22 @@ class SearchUpdater extends Object { $objs = DataObject::get($base, '"'.$base.'"."ID" IN ('.implode(',', array_keys($ids)).')'); if ($objs) foreach ($objs as $obj) { - foreach ($ids[$obj->ID] as $index) { $indexes[$index]->add($obj); $dirtyindexes[$index] = $index; } + foreach ($ids[$obj->ID] as $index) { + if (!$indexes[$index]->variantStateExcluded($state)) { + $indexes[$index]->add($obj); + $dirtyindexes[$index] = $index; + } + } unset($ids[$obj->ID]); } foreach ($ids as $id => $fromindexes) { - foreach ($fromindexes as $index) { $indexes[$index]->delete($base, $id, $state); $dirtyindexes[$index] = $index; } + foreach ($fromindexes as $index) { + if (!$indexes[$index]->variantStateExcluded($state)) { + $indexes[$index]->delete($base, $id, $state); + $dirtyindexes[$index] = $index; + } + } } } } diff --git a/code/solr/Solr.php b/code/solr/Solr.php index d52b10b..582032a 100644 --- a/code/solr/Solr.php +++ b/code/solr/Solr.php @@ -183,6 +183,8 @@ class Solr_Reindex extends BuildTask { $includeSubclasses = $options['include_children']; foreach (SearchVariant::reindex_states($class, $includeSubclasses) as $state) { + if ($instance->variantStateExcluded($state)) continue; + SearchVariant::activate_state($state); $filter = $includeSubclasses ? "" : '"ClassName" = \''.$class."'"; diff --git a/docs/README.md b/docs/README.md index ff51261..e45ef10 100644 --- a/docs/README.md +++ b/docs/README.md @@ -88,3 +88,24 @@ See Solr.md ### Sphinx Not written yet + +## FAQ + +### How do I exclude draft pages from the index? + +By default, the `SearchUpdater` class indexes all available "variant states", +so in the case of the `Versioned` extension, both "draft" and "live". +For most cases, you'll want to exclude draft content from your search results. + +You can either prevent the draft content from being indexed in the first place, +by adding the following to your `SearchIndex->init()` method: + + $this->excludeVariantState(array('SearchVariantVersioned' => 'Stage')); + +Alternatively, you can index draft content, but simply exclude it from searches. +This can be handy to preview search results on unpublished content, in case a CMS author is logged in. +Before constructing your `SearchQuery`, conditionally switch to the "live" stage: + + if(!Permission::check('CMS_ACCESS_CMSMain')) Versioned::reading_stage('Live'); + $query = new SearchQuery(); + // ... \ No newline at end of file diff --git a/tests/SearchVariantVersionedTest.php b/tests/SearchVariantVersionedTest.php index 3d519f3..0d59f60 100644 --- a/tests/SearchVariantVersionedTest.php +++ b/tests/SearchVariantVersionedTest.php @@ -14,6 +14,14 @@ class SearchVariantVersionedTest_Index extends SearchIndex_Recording { } } +class SearchVariantVersionedTest_IndexNoStage extends SearchIndex_Recording { + function init() { + $this->addClass('SearchVariantVersionedTest_Item'); + $this->addFilterField('TestText'); + $this->excludeVariantState(array('SearchVariantVersioned' => 'Stage')); + } +} + class SearchVariantVersionedTest extends SapphireTest { private static $index = null; @@ -68,4 +76,23 @@ class SearchVariantVersionedTest extends SapphireTest { array('ID' => $item->ID, '_versionedstage' => 'Stage') )); } + + function testExcludeVariantState() { + $index = singleton('SearchVariantVersionedTest_IndexNoStage'); + FullTextSearch::force_index_list($index); + + // Check that write doesn't update stage + $item = new SearchVariantVersionedTest_Item(array('TestText' => 'Foo')); + $item->write(); + SearchUpdater::flush_dirty_indexes(); + $this->assertEquals($index->getAdded(array('ID', '_versionedstage')), array()); + + // Check that publish updates Live + $index->reset(); + $item->publish("Stage", "Live"); + SearchUpdater::flush_dirty_indexes(); + $this->assertEquals($index->getAdded(array('ID', '_versionedstage')), array( + array('ID' => $item->ID, '_versionedstage' => 'Live') + )); + } }