jenkins-bot has submitted this change and it was merged. Change subject: Auto-forward to search suggestion when zero results ......................................................................
Auto-forward to search suggestion when zero results If the user gets zero results, but gets a "Did you mean" result, just run the query for the "Did you mean" result and inform the user that this happened. Adds a new query param 'runsuggestion' which will, when given a falsy value, prevent running the suggestion and give the result to the original query. Bug: T105202 Change-Id: I7ed79942c242b1957d46bdcad59985f37466fb83 (cherry picked from commit bbfc872871ad2c418aa28229a4b351d18130474d) --- M includes/DefaultSettings.php M includes/specials/SpecialSearch.php M languages/i18n/en.json M languages/i18n/qqq.json M tests/phpunit/includes/specials/SpecialSearchTest.php 5 files changed, 224 insertions(+), 31 deletions(-) Approvals: Catrope: Looks good to me, approved jenkins-bot: Verified diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 6d945af..b7ea8cb 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -7713,6 +7713,16 @@ ); /** + * Controls the percentage of zero-result search queries with suggestions that + * run the suggestion automatically. Must be a number between 0 and 1. This + * can be lowered to reduce query volume at the expense of result quality. + * + * @var float + * @since 1.26 + */ +$wgSearchRunSuggestedQueryPercent = 1; + +/** * For really cool vim folding this needs to be at the end: * vim: foldmarker=@{,@} foldmethod=marker * @} diff --git a/includes/specials/SpecialSearch.php b/includes/specials/SpecialSearch.php index a8fab92..f7faa2b 100644 --- a/includes/specials/SpecialSearch.php +++ b/includes/specials/SpecialSearch.php @@ -68,6 +68,11 @@ */ protected $fulltext; + /** + * @var bool + */ + protected $runSuggestion = true; + const NAMESPACES_CURRENT = 'sense'; public function __construct() { @@ -169,6 +174,7 @@ } $this->fulltext = $request->getVal( 'fulltext' ); + $this->runSuggestion = (bool)$request->getVal( 'runsuggestion', true ); $this->profile = $profile; } @@ -214,7 +220,6 @@ $search->setNamespaces( $this->namespaces ); $search->prefix = $this->mPrefix; $term = $search->transformSearchTerm( $term ); - $didYouMeanHtml = ''; Hooks::run( 'SpecialSearchSetupEngine', array( $this, $this->profile, $search ) ); @@ -265,37 +270,17 @@ } // did you mean... suggestions - if ( $showSuggestion && $textMatches && !$textStatus && $textMatches->hasSuggestion() ) { - # mirror Go/Search behavior of original request .. - $didYouMeanParams = array( 'search' => $textMatches->getSuggestionQuery() ); - - if ( $this->fulltext != null ) { - $didYouMeanParams['fulltext'] = $this->fulltext; + $didYouMeanHtml = ''; + if ( $showSuggestion && $textMatches && !$textStatus ) { + if ( $this->shouldRunSuggestedQuery( $textMatches ) ) { + $newMatches = $search->searchText( $textMatches->getSuggestionQuery() ); + if ( $newMatches instanceof SearchResultSet && $newMatches->numRows() > 0 ) { + $didYouMeanHtml = $this->getDidYouMeanRewrittenHtml( $term, $textMatches ); + $textMatches = $newMatches; + } + } elseif ( $textMatches->hasSuggestion() ) { + $didYouMeanHtml = $this->getDidYouMeanHtml( $textMatches ); } - - $stParams = array_merge( - $didYouMeanParams, - $this->powerSearchOptions() - ); - - $suggestionSnippet = $textMatches->getSuggestionSnippet(); - - if ( $suggestionSnippet == '' ) { - $suggestionSnippet = null; - } - - $suggestLink = Linker::linkKnown( - $this->getPageTitle(), - $suggestionSnippet, - array(), - $stParams - ); - - # html of did you mean... search suggestion link - $didYouMeanHtml = - Xml::openElement( 'div', array( 'class' => 'searchdidyoumean' ) ) . - $this->msg( 'search-suggest' )->rawParams( $suggestLink )->text() . - Xml::closeElement( 'div' ); } if ( !Hooks::run( 'SpecialSearchResultsPrepend', array( $this, $out, $term ) ) ) { @@ -417,6 +402,99 @@ } /** + * Decide if the suggested query should be run, and it's results returned + * instead of the provided $textMatches + * + * @param SearchResultSet $textMatches The results of a users query + * @return bool + */ + protected function shouldRunSuggestedQuery( SearchResultSet $textMatches ) { + global $wgSearchRunSuggestedQueryPercent; + + if ( !$this->runSuggestion || + !$textMatches->hasSuggestion() || + $textMatches->numRows() > 0 || + $textMatches->searchContainedSyntax() + ) { + return false; + } + + // Generate a random number between 0 and 1. If the + // number is less than the desired percentages run it. + $rand = rand( 0, getrandmax() ) / getrandmax(); + return $wgSearchRunSuggestedQueryPercent > $rand; + } + + /** + * Generates HTML shown to the user when we have a suggestion about a query + * that might give more results than their current query. + */ + protected function getDidYouMeanHtml( SearchResultSet $textMatches ) { + # mirror Go/Search behavior of original request .. + $params = array( 'search' => $textMatches->getSuggestionQuery() ); + if ( $this->fulltext != null ) { + $params['fulltext'] = $this->fulltext; + } + $stParams = array_merge( $params, $this->powerSearchOptions() ); + + $suggest = Linker::linkKnown( + $this->getPageTitle(), + $textMatches->getSuggestionSnippet() ?: null, + array(), + $stParams + ); + + # html of did you mean... search suggestion link + return Html::rawElement( + 'div', + array( 'class' => 'searchdidyoumean' ), + $this->msg( 'search-suggest' )->rawParams( $suggest )->escaped() + ); + } + + /** + * Generates HTML shown to user when their query has been internally rewritten, + * and the results of the rewritten query are being returned. + * + * @param string $term The users search input + * @param SearchResultSet $textMatches The response to the users initial search request + * @return string HTML linking the user to their original $term query, and the one + * suggested by $textMatches. + */ + protected function getDidYouMeanRewrittenHtml( $term, SearchResultSet $textMatches ) { + // Showing results for '$rewritten' + // Search instead for '$orig' + + $params = array( 'search' => $textMatches->getSuggestionQuery() ); + if ( $this->fulltext != null ) { + $params['fulltext'] = $this->fulltext; + } + $stParams = array_merge( $params, $this->powerSearchOptions() ); + + $rewritten = Linker::linkKnown( + $this->getPageTitle(), + $textMatches->getSuggestionSnippet() ?: null, + array(), + $stParams + ); + + $stParams['search'] = $term; + $stParams['runsuggestion'] = 0; + $original = Linker::linkKnown( + $this->getPageTitle(), + htmlspecialchars( $term ), + array(), + $stParams + ); + + return Html::rawElement( + 'div', + array( 'class' => 'searchdidyoumean' ), + $this->msg( 'search-rewritten')->rawParams( $rewritten, $original )->escaped() + ); + } + + /** * @param Title $title * @param int $num The number of search results found * @param null|SearchResultSet $titleMatches Results from title search diff --git a/languages/i18n/en.json b/languages/i18n/en.json index 1fdd75d..76b34cb 100644 --- a/languages/i18n/en.json +++ b/languages/i18n/en.json @@ -914,6 +914,7 @@ "search-category": "(category $1)", "search-file-match": "(matches file content)", "search-suggest": "Did you mean: $1", + "search-rewritten": "Showing results for $1. Search instead for $2.", "search-interwiki-caption": "Sister projects", "search-interwiki-default": "Results from $1:", "search-interwiki-custom": "", diff --git a/languages/i18n/qqq.json b/languages/i18n/qqq.json index 872a89f..4b49cd9 100644 --- a/languages/i18n/qqq.json +++ b/languages/i18n/qqq.json @@ -1085,6 +1085,7 @@ "search-category": "This text will be shown on the search result listing after the page title of a result if the search algorithm thinks that the page being in a particular category is relevant.\n\nParameters:\n* $1 - the category's name with any matching portion highlighted\n{{Identical|Category}}", "search-file-match": "This text will be shown on the search result listing after the page title of a result if the search engine got search results from the contents of files, rather than the pages.", "search-suggest": "Used for \"Did you mean\" suggestions:\n* $1 - suggested link", + "search-rewritten": "Used when the user is served the results for a query other than what they provided. Parameters:\n* $1 - a link to search for the current result set.* $2 - a link to perform the original search without rewriting.", "search-interwiki-caption": "Used in [[Special:Search]], when showing search results from other wikis.", "search-interwiki-default": "Parameters:\n* $1 - the hostname of the remote wiki from where the additional results listed below are returned", "search-interwiki-custom": "#REDIRECT [[MediaWiki:Wmf-search-interwiki-custom/qqq]]", diff --git a/tests/phpunit/includes/specials/SpecialSearchTest.php b/tests/phpunit/includes/specials/SpecialSearchTest.php index 5482b97..7e60fdd 100644 --- a/tests/phpunit/includes/specials/SpecialSearchTest.php +++ b/tests/phpunit/includes/specials/SpecialSearchTest.php @@ -141,4 +141,107 @@ "Search term '{$term}' should not be expanded in Special:Search <title>" ); } + + public function provideRewriteQueryWithSuggestion() { + return array( + array( + 'With results and a suggestion does not run suggested query', + '/Did you mean: <a[^>]+>first suggestion/', + array( + new SpecialSearchTestMockResultSet( 'first suggestion', array( + SearchResult::newFromTitle( Title::newMainPage() ), + ) ), + new SpecialSearchTestMockResultSet( 'was never run', array() ), + ), + ), + + array( + 'With no results and a suggestion responds with suggested query results', + '/Showing results for <a[^>]+>first suggestion/', + array( + new SpecialSearchTestMockResultSet( 'first suggestion', array() ), + new SpecialSearchTestMockResultSet( 'second suggestion', array( + SearchResult::newFromTitle( Title::newMainPage() ), + ) ), + ), + ), + + array( + 'When both queries have no results user gets no results', + '/There were no results matching the query/', + array( + new SpecialSearchTestMockResultSet( 'first suggestion', array() ), + new SpecialSearchTestMockResultSet( 'second suggestion', array() ), + ), + ), + ); + } + + /** + * @dataProvider provideRewriteQueryWithSuggestion + */ + public function testRewriteQueryWithSuggestion( $message, $expectRegex, $fromResults ) { + $mockSearchEngine = $this->mockSearchEngine( $fromResults ); + $search = $this->getMockBuilder( 'SpecialSearch' ) + ->setMethods( array( 'getSearchEngine' ) ) + ->getMock(); + $search->expects( $this->any() ) + ->method( 'getSearchEngine' ) + ->will( $this->returnValue( $mockSearchEngine ) ); + + $search->getContext()->setTitle( Title::makeTitle( NS_SPECIAL, 'Search' ) ); + $search->load(); + $search->showResults( 'this is a fake search' ); + + $html = $search->getContext()->getOutput()->getHTML(); + foreach ( (array)$expectRegex as $regex ) { + $this->assertRegExp( $regex, $html, $message ); + } + } + + protected function mockSearchEngine( array $returnValues ) { + $mock = $this->getMockBuilder( 'SearchEngine' ) + ->setMethods( array( 'searchText' ) ) + ->getMock(); + + $mock->expects( $this->any() ) + ->method( 'searchText' ) + ->will( call_user_func_array( + array( $this, 'onConsecutiveCalls' ), + array_map( array( $this, 'returnValue' ), $returnValues ) + ) ); + + return $mock; + } +} + +class SpecialSearchTestMockResultSet extends SearchResultSet { + protected $results; + protected $suggestion; + + public function __construct( $suggestion = null, array $results = array(), $containedSyntax = false) { + $this->results = $results; + $this->suggestion = $suggestion; + $this->containedSyntax = $containedSyntax; + } + + public function numRows() { + return count( $this->results ); + } + + public function getTotalHits() { + return $this->numRows(); + } + + public function hasSuggestion() { + return $this->suggestion !== null; + } + + public function getSuggestionQuery() { + return $this->suggestion; + } + + public function getSuggestionSnippet() { + return $this->suggestion; + } } -- To view, visit https://gerrit.wikimedia.org/r/227347 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I7ed79942c242b1957d46bdcad59985f37466fb83 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/core Gerrit-Branch: wmf/1.26wmf15 Gerrit-Owner: EBernhardson <ebernhard...@wikimedia.org> Gerrit-Reviewer: Catrope <roan.katt...@gmail.com> Gerrit-Reviewer: Siebrand <siebr...@kitano.nl> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits