From: Timo Tijhof Date: Wed, 17 Sep 2014 23:24:41 +0000 (-0700) Subject: PrefixSearch: Enforce including the exact match as first result X-Git-Tag: 1.31.0-rc.0~13637^2 X-Git-Url: https://git.cyclocoop.org/%7B%24admin_url%7Dmembres/cotisations/rappels.php?a=commitdiff_plain;h=8dd95a13e31af2116eca5edda275acb499c40316;p=lhc%2Fweb%2Fwiklou.git PrefixSearch: Enforce including the exact match as first result The default search backend implements proper prefix search and does this naturally. But extensions providing search backends like Lucene and CirrusSearch actually fail to implement proper prefix searching and instead use their search engine ranking for prefix search as well. Thus often the exact match is not on top or is not even in the first 10 results at all. On en.wikipedia.org: > Example 1. "Example (musician)" 2. "Example" 3. "Example.com" > John ive 1. "John Ives" 2. "John Ivey" 3. "John Ive" > Foo 1. "Football (soccer)" 2. "Football League Cup" 3. "Foot (length)" "Foo" exists but is NOT among the returned results. Bug: 70958 Change-Id: I78d419424baf43d38beeb6dabfc347f430fa45f6 --- diff --git a/includes/PrefixSearch.php b/includes/PrefixSearch.php index 9511579ef0..f32183d257 100644 --- a/includes/PrefixSearch.php +++ b/includes/PrefixSearch.php @@ -151,7 +151,31 @@ abstract class PrefixSearch { $srchres = array(); if ( wfRunHooks( 'PrefixSearchBackend', array( $namespaces, $search, $limit, &$srchres ) ) ) { return $this->titles( $this->defaultSearchBackend( $namespaces, $search, $limit ) ); + } else { + // Default search backend does proper prefix searching, but custom backends + // may sort based on other algorythms that may cause the exact title match + // to not be in the results or be lower down the list. + + // Pick namespace (based on PrefixSearch::defaultSearchBackend) + $ns = in_array( NS_MAIN, $namespaces ) ? NS_MAIN : $namespaces[0]; + $t = Title::newFromText( $search, $ns ); + $string = $t->getPrefixedText(); + + $key = array_search( $string, $srchres ); + if ( $key !== false ) { + // Move it to the front + $cut = array_splice( $srchres, $key, 1 ); + array_unshift( $srchres, $cut[0] ); + } elseif ( $t->exists() ) { + // Add it in front + array_unshift( $srchres, $string ); + + if ( count( $srchres ) > $limit ) { + array_pop( $srchres ); + } + } } + return $this->strings( $srchres ); } diff --git a/tests/phpunit/includes/PrefixSearchTest.php b/tests/phpunit/includes/PrefixSearchTest.php index 63dcc3f79b..5390dba63d 100644 --- a/tests/phpunit/includes/PrefixSearchTest.php +++ b/tests/phpunit/includes/PrefixSearchTest.php @@ -29,7 +29,7 @@ class PrefixSearchTest extends MediaWikiTestCase { public function addDBData() { $this->insertPage( 'Sandbox' ); - + $this->insertPage( 'Bar' ); $this->insertPage( 'Example' ); $this->insertPage( 'Example Bar' ); $this->insertPage( 'Example Foo' ); @@ -131,4 +131,80 @@ class PrefixSearchTest extends MediaWikiTestCase { $case[0] ); } + + public static function provideSearchBackend() { + return array( + array( array( + 'Simple case', + 'provision' => array( + 'Bar', + 'Barcelona', + 'Barbara', + ), + 'query' => 'Bar', + 'results' => array( + 'Bar', + 'Barcelona', + 'Barbara', + ), + ) ), + array( array( + 'Exact match not on top (bug 70958)', + 'provision' => array( + 'Barcelona', + 'Bar', + 'Barbara', + ), + 'query' => 'Bar', + 'results' => array( + 'Bar', + 'Barcelona', + 'Barbara', + ), + ) ), + array( array( + 'Exact match missing (bug 70958)', + 'provision' => array( + 'Barcelona', + 'Barbara', + 'Bart', + ), + 'query' => 'Bar', + 'results' => array( + 'Bar', + 'Barcelona', + 'Barbara', + ), + ) ), + array( array( + 'Exact match missing and not existing', + 'provision' => array( + 'Exile', + 'Exist', + 'External', + ), + 'query' => 'Ex', + 'results' => array( + 'Exile', + 'Exist', + 'External', + ), + ) ), + ); + } + + /** + * @dataProvider provideSearchBackend + * @covers PrefixSearch::searchBackend + */ + public function testSearchBackend( Array $case ) { + $this->searchProvision( $case['provision'] ); + $searcher = new StringPrefixSearch; + $results = $searcher->search( $case['query'], 3 ); + $this->assertEquals( + $case['results'], + $results, + $case[0] + ); + } }