Completed
Push — master ( 69947f...ffb107 )
by Maxence
01:51
created

IndexService::updateIndex()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 23
rs 8.5906
c 0
b 0
f 0
cc 5
eloc 12
nc 6
nop 1
1
<?php
2
/**
3
 * FullTextSearch - Full text search framework for 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\Service;
28
29
use Exception;
30
use OCA\FullTextSearch\Db\IndexesRequest;
31
use OCA\FullTextSearch\Exceptions\DatabaseException;
32
use OCA\FullTextSearch\Exceptions\IndexDoesNotExistException;
33
use OCA\FullTextSearch\Exceptions\InterruptException;
34
use OCA\FullTextSearch\Exceptions\NoResultException;
35
use OCA\FullTextSearch\Exceptions\TickDoesNotExistException;
36
use OCA\FullTextSearch\IFullTextSearchPlatform;
37
use OCA\FullTextSearch\IFullTextSearchProvider;
38
use OCA\FullTextSearch\Model\ExtendedIndex;
39
use OCA\FullTextSearch\Model\Index;
40
use OCA\FullTextSearch\Model\IndexDocument;
41
use OCA\FullTextSearch\Model\ProviderIndexes;
42
use OCA\FullTextSearch\Model\Runner;
43
44
class IndexService {
45
46
	/** @var IndexesRequest */
47
	private $indexesRequest;
48
49
	/** @var ConfigService */
50
	private $configService;
51
52
	/** @var ProviderService */
53
	private $providerService;
54
55
	/** @var PlatformService */
56
	private $platformService;
57
58
	/** @var MiscService */
59
	private $miscService;
60
61
62
	/** @var Runner */
63
	private $runner = null;
64
65
	/**
66
	 * IndexService constructor.
67
	 *
68
	 * @param IndexesRequest $indexesRequest
69
	 * @param ConfigService $configService
70
	 * @param ProviderService $providerService
71
	 * @param PlatformService $platformService
72
	 * @param MiscService $miscService
73
	 */
74 View Code Duplication
	public function __construct(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
75
		IndexesRequest $indexesRequest, ConfigService $configService, ProviderService $providerService,
76
		PlatformService $platformService, MiscService $miscService
77
	) {
78
		$this->indexesRequest = $indexesRequest;
79
		$this->configService = $configService;
80
		$this->providerService = $providerService;
81
		$this->platformService = $platformService;
82
		$this->miscService = $miscService;
83
	}
84
85
86
	/**
87
	 * @param Runner $runner
88
	 */
89
	public function setRunner(Runner $runner) {
90
		$this->runner = $runner;
91
	}
92
93
94
	/**
95
	 * @param $action
96
	 *
97
	 * @throws InterruptException
98
	 * @throws TickDoesNotExistException
99
	 */
100
	private function updateRunner($action) {
101
		if ($this->runner === null) {
102
			return;
103
		}
104
105
		$this->runner->update($action);
106
	}
107
108
109
	/**
110
	 * @param IFullTextSearchPlatform $platform
111
	 * @param IFullTextSearchProvider $provider
112
	 * @param string $userId
113
	 *
114
	 * @throws Exception
115
	 */
116
	public function indexProviderContentFromUser(
117
		IFullTextSearchPlatform $platform, IFullTextSearchProvider $provider, $userId
118
	) {
119
		$this->updateRunner('generateIndex' . $provider->getName());
120
		$documents = $provider->generateIndexableDocuments($userId);
121
122
		//$maxSize = sizeof($documents);
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
123
124
		$toIndex = $this->updateDocumentsWithCurrIndex($provider, $documents);
125
		$this->indexChunks($platform, $provider, $toIndex);
126
	}
127
128
129
	/**
130
	 * @param IFullTextSearchProvider $provider
131
	 * @param IndexDocument[] $documents
132
	 *
133
	 * @return IndexDocument[]
134
	 * @throws InterruptException
135
	 * @throws TickDoesNotExistException
136
	 */
