Completed
Push — master ( a3e598...0882e3 )
by Maxence
01:55
created

IndexService::updateDocument()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 23
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 23
rs 8.7972
cc 4
eloc 13
nc 6
nop 3
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 OC\User\NoUserException;
31
use OCA\FullTextSearch\Db\IndexesRequest;
32
use OCA\FullTextSearch\Exceptions\DatabaseException;
33
use OCA\FullTextSearch\Exceptions\IndexDoesNotExistException;
34
use OCA\FullTextSearch\Exceptions\InterruptException;
35
use OCA\FullTextSearch\Exceptions\NoResultException;
36
use OCA\FullTextSearch\Exceptions\TickDoesNotExistException;
37
use OCA\FullTextSearch\IFullTextSearchPlatform;
38
use OCA\FullTextSearch\IFullTextSearchProvider;
39
use OCA\FullTextSearch\Model\ExtendedIndex;
40
use OCA\FullTextSearch\Model\Index;
41
use OCA\FullTextSearch\Model\IndexDocument;
42
use OCA\FullTextSearch\Model\ProviderIndexes;
43
use OCA\FullTextSearch\Model\Runner;
44
45
class IndexService {
46
47
	/** @var IndexesRequest */
48
	private $indexesRequest;
49
50
	/** @var ConfigService */
51
	private $configService;
52
53
	/** @var ProviderService */
54
	private $providerService;
55
56
	/** @var PlatformService */
57
	private $platformService;
58
59
	/** @var MiscService */
60
	private $miscService;
61
62
63
	/** @var Runner */
64
	private $runner = null;
65
66
	/**
67
	 * IndexService constructor.
68
	 *
69
	 * @param IndexesRequest $indexesRequest
70
	 * @param ConfigService $configService
71
	 * @param ProviderService $providerService
72
	 * @param PlatformService $platformService
73
	 * @param MiscService $miscService
74
	 */
75
	public function __construct(
76
		IndexesRequest $indexesRequest, ConfigService $configService, ProviderService $providerService,
77
		PlatformService $platformService, MiscService $miscService
78
	) {
79
		$this->indexesRequest = $indexesRequest;
80
		$this->configService = $configService;
81
		$this->providerService = $providerService;
82
		$this->platformService = $platformService;
83
		$this->miscService = $miscService;
84
	}
85
86
87
	/**
88
	 * @param Runner $runner
89
	 */
90
	public function setRunner(Runner $runner) {
91
		$this->runner = $runner;
92
	}
93
94
95
	/**
96
	 * @param $action
97
	 *
98
	 * @throws InterruptException
99
	 * @throws TickDoesNotExistException
100
	 */
101
	private function updateRunner($action) {
102
		if ($this->runner === null) {
103
			return;
104
		}
105
106
		$this->runner->update($action);
107
	}
108
109
110
	/**
111
	 * @param IFullTextSearchPlatform $platform
112
	 * @param IFullTextSearchProvider $provider
113
	 * @param string $userId
114
	 *
115
	 * @throws Exception
116
	 */
117
	public function indexProviderContentFromUser(
118
		IFullTextSearchPlatform $platform, IFullTextSearchProvider $provider, $userId
119
	) {
120
		$this->updateRunner('generateIndex' . $provider->getName());
121
		$documents = $provider->generateIndexableDocuments($userId);
122
123
		//$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...
124
125
		$toIndex = $this->updateDocumentsWithCurrIndex($provider, $documents);
126
		$this->indexChunks($platform, $provider, $toIndex);
127
	}
128
129
130
	/**
131
	 * @param IFullTextSearchProvider $provider
132
	 * @param IndexDocument[] $documents
133
	 *
134
	 * @return IndexDocument[]
135
	 * @throws InterruptException
136
	 * @throws TickDoesNotExistException
137
	 */
138
	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...
139
140
		$currIndex = $this->getProviderIndexFromProvider($provider);
141
		$result = [];
142
		foreach ($documents as $document) {
143
			$this->updateRunner('compareWithCurrentIndex');
144
145
			$index = $currIndex->getIndex($document->getId());
146
			if ($index === null) {
147
				$index = new Index($document->getProviderId(), $document->getId());
148
				$index->setStatus(Index::INDEX_FULL);
149
				$index->setLastIndex();
150
			}
151
152
			$document->setIndex($index);
153
			if (!$this->isDocumentUpToDate($provider, $document)) {
154
				$result[] = $document;
155
			}
156
157
		}
158
159
		return $result;
160
	}
