*/
protected $fulltext;
+ /**
+ * @var bool
+ */
+ protected $runSuggestion = true;
+
const NAMESPACES_CURRENT = 'sense';
public function __construct() {
}
$this->fulltext = $request->getVal( 'fulltext' );
+ $this->runSuggestion = (bool)$request->getVal( 'runsuggestion', true );
$this->profile = $profile;
}
$search->setNamespaces( $this->namespaces );
$search->prefix = $this->mPrefix;
$term = $search->transformSearchTerm( $term );
- $didYouMeanHtml = '';
Hooks::run( 'SpecialSearchSetupEngine', array( $this, $this->profile, $search ) );
}
// 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;
- }
-
- $stParams = array_merge(
- $didYouMeanParams,
- $this->powerSearchOptions()
- );
-
- $suggestionSnippet = $textMatches->getSuggestionSnippet();
-
- if ( $suggestionSnippet == '' ) {
- $suggestionSnippet = null;
+ $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 );
}
-
- $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 ) ) ) {
}
+ /**
+ * 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
"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;
+ }
}