Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 43
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
MappingValidator
0.00% covered (danger)
0.00%
0 / 43
0.00% covered (danger)
0.00%
0 / 4
132
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 isNaturalSortConfigured
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 validate
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
56
 compareMappingToActual
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace CirrusSearch\Maintenance\Validators;
4
5use CirrusSearch\ElasticaErrorHandler;
6use CirrusSearch\Maintenance\Printer;
7use Elastica\Exception\ExceptionInterface;
8use Elastica\Index;
9use Elastica\Mapping;
10use MediaWiki\Language\RawMessage;
11use MediaWiki\Status\Status;
12use Wikimedia\Assert\Assert;
13
14class MappingValidator extends Validator {
15    /**
16     * @var Index
17     */
18    private $index;
19
20    /**
21     * @var string
22     */
23    private $masterTimeout;
24
25    /**
26     * @var bool
27     */
28    private $optimizeIndexForExperimentalHighlighter;
29
30    /**
31     * @var array
32     */
33    private $availablePlugins;
34
35    /**
36     * @var array
37     */
38    private $mappingConfig;
39
40    /**
41     * @todo this constructor takes way too much arguments - refactor
42     *
43     * @param Index $index
44     * @param string $masterTimeout
45     * @param bool $optimizeIndexForExperimentalHighlighter
46     * @param array $availablePlugins
47     * @param array $mappingConfig
48     * @param Printer|null $out
49     */
50    public function __construct(
51        Index $index,
52        $masterTimeout,
53        $optimizeIndexForExperimentalHighlighter,
54        array $availablePlugins,
55        array $mappingConfig,
56        Printer $out = null
57    ) {
58        parent::__construct( $out );
59
60        $this->index = $index;
61        $this->masterTimeout = $masterTimeout;
62        $this->optimizeIndexForExperimentalHighlighter = $optimizeIndexForExperimentalHighlighter;
63        $this->availablePlugins = $availablePlugins;
64        // Could be supported, but prefer consistency
65        Assert::parameter( isset( $mappingConfig['properties'] ), '$mappingConfig',
66            'Mapping types are no longer supported, properties must be top level' );
67        $this->mappingConfig = $mappingConfig;
68    }
69
70    private function isNaturalSortConfigured() {
71        // awkward much?
72        return isset( $this->mappingConfig['properties']['title']['fields']['natural_sort'] );
73    }
74
75    /**
76     * @return Status
77     */
78    public function validate() {
79        $this->outputIndented( "Validating mappings..." );
80        if ( $this->optimizeIndexForExperimentalHighlighter &&
81            !in_array( 'experimental-highlighter', $this->availablePlugins ) ) {
82            $this->output( "impossible!\n" );
83            return Status::newFatal( new RawMessage(
84                "wgCirrusSearchOptimizeIndexForExperimentalHighlighter is set to true but the " .
85                "'experimental-highlighter' plugin is not installed on all hosts." ) );
86        }
87        if ( $this->isNaturalSortConfigured() &&
88            !in_array( 'analysis-icu', $this->availablePlugins ) ) {
89            $this->output( "impossible!\n" );
90            return Status::newFatal( new RawMessage(
91                "wgCirrusSearchNaturalTitleSort is set to build but the " .
92                "'analysis-icu' plugin is not installed on all hosts." ) );
93        }
94
95        if ( !$this->compareMappingToActual() ) {
96            $action = new Mapping( $this->mappingConfig['properties'] );
97            $action->setParam( "dynamic", false );
98
99            try {
100                $action->send( $this->index, [
101                    'master_timeout' => $this->masterTimeout,
102                ] );
103                $this->output( "corrected\n" );
104            } catch ( ExceptionInterface $e ) {
105                $this->output( "failed!\n" );
106                $message = ElasticaErrorHandler::extractMessage( $e );
107                return Status::newFatal( new RawMessage(
108                    "Couldn't update existing mappings. You may need to reindex.\nHere is elasticsearch's error message: $message\n" ) );
109            }
110        }
111
112        return Status::newGood();
113    }
114
115    /**
116     * Check that the mapping returned from Elasticsearch is as we want it.
117     *
118     * @return bool is the mapping good enough for us?
119     */
120    private function compareMappingToActual() {
121        $actualMappings = $this->index->getMapping();
122        $this->output( "\n" );
123        $this->outputIndented( "\tValidating mapping..." );
124        if ( $this->checkConfig( $actualMappings, $this->mappingConfig ) ) {
125            $this->output( "ok\n" );
126            return true;
127        } else {
128            $this->output( "different..." );
129            return false;
130        }
131    }
132}