From 96fb0c4d71e11cbfb026ea93d6e009fe4c2c0682 Mon Sep 17 00:00:00 2001 From: Chad Horohoe Date: Tue, 18 Jun 2013 16:06:49 -0400 Subject: [PATCH] Support updating search index when page is deleted Right now, if you delete a page then you end up with a stale entry in the search index, this affects all core SQL-based searches. For extensions, this means they no longer have to implement something like ArticleDelete, they can just add the delete() method to their SearchEngine subclass. Change-Id: I9d8a9878aeebc53f453ab1cbacc03fe73fcca949 --- includes/WikiPage.php | 5 ++- includes/search/SearchEngine.php | 12 ++++++ includes/search/SearchMySQL.php | 13 ++++++ includes/search/SearchUpdate.php | 42 ++++++++++++------- .../includes/search/SearchUpdateTest.php | 12 +----- 5 files changed, 57 insertions(+), 27 deletions(-) diff --git a/includes/WikiPage.php b/includes/WikiPage.php index 5ed6c8d3b5..9e6a0c8a77 100644 --- a/includes/WikiPage.php +++ b/includes/WikiPage.php @@ -2648,7 +2648,7 @@ class WikiPage implements Page, IDBAccessObject { /** * Do some database updates after deletion * - * @param int $id page_id value of the page being deleted (B/C, currently unused) + * @param int $id page_id value of the page being deleted * @param $content Content: optional page content to be used when determining the required updates. * This may be needed because $this->getContent() may already return null when the page proper was deleted. */ @@ -2665,6 +2665,9 @@ class WikiPage implements Page, IDBAccessObject { // Reset this object and the Title object $this->loadFromRow( false, self::READ_LATEST ); + + // Search engine + DeferredUpdates::addUpdate( new SearchUpdate( $id, $this->mTitle ) ); } /** diff --git a/includes/search/SearchEngine.php b/includes/search/SearchEngine.php index a975b03741..00f7923257 100644 --- a/includes/search/SearchEngine.php +++ b/includes/search/SearchEngine.php @@ -494,6 +494,18 @@ class SearchEngine { // no-op } + /** + * Delete an indexed page + * Title should be pre-processed. + * STUB + * + * @param Integer $id Page id that was deleted + * @param String $title Title of page that was deleted + */ + function delete( $id, $title ) { + // no-op + } + /** * Get OpenSearch suggestion template * diff --git a/includes/search/SearchMySQL.php b/includes/search/SearchMySQL.php index c219b92bee..d8c638ea54 100644 --- a/includes/search/SearchMySQL.php +++ b/includes/search/SearchMySQL.php @@ -369,6 +369,19 @@ class SearchMySQL extends SearchEngine { array( $dbw->lowPriorityOption() ) ); } + /** + * Delete an indexed page + * Title should be pre-processed. + * + * @param Integer $id Page id that was deleted + * @param String $title Title of page that was deleted + */ + function delete( $id, $title ) { + $dbw = wfGetDB( DB_MASTER ); + + $dbw->delete( 'searchindex', array( 'si_page' => $id ), __METHOD__ ); + } + /** * Converts some characters for MySQL's indexing to grok it correctly, * and pads short words to overcome limitations. diff --git a/includes/search/SearchUpdate.php b/includes/search/SearchUpdate.php index 8c1bda21a8..759c7b9495 100644 --- a/includes/search/SearchUpdate.php +++ b/includes/search/SearchUpdate.php @@ -33,7 +33,7 @@ class SearchUpdate implements DeferrableUpdate { private $mId = 0, $mNamespace, $mTitle, $mText; private $mTitleWords; - function __construct( $id, $title, $text = false ) { + public function __construct( $id, $title, $text = false ) { if ( is_string( $title ) ) { $nt = Title::newFromText( $title ); } else { @@ -53,8 +53,8 @@ class SearchUpdate implements DeferrableUpdate { } } - function doUpdate() { - global $wgContLang, $wgDisableSearchUpdate; + public function doUpdate() { + global $wgDisableSearchUpdate; if ( $wgDisableSearchUpdate || !$this->mId ) { return; @@ -63,17 +63,34 @@ class SearchUpdate implements DeferrableUpdate { wfProfileIn( __METHOD__ ); $search = SearchEngine::create(); - $lc = SearchEngine::legalSearchChars() . '&#;'; + $normalTitle = $search->normalizeText( Title::indexTitle( $this->mNamespace, $this->mTitle ) ); - if ( $this->mText === false ) { - $search->updateTitle( $this->mId, - $search->normalizeText( Title::indexTitle( $this->mNamespace, $this->mTitle ) ) ); + if ( WikiPage::newFromId( $this->mId ) === null ) { + $search->delete( $this->mId, $normalTitle ); + wfProfileOut( __METHOD__ ); + return; + } elseif ( $this->mText === false ) { + $search->updateTitle( $this->mId, $normalTitle ); wfProfileOut( __METHOD__ ); return; } + $text = self::updateText( $this->mText ); + + wfRunHooks( 'SearchUpdate', array( $this->mId, $this->mNamespace, $this->mTitle, &$text ) ); + + # Perform the actual update + $search->update( $this->mId, $normalTitle, $search->normalizeText( $text ) ); + + wfProfileOut( __METHOD__ ); + } + + public static function updateText( $text ) { + global $wgContLang; + # Language-specific strip/conversion - $text = $wgContLang->normalizeForSearch( $this->mText ); + $text = $wgContLang->normalizeForSearch( $text ); + $lc = SearchEngine::legalSearchChars() . '&#;'; wfProfileIn( __METHOD__ . '-regexps' ); $text = preg_replace( "/<\\/?\\s*[A-Za-z][^>]*?>/", @@ -123,14 +140,7 @@ class SearchUpdate implements DeferrableUpdate { # Strip wiki '' and ''' $text = preg_replace( "/''[']*/", " ", $text ); wfProfileOut( __METHOD__ . '-regexps' ); - - wfRunHooks( 'SearchUpdate', array( $this->mId, $this->mNamespace, $this->mTitle, &$text ) ); - - # Perform the actual update - $search->update( $this->mId, $search->normalizeText( Title::indexTitle( $this->mNamespace, $this->mTitle ) ), - $search->normalizeText( $text ) ); - - wfProfileOut( __METHOD__ ); + return $text; } } diff --git a/tests/phpunit/includes/search/SearchUpdateTest.php b/tests/phpunit/includes/search/SearchUpdateTest.php index d19cdd031e..e947c346d4 100644 --- a/tests/phpunit/includes/search/SearchUpdateTest.php +++ b/tests/phpunit/includes/search/SearchUpdateTest.php @@ -17,6 +17,7 @@ class MockSearch extends SearchEngine { /** * @group Search + * @group Database */ class SearchUpdateTest extends MediaWikiTestCase { @@ -25,17 +26,8 @@ class SearchUpdateTest extends MediaWikiTestCase { $this->setMwGlobals( 'wgSearchType', 'MockSearch' ); } - function update( $text, $title = 'Test', $id = 1 ) { - $u = new SearchUpdate( $id, $title, $text ); - $u->doUpdate(); - - return array( MockSearch::$title, MockSearch::$text ); - } - function updateText( $text ) { - list( , $resultText ) = $this->update( $text ); - $resultText = trim( $resultText ); // abstract from some implementation details - return $resultText; + return trim( SearchUpdate::updateText( $text ) ); } function testUpdateText() { -- 2.20.1