Completed
Push — master ( 58d870...e50603 )
by
unknown
02:12
created

IndexService::indexProviderContentFromUser()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 11
rs 9.9
cc 1
nc 1
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 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,
76
		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(
139
		IFullTextSearchProvider $provider, array $documents
140
	) {
141
142
		$currIndex = $this->getProviderIndexFromProvider($provider);
143
		$result = [];
144
		foreach ($documents as $document) {
145
			$this->updateRunner('compareWithCurrentIndex');
146
147
			$index = $currIndex->getIndex($document->getId());
148
			if ($index === null) {
149
				$index = new Index($document->getProviderId(), $document->getId());
150
				$index->setStatus(Index::INDEX_FULL);
151
				$index->setLastIndex();
152
			}
153
154
			$document->setIndex($index);
155
			if (!$this->isDocumentUpToDate($provider, $document)) {
156
				$result[] = $document;
157
			}
158
159
		}
160
161
		return $result;
162
	}
163
164
165
	/**
166
	 * @param IFullTextSearchProvider $provider
167
	 * @param IndexDocument $document
168
	 *
169
	 * @return bool
170
	 */
171
	private function isDocumentUpToDate(IFullTextSearchProvider $provider, IndexDocument $document
172
	) {
173
		$index = $document->getIndex();
174
175
		if (!$index->isStatus(Index::INDEX_OK)) {
176
			return false;
177
		}
178
179
		if ($index->isStatus(Index::INDEX_META) || $index->isStatus(Index::INDEX_CONTENT)) {
180
			return false;
181
		}
182
183
		return $provider->isDocumentUpToDate($document);
184
	}
185
186
187
	/**
188
	 * @param IFullTextSearchProvider $provider
189
	 *
190
	 * @return ProviderIndexes
191
	 */
192
	private function getProviderIndexFromProvider(IFullTextSearchProvider $provider) {
193
		$indexes = $this->indexesRequest->getIndexesFromProvider($provider);
194
195
		return new ProviderIndexes($indexes);
196
	}
197
198
199
	/**
200
	 * @param IFullTextSearchPlatform $platform
201
	 * @param IFullTextSearchProvider $provider
202
	 * @param IndexDocument[] $documents
203
	 *
204
	 * @throws Exception
205
	 */
206
	private function indexChunks(
207
		IFullTextSearchPlatform $platform, IFullTextSearchProvider $provider, $documents
208
	) {
209
		$chunkSize = $this->configService->getAppValue(ConfigService::CHUNK_INDEX);
210
211
		$max = sizeof($documents);
212
		for ($i = 0; $i < $max; $i++) {
213
214
			$this->updateRunner('indexChunk');
215
			try {
216
				$chunk = array_splice($documents, 0, $chunkSize);
217
				$this->indexChunk($platform, $provider, $chunk);
218
219
				/** @var IndexDocument $doc */
220
				foreach ($chunk as $doc) {
221
					$doc->__destruct(); // because.
222
				}
223
			} catch (NoResultException $e) {
224
				return;
225
			} catch (Exception $e) {
226
				throw $e;
227
			}
228
		}
229
	}
230
231
232
	/**
233
	 * @param IFullTextSearchPlatform $platform
234
	 * @param IFullTextSearchProvider $provider
235
	 * @param IndexDocument[] $chunk
236
	 *
237
	 * @throws NoResultException
238
	 * @throws DatabaseException
239
	 */
240
	private function indexChunk(
241
		IFullTextSearchPlatform $platform, IFullTextSearchProvider $provider, $chunk
242
	) {
243
		if (sizeof($chunk) === 0) {
244
			throw new NoResultException();
245
		}
246
247
		$documents = $provider->fillIndexDocuments($chunk);
248
		$toIndex = $this->filterDocumentsToIndex($documents);
249
		$indexes = $platform->indexDocuments($provider, $toIndex);
250
251
		$this->updateIndexes($indexes);
252
	}
253
254
255
	/**
256
	 * @param IndexDocument[] $documents
257
	 *
258
	 * @return array
259
	 */
260
	private function filterDocumentsToIndex($documents) {
261
		$toIndex = [];
262
		foreach ($documents as $document) {
263
			// TODO - rework the index/not_index
264
			$index = $document->getIndex();
265
			$access = $document->getAccess();
266
267
			if ($access !== null && !$index->isStatus(Index::INDEX_IGNORE)) {
268
				$index->setOwnerId($access->getOwnerId());
269
				$toIndex[] = $document;
270
			}
271
		}
272
273
		return $toIndex;
274
	}
