IndexService::renameIndex()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
declare (strict_types=1);
3
4
namespace T3G\Elasticorn\Service;
5
6
use Elastica\Client;
7
use Elastica\Index;
8
use Elastica\Reindex;
9
use Psr\Log\LoggerInterface;
10
use T3G\Elasticorn\Configuration\ApplicationConfiguration;
11
12
/**
13
 * Class IndexService
14
 *
15
 */
16
class IndexService
17
{
18
    /**
19
     * @var \Elastica\Client
20
     */
21
    protected $client;
22
23
    /**
24
     * @var Index
25
     */
26
    private $index;
27
28
    /**
29
     * @var \Psr\Log\LoggerInterface
30
     */
31
    private $logger;
32
33
    /**
34
     * @var \T3G\Elasticorn\Service\ConfigurationService
35
     */
36
    private $configurationService;
37
38
    /**
39
     * IndexService constructor.
40
     *
41
     * @param \Elastica\Client                             $client
42
     * @param \T3G\Elasticorn\Service\ConfigurationService $configurationService
43
     * @param \Psr\Log\LoggerInterface                     $logger
44
     * @param string                                       $indexName
45
     */
46
    public function __construct(
47
        Client $client,
48
        ConfigurationService $configurationService,
49
        LoggerInterface $logger,
50
        string $indexName = null
51
    ) {
52
        $this->client = $client;
53
        $this->logger = $logger;
54
        $this->configurationService = $configurationService;
55
        if (null !== $indexName) {
56
            $this->index = $this->client->getIndex($indexName);
57
        }
58
    }
59
60
    /**
61
     * @return Index|null
62
     */
63
    public function getIndex()
64
    {
65
        return $this->index;
66
    }
67
68
    /**
69
     * Rename an index (creates new index with data from old)
70
     * CAUTION: All mappings are lost, only data is preserved
71
     *
72
     * @param string $newName
73
     */
74
    public function renameIndex(string $newName)
75
    {
76
        $newIndex = $this->client->getIndex($newName);
77
        $newIndex->create();
78
        $reindex = new Reindex($this->index, $newIndex);
79
        $reindex->run();
80
        $this->index->delete();
81
    }
82
83
    /**
84
     * Add all indices found in configuration directory
85
     * Creates indices with suffixes _a and _b and adds an alias as indexName
86
     */
87
    public function initIndices()
88
    {
89
        $indexConfigurations = $this->configurationService->getIndexConfigurations();
90
        foreach ($indexConfigurations as $indexName => $configuration) {
91
            $this->createIndex($indexName, $configuration);
92
        }
93
    }
94
95
    /**
96
     * Initializes a single index from config files
97
     *
98
     * @param string $indexName
99
     */
100
    public function initIndex(string $indexName)
101
    {
102
        $configuration = $this->configurationService->getIndexConfiguration($indexName);
103
104
        $this->createIndex($indexName, $configuration);
105
    }
106
107
    /**
108
     * Copy data from oldIndexName to newIndexName
109
     *
110
     * @param string $oldIndexName
111
     * @param string $newIndexName
112
     */
113
    public function copyData(string $oldIndexName, string $newIndexName)
114
    {
115
        $oldIndex = $this->client->getIndex($oldIndexName);
116
        $newIndex = $this->client->getIndex($newIndexName);
117
        $reindex = new Reindex($oldIndex, $newIndex);
118
        $reindex->run();
119
    }
120
121
    /**
122
     * Re-apply mappings to all indices found in configuration directory
123
     *
124
     * @see remap($indexName)
125
     *
126
     * @param bool $force
127
     */
128
    public function remapAll(bool $force = false)
129
    {
130
        $indexConfigurations = $this->configurationService->getIndexConfigurations();
131
        $indices = array_keys($indexConfigurations);
132
        foreach ($indices as $indexName) {
133
            $this->remap($indexName, $force);
134
        }
135
    }
136
137
    /**
138
     * @return array
139
     */
140
    public function getMappingForIndex()
141
    {
142
        $this->logger->debug('Get current mapping for ' . $this->index->getName());
143
        return $this->index->getMapping();
144
    }
145
146
147
    /**
148
     * Remap an index
149
     *
150
     * Drops and recreates the current inactive index, applies mappings and imports data from active index
151
     * After successfully importing data the alias gets set to the new index
152
     *
153
     * @param string $indexName
154
     * @param bool   $force
155
     *
156
     * @throws \InvalidArgumentException
157
     */
158
    public function remap(string $indexName, bool $force = false)
159
    {
160
        if ($this->configurationService->compareMappingConfiguration($indexName) === '') {
161
            if (false === $force) {
162
                $this->logger->info('No difference between configurations, no remapping done');
163
                return;
164
            } else {
165
                $this->logger->info('No difference between configurations but force given, remapping anyway.');
166
            }
167
        }
168
        $languages = ApplicationConfiguration::getLanguageConfiguration();
169
        if (count($languages) > 0) {
170
            foreach ($languages as $language) {
171
                $this->remapIndex($indexName, $language);
172
            }
173
            $this->logger->debug(
174
                'Adding alias to ' . $indexName . '_' . $languages[0] . ' from ' . $indexName
175
            );
176
            $this->client->getIndex($indexName . '_' . $languages[0])->addAlias($indexName);
177
        } else {
178
            $this->remapIndex($indexName);
179
        }
180
    }
181
182
    /**
183
     * @param string $indexName
184
     */
185
    private function remapIndex(string $indexName, string $language = '')
186
    {
187
        $aliasName = $indexName;
188
        $languageMessage = '';
189
        if ($language) {
190
            $aliasName .= '_' . $language;
191
            $languageMessage = ' in language ' . $language;
192
        }
193
        $this->logger->info('Remapping ' . $indexName . $languageMessage);
194
        $primaryIndexIdentifier = $this->getFullIndexIdentifier($indexName, 'a', $language);
195
        $indexA = $this->client->getIndex($primaryIndexIdentifier);
196
        if ($indexA->exists()) {
197
            $indexB = $this->client->getIndex($this->getFullIndexIdentifier($indexName, 'b', $language));
198
199
            if ($indexA->hasAlias($aliasName)) {
200
                $activeIndex = $indexA;
201
                $inactiveIndex = $indexB;
202
            } elseif ($indexB->hasAlias($aliasName)) {
203
                $activeIndex = $indexB;
204
                $inactiveIndex = $indexA;
205
            } else {
206
                throw new \InvalidArgumentException('No active index with name ' . $indexName . ' found.');
207
            }
208
209
            $this->recreateIndex($indexName, $inactiveIndex, $language);
210
            $this->logger->debug('Reindexing data with new mapping.');
211
            $reindex = new Reindex($activeIndex, $inactiveIndex);
212
            $reindex->run();
213
            $this->switchAlias($aliasName, $activeIndex, $inactiveIndex);
214
        } else {
215
            $configuration = $this->configurationService->getIndexConfigurations();
216
            $this->createPrimaryIndex($indexName, $configuration, $language);
217
            $this->createSecondaryIndex($indexName, $configuration, $language);
218
        }
219
    }
220
221
    /**
222
     * @param string          $indexName
223
     * @param \Elastica\Index $activeIndex
224
     * @param \Elastica\Index $inactiveIndex
225
     */
226
    private function switchAlias(string $indexName, Index $activeIndex, Index $inactiveIndex)
227
    {
228
        $this->logger->debug('Switching alias from ' . $activeIndex->getName() . ' to ' . $inactiveIndex->getName());
229
        $activeIndex->removeAlias($indexName);
230
        $inactiveIndex->addAlias($indexName);
231
    }
232
233
    /**
234
     * @param string $indexName
235
     * @param array  $configuration
236
     *
237
     * @throws \InvalidArgumentException
238
     */
239
    private function createIndex(string $indexName, array $configuration)
240
    {
241
        $this->logger->info('Creating index ' . $indexName);
242
        $index = $this->client->getIndex($indexName);
243
        $languages = ApplicationConfiguration::getLanguageConfiguration();
244
        if (!$index->exists()) {
245
            if (count($languages) > 0) {
246
                foreach ($languages as $language) {
247
                    $this->createPrimaryIndex($indexName, $configuration, $language);
248
                    $this->createSecondaryIndex($indexName, $configuration, $language);
249
                }
250
                $primaryIndex = $this->client->getIndex($indexName . '_' . $languages[0]);
251
                $primaryIndex->addAlias($indexName);
252
            } else {
253
                $this->createPrimaryIndex($indexName, $configuration);
254
                $this->createSecondaryIndex($indexName, $configuration);
255
            }
256
        } else {
257
            throw new \InvalidArgumentException('Index ' . $indexName . ' already exists.');
258
        }
259
    }
260
261
    private function createPrimaryIndex(string $indexName, array $configuration, string $language = '')
262
    {
263
        $index = $this->client->getIndex($this->getFullIndexIdentifier($indexName, 'a', $language));
264
        $this->createWithMapping($indexName, $index, $configuration, $language);
265
        $index->addAlias($indexName . ($language ? '_' . $language : ''));
266
    }
267
268
    /**
269
     * @param string $indexName
270
     * @param array  $configuration
271
     * @param string $language
272
     */
273
    private function createSecondaryIndex(string $indexName, array $configuration, string $language = '')
274
    {
275
        $index = $this->client->getIndex($this->getFullIndexIdentifier($indexName, 'b', $language));
276
        $this->createWithMapping($indexName, $index, $configuration, $language);
277
    }
278
279
    private function getFullIndexIdentifier(string $indexName, string $suffix = '', string $language = '')
280
    {
281
        return $indexName . ($language ? '_' . $language : '') . ($suffix ? '_' . $suffix : '');
282
    }
283
284
    /**
285
     * @param string          $indexName
286
     * @param \Elastica\Index $index
287
     * @param                 $indexConfiguration
288
     */
289
    private function createWithMapping(
290
        string $indexName,
291
        Index $index,
292
        array $indexConfiguration,
293
        string $language = ''
294
    ) {
295
        $index->create($indexConfiguration);
296
        $this->logger->debug('Creating index ' . $indexName);
297
        $this->configurationService->applyMapping($indexName, $index, $language);
298
    }
299
300
    /**
301
     * @param string $indexName
302
     * @param Index  $index
303
     * @param string $language
304
     */
305
    private function recreateIndex(string $indexName, Index $index, string $language)
306
    {
307
        $indexConfiguration = $this->configurationService->getIndexConfiguration($indexName);
308
        $this->logger->debug('Deleting index ' . $indexName);
309
        $index->delete();
310
        $this->createWithMapping($indexName, $index, $indexConfiguration, $language);
311
    }
312
}
313