161
162
163
	/**
164
	 * @param IFullTextSearchProvider $provider
165
	 * @param IndexDocument $document
166
	 *
167
	 * @return bool
168
	 */
169
	private function isDocumentUpToDate(IFullTextSearchProvider $provider, IndexDocument $document) {
170
		$index = $document->getIndex();
171
172
		if (!$index->isStatus(Index::INDEX_OK)) {
173
			return false;
174
		}
175
176
		if ($index->isStatus(Index::INDEX_META) || $index->isStatus(Index::INDEX_CONTENT)) {
177
			return false;
178
		}
179
180
		return $provider->isDocumentUpToDate($document);
181
	}
182
183
184
	/**
185
	 * @param IFullTextSearchProvider $provider
186
	 *
187
	 * @return ProviderIndexes
188
	 */
189
	private function getProviderIndexFromProvider(IFullTextSearchProvider $provider) {
190
		$indexes = $this->indexesRequest->getIndexesFromProvider($provider);
191
192
		return new ProviderIndexes($indexes);
193
	}
194
195
196
	/**
197
	 * @param IFullTextSearchPlatform $platform
198
	 * @param IFullTextSearchProvider $provider
199
	 * @param IndexDocument[] $documents
200
	 *
201
	 * @throws Exception
202
	 */
203
	private function indexChunks(
204
		IFullTextSearchPlatform $platform, IFullTextSearchProvider $provider, $documents
205
	) {
206
		$chunkSize = $this->configService->getAppValue(ConfigService::CHUNK_INDEX);
207
208
		$max = sizeof($documents);
209
		for ($i = 0; $i < $max; $i++) {
210
211
			$this->updateRunner('indexChunk');
212
			try {
213
				$chunk = array_splice($documents, 0, $chunkSize);
214
				$this->indexChunk($platform, $provider, $chunk);
215
216
				/** @var IndexDocument $doc */
217
				foreach ($chunk as $doc) {
218
					$doc->__destruct(); // because.
219
				}
220
			} catch (NoResultException $e) {
221
				return;
222
			} catch (Exception $e) {
223
				throw $e;
224
			}
225
		}
226
	}
227
228
229
	/**
230
	 * @param IFullTextSearchPlatform $platform
231
	 * @param IFullTextSearchProvider $provider
232
	 * @param IndexDocument[] $chunk
233
	 *
234
	 * @throws NoResultException
235
	 * @throws DatabaseException
236
	 */
237
	private function indexChunk(
238
		IFullTextSearchPlatform $platform, IFullTextSearchProvider $provider, $chunk
239
	) {
240
		if (sizeof($chunk) === 0) {
241
			throw new NoResultException();
242
		}
243
244
		$documents = $provider->fillIndexDocuments($chunk);
245
		$toIndex = $this->filterDocumentsToIndex($documents);
246
		$indexes = $platform->indexDocuments($provider, $toIndex);
247
248
		$this->updateIndexes($indexes);
249
	}
250
251
252
	/**
253
	 * @param IndexDocument[] $documents
254
	 *
255
	 * @return array
256
	 */
257
	private function filterDocumentsToIndex($documents) {
258
		$toIndex = [];
259
		foreach ($documents as $document) {
260
			// TODO - rework the index/not_index
261
			$index = $document->getIndex();
262
			$access = $document->getAccess();
263
264
			$index->setOwnerId($access->getOwnerId());
265
			if (!$index->isStatus(Index::INDEX_IGNORE)) {
266
				$toIndex[] = $document;
267
			}
268
		}
269
270
		return $toIndex;
271
	}
