Completed
Push — master ( 94b632...a799ec )
by Maxence
02:11
created

IndexService::testIndex()   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 1
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\Service;
28
29
use Elasticsearch\Client;
30
use Elasticsearch\Common\Exceptions\BadRequest400Exception;
31
use Elasticsearch\Common\Exceptions\Missing404Exception;
32
use OCA\FullTextSearch\Exceptions\IndexDoesNotExistException;
33
use OCA\FullTextSearch\IFullTextSearchProvider;
34
use OCA\FullTextSearch\Model\Index;
35
use OCA\FullTextSearch\Model\IndexDocument;
36
use OCA\FullTextSearch_ElasticSearch\Exceptions\AccessIsEmptyException;
37
use OCA\FullTextSearch_ElasticSearch\Exceptions\ConfigurationException;
38
39
class IndexService {
40
41
42
	/** @var IndexMappingService */
43
	private $indexMappingService;
44
45
	/** @var MiscService */
46
	private $miscService;
47
48
49
	/**
50
	 * IndexService constructor.
51
	 *
52
	 * @param IndexMappingService $indexMappingService
53
	 * @param MiscService $miscService
54
	 */
55
	public function __construct(
56
		IndexMappingService $indexMappingService, MiscService $miscService
57
	) {
58
		$this->indexMappingService = $indexMappingService;
59
		$this->miscService = $miscService;
60
	}
61
62
63
	/**
64
	 * @param Client $client
65
	 *
66
	 * @return bool
67
	 * @throws ConfigurationException
68
	 */
69
	public function testIndex(Client $client) {
70
71
		$map = $this->indexMappingService->generateGlobalMap(false);
72
		$map['client'] = [
73
			'verbose' => true
74
		];
75
76
		return $client->indices()
77
					  ->exists($map);
78
	}
79
80
	/**
81
	 * @param Client $client
82
	 *
83
	 * @throws ConfigurationException
84
	 * @throws BadRequest400Exception
85
	 */
86
	public function initializeIndex(Client $client) {
87
		try {
88
			if ($client->indices()
89
					   ->exists($this->indexMappingService->generateGlobalMap(false))) {
90
				return;
91
			}
92
		} catch (BadRequest400Exception $e) {
93
			$this->parseBadRequest400($e);
94
		}
95
96
		try {
97
			$client->indices()
98
				   ->create($this->indexMappingService->generateGlobalMap());
99
			$client->ingest()
100
				   ->putPipeline($this->indexMappingService->generateGlobalIngest());
101
		} catch (BadRequest400Exception $e) {
102
			$this->resetIndexAll($client);
103
			$this->parseBadRequest400($e);
104
		}
105
	}
106
107
108
	/**
109
	 * @param Client $client
110
	 * @param $providerId
111
	 *
112
	 * @throws ConfigurationException
113
	 */
114
	public function resetIndex(Client $client, $providerId) {
115
		try {
116
			$client->deleteByQuery($this->indexMappingService->generateDeleteQuery($providerId));
117
		} catch (Missing404Exception $e) {
118
			/** we do nothin' */
119
		}
120
	}
121
122
123
	/**
124
	 * @param Client $client
125
	 *
126
	 * @throws ConfigurationException
127
	 */
128
	public function resetIndexAll(Client $client) {
129
		try {
130
			$client->ingest()
131
				   ->deletePipeline($this->indexMappingService->generateGlobalIngest(false));
132
		} catch (Missing404Exception $e) {
133
			/* 404Exception will means that the mapping for that provider does not exist */
134
		} catch (BadRequest400Exception $e) {
135
			throw new ConfigurationException(
136
				'Check your user/password and the index assigned to that cloud'
137
			);
138
		}
139
140
		try {
141
			$client->indices()
142
				   ->delete($this->indexMappingService->generateGlobalMap(false));
143
		} catch (Missing404Exception $e) {
144
			/* 404Exception will means that the mapping for that provider does not exist */
145
		}
146
	}
147
148
149
	/**
150
	 * @param Client $client
151
	 * @param Index[] $indexes
152
	 *
153
	 * @throws ConfigurationException
154
	 */
155
	public function deleteIndexes($client, $indexes) {
156
		foreach ($indexes as $index) {
157
			$this->indexMappingService->indexDocumentRemove(
158
				$client, $index->getProviderId(), $index->getDocumentId()
159
			);
160
		}
161
	}
162
163
164
	/**
165
	 * @param Client $client
166
	 * @param IFullTextSearchProvider $provider
167
	 * @param IndexDocument $document
168
	 *
169
	 * @return array
0 ignored issues
show
Documentation introduced by
Should the return type not be callable? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
170
	 * @throws ConfigurationException
171
	 * @throws AccessIsEmptyException
172
	 */
173
	public function indexDocument(
174
		Client $client, IFullTextSearchProvider $provider, IndexDocument $document
175
	) {
176
		$result = [];
177
		$index = $document->getIndex();
178
		if ($index->isStatus(Index::INDEX_REMOVE)) {
179
			$this->indexMappingService->indexDocumentRemove(
180
				$client, $provider->getId(), $document->getId()
181
			);
182
		} else if ($index->isStatus(Index::INDEX_OK) && !$index->isStatus(Index::INDEX_CONTENT)) {
183
			$result = $this->indexMappingService->indexDocumentUpdate($client, $document);
184
		} else {
185
			$result = $this->indexMappingService->indexDocumentNew($client, $document);
186
		}
187
188
		return $result;
189
	}
190
191
192
	/**
193
	 * @param Index $index
194
	 * @param array $result
195
	 *
196
	 * @return Index
197
	 */
198
	public function parseIndexResult(Index $index, array $result) {
199
200
		$index->setLastIndex();
201
202
		if (array_key_exists('exception', $result)) {
203
			$index->setStatus(Index::INDEX_FAILED);
204
			$index->incrementError();
205
			$index->setMessage(json_encode($result));
206
207
			return $index;
208
		}
209
210
		// TODO: parse result
211
		$index->setStatus(Index::INDEX_DONE);
212
213
		return $index;
214
	}
215
216
217
	/**
218
	 * @param BadRequest400Exception $e
219
	 *
220
	 * @throws ConfigurationException
221
	 * @throws BadRequest400Exception
222
	 */
223
	private function parseBadRequest400(BadRequest400Exception $e) {
224
225
		if ($e->getMessage() === '') {
226
			throw new ConfigurationException(
227
				'Check your user/password and the index assigned to that cloud'
228
			);
229
		}
230
231
232
		$error = json_decode($e->getMessage(), true)['error'];
233
234
		if ($error['type'] === 'parse_exception') {
235
			if ($error['reason'] === 'No processor type exists with name [attachment]') {
236
				throw new ConfigurationException(
237
					'please add ingest-attachment plugin to elasticsearch'
238
				);
239
			}
240
		}
241
242
		throw $e;
243
	}
244
245
}
246