Completed
Push — master ( 448b39...a46d9a )
by Maxence
02:01
created

IndexService::getQueuedIndexes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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