Completed
Push — master ( 2566be...2cb116 )
by Maxence
01:40 queued 10s
created

ElasticSearchPlatform::indexDocumentError()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
declare(strict_types=1);
3
4
5
/**
6
 * FullTextSearch_Elasticsearch - Use Elasticsearch to index the content of your nextcloud
7
 *
8
 * This file is licensed under the Affero General Public License version 3 or
9
 * later. See the COPYING file.
10
 *
11
 * @author Maxence Lange <[email protected]>
12
 * @copyright 2018
13
 * @license GNU AGPL version 3 or any later version
14
 *
15
 * This program is free software: you can redistribute it and/or modify
16
 * it under the terms of the GNU Affero General Public License as
17
 * published by the Free Software Foundation, either version 3 of the
18
 * License, or (at your option) any later version.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU Affero General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Affero General Public License
26
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
27
 *
28
 */
29
30
31
namespace OCA\FullTextSearch_Elasticsearch\Platform;
32
33
34
use daita\MySmallPhpTools\Traits\TPathTools;
35
use Elasticsearch\Client;
36
use Elasticsearch\ClientBuilder;
37
use Elasticsearch\Common\Exceptions\BadRequest400Exception;
38
use Exception;
39
use OCA\FullTextSearch_Elasticsearch\Exceptions\AccessIsEmptyException;
40
use OCA\FullTextSearch_Elasticsearch\Exceptions\ConfigurationException;
41
use OCA\FullTextSearch_Elasticsearch\Service\ConfigService;
42
use OCA\FullTextSearch_Elasticsearch\Service\IndexService;
43
use OCA\FullTextSearch_Elasticsearch\Service\MiscService;
44
use OCA\FullTextSearch_Elasticsearch\Service\SearchService;
45
use OCP\FullTextSearch\IFullTextSearchPlatform;
46
use OCP\FullTextSearch\Model\IDocumentAccess;
47
use OCP\FullTextSearch\Model\IIndex;
48
use OCP\FullTextSearch\Model\IIndexDocument;
49
use OCP\FullTextSearch\Model\IRunner;
50
use OCP\FullTextSearch\Model\ISearchResult;
51
52
53
/**
54
 * Class ElasticSearchPlatform
55
 *
56
 * @package OCA\FullTextSearch_Elasticsearch\Platform
57
 */
