%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/RescoreBuilderTest.php |
<?php
namespace CirrusSearch\Search;
use CirrusSearch\Test\HashSearchConfig;
class RescoreBuilderTest extends \PHPUnit_Framework_TestCase {
public function testFunctionScoreDecorator() {
$func = new FunctionScoreDecorator();
$this->assertTrue( $func->isEmptyFunction() );
$func->addWeightFunction( 2.0, new \Elastica\Query\MatchAll() );
$this->assertFalse( $func->isEmptyFunction() );
$array = $func->toArray();
$this->assertTrue( isset( $array['function_score'] ) );
$this->assertEquals( 1, count( $array['function_score']['functions'] ) );
$func = new FunctionScoreDecorator();
$this->assertTrue( $func->isEmptyFunction() );
$func->addFunction( 'foo_function', [] );
$func->addFunction( 'foo_function', [] );
$this->assertFalse( $func->isEmptyFunction() );
$array = $func->toArray();
$this->assertEquals( 2, count( $array['function_score']['functions'] ) );
$func = new FunctionScoreDecorator();
$this->assertTrue( $func->isEmptyFunction() );
$func->addScriptScoreFunction( new \Elastica\Script("foo+2") );
$this->assertFalse( $func->isEmptyFunction() );
$array = $func->toArray();
$this->assertEquals( 1, count( $array['function_score']['functions'] ) );
}
public function testPreferRecent() {
$config = new HashSearchConfig( [] );
$context = new SearchContext( $config, null );
$builder = new PreferRecentFunctionScoreBuilder( $context, 1 );
$fScore = new FunctionScoreDecorator();
$builder->append( $fScore );
$this->assertTrue( $fScore->isEmptyFunction() );
$context->setPreferRecentOptions( 1, 0.6 );
$builder->append( $fScore );
$this->assertFalse( $fScore->isEmptyFunction() );
}
public function testLangWeight() {
// Default user lang seems to be en with unit tests
// Test that we generate 2 filters
$config = new HashSearchConfig( [
'CirrusSearchLanguageWeight' => [
'user' => 2,
'wiki' => 3,
],
'LanguageCode' => 'de'
] );
$context = new SearchContext( $config, null );
$builder = new LangWeightFunctionScoreBuilder( $context, 1 );
$fScore = new FunctionScoreDecorator();
$builder->append( $fScore );
$this->assertFalse( $fScore->isEmptyFunction() );
$array = $fScore->toArray();
$this->assertEquals( count( $array['function_score']['functions'] ), 2 );
// Set cont lang as en to we generate only 1 filter
$config = new HashSearchConfig( [
'CirrusSearchLanguageWeight' => [
'user' => 2,
'wiki' => 3,
],
'LanguageCode' => 'en'
] );
$context = new SearchContext( $config, null );
$builder = new LangWeightFunctionScoreBuilder( $context, 1 );
$fScore = new FunctionScoreDecorator();
$builder->append( $fScore );
$this->assertFalse( $fScore->isEmptyFunction() );
$array = $fScore->toArray();
$this->assertEquals( count( $array['function_score']['functions'] ), 1 );
// Test that we do not generate any filter is weight are not set.
$config = new HashSearchConfig( [
'CirrusSearchLanguageWeight' => [],
'LanguageCode' => 'de'
] );
$context = new SearchContext( $config, null );
$builder = new LangWeightFunctionScoreBuilder( $context, 1 );
$fScore = new FunctionScoreDecorator();
$builder->append( $fScore );
$this->assertTrue( $fScore->isEmptyFunction() );
}
public function testBoostTemplates() {
$config = new HashSearchConfig( [] );
$context = new SearchContext( $config, null );
$builder = new BoostTemplatesFunctionScoreBuilder( $context, 1 );
$fScore = new FunctionScoreDecorator();
$builder->append( $fScore );
$this->assertTrue( $fScore->isEmptyFunction() );
$context->setBoostTemplatesFromQuery( [ 'test' => 3.2 ] );
$builder = new BoostTemplatesFunctionScoreBuilder( $context, 1 );
$builder->append( $fScore );
$this->assertFalse( $fScore->isEmptyFunction() );
}
public function testCustomField() {
$config = new HashSearchConfig( [] );
$context = new SearchContext( $config, null );
$profile = [
'field' => 'test',
'factor' => 5,
'modifier' => 'sqrt',
'missing' => 1,
];
$builder = new CustomFieldFunctionScoreBuilder( $context, 1, $profile );
$fScore = new FunctionScoreDecorator();
$builder->append( $fScore );
$this->assertFalse( $fScore->isEmptyFunction() );
$array = $fScore->toArray();
$this->assertTrue( isset( $array['function_score']['functions'][0]['field_value_factor'] ) );
$this->assertEquals( $profile, $array['function_score']['functions'][0]['field_value_factor'] );
}
public function testScriptScore() {
$config = new HashSearchConfig( [] );
$context = new SearchContext( $config, null );
$script = "sqrt( doc['incoming_links'].value )";
$builder = new ScriptScoreFunctionScoreBuilder( $context, 2, $script );
$fScore = new FunctionScoreDecorator();
$builder->append( $fScore );
$this->assertFalse( $fScore->isEmptyFunction() );
$array = $fScore->toArray();
$this->assertTrue( isset( $array['function_score']['functions'][0]['script_score'] ) );
$this->assertEquals( $script, $array['function_score']['functions'][0]['script_score']['script'] );
$this->assertEquals( 'expression', $array['function_score']['functions'][0]['script_score']['lang'] );
$this->assertEquals( 2, $array['function_score']['functions'][0]['weight'] );
}
public function testBoostLinks() {
$settings = [];
$config = new HashSearchConfig( $settings );
$context = new SearchContext( $config, null );
$context->setBoostLinks( true );
$builder = new IncomingLinksFunctionScoreBuilder( $context, 1 );
$fScore = new FunctionScoreDecorator();
$builder->append( $fScore );
$this->assertFalse( $fScore->isEmptyFunction() );
$array = $fScore->toArray();
$this->assertTrue( isset( $array['function_score']['functions'][0] ) );
$array = $array['function_score']['functions'][0];
$this->assertTrue( isset( $array['field_value_factor'] ) );
$context->setBoostLinks( false );
$fScore = new FunctionScoreDecorator();
$builder->append( $fScore );
$this->assertTrue( $fScore->isEmptyFunction() );
}
public function testNamespacesBoost() {
$settings = [
'CirrusSearchNamespaceWeights' => [
NS_MAIN => 2.5,
NS_PROJECT => 1.3,
NS_HELP => 3,
],
'CirrusSearchDefaultNamespaceWeight' => 0.2,
'CirrusSearchTalkNamespaceWeight' => 0.25
];
$config = new HashSearchConfig( $settings );
// 5 namespaces in the query generates 5 filters
$context = new SearchContext( $config, [ NS_MAIN, NS_PROJECT, NS_HELP, NS_MEDIAWIKI, NS_TALK ] );
$builder = new NamespacesFunctionScoreBuilder( $context, 1 );
$fScore = new FunctionScoreDecorator();
$builder->append( $fScore );
$this->assertFalse( $fScore->isEmptyFunction() );
$array = $fScore->toArray();
$this->assertEquals( 5, count( $array['function_score']['functions'] ) );
// With a single namespace the function score is empty
$context->setNamespaces( [ 0 ] );
$fScore = new FunctionScoreDecorator();
$this->assertTrue( $fScore->isEmptyFunction() );
// with 2 namespaces we have 2 functions
$context->setNamespaces( [ NS_MAIN, NS_HELP ] );
$builder = new NamespacesFunctionScoreBuilder( $context, 1 );
$fScore = new FunctionScoreDecorator();
$builder->append( $fScore );
$this->assertFalse( $fScore->isEmptyFunction() );
$array = $fScore->toArray();
$this->assertEquals( 2, count( $array['function_score']['functions'] ) );
// Test that 2 similar boosts are flattened into the same filter
$settings = [
'CirrusSearchNamespaceWeights' => [
NS_MAIN => 2,
NS_PROJECT => 2,
NS_HELP => 3,
],
];
$config = new HashSearchConfig( $settings );
$context = new SearchContext( $config, [ NS_MAIN, NS_PROJECT, NS_HELP ] );
$builder = new NamespacesFunctionScoreBuilder( $context, 1 );
$fScore = new FunctionScoreDecorator();
$builder->append( $fScore );
$this->assertFalse( $fScore->isEmptyFunction() );
$array = $fScore->toArray();
$this->assertEquals( 2, count( $array['function_score']['functions'] ) );
// Test that a weigth to 1 is ignored
$settings = [
'CirrusSearchNamespaceWeights' => [
NS_MAIN => 2,
NS_PROJECT => 2,
NS_HELP => 1,
],
];
$config = new HashSearchConfig( $settings );
$context = new SearchContext( $config, [ NS_MAIN, NS_PROJECT, NS_HELP ] );
$builder = new NamespacesFunctionScoreBuilder( $context, 1 );
$fScore = new FunctionScoreDecorator();
$builder->append( $fScore );
$this->assertFalse( $fScore->isEmptyFunction() );
$array = $fScore->toArray();
$this->assertEquals( 1, count( $array['function_score']['functions'] ) );
}
/**
* @dataProvider provideRescoreProfilesWithFallback
*/
public function testFallbackProfile( $settings, $namespaces, $expectedFunctionCount ) {
$config = new HashSearchConfig( $settings );
$context = new SearchContext( $config, $namespaces );
$context->setBoostLinks( true );
$context->setBoostTemplatesFromQuery( [ 'Good' => 1.3 ] );
$builder = new RescoreBuilder( $context, $config->get( 'CirrusSearchRescoreProfile' ) );
$rescore = $builder->build();
$array = $rescore[0]['query']['rescore_query'];
$array = $array->toArray();
$this->assertEquals( $expectedFunctionCount, count( $array['function_score']['functions'] ) );
}
public static function provideRescoreProfilesWithFallback() {
$defaultChain = [
'functions' => [
[ 'type' => 'boostlinks' ]
]
];
$fullChain = [
'functions' => [
[ 'type' => 'boostlinks' ],
[ 'type' => 'templates' ]
]
];
$profile = [
'ContentNamespaces' => [ 1, 2 ],
'CirrusSearchRescoreProfiles' => [
'full' => [
'supported_namespaces' => [ 0, 1 ],
'fallback_profile' => 'default',
'rescore' => [
[
'window' => 123,
'type' => 'function_score',
'function_chain' => 'full',
]
]
],
'content' => [
'supported_namespaces' => 'content',
'fallback_profile' => 'default',
'rescore' => [
[
'window' => 123,
'type' => 'function_score',
'function_chain' => 'full',
]
]
],
'default' => [
'supported_namespaces' => 'all',
'rescore' => [
[
'window' => 123,
'type' => 'function_score',
'function_chain' => 'default',
]
]
]
],
'CirrusSearchRescoreFunctionScoreChains' => [
'full' => $fullChain,
'default' => $defaultChain
]
];
return [
'No fallback' => [
$profile + [ 'CirrusSearchRescoreProfile' => 'full' ],
[ 0 ],
2
],
'No fallback multi namespace' => [
$profile + [ 'CirrusSearchRescoreProfile' => 'full' ],
[ 0, 1 ],
2
],
'No fallback content ns' => [
$profile + [ 'CirrusSearchRescoreProfile' => 'content' ],
[ 1, 2 ],
2
],
'Fallback content ns' => [
$profile + [ 'CirrusSearchRescoreProfile' => 'content' ],
[ 0, 2 ],
1
],
'Fallback with multiple namespace' => [
$profile + [ 'CirrusSearchRescoreProfile' => 'full' ],
[ 0, 2 ],
1
],
'Fallback null ns' => [
$profile + [ 'CirrusSearchRescoreProfile' => 'full' ],
null,
1
],
];
}
/**
* @dataProvider provideRescoreProfilesWithWindowSize
*/
public function testWindowSizeOverride( $settings, $expected ) {
$config = new HashSearchConfig( $settings );
$context = new SearchContext( $config, null );
$context->setBoostLinks( true );
$builder = new RescoreBuilder( $context, $config->getElement( 'CirrusSearchRescoreProfiles', 'default' ) );
$rescore = $builder->build();
$this->assertEquals( $expected, $rescore[0]['window_size'] );
}
public static function provideRescoreProfilesWithWindowSize() {
$testChain = [
'functions' => [ [ 'type' => 'boostlinks' ] ]
];
return [
'Overridden' => [
[
'CirrusSearchRescoreProfiles' => [
'default' => [
'supported_namespaces' => 'all',
'rescore' => [
[
'window' => 123,
'window_size_override' => 'CirrusSearchOverrideWindow',
'type' => 'function_score',
'function_chain' => 'test',
]
]
]
],
'CirrusSearchOverrideWindow' => 321,
'CirrusSearchRescoreFunctionScoreChains' => [
'test' => $testChain
]
],
321
],
'Overridden with missing config' => [
[
'CirrusSearchRescoreProfiles' => [
'default' => [
'supported_namespaces' => 'all',
'rescore' => [
[
'window' => 123,
'window_size_override' => 'CirrusSearchOverrideWindow',
'type' => 'function_score',
'function_chain' => 'test',
]
]
]
],
'CirrusSearchRescoreFunctionScoreChains' => [
'test' => $testChain
]
],
123
],
'Not overridden' => [
[
'CirrusSearchRescoreProfiles' => [
'default' => [
'supported_namespaces' => 'all',
'rescore' => [
[
'window' => 123,
'type' => 'function_score',
'function_chain' => 'test',
]
]
]
],
'CirrusSearchRescoreFunctionScoreChains' => [
'test' => $testChain
]
],
123
],
];
}
/**
* @expectedException \CirrusSearch\Search\InvalidRescoreProfileException
* @dataProvider provideInvalidRescoreProfiles
*/
public function testBadRescoreProfile( $settings ) {
$config = new HashSearchConfig( $settings );
$context = new SearchContext( $config, null );
$builder = new RescoreBuilder( $context, $config->getElement( 'CirrusSearchRescoreProfiles', 'default' ) );
$builder->build();
}
public static function provideInvalidRescoreProfiles() {
return [
'Unsupported rescore query type' => [
[
'CirrusSearchRescoreProfiles' => [
'default' => [
'supported_namespaces' => 'all',
'rescore' => [
[
'window' => 123,
'type' => 'foobar',
]
]
]
],
],
],
"Invalid rescore profile: supported_namespaces should be 'all' or an array of namespaces" => [
[
'CirrusSearchRescoreProfiles' => [
'default' => [
'supported_namespaces' => 1,
]
],
],
],
"Invalid rescore profile: fallback_profile is mandatory" => [
[
'CirrusSearchRescoreProfiles' => [
'default' => [
'supported_namespaces' => [ 0 ],
]
],
],
],
"Unknown fallback profile" => [
[
'CirrusSearchRescoreProfiles' => [
'default' => [
'supported_namespaces' => [ 0 ],
'fallback_profile' => 'missing',
]
],
],
],
"Fallback profile must support all namespaces" => [
[
'CirrusSearchRescoreProfiles' => [
'default' => [
'supported_namespaces' => [ 0 ],
'fallback_profile' => 'fallback',
],
'fallback' => [
'supported_namespaces' => [ 3 ],
]
],
],
],
"Unknown rescore function chain" => [
[
'CirrusSearchRescoreProfiles' => [
'default' => [
'supported_namespaces' => 'all',
'rescore' => [
[
'window' => 123,
'type' => 'function_score',
'function_chain' => 'test',
],
[
'window' => 123,
'type' => 'function_score',
'function_chain' => 'test_missing',
]
]
],
],
'CirrusSearchRescoreFunctionScoreChains' => [
'test' => []
]
],
],
"Invalid function chain (none defined)" => [
[
'CirrusSearchRescoreProfiles' => [
'default' => [
'supported_namespaces' => 'all',
'rescore' => [
[
'window' => 123,
'type' => 'function_score',
'function_chain' => 'test',
],
]
],
],
'CirrusSearchRescoreFunctionScoreChains' => [
'test' => [ ]
]
],
],
"Invalid function score type" => [
[
'CirrusSearchRescoreProfiles' => [
'default' => [
'supported_namespaces' => 'all',
'rescore' => [
[
'window' => 123,
'type' => 'function_score',
'function_chain' => 'test',
],
]
],
],
'CirrusSearchRescoreFunctionScoreChains' => [
'test' => [ 'functions' => [ [ 'type' => 'foobar' ] ] ]
]
],
],
];
}
}