%PDF- %PDF-
Direktori : /www/varak.net/wiki.varak.net/extensions/CirrusSearch/tests/unit/ |
Current File : /www/varak.net/wiki.varak.net/extensions/CirrusSearch/tests/unit/CompletionSuggesterTest.php |
<?php namespace CirrusSearch; use CirrusSearch\Test\HashSearchConfig; use CirrusSearch\Test\DummyConnection; use CirrusSearch\BuildDocument\SuggestBuilder; /** * Completion Suggester Tests * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html */ class CompletionSuggesterTest extends \PHPUnit_Framework_TestCase { /** * @dataProvider provideQueries */ public function testQueries( $config, $limit, $search, $variants, $expectedProfiles, $expectedQueries ) { $completion = new MyCompletionSuggester( new HashSearchConfig( $config ), $limit ); list( $profiles, $suggest ) = $completion->testBuildQuery( $search, $variants ); $this->assertEquals( $expectedProfiles, $profiles ); $this->assertEquals( $expectedQueries, $suggest ); } public function provideQueries() { $simpleProfile = [ 'plain' => [ 'field' => 'suggest', 'min_query_len' => 0, 'discount' => 1.0, 'fetch_limit_factor' => 2, ], ]; $simpleFuzzy = $simpleProfile + [ 'plain-fuzzy' => [ 'field' => 'suggest', 'min_query_len' => 0, 'fuzzy' => [ 'fuzzyness' => 'AUTO', 'prefix_length' => 1, 'unicode_aware' => true, ], 'discount' => 0.5, 'fetch_limit_factor' => 1.5 ] ]; $profile = [ 'simple' => $simpleProfile, 'fuzzy' => $simpleFuzzy, ]; return [ "simple" => [ [ 'CirrusSearchCompletionSettings' => 'simple', 'CirrusSearchCompletionProfiles' => $profile, ], 10, ' complete me ', null, $simpleProfile, // The profile remains unmodified here [ 'plain' => [ 'text' => 'complete me ', // keep trailing white spaces 'completion' => [ 'field' => 'suggest', 'size' => 20, // effect of fetch_limit_factor ], ], ], ], "simple with fuzzy" => [ [ 'CirrusSearchCompletionSettings' => 'fuzzy', 'CirrusSearchCompletionProfiles' => $profile, ], 10, ' complete me ', null, $simpleFuzzy, // The profiles remains unmodified here [ 'plain' => [ 'text' => 'complete me ', // keep trailing white spaces 'completion' => [ 'field' => 'suggest', 'size' => 20, // effect of fetch_limit_factor ], ], 'plain-fuzzy' => [ 'text' => 'complete me ', // keep trailing white spaces 'completion' => [ 'field' => 'suggest', 'size' => 15.0, // effect of fetch_limit_factor // fuzzy config is simply copied from the profile 'fuzzy' => [ 'fuzzyness' => 'AUTO', 'prefix_length' => 1, 'unicode_aware' => true, ], ], ], ], ], "simple with variants" => [ [ 'CirrusSearchCompletionSettings' => 'simple', 'CirrusSearchCompletionProfiles' => $profile, ], 10, ' complete me ', [ ' variant1 ', ' complete me ', ' variant2 ' ], // Profile is updated with extra variant setup // to include an extra discount // ' complete me ' variant duplicate will be ignored $simpleProfile + [ 'plain-variant-1' => [ 'field' => 'suggest', 'min_query_len' => 0, 'discount' => 1.0 * CompletionSuggester::VARIANT_EXTRA_DISCOUNT, 'fetch_limit_factor' => 2, 'fallback' => true, // extra key added, not used for now ], 'plain-variant-2' => [ 'field' => 'suggest', 'min_query_len' => 0, 'discount' => 1.0 * (CompletionSuggester::VARIANT_EXTRA_DISCOUNT/2), 'fetch_limit_factor' => 2, 'fallback' => true, // extra key added, not used for now ] ], [ 'plain' => [ 'text' => 'complete me ', // keep trailing white spaces 'completion' => [ 'field' => 'suggest', 'size' => 20, // effect of fetch_limit_factor ], ], 'plain-variant-1' => [ 'text' => 'variant1 ', 'completion' => [ 'field' => 'suggest', 'size' => 20, // effect of fetch_limit_factor ], ], 'plain-variant-2' => [ 'text' => 'variant2 ', 'completion' => [ 'field' => 'suggest', 'size' => 20, // effect of fetch_limit_factor ], ], ], ], ]; } /** * @dataProvider provideMinMaxQueries */ public function testMinMaxDefaultProfile( $len, $query ) { global $wgCirrusSearchCompletionProfiles; $config = [ 'CirrusSearchCompletionSettings' => 'fuzzy', 'CirrusSearchCompletionProfiles' => $wgCirrusSearchCompletionProfiles, ]; // Test that we generate at most 4 profiles $completion = new MyCompletionSuggester( new HashSearchConfig( $config ), 1 ); list( $profiles, $suggest ) = $completion->testBuildQuery( $query, [] ); // Unused profiles are kept $this->assertEquals( count( $wgCirrusSearchCompletionProfiles['fuzzy'] ), count( $profiles ) ); // Never run more than 4 suggest query (without variants) $this->assertTrue( count( $suggest ) <= 4 ); // small queries $this->assertTrue( count( $suggest ) >= 2 ); if ( $len < 3 ) { // We do not run fuzzy for small queries $this->assertEquals( 2, count( $suggest ) ); foreach( $suggest as $key => $value ) { $this->assertArrayNotHasKey( 'fuzzy', $value ); } } foreach( $suggest as $key => $value ) { // Make sure the query is truncated otherwise elastic won't send results $this->assertTrue( mb_strlen( $value['text'] ) < SuggestBuilder::MAX_INPUT_LENGTH ); } foreach( array_keys( $suggest ) as $sug ) { // Makes sure we have the corresponding profile $this->assertArrayHasKey( $sug, $profiles ); } } public function provideMinMaxQueries() { $queries = []; // The completion should not count extra spaces // This is to avoid enbling costly fuzzy profiles // by cheating with spaces $query = ' '; for( $i = 0; $i < 100; $i++ ) { $test = "Query length {$i}"; $queries[$test] = [ $i, $query . ' ' ]; $query .= ''; } return $queries; } /** * @dataProvider provideResponse */ public function testOffsets( \Elastica\Response $resp, $limit, $offset, $first, $last, $size, $hardLimit ) { global $wgCirrusSearchCompletionProfiles; $config = [ 'CirrusSearchCompletionSettings' => 'fuzzy', 'CirrusSearchCompletionProfiles' => $wgCirrusSearchCompletionProfiles, 'CirrusSearchCompletionSuggesterHardLimit' => $hardLimit, ]; // Test that we generate at most 4 profiles $completion = new MyCompletionSuggester( new HashSearchConfig( $config ), $limit, $offset ); $suggestions = $completion->testPostProcess( 'Tit', $resp ); $this->assertEquals( $size, $suggestions->getSize() ); if ( $size > 0 ) { $suggestions = $suggestions->getSuggestions(); $firstS = reset( $suggestions ); $lastS = end( $suggestions ); $this->assertEquals( $first, $firstS->getText() ); $this->assertEquals( $last, $lastS->getText() ); } } public function provideResponse() { $suggestions = []; $max = 200; for( $i = 1; $i <= $max; $i++ ) { $score = $max - $i; $suggestions[] = [ 'text'=> "$i:t:Title$i", 'score' => $score, ]; } $suggestData = [ [ 'text' => 'Tit', 'options' => $suggestions ] ]; $data = [ '_shards' => [], 'plain' => $suggestData, 'plain_fuzzy_2' => $suggestData, 'plain_stop' => $suggestData, 'plain_stop_fuzzy_2' => $suggestData, ]; $resp = new \Elastica\Response( $data ); return [ 'Simple offset 0' => [ $resp, 5, 0, 'Title1', 'Title5', 5, 50 ], 'Simple offset 5' => [ $resp, 5, 5, 'Title6', 'Title10', 5, 50 ], 'Reach ES limit' => [ $resp, 5, $max-3, 'Title198', 'Title200', 3, 300 ], 'Reach Cirrus limit' => [ $resp, 5, 47, 'Title48', 'Title50', 3, 50 ], 'Out of Cirrus bounds' => [ $resp, 5, 67, null, null, 0, 50 ], 'Out of elastic results' => [ $resp, 5, 200, null, null, 0, 300 ], ]; } } /** * No package visibility in with PHP so we have to subclass... */ class MyCompletionSuggester extends CompletionSuggester { public function __construct( SearchConfig $config, $limit, $offset=0 ) { parent::__construct( new DummyConnection(), $limit, $offset, $config, [ NS_MAIN ], null, "dummy" ); } public function testBuildQuery( $search, $variants ) { $this->setTermAndVariants( $search, $variants ); return $this->buildQuery(); } public function testPostProcess( $search, \Elastica\Response $resp ) { $this->setTermAndVariants( $search ); list( $profiles ) = $this->buildQuery(); return $this->postProcessSuggest( $resp, $profiles ); } }