137
	private function updateDocumentsWithCurrIndex(IFullTextSearchProvider $provider, array $documents) {
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 100 characters; contains 101 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
138
139
		$currIndex = $this->getProviderIndexFromProvider($provider);
140
		$result = [];
141
		foreach ($documents as $document) {
142
			$this->updateRunner('compareWithCurrentIndex');
143
144
			$index = $currIndex->getIndex($document->getId());
145
			if ($index === null) {
146
				$index = new Index($document->getProviderId(), $document->getId());
147
				$index->setStatus(Index::INDEX_FULL);
148
				$index->setLastIndex();
149
			}
150
151
			$document->setIndex($index);
152
			if (!$this->isDocumentUpToDate($provider, $document)) {
153
				$result[] = $document;
154
			}
155
156
		}
157
158
		return $result;
159
	}
160
161
162
	/**
163
	 * @param IFullTextSearchProvider $provider
164
	 * @param IndexDocument $document
165
	 *
166
	 * @return bool
167
	 */
168
	private function isDocumentUpToDate(IFullTextSearchProvider $provider, IndexDocument $document) {
169
		$index = $document->getIndex();
170
171
		if (!$index->isStatus(Index::INDEX_OK)) {
172
			return false;
173
		}
174
175
		if ($index->isStatus(Index::INDEX_META) || $index->isStatus(Index::INDEX_CONTENT)) {
176
			return false;
177
		}
178
179
		return $provider->isDocumentUpToDate($document);
180
	}
181
182
183
	/**
184
	 * @param IFullTextSearchProvider $provider
185
	 *
186
	 * @return ProviderIndexes
187
	 */
188
	private function getProviderIndexFromProvider(IFullTextSearchProvider $provider) {
189
		$indexes = $this->indexesRequest->getIndexesFromProvider($provider);
190
191
		return new ProviderIndexes($indexes);
192
	}
193
194
195
	/**
196
	 * @param IFullTextSearchPlatform $platform
197
	 * @param IFullTextSearchProvider $provider
198
	 * @param IndexDocument[] $documents
199
	 *
200
	 * @throws Exception
201
	 */
202
	private function indexChunks(
203
		IFullTextSearchPlatform $platform, IFullTextSearchProvider $provider, $documents
204
	) {
205
		$chunkSize = $this->configService->getAppValue(ConfigService::CHUNK_INDEX);
206
207
		$max = sizeof($documents);
208
		for ($i = 0; $i < $max; $i++) {
209
210
			$this->updateRunner('indexChunk');
211
			try {
212
				$chunk = array_splice($documents, 0, $chunkSize);
213
				$this->indexChunk($platform, $provider, $chunk);
214
215
				/** @var IndexDocument $doc */
216
				foreach ($chunk as $doc) {
217
					$doc->__destruct(); // because.
218
				}
219
			} catch (NoResultException $e) {
220
				return;
221
			} catch (Exception $e) {
222
				throw $e;
223
			}
224
		}
225
	}
226
227
228
	/**
229
	 * @param IFullTextSearchPlatform $platform
230
	 * @param IFullTextSearchProvider $provider
231
	 * @param IndexDocument[] $chunk
232
	 *
233
	 * @throws NoResultException
234
	 * @throws DatabaseException
235
	 */
236
	private function indexChunk(
237
		IFullTextSearchPlatform $platform, IFullTextSearchProvider $provider, $chunk
238
	) {
239
		if (sizeof($chunk) === 0) {
240
			throw new NoResultException();
241
		}
242
243
		$documents = $provider->fillIndexDocuments($chunk);
244
		$toIndex = $this->filterDocumentsToIndex($documents);
245
		$indexes = $platform->indexDocuments($provider, $toIndex);
246
		$this->updateIndexes($indexes);
247
	}
248
249
250
	/**
251
	 * @param IndexDocument[] $documents
252
	 *
253
	 * @return array
254
	 */
255
	private function filterDocumentsToIndex($documents) {
256
		$toIndex = [];
257
		foreach ($documents as $document) {
258
			// TODO - rework the index/not_index
259
//			echo '==st==' . $document->getIndex()->getStatus() . "\n";
260
			if (!$document->getIndex()
261
						  ->isStatus(Index::INDEX_IGNORE)) {
262
				$toIndex[] = $document;
263
			}
264
		}
265
266
		return $toIndex;
267
	}
