1 | <?php declare(strict_types = 1); |
||
2 | namespace ApacheSolrForTypo3\Solr\Domain\Index\Queue; |
||
3 | |||
4 | /*************************************************************** |
||
5 | * Copyright notice |
||
6 | * |
||
7 | * (c) 2010-2017 dkd Internet Service GmbH <[email protected]> |
||
8 | * All rights reserved |
||
9 | * |
||
10 | * This script is part of the TYPO3 project. The TYPO3 project is |
||
11 | * free software; you can redistribute it and/or modify |
||
12 | * it under the terms of the GNU General Public License as published by |
||
13 | * the Free Software Foundation; either version 3 of the License, or |
||
14 | * (at your option) any later version. |
||
15 | * |
||
16 | * The GNU General Public License can be found at |
||
17 | * http://www.gnu.org/copyleft/gpl.html. |
||
18 | * |
||
19 | * This script is distributed in the hope that it will be useful, |
||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
22 | * GNU General Public License for more details. |
||
23 | * |
||
24 | * This copyright notice MUST APPEAR in all copies of the script! |
||
25 | ***************************************************************/ |
||
26 | |||
27 | use ApacheSolrForTypo3\Solr\IndexQueue\Item; |
||
28 | use ApacheSolrForTypo3\Solr\Domain\Site\Site; |
||
29 | use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager; |
||
30 | use ApacheSolrForTypo3\Solr\System\Records\AbstractRepository; |
||
31 | use Doctrine\DBAL\DBALException; |
||
32 | use TYPO3\CMS\Core\Database\ConnectionPool; |
||
33 | use TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression; |
||
34 | use TYPO3\CMS\Core\Database\Query\QueryBuilder; |
||
35 | use TYPO3\CMS\Core\Utility\GeneralUtility; |
||
36 | |||
37 | /** |
||
38 | * Class QueueItemRepository |
||
39 | * Handles all CRUD operations to tx_solr_indexqueue_item table |
||
40 | * |
||
41 | */ |
||
42 | class QueueItemRepository extends AbstractRepository |
||
43 | { |
||
44 | /** |
||
45 | * @var string |
||
46 | */ |
||
47 | protected $table = 'tx_solr_indexqueue_item'; |
||
48 | |||
49 | /** |
||
50 | * @var SolrLogManager |
||
51 | */ |
||
52 | protected $logger; |
||
53 | |||
54 | /** |
||
55 | * QueueItemRepository constructor. |
||
56 | * |
||
57 | * @param SolrLogManager|null $logManager |
||
58 | */ |
||
59 | 123 | public function __construct(SolrLogManager $logManager = null) |
|
60 | { |
||
61 | 123 | $this->logger = $logManager ?? GeneralUtility::makeInstance(SolrLogManager::class, /** @scrutinizer ignore-type */ __CLASS__); |
|
62 | 123 | } |
|
63 | |||
64 | /** |
||
65 | * Fetches the last indexed row |
||
66 | * |
||
67 | * @param int $rootPageId The root page uid for which to get the last indexed row |
||
68 | * @return array |
||
69 | */ |
||
70 | 5 | public function findLastIndexedRow(int $rootPageId) : array |
|
71 | { |
||
72 | 5 | $queryBuilder = $this->getQueryBuilder(); |
|
73 | $row = $queryBuilder |
||
74 | 5 | ->select('uid', 'indexed') |
|
75 | 5 | ->from($this->table) |
|
76 | 5 | ->where($queryBuilder->expr()->eq('root', $rootPageId)) |
|
77 | 5 | ->andWhere($queryBuilder->expr()->neq('indexed', 0)) |
|
78 | 5 | ->orderBy('indexed', 'DESC') |
|
79 | 5 | ->setMaxResults(1) |
|
80 | 5 | ->execute()->fetchAll(); |
|
81 | |||
82 | 5 | return $row; |
|
83 | } |
||
84 | |||
85 | /** |
||
86 | * Finds indexing errors for the current site |
||
87 | * |
||
88 | * @param Site $site |
||
89 | * @return array Error items for the current site's Index Queue |
||
90 | */ |
||
91 | 3 | public function findErrorsBySite(Site $site) : array |
|
92 | { |
||
93 | 3 | $queryBuilder = $this->getQueryBuilder(); |
|
94 | $errors = $queryBuilder |
||
95 | 3 | ->select('uid', 'item_type', 'item_uid', 'errors') |
|
96 | 3 | ->from($this->table) |
|
97 | 3 | ->andWhere( |
|
98 | 3 | $queryBuilder->expr()->notLike('errors', $queryBuilder->createNamedParameter('')), |
|
99 | 3 | $queryBuilder->expr()->eq('root', $site->getRootPageId()) |
|
100 | ) |
||
101 | 3 | ->execute()->fetchAll(); |
|
102 | |||
103 | 3 | return $errors; |
|
104 | } |
||
105 | |||
106 | /** |
||
107 | * Resets all the errors for all index queue items. |
||
108 | * |
||
109 | * @return int affected rows |
||
110 | */ |
||
111 | 1 | public function flushAllErrors() : int |
|
112 | { |
||
113 | 1 | $queryBuilder = $this->getQueryBuilder(); |
|
114 | 1 | $affectedRows = $this->getPreparedFlushErrorQuery($queryBuilder)->execute(); |
|
115 | 1 | return $affectedRows; |
|
0 ignored issues
–
show
Bug
Best Practice
introduced
by
Loading history...
|
|||
116 | } |
||
117 | |||
118 | /** |
||
119 | * Flushes the errors for a single site. |
||
120 | * |
||
121 | * @param Site $site |
||
122 | * @return int |
||
123 | */ |
||
124 | 1 | public function flushErrorsBySite(Site $site) : int |
|
125 | { |
||
126 | 1 | $queryBuilder = $this->getQueryBuilder(); |
|
127 | 1 | $affectedRows = $this->getPreparedFlushErrorQuery($queryBuilder) |
|
128 | 1 | ->andWhere( |
|
129 | 1 | $queryBuilder->expr()->eq('root', (int)$site->getRootPageId()) |
|
130 | ) |
||
131 | 1 | ->execute(); |
|
132 | 1 | return $affectedRows; |
|
0 ignored issues
–
show
|
|||
133 | } |
||
134 | |||
135 | /** |
||
136 | * Flushes the error for a single item. |
||
137 | * |
||
138 | * @param Item $item |
||
139 | * @return int affected rows |
||
140 | */ |
||
141 | 2 | public function flushErrorByItem(Item $item) : int |
|
142 | { |
||
143 | 2 | $queryBuilder = $this->getQueryBuilder(); |
|
144 | 2 | $affectedRows = $this->getPreparedFlushErrorQuery($queryBuilder) |
|
145 | 2 | ->andWhere( |
|
146 | 2 | $queryBuilder->expr()->eq('uid', $item->getIndexQueueUid()) |
|
147 | ) |
||
148 | 2 | ->execute(); |
|
149 | 2 | return $affectedRows; |
|
0 ignored issues
–
show
|
|||
150 | } |
||
151 | |||
152 | /** |
||
153 | * Initializes the QueryBuilder with a query the resets the error field for items that have an error. |
||
154 | * |
||
155 | * @return QueryBuilder |
||
156 | */ |
||
157 | 4 | private function getPreparedFlushErrorQuery(QueryBuilder $queryBuilder) |
|
158 | { |
||
159 | return $queryBuilder |
||
160 | 4 | ->update($this->table) |
|
161 | 4 | ->set('errors', '') |
|
162 | 4 | ->where( |
|
163 | 4 | $queryBuilder->expr()->notLike('errors', $queryBuilder->createNamedParameter('')) |
|
164 | ); |
||
165 | } |
||
166 | |||
167 | /** |
||
168 | * Updates an existing queue entry by $itemType $itemUid and $rootPageId. |
||
169 | * |
||
170 | * @param string $itemType The item's type, usually a table name. |
||
171 | * @param int $itemUid The item's uid, usually an integer uid, could be a |
||
172 | * different value for non-database-record types. |
||
173 | * @param string $indexingConfiguration The name of the related indexConfiguration |
||
174 | * @param int $rootPageId The uid of the rootPage |
||
175 | * @param int $changedTime The forced change time that should be used for updating |
||
176 | * @return int affected rows |
||
177 | */ |
||
178 | 13 | public function updateExistingItemByItemTypeAndItemUidAndRootPageId(string $itemType, int $itemUid, int $rootPageId, int $changedTime, string $indexingConfiguration = '') : int |
|
179 | { |
||
180 | 13 | $queryBuilder = $this->getQueryBuilder(); |
|
181 | $queryBuilder |
||
182 | 13 | ->update($this->table) |
|
183 | 13 | ->set('changed', $changedTime) |
|
184 | 13 | ->andWhere( |
|
185 | 13 | $queryBuilder->expr()->eq('item_type', $queryBuilder->createNamedParameter($itemType)), |
|
186 | 13 | $queryBuilder->expr()->eq('item_uid', $itemUid), |
|
187 | 13 | $queryBuilder->expr()->eq('root', $rootPageId) |
|
188 | ); |
||
189 | |||
190 | 13 | if (!empty($indexingConfiguration)) { |
|
191 | 13 | $queryBuilder->set('indexing_configuration', $indexingConfiguration); |
|
192 | } |
||
193 | |||
194 | 13 | return $queryBuilder->execute(); |
|
0 ignored issues
–
show
|
|||
195 | } |
||
196 | |||
197 | /** |
||
198 | * Adds an item to the index queue. |
||
199 | * |
||
200 | * Not meant for public use. |
||
201 | * |
||
202 | * @param string $itemType The item's type, usually a table name. |
||
203 | * @param int $itemUid The item's uid, usually an integer uid, could be a different value for non-database-record types. |
||
204 | * @param int $rootPageId |
||
205 | * @param int $changedTime |
||
206 | * @param string $indexingConfiguration The item's indexing configuration to use. Optional, overwrites existing / determined configuration. |
||
207 | * @return int the number of inserted rows, which is typically 1 |
||
208 | */ |
||
209 | 56 | public function add(string $itemType, int $itemUid, int $rootPageId, int $changedTime, string $indexingConfiguration) : int |
|
210 | { |
||
211 | 56 | $queryBuilder = $this->getQueryBuilder(); |
|
212 | return $queryBuilder |
||
0 ignored issues
–
show
|
|||
213 | 56 | ->insert($this->table) |
|
214 | 56 | ->values([ |
|
215 | 56 | 'root' => $rootPageId, |
|
216 | 56 | 'item_type' => $itemType, |
|
217 | 56 | 'item_uid' => $itemUid, |
|
218 | 56 | 'changed' => $changedTime, |
|
219 | 56 | 'errors' => '', |
|
220 | 56 | 'indexing_configuration' => $indexingConfiguration |
|
221 | ]) |
||
222 | 56 | ->execute(); |
|
223 | |||
224 | } |
||
225 | |||
226 | /** |
||
227 | * Retrieves the count of items that match certain filters. Each filter is passed as parts of the where claus combined with AND. |
||
228 | * |
||
229 | * @param array $sites |
||
230 | * @param array $indexQueueConfigurationNames |
||
231 | * @param array $itemTypes |
||
232 | * @param array $itemUids |
||
233 | * @param array $uids |
||
234 | * @return int |
||
235 | */ |
||
236 | 1 | public function countItems(array $sites = [], array $indexQueueConfigurationNames = [], array $itemTypes = [], array $itemUids = [], array $uids = []): int |
|
237 | { |
||
238 | 1 | $rootPageIds = Site::getRootPageIdsFromSites($sites); |
|
239 | 1 | $indexQueueConfigurationList = implode(",", $indexQueueConfigurationNames); |
|
240 | 1 | $itemTypeList = implode(",", $itemTypes); |
|
241 | 1 | $itemUids = array_map("intval", $itemUids); |
|
242 | 1 | $uids = array_map("intval", $uids); |
|
243 | |||
244 | 1 | $queryBuilderForCountingItems = $this->getQueryBuilder(); |
|
245 | 1 | $queryBuilderForCountingItems->count('uid')->from($this->table); |
|
246 | 1 | $queryBuilderForCountingItems = $this->addItemWhereClauses($queryBuilderForCountingItems, $rootPageIds, $indexQueueConfigurationList, $itemTypeList, $itemUids, $uids); |
|
247 | |||
248 | 1 | return (int)$queryBuilderForCountingItems->execute()->fetchColumn(0); |
|
249 | } |
||
250 | |||
251 | /** |
||
252 | * Gets the most recent changed time of a page's content elements |
||
253 | * |
||
254 | * @param int $pageUid |
||
255 | * @return int|null Timestamp of the most recent content element change or null if nothing is found. |
||
256 | */ |
||
257 | 38 | public function getPageItemChangedTimeByPageUid(int $pageUid) |
|
258 | { |
||
259 | 38 | $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content'); |
|
260 | 38 | $queryBuilder->getRestrictions()->removeAll(); |
|
261 | $pageContentLastChangedTime = $queryBuilder |
||
262 | 38 | ->add('select', $queryBuilder->expr()->max('tstamp', 'changed_time')) |
|
263 | 38 | ->from('tt_content') |
|
264 | 38 | ->where( |
|
265 | 38 | $queryBuilder->expr()->eq('pid', $pageUid) |
|
266 | ) |
||
267 | 38 | ->execute()->fetch(); |
|
268 | |||
269 | 38 | return $pageContentLastChangedTime['changed_time']; |
|
270 | } |
||
271 | |||
272 | /** |
||
273 | * Gets the most recent changed time for an item taking into account |
||
274 | * localized records. |
||
275 | * |
||
276 | * @param string $itemType The item's type, usually a table name. |
||
277 | * @param int $itemUid The item's uid |
||
278 | * @return int Timestamp of the most recent content element change |
||
279 | */ |
||
280 | 61 | public function getLocalizableItemChangedTime(string $itemType, int $itemUid) : int |
|
281 | { |
||
282 | 61 | $localizedChangedTime = 0; |
|
283 | |||
284 | 61 | if (isset($GLOBALS['TCA'][$itemType]['ctrl']['transOrigPointerField'])) { |
|
285 | // table is localizable |
||
286 | 61 | $translationOriginalPointerField = $GLOBALS['TCA'][$itemType]['ctrl']['transOrigPointerField']; |
|
287 | |||
288 | 61 | $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($itemType); |
|
289 | 61 | $queryBuilder->getRestrictions()->removeAll(); |
|
290 | $localizedChangedTime = $queryBuilder |
||
291 | 61 | ->add('select', $queryBuilder->expr()->max('tstamp', 'changed_time')) |
|
292 | 61 | ->from($itemType) |
|
293 | 61 | ->orWhere( |
|
294 | 61 | $queryBuilder->expr()->eq('uid', $itemUid), |
|
295 | 61 | $queryBuilder->expr()->eq($translationOriginalPointerField, $itemUid) |
|
296 | 61 | )->execute()->fetchColumn(0); |
|
297 | } |
||
298 | |||
299 | 61 | return (int)$localizedChangedTime; |
|
300 | } |
||
301 | |||
302 | /** |
||
303 | * Returns prepared QueryBuilder for contains* methods in this repository |
||
304 | * |
||
305 | * @param string $itemType |
||
306 | * @param int $itemUid |
||
307 | * @return QueryBuilder |
||
308 | */ |
||
309 | 66 | protected function getQueryBuilderForContainsMethods(string $itemType, int $itemUid) : QueryBuilder |
|
310 | { |
||
311 | 66 | $queryBuilder = $this->getQueryBuilder(); |
|
312 | 66 | return $queryBuilder->count('uid')->from($this->table) |
|
313 | 66 | ->andWhere( |
|
314 | 66 | $queryBuilder->expr()->eq('item_type', $queryBuilder->createNamedParameter($itemType)), |
|
315 | 66 | $queryBuilder->expr()->eq('item_uid', $itemUid) |
|
316 | ); |
||
317 | } |
||
318 | |||
319 | /** |
||
320 | * Checks whether the Index Queue contains a specific item. |
||
321 | * |
||
322 | * @param string $itemType The item's type, usually a table name. |
||
323 | * @param int $itemUid The item's uid |
||
324 | * @return bool TRUE if the item is found in the queue, FALSE otherwise |
||
325 | */ |
||
326 | 7 | public function containsItem(string $itemType, int $itemUid) : bool |
|
327 | { |
||
328 | 7 | return (bool)$this->getQueryBuilderForContainsMethods($itemType, $itemUid)->execute()->fetchColumn(0); |
|
329 | } |
||
330 | |||
331 | /** |
||
332 | * Checks whether the Index Queue contains a specific item. |
||
333 | * |
||
334 | * @param string $itemType The item's type, usually a table name. |
||
335 | * @param int $itemUid The item's uid |
||
336 | * @param integer $rootPageId |
||
337 | * @return bool TRUE if the item is found in the queue, FALSE otherwise |
||
338 | */ |
||
339 | 61 | public function containsItemWithRootPageId(string $itemType, int $itemUid, int $rootPageId) : bool |
|
340 | { |
||
341 | 61 | $queryBuilder = $this->getQueryBuilderForContainsMethods($itemType, $itemUid); |
|
342 | return (bool)$queryBuilder |
||
343 | 61 | ->andWhere($queryBuilder->expr()->eq('root', $rootPageId)) |
|
344 | 61 | ->execute()->fetchColumn(0); |
|
345 | } |
||
346 | |||
347 | /** |
||
348 | * Checks whether the Index Queue contains a specific item that has been |
||
349 | * marked as indexed. |
||
350 | * |
||
351 | * @param string $itemType The item's type, usually a table name. |
||
352 | * @param int $itemUid The item's uid |
||
353 | * @return bool TRUE if the item is found in the queue and marked as |
||
354 | * indexed, FALSE otherwise |
||
355 | */ |
||
356 | 2 | public function containsIndexedItem(string $itemType, int $itemUid) : bool |
|
357 | { |
||
358 | 2 | $queryBuilder = $this->getQueryBuilderForContainsMethods($itemType, $itemUid); |
|
359 | return (bool)$queryBuilder |
||
360 | 2 | ->andWhere($queryBuilder->expr()->gt('indexed', 0)) |
|
361 | 2 | ->execute()->fetchColumn(0); |
|
362 | } |
||
363 | |||
364 | /** |
||
365 | * Removes an item from the Index Queue. |
||
366 | * |
||
367 | * @param string $itemType The type of the item to remove, usually a table name. |
||
368 | * @param int $itemUid The uid of the item to remove |
||
369 | */ |
||
370 | 35 | public function deleteItem(string $itemType, int $itemUid = null) |
|
371 | { |
||
372 | 35 | $itemUids = empty($itemUid) ? [] : [$itemUid]; |
|
373 | 35 | $this->deleteItems([], [], [$itemType], $itemUids); |
|
374 | 35 | } |
|
375 | |||
376 | /** |
||
377 | * Removes all items of a certain type from the Index Queue. |
||
378 | * |
||
379 | * @param string $itemType The type of items to remove, usually a table name. |
||
380 | */ |
||
381 | 1 | public function deleteItemsByType(string $itemType) |
|
382 | { |
||
383 | 1 | $this->deleteItem($itemType); |
|
384 | 1 | } |
|
385 | |||
386 | /** |
||
387 | * Removes all items of a certain site from the Index Queue. Accepts an |
||
388 | * optional parameter to limit the deleted items by indexing configuration. |
||
389 | * |
||
390 | * @param Site $site The site to remove items for. |
||
391 | * @param string $indexingConfigurationName Name of a specific indexing configuration |
||
392 | * @throws \Exception |
||
393 | */ |
||
394 | 7 | public function deleteItemsBySite(Site $site, string $indexingConfigurationName = '') |
|
395 | { |
||
396 | 7 | $indexingConfigurationNames = empty($indexingConfigurationName) ? [] : [$indexingConfigurationName]; |
|
397 | 7 | $this->deleteItems([$site], $indexingConfigurationNames); |
|
398 | 7 | } |
|
399 | |||
400 | /** |
||
401 | * Removes items in the index queue filtered by the passed arguments. |
||
402 | * |
||
403 | * @param array $sites |
||
404 | * @param array $indexQueueConfigurationNames |
||
405 | * @param array $itemTypes |
||
406 | * @param array $itemUids |
||
407 | * @param array $uids |
||
408 | * @throws \Exception |
||
409 | */ |
||
410 | 42 | public function deleteItems(array $sites = [], array $indexQueueConfigurationNames = [], array $itemTypes = [], array $itemUids = [], array $uids = []) |
|
411 | { |
||
412 | 42 | $rootPageIds = Site::getRootPageIdsFromSites($sites); |
|
413 | 42 | $indexQueueConfigurationList = implode(",", $indexQueueConfigurationNames); |
|
414 | 42 | $itemTypeList = implode(",", $itemTypes); |
|
415 | 42 | $itemUids = array_map("intval", $itemUids); |
|
416 | 42 | $uids = array_map("intval", $uids); |
|
417 | |||
418 | 42 | $queryBuilderForDeletingItems = $this->getQueryBuilder(); |
|
419 | 42 | $queryBuilderForDeletingItems->delete($this->table); |
|
420 | 42 | $queryBuilderForDeletingItems = $this->addItemWhereClauses($queryBuilderForDeletingItems, $rootPageIds, $indexQueueConfigurationList, $itemTypeList, $itemUids, $uids); |
|
421 | |||
422 | 42 | $queryBuilderForDeletingProperties = $this->buildQueryForPropertyDeletion($queryBuilderForDeletingItems, $rootPageIds, $indexQueueConfigurationList, $itemTypeList, $itemUids, $uids); |
|
423 | |||
424 | 42 | $queryBuilderForDeletingItems->getConnection()->beginTransaction(); |
|
425 | try { |
||
426 | 42 | $queryBuilderForDeletingItems->execute(); |
|
427 | 42 | $queryBuilderForDeletingProperties->execute(); |
|
428 | |||
429 | 42 | $queryBuilderForDeletingItems->getConnection()->commit(); |
|
430 | } catch (\Exception $e) { |
||
431 | $queryBuilderForDeletingItems->getConnection()->rollback(); |
||
432 | throw $e; |
||
433 | } |
||
434 | 42 | } |
|
435 | |||
436 | /** |
||
437 | * Initializes the query builder to delete items in the index queue filtered by the passed arguments. |
||
438 | * |
||
439 | * @param array $rootPageIds filter on a set of rootPageUids. |
||
440 | * @param string $indexQueueConfigurationList |
||
441 | * @param string $itemTypeList |
||
442 | * @param array $itemUids filter on a set of item uids |
||
443 | * @param array $uids filter on a set of queue item uids |
||
444 | * @return QueryBuilder |
||
445 | */ |
||
446 | 44 | private function addItemWhereClauses(QueryBuilder $queryBuilderForDeletingItems, array $rootPageIds, string $indexQueueConfigurationList, string $itemTypeList, array $itemUids, array $uids): QueryBuilder |
|
447 | { |
||
448 | |||
449 | 44 | if (!empty($rootPageIds)) { |
|
450 | 7 | $queryBuilderForDeletingItems->andWhere($queryBuilderForDeletingItems->expr()->in('root', $rootPageIds)); |
|
451 | }; |
||
452 | |||
453 | 44 | if (!empty($indexQueueConfigurationList)) { |
|
454 | 9 | $queryBuilderForDeletingItems->andWhere($queryBuilderForDeletingItems->expr()->in('indexing_configuration', $queryBuilderForDeletingItems->createNamedParameter($indexQueueConfigurationList))); |
|
455 | } |
||
456 | |||
457 | 44 | if (!empty($itemTypeList)) { |
|
458 | 35 | $queryBuilderForDeletingItems->andWhere($queryBuilderForDeletingItems->expr()->in('item_type', $queryBuilderForDeletingItems->createNamedParameter($itemTypeList))); |
|
459 | } |
||
460 | |||
461 | 44 | if (!empty($itemUids)) { |
|
462 | 34 | $queryBuilderForDeletingItems->andWhere($queryBuilderForDeletingItems->expr()->in('item_uid', $itemUids)); |
|
463 | } |
||
464 | |||
465 | 44 | if (!empty($uids)) { |
|
466 | 1 | $queryBuilderForDeletingItems->andWhere($queryBuilderForDeletingItems->expr()->in('uid', $uids)); |
|
467 | } |
||
468 | |||
469 | 44 | return $queryBuilderForDeletingItems; |
|
470 | } |
||
471 | |||
472 | /** |
||
473 | * Initializes a query builder to delete the indexing properties of an item by the passed conditions. |
||
474 | * |
||
475 | * @param QueryBuilder $queryBuilderForDeletingItems |
||
476 | * @param array $rootPageIds |
||
477 | * @param string $indexQueueConfigurationList |
||
478 | * @param string $itemTypeList |
||
479 | * @param array $itemUids |
||
480 | * @param array $uids |
||
481 | * @return QueryBuilder |
||
482 | */ |
||
483 | 42 | private function buildQueryForPropertyDeletion(QueryBuilder $queryBuilderForDeletingItems, array $rootPageIds, string $indexQueueConfigurationList, string $itemTypeList, array $itemUids, array $uids): QueryBuilder |
|
484 | { |
||
485 | 42 | $queryBuilderForSelectingProperties = $queryBuilderForDeletingItems->getConnection()->createQueryBuilder(); |
|
486 | 42 | $queryBuilderForSelectingProperties->select('items.uid')->from('tx_solr_indexqueue_indexing_property', 'properties')->innerJoin( |
|
487 | 42 | 'properties', |
|
488 | 42 | $this->table, |
|
489 | 42 | 'items', |
|
490 | 42 | (string)$queryBuilderForSelectingProperties->expr()->andX( |
|
491 | 42 | $queryBuilderForSelectingProperties->expr()->eq('items.uid', $queryBuilderForSelectingProperties->quoteIdentifier('properties.item_id')), |
|
492 | 42 | empty($rootPageIds) ? '' : $queryBuilderForSelectingProperties->expr()->in('items.root', $rootPageIds), |
|
493 | 42 | empty($indexQueueConfigurationList) ? '' : $queryBuilderForSelectingProperties->expr()->in('items.indexing_configuration', $queryBuilderForSelectingProperties->createNamedParameter($indexQueueConfigurationList)), |
|
494 | 42 | empty($itemTypeList) ? '' : $queryBuilderForSelectingProperties->expr()->in('items.item_type', $queryBuilderForSelectingProperties->createNamedParameter($itemTypeList)), |
|
495 | 42 | empty($itemUids) ? '' : $queryBuilderForSelectingProperties->expr()->in('items.item_uid', $itemUids), |
|
496 | 42 | empty($uids) ? '' : $queryBuilderForSelectingProperties->expr()->in('items.uid', $uids) |
|
497 | ) |
||
498 | ); |
||
499 | 42 | $propertyEntriesToDelete = implode(',', array_column($queryBuilderForSelectingProperties->execute()->fetchAll(), 'uid')); |
|
500 | |||
501 | 42 | $queryBuilderForDeletingProperties = $queryBuilderForDeletingItems->getConnection()->createQueryBuilder(); |
|
502 | |||
503 | // make sure executing the propety deletion query doesn't fail if there are no properties to delete |
||
504 | 42 | if (empty($propertyEntriesToDelete)) { |
|
505 | 41 | $propertyEntriesToDelete = '0'; |
|
506 | } |
||
507 | |||
508 | 42 | $queryBuilderForDeletingProperties->delete('tx_solr_indexqueue_indexing_property')->where( |
|
509 | 42 | $queryBuilderForDeletingProperties->expr()->in('item_id', $propertyEntriesToDelete) |
|
510 | ); |
||
511 | |||
512 | 42 | return $queryBuilderForDeletingProperties; |
|
513 | } |
||
514 | |||
515 | /** |
||
516 | * Removes all items from the Index Queue. |
||
517 | * |
||
518 | * @return int The number of affected rows. For a truncate this is unreliable as theres no meaningful information. |
||
519 | */ |
||
520 | 1 | public function deleteAllItems() |
|
521 | { |
||
522 | 1 | return $this->getQueryBuilder()->getConnection()->truncate($this->table); |
|
523 | } |
||
524 | |||
525 | /** |
||
526 | * Gets a single Index Queue item by its uid. |
||
527 | * |
||
528 | * @param int $uid Index Queue item uid |
||
529 | * @return Item|null The request Index Queue item or NULL if no item with $itemId was found |
||
530 | */ |
||
531 | 28 | public function findItemByUid(int $uid) |
|
532 | { |
||
533 | 28 | $queryBuilder = $this->getQueryBuilder(); |
|
534 | $indexQueueItemRecord = $queryBuilder |
||
535 | 28 | ->select('*') |
|
536 | 28 | ->from($this->table) |
|
537 | 28 | ->where($queryBuilder->expr()->eq('uid', $uid)) |
|
538 | 28 | ->execute()->fetch(); |
|
539 | |||
540 | 28 | if (!isset($indexQueueItemRecord['uid'])) { |
|
541 | 3 | return null; |
|
542 | } |
||
543 | |||
544 | /** @var Item $item*/ |
||
545 | 25 | $item = GeneralUtility::makeInstance(Item::class, /** @scrutinizer ignore-type */ $indexQueueItemRecord); |
|
546 | 25 | return $item; |
|
547 | } |
||
548 | |||
549 | /** |
||
550 | * Gets Index Queue items by type and uid. |
||
551 | * |
||
552 | * @param string $itemType item type, usually the table name |
||
553 | * @param int $itemUid item uid |
||
554 | * @return Item[] An array of items matching $itemType and $itemUid |
||
555 | */ |
||
556 | 38 | public function findItemsByItemTypeAndItemUid(string $itemType, int $itemUid) : array |
|
557 | { |
||
558 | 38 | $queryBuilder = $this->getQueryBuilder(); |
|
559 | 38 | $compositeExpression = $queryBuilder->expr()->andX( |
|
560 | 38 | $queryBuilder->expr()->eq('item_type', $queryBuilder->getConnection()->quote($itemType, \PDO::PARAM_STR)), |
|
561 | 38 | $queryBuilder->expr()->eq('item_uid', $itemUid) |
|
562 | ); |
||
563 | 38 | return $this->getItemsByCompositeExpression($compositeExpression, $queryBuilder); |
|
564 | } |
||
565 | |||
566 | /** |
||
567 | * Returns a collection of items by CompositeExpression. |
||
568 | * D |
||
569 | * |
||
570 | * @param CompositeExpression|null $expression Optional expression to filter records. |
||
571 | * @param QueryBuilder|null $queryBuilder QueryBuilder to use |
||
572 | * @return array |
||
573 | */ |
||
574 | 38 | protected function getItemsByCompositeExpression(CompositeExpression $expression = null, QueryBuilder $queryBuilder = null) : array |
|
575 | { |
||
576 | 38 | if (!$queryBuilder instanceof QueryBuilder) { |
|
577 | $queryBuilder = $this->getQueryBuilder(); |
||
578 | } |
||
579 | |||
580 | 38 | $queryBuilder->select('*')->from($this->table); |
|
581 | 38 | if (isset($expression)) { |
|
582 | 38 | $queryBuilder->where($expression); |
|
583 | } |
||
584 | |||
585 | 38 | $indexQueueItemRecords = $queryBuilder->execute()->fetchAll(); |
|
586 | 38 | return $this->getIndexQueueItemObjectsFromRecords($indexQueueItemRecords); |
|
587 | } |
||
588 | |||
589 | /** |
||
590 | * Returns all items in the queue. |
||
591 | * |
||
592 | * @return Item[] all Items from Queue without restrictions |
||
593 | */ |
||
594 | 1 | public function findAll() : array |
|
595 | { |
||
596 | 1 | $queryBuilder = $this->getQueryBuilder(); |
|
597 | $allRecords = $queryBuilder |
||
598 | 1 | ->select('*') |
|
599 | 1 | ->from($this->table) |
|
600 | 1 | ->execute()->fetchAll(); |
|
601 | 1 | return $this->getIndexQueueItemObjectsFromRecords($allRecords); |
|
602 | } |
||
603 | |||
604 | /** |
||
605 | * Gets $limit number of items to index for a particular $site. |
||
606 | * |
||
607 | * @param Site $site TYPO3 site |
||
608 | * @param int $limit Number of items to get from the queue |
||
609 | * @return Item[] Items to index to the given solr server |
||
610 | */ |
||
611 | 3 | public function findItemsToIndex(Site $site, int $limit = 50) : array |
|
612 | { |
||
613 | 3 | $queryBuilder = $this->getQueryBuilder(); |
|
614 | // determine which items to index with this run |
||
615 | $indexQueueItemRecords = $queryBuilder |
||
616 | 3 | ->select('*') |
|
617 | 3 | ->from($this->table) |
|
618 | 3 | ->andWhere( |
|
619 | 3 | $queryBuilder->expr()->eq('root', $site->getRootPageId()), |
|
620 | 3 | $queryBuilder->expr()->gt('changed', 'indexed'), |
|
621 | 3 | $queryBuilder->expr()->lte('changed', time()), |
|
622 | 3 | $queryBuilder->expr()->eq('errors', $queryBuilder->createNamedParameter('')) |
|
623 | ) |
||
624 | 3 | ->orderBy('indexing_priority', 'DESC') |
|
625 | 3 | ->addOrderBy('changed', 'DESC') |
|
626 | 3 | ->addOrderBy('uid', 'DESC') |
|
627 | 3 | ->setMaxResults($limit) |
|
628 | 3 | ->execute()->fetchAll(); |
|
629 | |||
630 | 3 | return $this->getIndexQueueItemObjectsFromRecords($indexQueueItemRecords); |
|
631 | } |
||
632 | |||
633 | /** |
||
634 | * Retrieves the count of items that match certain filters. Each filter is passed as parts of the where claus combined with AND. |
||
635 | * |
||
636 | * @param array $sites |
||
637 | * @param array $indexQueueConfigurationNames |
||
638 | * @param array $itemTypes |
||
639 | * @param array $itemUids |
||
640 | * @param array $uids |
||
641 | * @param int $start |
||
642 | * @param int $limit |
||
643 | * @return array |
||
644 | */ |
||
645 | 1 | public function findItems(array $sites = [], array $indexQueueConfigurationNames = [], array $itemTypes = [], array $itemUids = [], array $uids = [], $start = 0, $limit = 50): array |
|
646 | { |
||
647 | 1 | $rootPageIds = Site::getRootPageIdsFromSites($sites); |
|
648 | 1 | $indexQueueConfigurationList = implode(",", $indexQueueConfigurationNames); |
|
649 | 1 | $itemTypeList = implode(",", $itemTypes); |
|
650 | 1 | $itemUids = array_map("intval", $itemUids); |
|
651 | 1 | $uids = array_map("intval", $uids); |
|
652 | 1 | $itemQueryBuilder = $this->getQueryBuilder()->select('*')->from($this->table); |
|
653 | 1 | $itemQueryBuilder = $this->addItemWhereClauses($itemQueryBuilder, $rootPageIds, $indexQueueConfigurationList, $itemTypeList, $itemUids, $uids); |
|
654 | 1 | $itemRecords = $itemQueryBuilder->setFirstResult($start)->setMaxResults($limit)->execute()->fetchAll(); |
|
655 | 1 | return $this->getIndexQueueItemObjectsFromRecords($itemRecords); |
|
656 | } |
||
657 | |||
658 | /** |
||
659 | * Creates an array of ApacheSolrForTypo3\Solr\IndexQueue\Item objects from an array of |
||
660 | * index queue records. |
||
661 | * |
||
662 | * @param array $indexQueueItemRecords Array of plain index queue records |
||
663 | * @return array Array of ApacheSolrForTypo3\Solr\IndexQueue\Item objects |
||
664 | */ |
||
665 | 40 | protected function getIndexQueueItemObjectsFromRecords(array $indexQueueItemRecords) : array |
|
666 | { |
||
667 | 40 | $tableRecords = $this->getAllQueueItemRecordsByUidsGroupedByTable($indexQueueItemRecords); |
|
668 | 40 | return $this->getQueueItemObjectsByRecords($indexQueueItemRecords, $tableRecords); |
|
669 | } |
||
670 | |||
671 | /** |
||
672 | * Returns the records for suitable item type. |
||
673 | * |
||
674 | * @param array $indexQueueItemRecords |
||
675 | * @return array |
||
676 | */ |
||
677 | 40 | protected function getAllQueueItemRecordsByUidsGroupedByTable(array $indexQueueItemRecords) : array |
|
678 | { |
||
679 | 40 | $tableUids = []; |
|
680 | 40 | $tableRecords = []; |
|
681 | // grouping records by table |
||
682 | 40 | foreach ($indexQueueItemRecords as $indexQueueItemRecord) { |
|
683 | 40 | $tableUids[$indexQueueItemRecord['item_type']][] = $indexQueueItemRecord['item_uid']; |
|
684 | } |
||
685 | |||
686 | // fetching records by table, saves us a lot of single queries |
||
687 | 40 | foreach ($tableUids as $table => $uids) { |
|
688 | 40 | $uidList = implode(',', $uids); |
|
689 | |||
690 | 40 | $queryBuilderForRecordTable = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); |
|
691 | 40 | $queryBuilderForRecordTable->getRestrictions()->removeAll(); |
|
692 | $resultsFromRecordTable = $queryBuilderForRecordTable |
||
693 | 40 | ->select('*') |
|
694 | 40 | ->from($table) |
|
695 | 40 | ->where($queryBuilderForRecordTable->expr()->in('uid', $uidList)) |
|
696 | 40 | ->execute(); |
|
697 | 40 | $records = []; |
|
698 | 40 | while ($record = $resultsFromRecordTable->fetch()) { |
|
699 | 39 | $records[$record['uid']] = $record; |
|
700 | } |
||
701 | |||
702 | 40 | $tableRecords[$table] = $records; |
|
703 | 40 | $this->hookPostProcessFetchRecordsForIndexQueueItem($table, $uids, $tableRecords); |
|
704 | } |
||
705 | |||
706 | 40 | return $tableRecords; |
|
707 | } |
||
708 | |||
709 | /** |
||
710 | * Calls defined in postProcessFetchRecordsForIndexQueueItem hook method. |
||
711 | * |
||
712 | * @param string $table |
||
713 | * @param array $uids |
||
714 | * @param array $tableRecords |
||
715 | * |
||
716 | * @return void |
||
717 | */ |
||
718 | 40 | protected function hookPostProcessFetchRecordsForIndexQueueItem(string $table, array $uids, array &$tableRecords) |
|
719 | { |
||
720 | 40 | if (!is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['postProcessFetchRecordsForIndexQueueItem'])) { |
|
721 | 40 | return; |
|
722 | } |
||
723 | $params = ['table' => $table, 'uids' => $uids, 'tableRecords' => &$tableRecords]; |
||
724 | foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['postProcessFetchRecordsForIndexQueueItem'] as $reference) { |
||
725 | GeneralUtility::callUserFunction($reference, $params, $this); |
||
726 | } |
||
727 | } |
||
728 | |||
729 | /** |
||
730 | * Instantiates a list of Item objects from database records. |
||
731 | * |
||
732 | * @param array $indexQueueItemRecords records from database |
||
733 | * @param array $tableRecords |
||
734 | * @return array |
||
735 | */ |
||
736 | 40 | protected function getQueueItemObjectsByRecords(array $indexQueueItemRecords, array $tableRecords) : array |
|
737 | { |
||
738 | 40 | $indexQueueItems = []; |
|
739 | 40 | foreach ($indexQueueItemRecords as $indexQueueItemRecord) { |
|
740 | 40 | if (isset($tableRecords[$indexQueueItemRecord['item_type']][$indexQueueItemRecord['item_uid']])) { |
|
741 | 39 | $indexQueueItems[] = GeneralUtility::makeInstance( |
|
742 | 39 | Item::class, |
|
743 | 39 | /** @scrutinizer ignore-type */ $indexQueueItemRecord, |
|
744 | 39 | /** @scrutinizer ignore-type */ $tableRecords[$indexQueueItemRecord['item_type']][$indexQueueItemRecord['item_uid']] |
|
745 | ); |
||
746 | } else { |
||
747 | 1 | $this->logger->log( |
|
748 | 1 | SolrLogManager::ERROR, |
|
749 | 1 | 'Record missing for Index Queue item. Item removed.', |
|
750 | [ |
||
751 | 1 | $indexQueueItemRecord |
|
752 | ] |
||
753 | ); |
||
754 | 1 | $this->deleteItem($indexQueueItemRecord['item_type'], |
|
755 | 1 | $indexQueueItemRecord['item_uid']); |
|
756 | } |
||
757 | } |
||
758 | |||
759 | 40 | return $indexQueueItems; |
|
760 | } |
||
761 | |||
762 | /** |
||
763 | * Marks an item as failed and causes the indexer to skip the item in the |
||
764 | * next run. |
||
765 | * |
||
766 | * @param int|Item $item Either the item's Index Queue uid or the complete item |
||
767 | * @param string $errorMessage Error message |
||
768 | * @return int affected rows |
||
769 | */ |
||
770 | 6 | public function markItemAsFailed($item, string $errorMessage = ''): int |
|
771 | { |
||
772 | 6 | $itemUid = ($item instanceof Item) ? $item->getIndexQueueUid() : (int)$item; |
|
773 | 6 | $errorMessage = empty($errorMessage) ? '1' : $errorMessage; |
|
774 | |||
775 | 6 | $queryBuilder = $this->getQueryBuilder(); |
|
776 | return (int)$queryBuilder |
||
777 | 6 | ->update($this->table) |
|
778 | 6 | ->set('errors', $errorMessage) |
|
779 | 6 | ->where($queryBuilder->expr()->eq('uid', $itemUid)) |
|
780 | 6 | ->execute(); |
|
781 | } |
||
782 | |||
783 | /** |
||
784 | * Sets the timestamp of when an item last has been indexed. |
||
785 | * |
||
786 | * @param Item $item |
||
787 | * @return int affected rows |
||
788 | */ |
||
789 | 2 | public function updateIndexTimeByItem(Item $item) : int |
|
790 | { |
||
791 | 2 | $queryBuilder = $this->getQueryBuilder(); |
|
792 | return (int)$queryBuilder |
||
793 | 2 | ->update($this->table) |
|
794 | 2 | ->set('indexed', time()) |
|
795 | 2 | ->where($queryBuilder->expr()->eq('uid', $item->getIndexQueueUid())) |
|
796 | 2 | ->execute(); |
|
797 | } |
||
798 | |||
799 | /** |
||
800 | * Sets the change timestamp of an item. |
||
801 | * |
||
802 | * @param Item $item |
||
803 | * @param int $changedTime |
||
804 | * @return int affected rows |
||
805 | */ |
||
806 | public function updateChangedTimeByItem(Item $item, int $changedTime) : int |
||
807 | { |
||
808 | $queryBuilder = $this->getQueryBuilder(); |
||
809 | return (int)$queryBuilder |
||
810 | ->update($this->table) |
||
811 | ->set('changed', $changedTime) |
||
812 | ->where($queryBuilder->expr()->eq('uid', $item->getIndexQueueUid())) |
||
813 | ->execute(); |
||
814 | } |
||
815 | |||
816 | /** |
||
817 | * Initializes Queue by given sql |
||
818 | * |
||
819 | * Note: Do not use platform specific functions! |
||
820 | * |
||
821 | * @param string $sqlStatement Native SQL statement |
||
822 | * @return int The number of affected rows. |
||
823 | * @internal |
||
824 | * @throws DBALException |
||
825 | */ |
||
826 | 12 | public function initializeByNativeSQLStatement(string $sqlStatement) : int |
|
827 | { |
||
828 | 12 | return $this->getQueryBuilder()->getConnection()->exec($sqlStatement); |
|
829 | } |
||
830 | |||
831 | /** |
||
832 | * Retrieves an array of pageIds from mountPoints that allready have a queue entry. |
||
833 | * |
||
834 | * @param string $identifier identifier of the mount point |
||
835 | * @return array pageIds from mountPoints that allready have a queue entry |
||
836 | */ |
||
837 | 7 | public function findPageIdsOfExistingMountPagesByMountIdentifier(string $identifier) : array |
|
838 | { |
||
839 | 7 | $queryBuilder = $this->getQueryBuilder(); |
|
840 | $resultSet = $queryBuilder |
||
841 | 7 | ->select('item_uid') |
|
842 | 7 | ->add('select', $queryBuilder->expr()->count('*', 'queueItemCount'), true) |
|
843 | 7 | ->from($this->table) |
|
844 | 7 | ->where( |
|
845 | 7 | $queryBuilder->expr()->eq('item_type', $queryBuilder->createNamedParameter('pages')), |
|
846 | 7 | $queryBuilder->expr()->eq('pages_mountidentifier', $queryBuilder->createNamedParameter($identifier)) |
|
847 | ) |
||
848 | 7 | ->groupBy('item_uid') |
|
849 | 7 | ->execute(); |
|
850 | |||
851 | 7 | $mountedPagesIdsWithQueueItems = []; |
|
852 | 7 | while ($record = $resultSet->fetch()) { |
|
853 | if ($record['queueItemCount'] > 0) { |
||
854 | $mountedPagesIdsWithQueueItems[] = $record['item_uid']; |
||
855 | } |
||
856 | } |
||
857 | |||
858 | 7 | return $mountedPagesIdsWithQueueItems; |
|
859 | } |
||
860 | |||
861 | /** |
||
862 | * Retrieves an array of items for mount destinations mathed by root page ID, Mount Identifier and a list of mounted page IDs. |
||
863 | * |
||
864 | * @param int $rootPid |
||
865 | * @param string $identifier identifier of the mount point |
||
866 | * @param array $mountedPids An array of mounted page IDs |
||
867 | * @return array |
||
868 | */ |
||
869 | 7 | public function findAllIndexQueueItemsByRootPidAndMountIdentifierAndMountedPids(int $rootPid, string $identifier, array $mountedPids) : array |
|
870 | { |
||
871 | 7 | $queryBuilder = $this->getQueryBuilder(); |
|
872 | return $queryBuilder |
||
873 | 7 | ->select('*') |
|
874 | 7 | ->from($this->table) |
|
875 | 7 | ->where( |
|
876 | 7 | $queryBuilder->expr()->eq('root', $queryBuilder->createNamedParameter($rootPid, \PDO::PARAM_INT)), |
|
877 | 7 | $queryBuilder->expr()->eq('item_type', $queryBuilder->createNamedParameter('pages')), |
|
878 | 7 | $queryBuilder->expr()->in('item_uid', $mountedPids), |
|
879 | 7 | $queryBuilder->expr()->eq('has_indexing_properties', $queryBuilder->createNamedParameter(1, \PDO::PARAM_INT)), |
|
880 | 7 | $queryBuilder->expr()->eq('pages_mountidentifier', $queryBuilder->createNamedParameter($identifier)) |
|
881 | ) |
||
882 | 7 | ->execute()->fetchAll(); |
|
883 | } |
||
884 | |||
885 | /** |
||
886 | * Updates has_indexing_properties field for given Item |
||
887 | * |
||
888 | * @param int $itemUid |
||
889 | * @param bool $hasIndexingPropertiesFlag |
||
890 | * @return int number of affected rows, 1 on success |
||
891 | */ |
||
892 | 9 | public function updateHasIndexingPropertiesFlagByItemUid(int $itemUid, bool $hasIndexingPropertiesFlag): int |
|
893 | { |
||
894 | 9 | $queryBuilder = $this->getQueryBuilder(); |
|
895 | |||
896 | return $queryBuilder |
||
0 ignored issues
–
show
|
|||
897 | 9 | ->update($this->table) |
|
898 | 9 | ->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($itemUid, \PDO::PARAM_INT))) |
|
899 | 9 | ->set('has_indexing_properties', $queryBuilder->createNamedParameter($hasIndexingPropertiesFlag, \PDO::PARAM_INT), false) |
|
900 | 9 | ->execute(); |
|
901 | } |
||
902 | } |
||
903 |