Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
77.08% |
37 / 48 |
|
33.33% |
1 / 3 |
CRAP | |
0.00% |
0 / 1 |
NamespacesFunctionScoreBuilder | |
77.08% |
37 / 48 |
|
33.33% |
1 / 3 |
26.31 | |
0.00% |
0 / 1 |
__construct | |
64.71% |
11 / 17 |
|
0.00% |
0 / 1 |
10.81 | |||
getBoostForNamespace | |
64.29% |
9 / 14 |
|
0.00% |
0 / 1 |
7.64 | |||
append | |
100.00% |
17 / 17 |
|
100.00% |
1 / 1 |
7 |
1 | <?php |
2 | |
3 | namespace CirrusSearch\Search\Rescore; |
4 | |
5 | use CirrusSearch\SearchConfig; |
6 | use Elastica\Query\FunctionScore; |
7 | use MediaWiki\Logger\LoggerFactory; |
8 | use MediaWiki\MediaWikiServices; |
9 | use MediaWiki\Title\NamespaceInfo; |
10 | |
11 | /** |
12 | * Builds a set of functions with namespaces. |
13 | * Uses a weight function with a filter for each namespace. |
14 | * Activated only if more than one namespace is requested. |
15 | */ |
16 | class NamespacesFunctionScoreBuilder extends FunctionScoreBuilder { |
17 | /** |
18 | * @var null|float[] initialized version of $wgCirrusSearchNamespaceWeights with all string keys |
19 | * translated into integer namespace codes using $this->language. |
20 | */ |
21 | private $normalizedNamespaceWeights; |
22 | |
23 | /** |
24 | * @var int[] List of namespace id's |
25 | */ |
26 | private $namespacesToBoost; |
27 | |
28 | /** |
29 | * @var NamespaceInfo |
30 | */ |
31 | private $namespaceInfo; |
32 | |
33 | /** |
34 | * @param SearchConfig $config |
35 | * @param int[]|null $namespaces |
36 | * @param float $weight |
37 | * @param NamespaceInfo|null $namespaceInfo |
38 | */ |
39 | public function __construct( SearchConfig $config, $namespaces, $weight, ?NamespaceInfo $namespaceInfo = null ) { |
40 | parent::__construct( $config, $weight ); |
41 | |
42 | $this->namespaceInfo = $namespaceInfo ?: MediaWikiServices::getInstance()->getNamespaceInfo(); |
43 | $this->namespacesToBoost = |
44 | $namespaces ?: $this->namespaceInfo->getValidNamespaces(); |
45 | if ( !$this->namespacesToBoost || count( $this->namespacesToBoost ) == 1 ) { |
46 | // nothing to boost, no need to initialize anything else. |
47 | return; |
48 | } |
49 | $this->normalizedNamespaceWeights = []; |
50 | foreach ( $config->get( 'CirrusSearchNamespaceWeights' ) as $ns => |
51 | $weight |
52 | ) { |
53 | if ( !is_int( $ns ) && !ctype_digit( $ns ) ) { |
54 | LoggerFactory::getInstance( 'CirrusSearch' )->warning( |
55 | 'Namespace names are no longer accepted as namespaces in ' |
56 | . "CirrusSearchNamespaceWeights. Ignoring {invalid_ns}", |
57 | [ 'invalid_ns' => $ns ] |
58 | ); |
59 | |
60 | continue; |
61 | } |
62 | // Now $ns should always be an integer. |
63 | $this->normalizedNamespaceWeights[(int)$ns] = $weight; |
64 | } |
65 | } |
66 | |
67 | /** |
68 | * Get the weight of a namespace. |
69 | * |
70 | * @param int $namespace |
71 | * @return float the weight of the namespace |
72 | */ |
73 | private function getBoostForNamespace( $namespace ) { |
74 | if ( isset( $this->normalizedNamespaceWeights[$namespace] ) ) { |
75 | return $this->normalizedNamespaceWeights[$namespace]; |
76 | } |
77 | if ( $this->namespaceInfo->isSubject( $namespace ) ) { |
78 | if ( $namespace === NS_MAIN ) { |
79 | return 1; |
80 | } |
81 | |
82 | return $this->config->get( 'CirrusSearchDefaultNamespaceWeight' ); |
83 | } |
84 | $subjectNs = $this->namespaceInfo->getSubject( $namespace ); |
85 | if ( isset( $this->normalizedNamespaceWeights[$subjectNs] ) ) { |
86 | return $this->config->get( 'CirrusSearchTalkNamespaceWeight' ) * |
87 | $this->normalizedNamespaceWeights[$subjectNs]; |
88 | } |
89 | if ( $namespace === NS_TALK ) { |
90 | return $this->config->get( 'CirrusSearchTalkNamespaceWeight' ); |
91 | } |
92 | |
93 | return $this->config->get( 'CirrusSearchDefaultNamespaceWeight' ) * |
94 | $this->config->get( 'CirrusSearchTalkNamespaceWeight' ); |
95 | } |
96 | |
97 | public function append( FunctionScore $functionScore ) { |
98 | if ( !$this->namespacesToBoost || count( $this->namespacesToBoost ) == 1 ) { |
99 | // nothing to boost, no need to initialize anything else. |
100 | return; |
101 | } |
102 | |
103 | // first build the opposite map, this will allow us to add a |
104 | // single factor function per weight by using a terms filter. |
105 | $weightToNs = []; |
106 | foreach ( $this->namespacesToBoost as $ns ) { |
107 | $weight = $this->getBoostForNamespace( $ns ) * $this->weight; |
108 | $key = (string)$weight; |
109 | if ( $key == '1' ) { |
110 | // such weights would have no effect |
111 | // we can ignore them. |
112 | continue; |
113 | } |
114 | if ( !isset( $weightToNs[$key] ) ) { |
115 | $weightToNs[$key] = [ |
116 | 'weight' => $weight, |
117 | 'ns' => [ $ns ] |
118 | ]; |
119 | } else { |
120 | $weightToNs[$key]['ns'][] = $ns; |
121 | } |
122 | } |
123 | foreach ( $weightToNs as $weight => $namespacesAndWeight ) { |
124 | $filter = new \Elastica\Query\Terms( 'namespace', $namespacesAndWeight['ns'] ); |
125 | $functionScore->addWeightFunction( $namespacesAndWeight['weight'], $filter ); |
126 | } |
127 | } |
128 | } |