272
273
274
	/**
275
	 * @param IFullTextSearchPlatform $platform
276
	 * @param IFullTextSearchProvider $provider
277
	 * @param Index $index
278
	 *
279
	 * @internal param int|string $documentId
280
	 * @throws Exception
281
	 */
282
	public function updateDocument(
283
		IFullTextSearchPlatform $platform, IFullTextSearchProvider $provider, Index $index
284
	) {
285
		$document = null;
286
287
		if (!$index->isStatus(Index::INDEX_REMOVE)) {
288
			try {
289
				$document = $provider->updateDocument($index);
290
			} catch (Exception $e) {
291
				/** we do nothing, because we're not sure provider manage the right MissingDocumentException */
292
			}
293
		}
294
295
		if ($document === null) {
296
			$platform->deleteIndexes([$index]);
297
			$this->indexesRequest->deleteIndex($index);
298
299
			return;
300
		}
301
302
		$index = $platform->indexDocument($provider, $document);
303
		$this->updateIndex($index);
304
	}
305
306
307
	/**
308
	 * @param Index[] $indexes
309
	 *
310
	 * @throws DatabaseException
311
	 */
312
	public function updateIndexes($indexes) {
313
314
		try {
315
			foreach ($indexes as $index) {
316
				$this->updateIndex($index);
317
			}
318
		} catch (Exception $e) {
319
			throw new DatabaseException($e->getMessage());
320
		}
321
	}
322
323
324
	/**
325
	 * @param Index $index
326
	 *
327
	 * @throws Exception
328
	 */
329
	private function updateIndex(Index $index) {
330
331
		if ($index->isStatus(Index::INDEX_REMOVE)) {
332
333
			if ($index->isStatus(Index::INDEX_DONE)) {
334
				$this->indexesRequest->deleteIndex($index);
335
336
				return;
337
			}
338
339
			$this->indexesRequest->update($index);
340
341
			return;
342
		}
343
344
		if ($index->isStatus(Index::INDEX_DONE)) {
345
			$index->setStatus(Index::INDEX_OK, true);
346
		}
347
348
		if (!$this->indexesRequest->update($index)) {
349
			$this->indexesRequest->create($index);
350
		}
351
	}
352
353
354
	/**
355
	 * @param string $providerId
356
	 * @param string|int $documentId
357
	 * @param int $status
358
	 * @param bool $reset
359
	 *
360
	 * @throws DatabaseException
361
	 */
362
	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...
363
		try {
364
			$curr = $this->getIndex($providerId, $documentId);
365
		} catch (IndexDoesNotExistException $e) {
366
			$curr = new Index($providerId, $documentId);
367
			$curr->setStatus(Index::INDEX_FULL);
368
		}
369
370
		$curr->setStatus($status);
371
		$this->updateIndexes([$curr]);
372
	}
373
374
375
	/**
376
	 * @param string $providerId
377
	 * @param string|int $documentId
378
	 *
379
	 * @return ExtendedIndex
380
	 * @throws IndexDoesNotExistException
381
	 */
382
	public function getIndex($providerId, $documentId) {
383
		return $this->indexesRequest->getIndex($providerId, $documentId);
384
	}
385
386
387
	/**
388
	 * @return Index[]
389
	 */
390
	public function getQueuedIndexes() {
391
		return $this->indexesRequest->getQueuedIndexes();
392
	}
393
394
395
	/**
396
	 * @param string $providerId
397
	 *
398
	 * @throws Exception
399
	 */
400
	public function resetIndex($providerId = '') {
401
		$platform = $this->platformService->getPlatform();
402
403
		if ($providerId === '') {
404
			$platform->resetIndex(null);
405
			$this->providerService->setProvidersAsNotIndexed();
406
			$this->indexesRequest->reset();
407
408
			return;
409
		} else {
410
			$providers = [$this->providerService->getProvider($providerId)];
411
		}
412
413
		foreach ($providers AS $provider) {
414
			$platform->resetIndex($provider);
415
			$this->providerService->setProviderAsIndexed($provider, false);
416
			$this->indexesRequest->deleteFromProviderId($provider->getId());
417
		}
418
	}
419
420
421
}