268
269
270
	/**
271
	 * @param IFullTextSearchPlatform $platform
272
	 * @param IFullTextSearchProvider $provider
273
	 * @param Index $index
274
	 *
275
	 * @internal param int|string $documentId
276
	 * @throws Exception
277
	 */
278
	public function updateDocument(
279
		IFullTextSearchPlatform $platform, IFullTextSearchProvider $provider, Index $index
280
	) {
281
		$document = $provider->updateDocument($index);
282
		if ($document === null) {
283
			$this->indexesRequest->deleteIndex($index);
284
285
			return;
286
		}
287
288
		$index = $platform->indexDocument($provider, $document);
289
		$this->updateIndex($index);
290
	}
291
292
293
	/**
294
	 * @param Index[] $indexes
295
	 *
296
	 * @throws DatabaseException
297
	 */
298
	public function updateIndexes($indexes) {
299
300
		try {
301
			foreach ($indexes as $index) {
302
				$this->updateIndex($index);
303
			}
304
		} catch (Exception $e) {
305
			throw new DatabaseException($e->getMessage());
306
		}
307
	}
308
309
310
	/**
311
	 * @param Index $index
312
	 *
313
	 * @throws Exception
314
	 */
315
	private function updateIndex(Index $index) {
316
317
		if ($index->isStatus(Index::INDEX_REMOVE)) {
318
319
			if ($index->isStatus(Index::INDEX_OK)) {
320
				$this->indexesRequest->deleteIndex($index);
321
322
				return;
323
			}
324
			$index->setStatus(Index::INDEX_FAILED);
325
			$this->indexesRequest->update($index);
326
327
			return;
328
		}
329
330
		if ($index->isStatus(Index::INDEX_DONE)) {
331
			$index->setStatus(Index::INDEX_OK, true);
332
		}
333
334
		if (!$this->indexesRequest->update($index)) {
335
			$this->indexesRequest->create($index);
336
		}
337
	}
338
339
340
	/**
341
	 * @param string $providerId
342
	 * @param string|int $documentId
343
	 * @param int $status
344
	 * @param bool $reset
345
	 *
346
	 * @throws DatabaseException
347
	 */
348
	public function updateIndexStatus($providerId, $documentId, $status, $reset = false) {
0 ignored issues
show
Unused Code introduced by
The parameter $reset is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
349
		try {
350
			$curr = $this->getIndex($providerId, $documentId);
351
		} catch (IndexDoesNotExistException $e) {
352
			$curr = new Index($providerId, $documentId);
353
			$curr->setStatus(Index::INDEX_FULL);
354
		}
355
356
		$curr->setStatus($status);
357
		$this->updateIndexes([$curr]);
358
	}
359
360
361
	/**
362
	 * @param string $providerId
363
	 * @param string|int $documentId
364
	 *
365
	 * @return ExtendedIndex
366
	 * @throws IndexDoesNotExistException
367
	 */
368
	public function getIndex($providerId, $documentId) {
369
		return $this->indexesRequest->getIndex($providerId, $documentId);
370
	}
371
372
373
	/**
374
	 * @return Index[]
375
	 */
376
	public function getQueuedIndexes() {
377
		return $this->indexesRequest->getQueuedIndexes();
378
	}
379
380
381
	/**
382
	 * @param string $providerId
383
	 *
384
	 * @throws Exception
385
	 */
386
	public function resetIndex($providerId = '') {
387
		$platform = $this->platformService->getPlatform();
388
389
		if ($providerId === '') {
390
			$platform->removeIndex(null);
391
			$this->providerService->setProvidersAsNotIndexed();
392
			$this->indexesRequest->reset();
393
394
			return;
395
		} else {
396
			$providers = [$this->providerService->getProvider($providerId)];
397
		}
398
399
		foreach ($providers AS $provider) {
400
			$platform->removeIndex($provider);
401
			$this->providerService->setProviderAsIndexed($provider, false);
402
			$this->indexesRequest->deleteFromProviderId($provider->getId());
403
		}
404
	}
405
406
407
}