Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 279 |
|
0.00% |
0 / 28 |
CRAP | |
0.00% |
0 / 1 |
UpdateOneSearchIndexConfig | |
0.00% |
0 / 272 |
|
0.00% |
0 / 28 |
2550 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
addSharedOptions | |
0.00% |
0 / 32 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 69 |
|
0.00% |
0 / 1 |
90 | |||
updateVersions | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
2 | |||
validateIndex | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
createIndex | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
2 | |||
getIndexSettingsValidators | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 | |||
validateIndexSettings | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
validateAnalyzers | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
validateMapping | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
2 | |||
validateAlias | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
validateSpecificAlias | |
0.00% |
0 / 26 |
|
0.00% |
0 / 1 |
2 | |||
validateAllAlias | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 | |||
getShardAllocationValidator | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
validateShardAllocation | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
pickAnalyzer | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
initMappingConfigBuilder | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
30 | |||
getIndex | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getSpecificIndexName | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getIndexAliasName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getIndexName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getOldIndex | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getMergeSettings | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
getShardCount | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getReplicaCount | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getMaxShardsPerNode | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
initAnalysisConfig | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
6 | |||
fatalError | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
42 |
1 | <?php |
2 | |
3 | namespace CirrusSearch\Maintenance; |
4 | |
5 | use CirrusSearch\Connection; |
6 | use CirrusSearch\ElasticaErrorHandler; |
7 | use CirrusSearch\Maintenance\Validators\MappingValidator; |
8 | use CirrusSearch\SearchConfig; |
9 | use CirrusSearch\Util; |
10 | |
11 | /** |
12 | * Update the search configuration on the search backend. |
13 | * |
14 | * This program is free software; you can redistribute it and/or modify |
15 | * it under the terms of the GNU General Public License as published by |
16 | * the Free Software Foundation; either version 2 of the License, or |
17 | * (at your option) any later version. |
18 | * |
19 | * This program is distributed in the hope that it will be useful, |
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
22 | * GNU General Public License for more details. |
23 | * |
24 | * You should have received a copy of the GNU General Public License along |
25 | * with this program; if not, write to the Free Software Foundation, Inc., |
26 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
27 | * http://www.gnu.org/copyleft/gpl.html |
28 | */ |
29 | |
30 | $IP = getenv( 'MW_INSTALL_PATH' ); |
31 | if ( $IP === false ) { |
32 | $IP = __DIR__ . '/../../..'; |
33 | } |
34 | require_once "$IP/maintenance/Maintenance.php"; |
35 | require_once __DIR__ . '/../includes/Maintenance/Maintenance.php'; |
36 | |
37 | /** |
38 | * Update the elasticsearch configuration for this index. |
39 | */ |
40 | class UpdateOneSearchIndexConfig extends Maintenance { |
41 | /** |
42 | * @var string |
43 | */ |
44 | private $indexSuffix; |
45 | |
46 | /** |
47 | * @var bool Are we going to blow the index away and start from scratch? |
48 | */ |
49 | private $startOver; |
50 | |
51 | /** |
52 | * @var int |
53 | */ |
54 | private $reindexChunkSize; |
55 | |
56 | /** |
57 | * @var string |
58 | */ |
59 | private $indexBaseName; |
60 | |
61 | /** |
62 | * @var string |
63 | */ |
64 | private $indexIdentifier; |
65 | |
66 | /** |
67 | * @var bool |
68 | */ |
69 | private $reindexAndRemoveOk; |
70 | |
71 | /** |
72 | * @var int number of scan slices to use when reindexing |
73 | */ |
74 | private $reindexSlices; |
75 | |
76 | /** |
77 | * @var string language code we're building for |
78 | */ |
79 | private $langCode; |
80 | |
81 | /** |
82 | * @var bool prefix search on any term |
83 | */ |
84 | private $prefixSearchStartsWithAny; |
85 | |
86 | /** |
87 | * @var bool use suggestions on text fields |
88 | */ |
89 | private $phraseSuggestUseText; |
90 | |
91 | /** |
92 | * @var bool print config as it is being checked |
93 | */ |
94 | private $printDebugCheckConfig; |
95 | |
96 | /** |
97 | * @var float how much can the reindexed copy of an index is allowed to deviate from the current |
98 | * copy without triggering a reindex failure |
99 | */ |
100 | private $reindexAcceptableCountDeviation; |
101 | |
102 | /** |
103 | * @var array filtered analysis config |
104 | */ |
105 | private $analysisConfig; |
106 | |
107 | /** |
108 | * @var array list of available plugins |
109 | */ |
110 | private $availablePlugins; |
111 | |
112 | /** |
113 | * @var array |
114 | */ |
115 | protected $bannedPlugins; |
116 | |
117 | /** |
118 | * @var bool |
119 | */ |
120 | protected $optimizeIndexForExperimentalHighlighter; |
121 | |
122 | /** |
123 | * @var int |
124 | */ |
125 | protected $refreshInterval; |
126 | |
127 | /** |
128 | * @var string |
129 | */ |
130 | protected $masterTimeout; |
131 | |
132 | /** |
133 | * @var array |
134 | */ |
135 | private $mapping = []; |
136 | |
137 | /** |
138 | * @var array |
139 | */ |
140 | private $similarityConfig; |
141 | |
142 | /** |
143 | * @var bool true if the analysis config can be optimized |
144 | */ |
145 | private $safeToOptimizeAnalysisConfig; |
146 | |
147 | /** @var bool State flag indicating if we should attempt deleting the index we created */ |
148 | private $canCleanupCreatedIndex = false; |
149 | |
150 | public function __construct() { |
151 | parent::__construct(); |
152 | $this->addDescription( "Update the configuration or contents of one search index. This always " . |
153 | "operates on a single cluster." ); |
154 | $this->addOption( 'indexSuffix', 'Index to update. Either content or general.', false, true ); |
155 | $this->addOption( 'indexType', 'BC form of --indexSuffix', false, true ); |
156 | self::addSharedOptions( $this ); |
157 | } |
158 | |
159 | /** |
160 | * @param Maintenance $maintenance |
161 | */ |
162 | public static function addSharedOptions( $maintenance ) { |
163 | $maintenance->addOption( 'startOver', 'Blow away the identified index and rebuild it with ' . |
164 | 'no data.' ); |
165 | $maintenance->addOption( 'indexIdentifier', "Set the identifier of the index to work on. " . |
166 | "You'll need this if you have an index in production serving queries and you have " . |
167 | "to alter some portion of its configuration that cannot safely be done without " . |
168 | "rebuilding it. Once you specify a new indexIdentifier for this wiki you'll have to " . |
169 | "run this script with the same identifier each time. Defaults to 'current' which " . |
170 | "infers the currently in use identifier. You can also use 'now' to set the identifier " . |
171 | "to the current time in seconds which should give you a unique identifier.", false, true ); |
172 | $maintenance->addOption( 'reindexAndRemoveOk', "If the alias is held by another index then " . |
173 | "reindex all documents from that index (via the alias) to this one, swing the " . |
174 | "alias to this index, and then remove other index. Updates performed while this" . |
175 | "operation is in progress will be queued up in the job queue. Defaults to false." ); |
176 | $maintenance->addOption( 'reindexSlices', 'Number of slices to use in reindex. Roughly ' |
177 | . 'equivalent to the level of indexing parallelism. Defaults to number of shards.', false, true ); |
178 | $maintenance->addOption( 'reindexAcceptableCountDeviation', 'How much can the reindexed ' . |
179 | 'copy of an index is allowed to deviate from the current copy without triggering a ' . |
180 | 'reindex failure. Defaults to 5%.', false, true ); |
181 | $maintenance->addOption( 'reindexChunkSize', 'Documents per shard to reindex in a batch. ' . |
182 | 'Note when changing the number of shards that the old shard size is used, not the new ' . |
183 | 'one. If you see many errors submitting documents in bulk but the automatic retry as ' . |
184 | 'singles works then lower this number. Defaults to 100.', false, true ); |
185 | $maintenance->addOption( 'baseName', 'What basename to use for all indexes, ' . |
186 | 'defaults to wiki id', false, true ); |
187 | $maintenance->addOption( 'debugCheckConfig', 'Print the configuration as it is checked ' . |
188 | 'to help debug unexpected configuration mismatches.' ); |
189 | $maintenance->addOption( 'justAllocation', 'Just validate the shard allocation settings. Use ' . |
190 | "when you need to apply new cache warmers but want to be sure that you won't apply any other " . |
191 | 'changes at an inopportune time.' ); |
192 | $maintenance->addOption( 'fieldsToDelete', 'List of of comma separated field names to delete ' . |
193 | 'while reindexing documents (defaults to empty)', false, true ); |
194 | $maintenance->addOption( 'justMapping', 'Just try to update the mapping.' ); |
195 | } |
196 | |
197 | public function execute() { |
198 | global $wgLanguageCode, |
199 | $wgCirrusSearchPhraseSuggestUseText, |
200 | $wgCirrusSearchPrefixSearchStartsWithAnyWord, |
201 | $wgCirrusSearchBannedPlugins, |
202 | $wgCirrusSearchOptimizeIndexForExperimentalHighlighter, |
203 | $wgCirrusSearchRefreshInterval, |
204 | $wgCirrusSearchMasterTimeout; |
205 | |
206 | $this->disablePoolCountersAndLogging(); |
207 | |
208 | $utils = new ConfigUtils( $this->getConnection()->getClient(), $this ); |
209 | |
210 | $this->indexSuffix = $this->getBackCompatOption( 'indexSuffix', 'indexType' ); |
211 | $this->startOver = $this->getOption( 'startOver', false ); |
212 | $this->indexBaseName = $this->getOption( 'baseName', |
213 | $this->getSearchConfig()->get( SearchConfig::INDEX_BASE_NAME ) ); |
214 | $this->reindexAndRemoveOk = $this->getOption( 'reindexAndRemoveOk', false ); |
215 | $this->reindexSlices = $this->getOption( 'reindexSlices', null ); |
216 | $this->reindexAcceptableCountDeviation = Util::parsePotentialPercent( |
217 | $this->getOption( 'reindexAcceptableCountDeviation', '5%' ) ); |
218 | $this->reindexChunkSize = $this->getOption( 'reindexChunkSize', 100 ); |
219 | $this->printDebugCheckConfig = $this->getOption( 'debugCheckConfig', false ); |
220 | $this->langCode = $wgLanguageCode; |
221 | $this->prefixSearchStartsWithAny = $wgCirrusSearchPrefixSearchStartsWithAnyWord; |
222 | $this->phraseSuggestUseText = $wgCirrusSearchPhraseSuggestUseText; |
223 | $this->bannedPlugins = $wgCirrusSearchBannedPlugins; |
224 | $this->optimizeIndexForExperimentalHighlighter = $wgCirrusSearchOptimizeIndexForExperimentalHighlighter; |
225 | $this->masterTimeout = $wgCirrusSearchMasterTimeout; |
226 | $this->refreshInterval = $wgCirrusSearchRefreshInterval; |
227 | |
228 | if ( $this->indexSuffix === Connection::ARCHIVE_INDEX_SUFFIX ) { |
229 | if ( !$this->getSearchConfig()->get( 'CirrusSearchEnableArchive' ) ) { |
230 | $this->output( "Warning: Not allowing {$this->indexSuffix}, archives are disabled\n" ); |
231 | return true; |
232 | } |
233 | if ( !$this->getConnection()->getSettings()->isPrivateCluster() ) { |
234 | $this->output( "Warning: Not allowing {$this->indexSuffix} on a non-private cluster\n" ); |
235 | return true; |
236 | } |
237 | } |
238 | |
239 | $this->initMappingConfigBuilder(); |
240 | |
241 | try{ |
242 | $indexSuffixes = $this->getConnection()->getAllIndexSuffixes( null ); |
243 | if ( !in_array( $this->indexSuffix, $indexSuffixes ) ) { |
244 | $this->fatalError( 'indexSuffix option must be one of ' . |
245 | implode( ', ', $indexSuffixes ) ); |
246 | } |
247 | |
248 | $this->unwrap( $utils->checkElasticsearchVersion() ); |
249 | $this->availablePlugins = $this->unwrap( $utils->scanAvailablePlugins( $this->bannedPlugins ) ); |
250 | |
251 | if ( $this->getOption( 'justAllocation', false ) ) { |
252 | $this->validateShardAllocation(); |
253 | return true; |
254 | } |
255 | |
256 | if ( $this->getOption( 'justMapping', false ) ) { |
257 | $this->validateMapping(); |
258 | return true; |
259 | } |
260 | |
261 | $this->initAnalysisConfig(); |
262 | $this->indexIdentifier = $this->unwrap( $utils->pickIndexIdentifierFromOption( |
263 | $this->getOption( 'indexIdentifier', 'current' ), $this->getIndexAliasName() ) ); |
264 | $this->validateIndex(); |
265 | $this->validateAnalyzers(); |
266 | $this->validateMapping(); |
267 | $this->validateAlias(); |
268 | $this->updateVersions(); |
269 | } catch ( \Elastica\Exception\Connection\HttpException $e ) { |
270 | $message = $e->getMessage(); |
271 | $this->output( "\nUnexpected Elasticsearch failure.\n" ); |
272 | $this->fatalError( "Http error communicating with Elasticsearch: $message.\n" ); |
273 | } catch ( \Elastica\Exception\ExceptionInterface $e ) { |
274 | $type = get_class( $e ); |
275 | $message = ElasticaErrorHandler::extractMessage( $e ); |
276 | /** @suppress PhanUndeclaredMethod ExceptionInterface has no methods */ |
277 | $trace = $e->getTraceAsString(); |
278 | $this->output( "\nUnexpected Elasticsearch failure.\n" ); |
279 | $this->fatalError( "Elasticsearch failed in an unexpected way. " . |
280 | "This is always a bug in CirrusSearch.\n" . |
281 | "Error type: $type\n" . |
282 | "Message: $message\n" . |
283 | "Trace:\n" . $trace ); |
284 | } |
285 | |
286 | return true; |
287 | } |
288 | |
289 | /** |
290 | * @suppress PhanUndeclaredMethod runChild technically returns a |
291 | * \Maintenance instance but only \CirrusSearch\Maintenance\Maintenance |
292 | * classes have the done method. Just allow it since we know what type of |
293 | * maint class is being created |
294 | */ |
295 | private function updateVersions() { |
296 | $child = $this->runChild( Metastore::class ); |
297 | $child->done(); |
298 | $child->loadParamsAndArgs( |
299 | null, |
300 | array_merge( $this->parameters->getOptions(), [ |
301 | 'index-version-basename' => $this->indexBaseName, |
302 | 'update-index-version' => true, |
303 | ] ), |
304 | $this->parameters->getArgs() |
305 | ); |
306 | $child->execute(); |
307 | $child->done(); |
308 | } |
309 | |
310 | private function validateIndex() { |
311 | if ( $this->startOver ) { |
312 | $this->createIndex( true, "Blowing away index to start over...\n" ); |
313 | } elseif ( !$this->getIndex()->exists() ) { |
314 | $this->createIndex( false, "Creating index...\n" ); |
315 | } |
316 | |
317 | $this->validateIndexSettings(); |
318 | } |
319 | |
320 | /** |
321 | * @param bool $rebuild |
322 | * @param string $msg |
323 | */ |
324 | private function createIndex( $rebuild, $msg ) { |
325 | global $wgCirrusSearchAllFields, $wgCirrusSearchExtraIndexSettings; |
326 | |
327 | $this->canCleanupCreatedIndex = true; |
328 | $index = $this->getIndex(); |
329 | $indexCreator = new \CirrusSearch\Maintenance\IndexCreator( |
330 | $index, |
331 | new ConfigUtils( $index->getClient(), $this ), |
332 | $this->analysisConfig, |
333 | $this->similarityConfig |
334 | ); |
335 | |
336 | $this->outputIndented( $msg ); |
337 | |
338 | $this->unwrap( $indexCreator->createIndex( |
339 | $rebuild, |
340 | $this->getMaxShardsPerNode(), |
341 | $this->getShardCount(), |
342 | $this->getReplicaCount(), |
343 | $this->refreshInterval, |
344 | $this->getMergeSettings(), |
345 | $wgCirrusSearchAllFields['build'], |
346 | $wgCirrusSearchExtraIndexSettings |
347 | ) ); |
348 | |
349 | $this->outputIndented( "Index created.\n" ); |
350 | } |
351 | |
352 | /** |
353 | * @return \CirrusSearch\Maintenance\Validators\Validator[] |
354 | */ |
355 | private function getIndexSettingsValidators() { |
356 | $validators = []; |
357 | $validators[] = new \CirrusSearch\Maintenance\Validators\NumberOfShardsValidator( |
358 | $this->getIndex(), $this->getShardCount(), $this ); |
359 | $validators[] = new \CirrusSearch\Maintenance\Validators\ReplicaRangeValidator( |
360 | $this->getIndex(), $this->getReplicaCount(), $this ); |
361 | $validators[] = $this->getShardAllocationValidator(); |
362 | $validators[] = new \CirrusSearch\Maintenance\Validators\MaxShardsPerNodeValidator( |
363 | $this->getIndex(), $this->getMaxShardsPerNode(), $this ); |
364 | return $validators; |
365 | } |
366 | |
367 | private function validateIndexSettings() { |
368 | $validators = $this->getIndexSettingsValidators(); |
369 | foreach ( $validators as $validator ) { |
370 | $this->unwrap( $validator->validate() ); |
371 | } |
372 | } |
373 | |
374 | private function validateAnalyzers() { |
375 | $validator = new \CirrusSearch\Maintenance\Validators\AnalyzersValidator( |
376 | $this->getIndex(), $this->analysisConfig, $this ); |
377 | $validator->printDebugCheckConfig( $this->printDebugCheckConfig ); |
378 | $this->unwrap( $validator->validate() ); |
379 | } |
380 | |
381 | private function validateMapping() { |
382 | $validator = new MappingValidator( |
383 | $this->getIndex(), |
384 | $this->masterTimeout, |
385 | $this->optimizeIndexForExperimentalHighlighter, |
386 | $this->availablePlugins, |
387 | $this->mapping, |
388 | $this |
389 | ); |
390 | $validator->printDebugCheckConfig( $this->printDebugCheckConfig ); |
391 | $this->unwrap( $validator->validate() ); |
392 | } |
393 | |
394 | private function validateAlias() { |
395 | $this->outputIndented( "Validating aliases...\n" ); |
396 | // Since validate the specific alias first as that can cause reindexing |
397 | // and we want the all index to stay with the old index during reindexing |
398 | $this->validateSpecificAlias(); |
399 | // At this point the index is live and under no circumstances should it be |
400 | // automatically deleted. |
401 | $this->canCleanupCreatedIndex = false; |
402 | |
403 | if ( $this->indexSuffix !== Connection::ARCHIVE_INDEX_SUFFIX ) { |
404 | // Do not add the archive index to the global alias |
405 | $this->validateAllAlias(); |
406 | } |
407 | } |
408 | |
409 | /** |
410 | * Validate the alias that is just for this index's type. |
411 | */ |
412 | private function validateSpecificAlias() { |
413 | $connection = $this->getConnection(); |
414 | |
415 | $reindexer = new Reindexer( |
416 | $this->getSearchConfig(), |
417 | $connection, |
418 | $connection, |
419 | $this->getIndex(), |
420 | $this->getOldIndex(), |
421 | $this, |
422 | array_filter( explode( ',', $this->getOption( 'fieldsToDelete', '' ) ) ) |
423 | ); |
424 | |
425 | $validator = new \CirrusSearch\Maintenance\Validators\SpecificAliasValidator( |
426 | $this->getConnection()->getClient(), |
427 | $this->getIndexAliasName(), |
428 | $this->getSpecificIndexName(), |
429 | $this->startOver, |
430 | $reindexer, |
431 | [ |
432 | $this->reindexSlices, |
433 | $this->reindexChunkSize, |
434 | $this->reindexAcceptableCountDeviation |
435 | ], |
436 | $this->getIndexSettingsValidators(), |
437 | $this->reindexAndRemoveOk, |
438 | $this |
439 | ); |
440 | $this->unwrap( $validator->validate() ); |
441 | } |
442 | |
443 | public function validateAllAlias() { |
444 | $validator = new \CirrusSearch\Maintenance\Validators\IndexAllAliasValidator( |
445 | $this->getConnection()->getClient(), |
446 | $this->getIndexName(), |
447 | $this->getSpecificIndexName(), |
448 | $this->startOver, |
449 | $this->getIndexAliasName(), |
450 | $this |
451 | ); |
452 | $this->unwrap( $validator->validate() ); |
453 | } |
454 | |
455 | /** |
456 | * @return \CirrusSearch\Maintenance\Validators\Validator |
457 | */ |
458 | private function getShardAllocationValidator() { |
459 | global $wgCirrusSearchIndexAllocation; |
460 | return new \CirrusSearch\Maintenance\Validators\ShardAllocationValidator( |
461 | $this->getIndex(), $wgCirrusSearchIndexAllocation, $this ); |
462 | } |
463 | |
464 | protected function validateShardAllocation() { |
465 | $this->unwrap( $this->getShardAllocationValidator()->validate() ); |
466 | } |
467 | |
468 | /** |
469 | * @param string $langCode |
470 | * @param array $availablePlugins |
471 | * @return AnalysisConfigBuilder |
472 | */ |
473 | private function pickAnalyzer( $langCode, array $availablePlugins = [] ) { |
474 | $analysisConfigBuilder = new \CirrusSearch\Maintenance\AnalysisConfigBuilder( |
475 | $langCode, $availablePlugins ); |
476 | $this->outputIndented( 'Picking analyzer...' . |
477 | $analysisConfigBuilder->getDefaultTextAnalyzerType( $langCode ) . |
478 | "\n" ); |
479 | return $analysisConfigBuilder; |
480 | } |
481 | |
482 | /** |
483 | * @throws \ConfigException |
484 | */ |
485 | protected function initMappingConfigBuilder() { |
486 | $configFlags = 0; |
487 | if ( $this->prefixSearchStartsWithAny ) { |
488 | $configFlags |= MappingConfigBuilder::PREFIX_START_WITH_ANY; |
489 | } |
490 | if ( $this->phraseSuggestUseText ) { |
491 | $configFlags |= MappingConfigBuilder::PHRASE_SUGGEST_USE_TEXT; |
492 | } |
493 | switch ( $this->indexSuffix ) { |
494 | case Connection::ARCHIVE_DOC_TYPE: |
495 | $mappingConfigBuilder = new ArchiveMappingConfigBuilder( $this->optimizeIndexForExperimentalHighlighter, $configFlags ); |
496 | break; |
497 | default: |
498 | $mappingConfigBuilder = new MappingConfigBuilder( $this->optimizeIndexForExperimentalHighlighter, $configFlags ); |
499 | } |
500 | $this->mapping = $mappingConfigBuilder->buildConfig(); |
501 | $this->safeToOptimizeAnalysisConfig = $mappingConfigBuilder->canOptimizeAnalysisConfig(); |
502 | } |
503 | |
504 | /** |
505 | * @return \Elastica\Index being updated |
506 | */ |
507 | public function getIndex() { |
508 | return $this->getConnection()->getIndex( |
509 | $this->indexBaseName, $this->indexSuffix, $this->indexIdentifier ); |
510 | } |
511 | |
512 | /** |
513 | * @return string name of the index being updated |
514 | */ |
515 | protected function getSpecificIndexName() { |
516 | return $this->getConnection()->getIndexName( |
517 | $this->indexBaseName, $this->indexSuffix, $this->indexIdentifier ); |
518 | } |
519 | |
520 | /** |
521 | * @return string name of the index type being updated |
522 | */ |
523 | protected function getIndexAliasName() { |
524 | return $this->getConnection()->getIndexName( $this->indexBaseName, $this->indexSuffix ); |
525 | } |
526 | |
527 | /** |
528 | * @return string |
529 | */ |
530 | protected function getIndexName() { |
531 | return $this->getConnection()->getIndexName( $this->indexBaseName ); |
532 | } |
533 | |
534 | /** |
535 | * @return \Elastica\Index |
536 | */ |
537 | protected function getOldIndex() { |
538 | return $this->getConnection()->getIndex( $this->indexBaseName, $this->indexSuffix ); |
539 | } |
540 | |
541 | /** |
542 | * Get the merge settings for this index. |
543 | * @return array |
544 | */ |
545 | private function getMergeSettings() { |
546 | global $wgCirrusSearchMergeSettings; |
547 | |
548 | return $wgCirrusSearchMergeSettings[$this->indexSuffix] |
549 | // If there aren't configured merge settings for this index type |
550 | // default to the content type. |
551 | ?? $wgCirrusSearchMergeSettings['content'] |
552 | // It's also fine to not specify merge settings. |
553 | ?? []; |
554 | } |
555 | |
556 | /** |
557 | * @return int Number of shards this index should have |
558 | */ |
559 | private function getShardCount() { |
560 | return $this->getConnection()->getSettings()->getShardCount( $this->indexSuffix ); |
561 | } |
562 | |
563 | /** |
564 | * @return string Number of replicas this index should have. May be a range such as '0-2' |
565 | */ |
566 | private function getReplicaCount() { |
567 | return $this->getConnection()->getSettings()->getReplicaCount( $this->indexSuffix ); |
568 | } |
569 | |
570 | /** |
571 | * @return int Maximum number of shards that can be allocated on a single elasticsearch |
572 | * node. -1 for unlimited. |
573 | */ |
574 | private function getMaxShardsPerNode() { |
575 | return $this->getConnection()->getSettings()->getMaxShardsPerNode( $this->indexSuffix ); |
576 | } |
577 | |
578 | private function initAnalysisConfig() { |
579 | $analysisConfigBuilder = $this->pickAnalyzer( $this->langCode, $this->availablePlugins ); |
580 | |
581 | $this->analysisConfig = $analysisConfigBuilder->buildConfig(); |
582 | if ( $this->safeToOptimizeAnalysisConfig ) { |
583 | $filter = new AnalysisFilter(); |
584 | $deduplicate = $this->getSearchConfig()->get( 'CirrusSearchDeduplicateAnalysis' ); |
585 | // A bit adhoc, this is the list of analyzers that should not be renamed, because |
586 | // they are referenced at query time. |
587 | $protected = [ 'token_reverse' ]; |
588 | list( $this->analysisConfig, $this->mapping ) = $filter |
589 | ->filterAnalysis( $this->analysisConfig, $this->mapping, $deduplicate, $protected ); |
590 | } |
591 | $this->similarityConfig = $analysisConfigBuilder->buildSimilarityConfig(); |
592 | } |
593 | |
594 | /** |
595 | * Output a message and terminate the current script. |
596 | * |
597 | * @param string $msg Error Message |
598 | * @param int $exitCode PHP exit status. Should be in range 1-254 |
599 | * @return never |
600 | */ |
601 | protected function fatalError( $msg, $exitCode = 1 ) { |
602 | try { |
603 | if ( $this->canCleanupCreatedIndex && $this->getIndex()->exists() ) { |
604 | $utils = new ConfigUtils( $this->getConnection()->getClient(), $this ); |
605 | $indexName = $this->getSpecificIndexName(); |
606 | $status = $utils->isIndexLive( $indexName ); |
607 | if ( !$status->isGood() ) { |
608 | $this->output( (string)$status ); |
609 | } elseif ( $status->getValue() === false ) { |
610 | $this->output( "Cleaning up incomplete index {$indexName}\n" ); |
611 | $this->getIndex()->delete(); |
612 | } |
613 | } |
614 | } catch ( \Elastica\Exception\ExceptionInterface $e ) { |
615 | $this->output( "Exception thrown while cleaning up created index: $e\n" ); |
616 | } finally { |
617 | parent::fatalError( $msg, $exitCode ); |
618 | } |
619 | } |
620 | } |
621 | |
622 | $maintClass = UpdateOneSearchIndexConfig::class; |
623 | require_once RUN_MAINTENANCE_IF_MAIN; |