275
276
277
	/**
278
	 * @param IFullTextSearchPlatform $platform
279
	 * @param IFullTextSearchProvider $provider
280
	 * @param Index $index
281
	 *
282
	 * @internal param int|string $documentId
283
	 * @throws Exception
284
	 */
285
	public function updateDocument(
286
		IFullTextSearchPlatform $platform, IFullTextSearchProvider $provider, Index $index
287
	) {
288
		$document = null;
289
		if (!$index->isStatus(Index::INDEX_REMOVE)) {
290
			try {
291
				$document = $provider->updateDocument($index);
292
			} catch (Exception $e) {
293
				/** we do nothing, because we're not sure provider manage the right MissingDocumentException */
294
			}
295
		}
296
297
		if ($document === null) {
298
			$platform->deleteIndexes([$index]);
299
			$this->indexesRequest->deleteIndex($index);
300
301
			return;
302
		}
303
304
		$index = $platform->indexDocument($provider, $document);
305
		$this->updateIndex($index);
306
	}
307
308
309
	/**
310
	 * @param Index[] $indexes
311
	 *
312
	 * @throws DatabaseException
313
	 */
314
	public function updateIndexes($indexes) {
315
316
		try {
317
			foreach ($indexes as $index) {
318
				$this->updateIndex($index);
319
			}
320
		} catch (Exception $e) {
321
			throw new DatabaseException($e->getMessage());
322
		}
323
	}
324
325
326
	/**
327
	 * @param Index $index
328
	 *
329
	 * @throws Exception
330
	 */
331
	private function updateIndex(Index $index) {
332
333
		if ($index->isStatus(Index::INDEX_REMOVE)) {
334
335
			if ($index->isStatus(Index::INDEX_DONE)) {
336
				$this->indexesRequest->deleteIndex($index);
337
338
				return;
339
			}
340
341
			$this->indexesRequest->update($index);
342
343
			return;
344
		}
345
346
		if ($index->isStatus(Index::INDEX_DONE)) {
347
			$index->setStatus(Index::INDEX_OK, true);
348
		}
349
350
		if (!$this->indexesRequest->update($index)) {
351
			$this->indexesRequest->create($index);
352
		}
353
	}
354
355
356
	/**
357
	 * @param string $providerId
358
	 * @param array $documentIds
359
	 * @param int $status
360
	 * @param bool $reset
361
	 *
362
	 * @throws DatabaseException
363
	 */
364
	public function updateIndexesStatus($providerId, $documentIds, $status, $reset = false) {
365
		if ($reset === true) {
366
			$this->indexesRequest->updateStatus($providerId, $documentIds, $status);
367
368
			return;
369
		}
370
371
		try {
372
			$all = $this->getIndexes($providerId, $documentIds);
373
		} catch (IndexDoesNotExistException $e) {
374
			return;
375
		}
376
377
		foreach ($all as $curr) {
378
			$curr->setStatus($status);
379
			$this->updateIndexes([$curr]);
380
		}
381
382
	}
383
384
385
	/**
386
	 * @param string $providerId
387
	 * @param array $documentId
388
	 *
389
	 * @return ExtendedIndex[]
390
	 * @throws IndexDoesNotExistException
391
	 */
392
	public function getIndexes($providerId, $documentId) {
393
		return $this->indexesRequest->getIndexes($providerId, $documentId);
394
	}
395
396
397
	/**
398
	 * @return Index[]
399
	 */
400
	public function getQueuedIndexes() {
401
		return $this->indexesRequest->getQueuedIndexes();
402
	}
403
404
405
	/**
406
	 * @param string $providerId
407
	 *
408
	 * @throws Exception
409
	 */
410
	public function resetIndex($providerId = '') {
411
		$platform = $this->platformService->getPlatform();
412
413
		if ($providerId === '') {
414
			$platform->resetIndex();
415
			$this->providerService->setProvidersAsNotIndexed();
416
			$this->indexesRequest->reset();
417
418
			return;
419
		} else {
420
			$providers = [$this->providerService->getProvider($providerId)];
421
		}
422
423
		foreach ($providers AS $provider) {
424
			// TODO: need to specify the map to remove
425
			// TODO: need to remove entries with type=providerId
426
			$provider->onResettingIndex($this);
0 ignored issues
show
Documentation introduced by
$this is of type this<OCA\FullTextSearch\Service\IndexService>, but the function expects a object<OCA\FullTextSearc...FullTextSearchPlatform>.

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...
427
428
			$platform->resetIndex();
429
			$this->providerService->setProviderAsIndexed($provider, false);
430
			$this->indexesRequest->deleteFromProviderId($provider->getId());
431
		}
432
	}
433
434
435
}