Skip to content

Commit

Permalink
BUG Fix sold indexing storing against the incorrect class key
Browse files Browse the repository at this point in the history
  • Loading branch information
Darren Inwood authored and Damian Mooyman committed May 22, 2014
1 parent 1785268 commit a2cfbb5
Show file tree
Hide file tree
Showing 7 changed files with 227 additions and 4 deletions.
3 changes: 3 additions & 0 deletions _config/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
DataObject:
extensions:
- 'SearchUpdater_DeleteHandler'
7 changes: 4 additions & 3 deletions code/search/SearchIndex.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ function fieldData($field, $forceType = null, $extraOptions = array()) {
$sources = $this->getClasses();

foreach ($sources as $source => $options) {
$sources[$source]['base'] = $source;
$sources[$source]['base'] = ClassInfo::baseDataClass($source);
$sources[$source]['lookup_chain'] = array();
}

Expand Down Expand Up @@ -440,10 +440,11 @@ function getDirtyIDs($class, $id, $statefulids, $fields) {
foreach ($this->classes as $searchclass => $options) {
if ($searchclass == $class || ($options['include_children'] && is_subclass_of($class, $searchclass))) {

$dirty[$searchclass] = array();
$base = ClassInfo::baseDataClass($searchclass);
$dirty[$base] = array();
foreach ($statefulids as $statefulid) {
$key = serialize($statefulid);
$dirty[$searchclass][$key] = $statefulid;
$dirty[$base][$key] = $statefulid;
}
}
}
Expand Down
27 changes: 27 additions & 0 deletions code/search/SearchUpdater.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,30 @@ public function postRequest(SS_HTTPRequest $request, SS_HTTPResponse $response,
/* NOP */
}
}

/**
* Delete operations do not use database manipulations.
*
* If a delete has been requested, force a write on objects that should be
* indexed. This causes the object to be marked for deletion from the index.
*/

class SearchUpdater_DeleteHandler extends DataExtension {

public function onAfterDelete() {
// Calling delete() on empty objects does nothing
if (!$this->owner->ID) return;

// Force SearchUpdater to mark this record as dirty
$manipulation = array(
$this->owner->ClassName => array(
'fields' => array(),
'id' => $this->owner->ID,
'command' => 'update'
)
);
$this->owner->extend('augmentWrite', $manipulation);
SearchUpdater::handle_manipulation($manipulation);
}

}
3 changes: 2 additions & 1 deletion code/solr/SolrIndex.php
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,8 @@ function add($object) {

foreach ($this->getClasses() as $searchclass => $options) {
if ($searchclass == $class || ($options['include_children'] && is_subclass_of($class, $searchclass))) {
$docs[] = $this->_addAs($object, $searchclass, $options);
$base = ClassInfo::baseDataClass($searchclass);
$docs[] = $this->_addAs($object, $base, $options);
}
}

Expand Down
20 changes: 20 additions & 0 deletions docs/en/changelogs/1.0.3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# 1.0.3

## Upgrading

Users upgrading from 1.0.2 or below will need to run the Solr_Reindex task to refresh
each SolrIndex. This is due to a change in record IDs, which are now generated from
the base class of each DataObject, rather than the instance class.

Developers working locally should be aware that by default, all indexes will be updated
in realtime when the environment is in dev mode, rather than attempting to queue these
updates with the queued jobs module (if installed).

## Bugfixes

* BUG Fix old indexing storing against the incorrect class key
* [Don't rely on MySQL ordering of index->getAdded()](https://github.com/silverstripe-labs/silverstripe-fulltextsearch/commit/4b51393e014fc4c0cc8e192c74eb4594acaca605)

## API

* [API Disable queued processing for development environments](https://github.com/silverstripe-labs/silverstripe-fulltextsearch/commit/71fc359b3711cf5b9429d86da0f1e0b20bd43dee)
12 changes: 12 additions & 0 deletions tests/SearchVariantVersionedTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,22 @@ function setUp() {

SearchUpdater::bind_manipulation_capture();

Config::nest();

Config::inst()->update('Injector', 'SearchUpdateProcessor', array(
'class' => 'SearchUpdateImmediateProcessor'
));

FullTextSearch::force_index_list(self::$index);
SearchUpdater::clear_dirty_indexes();
}

function tearDown() {
Config::unnest();

parent::tearDown();
}

function testPublishing() {
// Check that write updates Stage

Expand Down
159 changes: 159 additions & 0 deletions tests/SolrIndexVersionedTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
<?php

if (class_exists('Phockito')) Phockito::include_hamcrest();

class SolrIndexVersionedTest extends SapphireTest {

protected $oldMode = null;

protected static $index = null;

protected $extraDataObjects = array(
'SearchVariantVersionedTest_Item'
);

public function setUp() {

parent::setUp();

if (!class_exists('Phockito')) {
$this->skipTest = true;
return $this->markTestSkipped("These tests need the Phockito module installed to run");
}

// Check versioned available
if(!class_exists('Versioned')) {
$this->skipTest = true;
return $this->markTestSkipped('The versioned decorator is not installed');
}

if (self::$index === null) self::$index = singleton('SolrVersionedTest_Index');

SearchUpdater::bind_manipulation_capture();

Config::nest();

Config::inst()->update('Injector', 'SearchUpdateProcessor', array(
'class' => 'SearchUpdateImmediateProcessor'
));

FullTextSearch::force_index_list(self::$index);
SearchUpdater::clear_dirty_indexes();

$this->oldMode = Versioned::get_reading_mode();
Versioned::reading_stage('Stage');
}

public function tearDown() {
Versioned::set_reading_mode($this->oldMode);
Config::unnest();
parent::tearDown();
}

protected function getServiceMock() {
return Phockito::mock('Solr3Service');
}


public function testPublishing() {

// Setup mocks
$serviceMock = $this->getServiceMock();
self::$index->setService($serviceMock);

// Check that write updates Stage
Versioned::reading_stage('Stage');
Phockito::reset($serviceMock);
$item = new SearchVariantVersionedTest_Item(array('Title' => 'Foo'));
$item->write();
SearchUpdater::flush_dirty_indexes();
$doc = new SolrDocumentMatcher(array(
'_documentid' => $item->ID.'-SiteTree-{"SearchVariantVersioned":"Stage"}',
'ClassName' => 'SearchVariantVersionedTest_Item'
));
Phockito::verify($serviceMock)->addDocument($doc);

// Check that write updates Live
Versioned::reading_stage('Stage');
Phockito::reset($serviceMock);
$item = new SearchVariantVersionedTest_Item(array('Title' => 'Bar'));
$item->write();
$item->publish('Stage', 'Live');
SearchUpdater::flush_dirty_indexes();
$doc = new SolrDocumentMatcher(array(
'_documentid' => $item->ID.'-SiteTree-{"SearchVariantVersioned":"Live"}',
'ClassName' => 'SearchVariantVersionedTest_Item'
));
Phockito::verify($serviceMock)->addDocument($doc);
}

public function testDelete() {

// Setup mocks
$serviceMock = $this->getServiceMock();
self::$index->setService($serviceMock);

// Delete the live record (not the stage)
Versioned::reading_stage('Stage');
Phockito::reset($serviceMock);
$item = new SearchVariantVersionedTest_Item(array('Title' => 'Too'));
$item->write();
$item->publish('Stage', 'Live');
Versioned::reading_stage('Live');
$id = $item->ID;
$item->delete();
SearchUpdater::flush_dirty_indexes();
Phockito::verify($serviceMock, 1)
->deleteById($id.'-SiteTree-{"SearchVariantVersioned":"Live"}');
Phockito::verify($serviceMock, 0)
->deleteById($id.'-SiteTree-{"SearchVariantVersioned":"Stage"}');

// Delete the stage record
Versioned::reading_stage('Stage');
Phockito::reset($serviceMock);
$item = new SearchVariantVersionedTest_Item(array('Title' => 'Too'));
$item->write();
$item->publish('Stage', 'Live');
$id = $item->ID;
$item->delete();
SearchUpdater::flush_dirty_indexes();
Phockito::verify($serviceMock, 1)
->deleteById($id.'-SiteTree-{"SearchVariantVersioned":"Stage"}');
Phockito::verify($serviceMock, 0)
->deleteById($id.'-SiteTree-{"SearchVariantVersioned":"Live"}');
}
}


class SolrVersionedTest_Index extends SolrIndex {
function init() {
$this->addClass('SearchVariantVersionedTest_Item');
$this->addFilterField('TestText');
}
}


class SolrDocumentMatcher extends Hamcrest_BaseMatcher {

protected $properties;

public function __construct($properties) {
$this->properties = $properties;
}

public function describeTo(\Hamcrest_Description $description) {
$description->appendText('Apache_Solr_Document with properties '.var_export($this->properties, true));
}

public function matches($item) {

if(! ($item instanceof Apache_Solr_Document)) return false;

foreach($this->properties as $key => $value) {
if($item->{$key} != $value) return false;
}

return true;
}

}

0 comments on commit a2cfbb5

Please sign in to comment.