58
class ElasticSearchPlatform implements IFullTextSearchPlatform {
59
60
61
	use TPathTools;
0 ignored issues
show
Deprecated Code introduced by
The trait daita\MySmallPhpTools\Traits\TPathTools has been deprecated with message: - 19

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
62
63
64
	/** @var ConfigService */
65
	private $configService;
66
67
	/** @var IndexService */
68
	private $indexService;
69
70
	/** @var SearchService */
71
	private $searchService;
72
73
	/** @var MiscService */
74
	private $miscService;
75
76
	/** @var Client */
77
	private $client;
78
79
	/** @var IRunner */
80
	private $runner;
81
82
83
	/**
84
	 * ElasticSearchPlatform constructor.
85
	 *
86
	 * @param ConfigService $configService
87
	 * @param IndexService $indexService
88
	 * @param SearchService $searchService
89
	 * @param MiscService $miscService
90
	 */
91
	public function __construct(
92
		ConfigService $configService, IndexService $indexService, SearchService $searchService,
93
		MiscService $miscService
94
	) {
95
		$this->configService = $configService;
96
		$this->indexService = $indexService;
97
		$this->searchService = $searchService;
98
		$this->miscService = $miscService;
99
	}
100
101
102
	/**
103
	 * return a unique Id of the platform.
104
	 */
105
	public function getId(): string {
106
		return 'elastic_search';
107
	}
108
109
110
	/**
111
	 * return a unique Id of the platform.
112
	 */
113
	public function getName(): string {
114
		return 'Elasticsearch';
115
	}
116
117
118
	/**
119
	 * @return array
120
	 * @throws ConfigurationException
121
	 */
122
	public function getConfiguration(): array {
123
		$result = $this->configService->getConfig();
124
125
		$sanitizedHosts = [];
126
		$hosts = $this->configService->getElasticHost();
127
		foreach ($hosts as $host) {
128
			$parsedHost = parse_url($host);
129
			$safeHost = $parsedHost['scheme'] . '://';
130
			if (array_key_exists('user', $parsedHost)) {
131
				$safeHost .= $parsedHost['user'] . ':' . '********' . '@';
132
			}
133
			$safeHost .= $parsedHost['host'];
134
			$safeHost .= ':' . $parsedHost['port'];
135
136
			$sanitizedHosts[] = $safeHost;
137
		}
138
139
		$result['elastic_host'] = $sanitizedHosts;
140
141
		return $result;
142
	}
143
144
145
	/**
146
	 * @param IRunner $runner
147
	 */
148
	public function setRunner(IRunner $runner) {
149
		$this->runner = $runner;
150
	}
151
152
153
	/**
154
	 * Called when loading the platform.
155
	 *
156
	 * Loading some container and connect to ElasticSearch.
157
	 *
158
	 * @throws ConfigurationException
159
	 * @throws Exception
160
	 */
161
	public function loadPlatform() {
162
		try {
163
			$this->connectToElastic($this->configService->getElasticHost());
164
		} catch (ConfigurationException $e) {
165
			throw $e;
166
		}
167
	}
168
169
170
	/**
171
	 * not used yet.
172
	 *
173
	 * @return bool
174
	 */
175
	public function testPlatform(): bool {
176
		return $this->client->ping();
177
	}
178
179
180
	/**
181
	 * called before any index
182
	 *
183
	 * We create a general index.
184
	 *
185
	 * @throws ConfigurationException
186
	 * @throws BadRequest400Exception
187
	 */
188
	public function initializeIndex() {
189
		$this->indexService->initializeIndex($this->client);
190
	}
191
192
193
	/**
194
	 * resetIndex();
195
	 *
196
	 * Called when admin wants to remove an index specific to a $provider.
197
	 * $provider can be null, meaning a reset of the whole index.
198
	 *
199
	 * @param string $providerId
200
	 *
201
	 * @throws ConfigurationException
202
	 */
203
	public function resetIndex(string $providerId) {
204
		if ($providerId === 'all') {
205
			$this->indexService->resetIndexAll($this->client);
206
		} else {
207
			$this->indexService->resetIndex($this->client, $providerId);
208
		}
209
	}
210
211
212
	/**
213
	 * @param IIndexDocument $document
214
	 *
215
	 * @return IIndex
216
	 */
217
	public function indexDocument(IIndexDocument $document): IIndex {
218
219
		$document->initHash();
220
221
		try {
222
			$result = $this->indexService->indexDocument($this->client, $document);
223
			$index = $this->indexService->parseIndexResult($document->getIndex(), $result);
224
225
			$this->updateNewIndexResult(
226
				$document->getIndex(), json_encode($result), 'ok',
227
				IRunner::RESULT_TYPE_SUCCESS
228
			);
229
230
			return $index;
231
		} catch (Exception $e) {
232
			$this->updateNewIndexResult(
233
				$document->getIndex(), '', 'issue while indexing, testing with empty content',
234
				IRunner::RESULT_TYPE_WARNING
235
			);
236
237
			$this->manageIndexErrorException($document, $e);
238
		}
239
240
		try {
241
			$result = $this->indexDocumentError($document, $e);
242
			$index = $this->indexService->parseIndexResult($document->getIndex(), $result);
243
244
			$this->updateNewIndexResult(
245
				$document->getIndex(), json_encode($result), 'ok',
246
				IRunner::RESULT_TYPE_WARNING
247
			);
248
249
			return $index;
250
		} catch (Exception $e) {
251
			$this->updateNewIndexResult(
252
				$document->getIndex(), '', 'fail',
253
				IRunner::RESULT_TYPE_FAIL
254
			);
255
			$this->manageIndexErrorException($document, $e);
256
		}
257
258
		return $document->getIndex();
259
	}
260
261
262
	/**
263
	 * @param IIndexDocument $document
264
	 * @param Exception $e
265
	 *
266
	 * @return array
267
	 * @throws AccessIsEmptyException
268
	 * @throws ConfigurationException
269
	 * @throws Exception
270
	 */
271
	private function indexDocumentError(IIndexDocument $document, Exception $e): array {
272
273
		$this->updateRunnerAction('indexDocumentWithoutContent', true);
274
275
		$document->setContent('');
276
//		$index = $document->getIndex();
277
//		$index->unsetStatus(Index::INDEX_CONTENT);
278
279
		return $this->indexService->indexDocument($this->client, $document);
280
	}
281
282
283
	/**
284
	 * @param IIndexDocument $document
285
	 * @param Exception $e
286
	 */
287
	private function manageIndexErrorException(IIndexDocument $document, Exception $e) {
288
289
		$message = $this->parseIndexErrorException($e);
290
		$document->getIndex()
291
				 ->addError($message, get_class($e), IIndex::ERROR_SEV_3);
292
		$this->updateNewIndexError(
293
			$document->getIndex(), $message, get_class($e), IIndex::ERROR_SEV_3
294
		);
295
	}
296
297
298
	/**
299
	 * @param Exception $e
300
	 *
301
	 * @return string
302
	 */
303
	private function parseIndexErrorException(Exception $e): string {
304
305
		$arr = json_decode($e->getMessage(), true);
306
		if (!is_array($arr)) {
307
			return $e->getMessage();
308
		}
309
310
		if (array_key_exists('reason', $arr['error']['root_cause'][0])) {
311
			return $arr['error']['root_cause'][0]['reason'];
312
		}
313
314
		return $e->getMessage();
315
	}
316
317
318
	/**
319
	 * {@inheritdoc}
320
	 * @throws ConfigurationException
321
	 */
322
	public function deleteIndexes(array $indexes) {
323
		try {
324
			$this->indexService->deleteIndexes($this->client, $indexes);
325
		} catch (ConfigurationException $e) {
326
			throw $e;
327
		}
328
	}
329
330
331
	/**
332
	 * {@inheritdoc}
333
	 * @throws Exception
334
	 */
335
	public function searchRequest(ISearchResult $result, IDocumentAccess $access) {
336
		$this->searchService->searchRequest($this->client, $result, $access);
337
	}
338
339
340
	/**
341
	 * @param string $providerId
342
	 * @param string $documentId
343
	 *
344
	 * @return IIndexDocument
345
	 * @throws ConfigurationException
346
	 */
347
	public function getDocument(string $providerId, string $documentId): IIndexDocument {
348
		return $this->searchService->getDocument($this->client, $providerId, $documentId);
349
	}
350
351
352
	private function cleanHost($host) {
353
		return $this->withoutEndSlash($host, false, false);
354
	}
355
356
	/**
357
	 * @param array $hosts
358
	 *
359
	 * @throws Exception
360
	 */
361
	private function connectToElastic(array $hosts) {
362
363
		try {
364
			$hosts = array_map([$this, 'cleanHost'], $hosts);
365
			$this->client = ClientBuilder::create()
366
										 ->setHosts($hosts)
367
										 ->setRetries(3)
368
										 ->build();
369
370
//		}
371
//		catch (CouldNotConnectToHost $e) {
372
//			$this 'CouldNotConnectToHost';
373
//			$previous = $e->getPrevious();
374
//			if ($previous instanceof MaxRetriesException) {
375
//				echo "Max retries!";
376
//			}
377
		} catch (Exception $e) {
378
			throw $e;
379
//			echo ' ElasticSearchPlatform::load() Exception --- ' . $e->getMessage() . "\n";
380
		}
381
	}
382
383
384
	/**
385
	 * @param string $action
386
	 * @param bool $force
387
	 *
388
	 * @throws Exception
389
	 */
390
	private function updateRunnerAction(string $action, bool $force = false) {
391
		if ($this->runner === null) {
392
			return;
393
		}
394
395
		$this->runner->updateAction($action, $force);
396
	}
397
398
399
	/**
400
	 * @param IIndex $index
401
	 * @param string $message
402
	 * @param string $exception
403
	 * @param int $sev
404
	 */
405
	private function updateNewIndexError(IIndex $index, string $message, string $exception, int $sev
406
	) {
407
		if ($this->runner === null) {
408
			return;
409
		}
410
411
		$this->runner->newIndexError($index, $message, $exception, $sev);
412
	}
413
414
415
	/**
416
	 * @param IIndex $index
417
	 * @param string $message
418
	 * @param string $status
419
	 * @param int $type
420
	 */
421
	private function updateNewIndexResult(IIndex $index, string $message, string $status, int $type
422
	) {
423
		if ($this->runner === null) {
424
			return;
425
		}
426
427
		$this->runner->newIndexResult($index, $message, $status, $type);
428
	}
429
430
431
}
432