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