Completed
Push — master ( bd89d8...052aca )
by Maxence
01:34
created

ElasticSearchPlatform::indexDocumentError()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 26
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 26
rs 8.8571
cc 2
eloc 17
nc 2
nop 3
1
<?php
2
/**
3
 * FullTextSearch_ElasticSearch - Use Elasticsearch to index the content of your nextcloud
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Maxence Lange <[email protected]>
9
 * @copyright 2018
10
 * @license GNU AGPL version 3 or any later version
11
 *
12
 * This program is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License as
14
 * published by the Free Software Foundation, either version 3 of the
15
 * License, or (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
24
 *
25
 */
26
27
namespace OCA\FullTextSearch_ElasticSearch\Platform;
28
29
use Elasticsearch\Client;
30
use Elasticsearch\ClientBuilder;
31
use Elasticsearch\Common\Exceptions\Curl\CouldNotConnectToHost;
32
use Elasticsearch\Common\Exceptions\MaxRetriesException;
33
use Elasticsearch\Common\Exceptions\RuntimeException;
34
use Elasticsearch\Common\Exceptions\ServerErrorResponseException;
35
use Exception;
36
use OCA\FullTextSearch\Exceptions\InterruptException;
37
use OCA\FullTextSearch\Exceptions\TickDoesNotExistException;
38
use OCA\FullTextSearch\IFullTextSearchPlatform;
39
use OCA\FullTextSearch\IFullTextSearchProvider;
40
use OCA\FullTextSearch\Model\DocumentAccess;
41
use OCA\FullTextSearch\Model\Index;
42
use OCA\FullTextSearch\Model\IndexDocument;
43
use OCA\FullTextSearch\Model\Runner;
44
use OCA\FullTextSearch_ElasticSearch\AppInfo\Application;
45
use OCA\FullTextSearch_ElasticSearch\Exceptions\ConfigurationException;
46
use OCA\FullTextSearch_ElasticSearch\Service\ConfigService;
47
use OCA\FullTextSearch_ElasticSearch\Service\IndexService;
48
use OCA\FullTextSearch_ElasticSearch\Service\MiscService;
49
use OCA\FullTextSearch_ElasticSearch\Service\SearchService;
50
use OCP\AppFramework\QueryException;
51
52
53
class ElasticSearchPlatform implements IFullTextSearchPlatform {
54
55
	/** @var ConfigService */
56
	private $configService;
57
58
	/** @var IndexService */
59
	private $indexService;
60
61
	/** @var SearchService */
62
	private $searchService;
63
64
	/** @var MiscService */
65
	private $miscService;
66
67
	/** @var Client */
68
	private $client;
69
70
	/** @var Runner */
71
	private $runner;
72
73
74
	/**
75
	 * return a unique Id of the platform.
76
	 */
77
	public function getId() {
78
		return 'elastic_search';
79
	}
80
81
	/**
82
	 * return a unique Id of the platform.
83
	 */
84
	public function getName() {
85
		return 'ElasticSearch';
86
	}
87
88
89
	public function getClient() {
90
		return $this->client;
91
	}
92
93
94
	/**
95
	 * @param Runner $runner
96
	 */
97
	public function setRunner(Runner $runner) {
98
		$this->runner = $runner;
99
	}
100
101
	/**
102
	 * @param $action
103
	 *
104
	 * @throws InterruptException
105
	 * @throws TickDoesNotExistException
106
	 */
107
	private function updateRunner($action) {
108
		if ($this->runner === null) {
109
			return;
110
		}
111
112
		$this->runner->update($action);
113
	}
114
115
116
	/**
117
	 * @param $line
118
	 */
119
	private function outputRunner($line) {
120
		if ($this->runner === null) {
121
			return;
122
		}
123
124
		$this->runner->output($line);
125
	}
126
127
128
	/**
129
	 * Called when loading the platform.
130
	 *
131
	 * Loading some container and connect to ElasticSearch.
132
	 *
133
	 * @throws ConfigurationException
134
	 * @throws QueryException
135
	 */
136
	public function loadPlatform() {
137
		$app = new Application();
138
139
		$container = $app->getContainer();
140
		$this->configService = $container->query(ConfigService::class);
141
		$this->indexService = $container->query(IndexService::class);
142
		$this->searchService = $container->query(SearchService::class);
143
		$this->miscService = $container->query(MiscService::class);
144
145
		try {
146
			$this->connectToElastic($this->configService->getElasticHost());
147
		} catch (ConfigurationException $e) {
148
			throw $e;
149
		}
150
	}
151
152
153
	/**
154
	 * not used yet.
155
	 */
156
	public function testPlatform() {
157
	}
158
159
160
	/**
161
	 * called before any index
162
	 *
163
	 * We create a general index.
164
	 *
165
	 * @param IFullTextSearchProvider $provider
166
	 *
167
	 * @throws ConfigurationException
168
	 */
169
	public function initializeIndex(IFullTextSearchProvider $provider) {
170
		$this->indexService->initializeIndex($this->client);
171
172
		$provider->onInitializingIndex($this);
173
	}
174
175
176
	/**
177
	 * removeIndex();
178
	 *
179
	 * Called when admin wants to remove an index specific to a $provider.
180
	 * $provider can be null, meaning a reset of the whole index.
181
	 *
182
	 * @param IFullTextSearchProvider|null $provider
183
	 *
184
	 * @throws ConfigurationException
185
	 */
186
	public function removeIndex($provider) {
187
188
		if ($provider instanceof IFullTextSearchProvider) {
0 ignored issues
show
Bug introduced by
The class OCA\FullTextSearch\IFullTextSearchProvider does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
189
			// TODO: need to specify the map to remove
190
			// TODO: need to remove entries with type=providerId
191
			$provider->onRemovingIndex($this);
192
		}
193
194
		$this->indexService->removeIndex($this->client);
195
	}
196
197
198
	/**
199
	 * {@inheritdoc}
200
	 */
201
	public function indexDocuments(IFullTextSearchProvider $provider, $documents) {
202
		$indexes = [];
203
		foreach ($documents as $document) {
204
			$index = $this->indexDocument($provider, $document);
205
			if ($index !== null) {
206
				$indexes[] = $index;
207
			}
208
		}
209
210
		return $indexes;
211
	}
212
213
214
	/**
215
	 * {@inheritdoc}
216
	 */
217
	public function indexDocument(IFullTextSearchProvider $provider, IndexDocument $document) {
218
219
		$this->updateRunner('indexDocument');
220
		$this->outputRunner(' . Indexing: ' . $document->getTitle());
221
222
		try {
223
			$result = $this->indexService->indexDocument($this, $this->client, $provider, $document);
224
			$this->outputRunner('  result: ' . json_encode($result));
225
226
			return $this->indexService->parseIndexResult($document->getIndex(), $result);
0 ignored issues
show
Documentation introduced by
$result is of type callable, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
227
		} catch (Exception $e) {
228
			return $this->indexDocumentError($provider, $document, $e);
229
		}
230
231
	}
232
233
234
	private function indexDocumentError(
235
		IFullTextSearchProvider $provider, IndexDocument $document, Exception $e
236
	) {
237
		$message = [
238
			'exception' => get_class($e),
239
			'message'   => $e->getMessage()
240
		];
241
242
		$document->setContent(null);
243
		$index = $document->getIndex();
244
		$index->unsetStatus(Index::INDEX_CONTENT);
245
		$index->setMessage(json_encode($message));
246
247
		try {
248
			$result = $this->indexService->indexDocument($this, $this->client, $provider, $document);
249
		} catch (Exception $e) {
250
			$result = [
251
				'exception' => get_class($e),
252
				'message'   => $e->getMessage()
253
			];
254
		}
255
256
		$this->outputRunner('  result with no content: ' . json_encode($result));
257
258
		return $this->indexService->parseIndexResult($document->getIndex(), $result);
0 ignored issues
show
Documentation introduced by
$result is of type callable, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
259
	}
260
261
262
	/**
263
	 * {@inheritdoc}
264
	 */
265
	public function searchDocuments(IFullTextSearchProvider $provider, DocumentAccess $access, $request
266
	) {
267
		try {
268
			return $this->searchService->searchDocuments(
269
				$this, $this->client, $provider, $access, $request
270
			);
271
		} catch (ConfigurationException $e) {
272
			throw $e;
273
		}
274
	}
275
276
277
	/**
278
	 * @param string $host
279
	 */
280
	private function connectToElastic($host) {
281
282
		try {
283
			$hosts = [MiscService::noEndSlash($host)];
284
			$this->client = ClientBuilder::create()
285
										 ->setHosts($hosts)
286
										 ->setRetries(2)
287
										 ->build();
288
289
		} catch (CouldNotConnectToHost $e) {
290
			echo 'CouldNotConnectToHost';
291
			$previous = $e->getPrevious();
292
			if ($previous instanceof MaxRetriesException) {
293
				echo "Max retries!";
294
			}
295
		} catch (Exception $e) {
296
			echo ' ElasticSearchPlatform::load() Exception --- ' . $e->getMessage() . "\n";
297
		}
298
	}
299
300
301
}