‪TYPO3CMS  ‪main
IndexSearchRepository.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Doctrine\DBAL\Platforms\MariaDBPlatform as DoctrineMariaDBPlatform;
21 use Doctrine\DBAL\Platforms\MySQLPlatform as DoctrineMySQLPlatform;
22 use Doctrine\DBAL\Result;
37 
43 {
47  protected array ‪$externalParsers = [];
48 
52  protected string ‪$frontendUserGroupList = '';
53 
58  protected string ‪$sections = '';
59 
64  protected int ‪$searchType = 0;
65 
70  protected int ‪$languageUid = 0;
71 
76  protected int ‪$mediaType = 0;
77 
82  protected string ‪$sortOrder = '';
83 
88  protected bool ‪$descendingSortOrderFlag = false;
89 
94  protected int ‪$resultpagePointer = 0;
95 
100  protected int ‪$numberOfResults = 10;
101 
107  protected string ‪$searchRootPageIdList = '';
108 
112  protected array ‪$wSelClauses = [];
113 
123  protected bool ‪$useExactCount = false;
124 
131  protected bool ‪$displayForbiddenRecords = false;
132 
139  public function ‪initialize(array $settings, array $searchData, array ‪$externalParsers, int|string ‪$searchRootPageIdList): void
140  {
141  $this->externalParsers = ‪$externalParsers;
142  $this->searchRootPageIdList = (string)‪$searchRootPageIdList;
143  $this->frontendUserGroupList = implode(',', GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('frontend.user', 'groupIds', [0, -1]));
144  if ($settings['exactCount'] ?? false) {
145  $this->useExactCount = true;
146  }
147  if ($settings['displayForbiddenRecords'] ?? false) {
148  $this->displayForbiddenRecords = true;
149  }
150  $this->sections = (string)($searchData['sections'] ?? '');
151  $this->searchType = (int)($searchData['searchType'] ?? 0);
152  $this->languageUid = (int)($searchData['languageUid'] ?? 0);
153  $this->mediaType = (int)($searchData['mediaType'] ?? 0);
154  $this->sortOrder = (string)($searchData['sortOrder'] ?? '');
155  $this->descendingSortOrderFlag = (bool)($searchData['desc'] ?? false);
156  $this->resultpagePointer = (int)($searchData['pointer'] ?? 0);
157  if (is_numeric($searchData['numberOfResults'] ?? null)) {
158  $this->numberOfResults = (int)$searchData['numberOfResults'];
159  }
160  }
161 
169  public function ‪doSearch(array $searchWords, int $freeIndexUid): array|false
170  {
171  $useMysqlFulltext = (bool)GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('indexed_search', 'useMysqlFulltext');
172  // Getting SQL result pointer:
173  $this->‪getTimeTracker()->push('Searching result');
174  // @todo Change hook and method signatures to return the QueryBuilder instead the Result. Consider to move
175  // from hook to a proper PSR-14 event.
176  if ($hookObj = $this->‪hookRequest('getResultRows_SQLpointer')) {
177  $result = $hookObj->getResultRows_SQLpointer($searchWords, $freeIndexUid);
178  } elseif ($useMysqlFulltext) {
179  $result = $this->‪getResultRows_SQLpointerMysqlFulltext($searchWords, $freeIndexUid);
180  } else {
181  $result = $this->‪getResultRows_SQLpointer($searchWords, $freeIndexUid);
182  }
183  $this->‪getTimeTracker()->pull();
184  // Organize and process result:
185  if ($result) {
186  // We need the result row count beforehand for the pointer calculation. Using $result->rowCount() for
187  // select queries is not reliable across dbms systems, and in case of sqlite this will return 0 here,
188  // even if there is a result set, we need to retrieve all rows and doing a count on the array.
189  // @todo Change this to second count() query call, after getResultRows_SQLpointer() signatures/hook has
190  // been changed to return QueryBuilder instead of the Result.
191  $rows = $result->fetchAllAssociative();
192  // Total search-result count
193  $count = count($rows);
194  // The pointer is set to the result page that is currently being viewed
195  $pointer = ‪MathUtility::forceIntegerInRange($this->resultpagePointer, 0, (int)floor($count / $this->numberOfResults));
196  // Initialize result accumulation variables:
197  $c = 0;
198  // Result pointer: Counts up the position in the current search-result
199  $grouping_phashes = [];
200  // Used to filter out duplicates.
201  $grouping_chashes = [];
202  // Used to filter out duplicates BASED ON cHash.
203  $firstRow = [];
204  // Will hold the first row in result - used to calculate relative hit-ratings.
205  $resultRows = [];
206  // Will hold the results rows for display.
207  // Now, traverse result and put the rows to be displayed into an array
208  // Each row should contain the fields from 'ISEC.*, IP.*' combined
209  // + artificial fields "show_resume" (bool) and "result_number" (counter)
210  // @todo Change this back to while($row = $result->fetchAssociative()) after changing
211  // getResultRows_SQLpointer() returning QueryBuilder instead of a Result.
212  foreach ($rows as $row) {
213  // Set first row
214  if (!$c) {
215  $firstRow = $row;
216  }
217  // Tells whether we can link directly to a document
218  // or not (depends on possible right problems)
219  $row['show_resume'] = $this->‪checkResume($row);
220  $phashGr = !in_array($row['phash_grouping'], $grouping_phashes);
221  $chashGr = !in_array($row['contentHash'] . '.' . $row['data_page_id'], $grouping_chashes);
222  if ($phashGr && $chashGr) {
223  // Only if the resume may be shown are we going to filter out duplicates...
224  if ($row['show_resume'] || $this->displayForbiddenRecords) {
225  // Only on documents which are not multiple pages documents
226  if (!$this->‪multiplePagesType((string)($row['item_type'] ?? ''))) {
227  $grouping_phashes[] = $row['phash_grouping'];
228  }
229  $grouping_chashes[] = $row['contentHash'] . '.' . $row['data_page_id'];
230  // Increase the result pointer
231  $c++;
232  // All rows for display is put into resultRows[]
233  if ($c > $pointer * $this->numberOfResults && $c <= $pointer * $this->numberOfResults + $this->numberOfResults) {
234  $row['result_number'] = $c;
235  $resultRows[] = $row;
236  // This may lead to a problem: If the result check is not stopped here, the search will take longer.
237  // However the result counter will not filter out grouped cHashes/pHashes that were not processed yet.
238  // You can change this behavior using the "settings.exactCount" property (see above).
239  if (!$this->useExactCount && $c + 1 > ($pointer + 1) * $this->numberOfResults) {
240  break;
241  }
242  }
243  } else {
244  // Skip this row if the user cannot
245  // view it (missing permission)
246  $count--;
247  }
248  } else {
249  // For each time a phash_grouping document is found
250  // (which is thus not displayed) the search-result count is reduced,
251  // so that it matches the number of rows displayed.
252  $count--;
253  }
254  }
255 
256  return [
257  'resultRows' => $resultRows,
258  'firstRow' => $firstRow,
259  'count' => $count,
260  ];
261  }
262  // No results found
263  return false;
264  }
265 
271  public function ‪writeSearchStat(int $pageId, array $searchWords): void
272  {
273  if (empty($searchWords)) {
274  return;
275  }
276  $entries = [];
277  foreach ($searchWords as $val) {
278  $entries[] = [
279  mb_substr($val['sword'], 0, 50),
280  ‪$GLOBALS['EXEC_TIME'],
281  $pageId,
282  ];
283  }
284  GeneralUtility::makeInstance(ConnectionPool::class)
285  ->getConnectionForTable('index_stat_word')
286  ->bulkInsert(
287  'index_stat_word',
288  $entries,
289  ['word', 'tstamp', 'pageid'],
291  );
292  }
293 
294  public function ‪getFullTextRowByPhash(int $phash): ?array
295  {
296  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_fulltext');
297  return $queryBuilder
298  ->select('*')
299  ->from('index_fulltext')
300  ->where(
301  $queryBuilder->expr()->eq(
302  'phash',
303  $queryBuilder->createNamedParameter($phash, ‪Connection::PARAM_INT)
304  )
305  )
306  ->setMaxResults(1)
307  ->executeQuery()
308  ->fetchAssociative() ?: null;
309  }
310 
311  public function ‪getIndexConfigurationById(int $id): ?array
312  {
313  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
314  ->getQueryBuilderForTable('index_config');
315  return $queryBuilder
316  ->select('uid', 'title')
317  ->from('index_config')
318  ->where(
319  $queryBuilder->expr()->eq(
320  'uid',
321  $queryBuilder->createNamedParameter($id, ‪Connection::PARAM_INT)
322  )
323  )
324  ->setMaxResults(1)
325  ->executeQuery()
326  ->fetchAssociative() ?: null;
327  }
328 
335  protected function ‪getResultRows_SQLpointer(array $searchWords, int $freeIndexUid): Result|false
336  {
337  // This SEARCHES for the searchwords in $searchWords AND returns a
338  // COMPLETE list of phash-integers of the matches.
339  $list = $this->‪getPhashList($searchWords);
340  // Perform SQL Search / collection of result rows array:
341  if ($list) {
342  // Do the search:
343  $this->‪getTimeTracker()->push('execFinalQuery');
344  $res = $this->‪execFinalQuery($list, $freeIndexUid);
345  $this->‪getTimeTracker()->pull();
346  return $res;
347  }
348  return false;
349  }
350 
359  protected function ‪getResultRows_SQLpointerMysqlFulltext(array $searchWordsArray, int $freeIndexUid): Result|false
360  {
361  $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('index_fulltext');
362  $platform = $connection->getDatabasePlatform();
363  if (!($platform instanceof DoctrineMariaDBPlatform || $platform instanceof DoctrineMySQLPlatform)) {
364  throw new \RuntimeException(
365  'Extension indexed_search is configured to use mysql fulltext, but table \'index_fulltext\''
366  . ' is running on a different DBMS.',
367  1472585525
368  );
369  }
370  // Build the search string, detect which fulltext index to use, and decide whether boolean search is needed or not
371  $searchData = $this->‪getSearchString($searchWordsArray);
372  // Perform SQL Search / collection of result rows array:
373  $resource = false;
374  if ($searchData) {
375  $timeTracker = GeneralUtility::makeInstance(TimeTracker::class);
376  // Do the search:
377  $timeTracker->push('execFinalQuery');
378  $resource = $this->‪execFinalQuery_fulltext($searchData, $freeIndexUid);
379  $timeTracker->pull();
380  }
381  return $resource;
382  }
383 
392  protected function ‪getSearchString(array $searchWordArray): array
393  {
394  // Change this to TRUE to force BOOLEAN SEARCH MODE (useful if fulltext index is still empty)
395  $searchBoolean = false;
396  $fulltextIndex = 'index_fulltext.fulltextdata';
397  // This holds the result if the search is natural (doesn't contain any boolean operators)
398  $naturalSearchString = '';
399  // This holds the result if the search is boolean (contains +/-/| operators)
400  $booleanSearchString = '';
401 
402  ‪$searchType = $this->‪getSearchType();
403 
404  // Traverse searchwords and prefix them with corresponding operator
405  foreach ($searchWordArray as $searchWordData) {
406  // Making the query for a single search word based on the search-type
407  $searchWord = $searchWordData['sword'];
408  $wildcard = '';
409  if (str_contains($searchWord, ' ')) {
410  ‪$searchType = 20;
411  }
412  switch (‪$searchType) {
413  case 1:
414  case 2:
415  case 3:
416  // First part of word
417  $wildcard = '*';
418  // Part-of-word search requires boolean mode!
419  $searchBoolean = true;
420  break;
421  case 10:
422  $indexerObj = GeneralUtility::makeInstance(Indexer::class);
423  $searchWord = $indexerObj->metaphone($searchWord, $indexerObj->storeMetaphoneInfoAsWords);
424  $fulltextIndex = 'index_fulltext.metaphonedata';
425  break;
426  case 20:
427  $searchBoolean = true;
428  // Remove existing quotes and fix misplaced quotes.
429  $searchWord = trim(str_replace('"', ' ', $searchWord));
430  break;
431  }
432  // Perform search for word:
433  switch ($searchWordData['oper']) {
434  case 'AND NOT':
435  $booleanSearchString .= ' -' . $searchWord . $wildcard;
436  $searchBoolean = true;
437  break;
438  case 'OR':
439  $booleanSearchString .= ' ' . $searchWord . $wildcard;
440  $searchBoolean = true;
441  break;
442  default:
443  $booleanSearchString .= ' +' . $searchWord . $wildcard;
444  $naturalSearchString .= ' ' . $searchWord;
445  }
446  }
447  if (‪$searchType === 20) {
448  $searchString = '"' . trim($naturalSearchString) . '"';
449  } elseif ($searchBoolean) {
450  $searchString = trim($booleanSearchString);
451  } else {
452  $searchString = trim($naturalSearchString);
453  }
454  return [
455  'searchBoolean' => $searchBoolean,
456  'searchString' => $searchString,
457  'fulltextIndex' => $fulltextIndex,
458  ];
459  }
460 
469  protected function ‪execFinalQuery_fulltext(array $searchData, int $freeIndexUid): Result
470  {
471  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_fulltext');
472  $queryBuilder->getRestrictions()->removeAll();
473  $queryBuilder->select('index_fulltext.*', 'ISEC.*', 'IP.*')
474  ->from('index_fulltext')
475  ->join(
476  'index_fulltext',
477  'index_phash',
478  'IP',
479  $queryBuilder->expr()->eq('index_fulltext.phash', $queryBuilder->quoteIdentifier('IP.phash'))
480  )
481  ->join(
482  'IP',
483  'index_section',
484  'ISEC',
485  $queryBuilder->expr()->eq('IP.phash', $queryBuilder->quoteIdentifier('ISEC.phash'))
486  );
487 
488  // Calling hook for alternative creation of page ID list
490  if ($hookObj = $this->‪hookRequest('execFinalQuery_idList')) {
491  $pageWhere = $hookObj->execFinalQuery_idList('');
492  $queryBuilder->andWhere(‪QueryHelper::stripLogicalOperatorPrefix($pageWhere));
493  } elseif (‪$searchRootPageIdList[0] >= 0) {
494  // Collecting all pages IDs in which to search
495  // filtering out ALL pages that are not accessible due to restriction containers. Does NOT look for "no_search" field!
496  $pageRepository = GeneralUtility::makeInstance(PageRepository::class);
497  $idList = $pageRepository->getPageIdsRecursive(‪$searchRootPageIdList, 9999);
498  $queryBuilder->andWhere(
499  $queryBuilder->expr()->in(
500  'ISEC.page_id',
501  $queryBuilder->quoteArrayBasedValueListToIntegerList($idList)
502  )
503  );
504  }
505 
506  $searchBoolean = '';
507  if ($searchData['searchBoolean']) {
508  $searchBoolean = ' IN BOOLEAN MODE';
509  }
510  $queryBuilder->andWhere(
511  'MATCH (' . $queryBuilder->quoteIdentifier($searchData['fulltextIndex']) . ')'
512  . ' AGAINST (' . $queryBuilder->createNamedParameter($searchData['searchString'])
513  . $searchBoolean
514  . ')'
515  );
516 
517  $queryBuilder->andWhere(
522  );
523 
524  $queryBuilder->groupBy(
525  'IP.phash',
526  'ISEC.phash',
527  'ISEC.phash_t3',
528  'ISEC.rl0',
529  'ISEC.rl1',
530  'ISEC.rl2',
531  'ISEC.page_id',
532  'ISEC.uniqid',
533  'IP.phash_grouping',
534  'IP.data_filename',
535  'IP.data_page_id',
536  'IP.data_page_type',
537  'IP.data_page_mp',
538  'IP.gr_list',
539  'IP.item_type',
540  'IP.item_title',
541  'IP.item_description',
542  'IP.item_mtime',
543  'IP.tstamp',
544  'IP.item_size',
545  'IP.contentHash',
546  'IP.crdate',
547  'IP.parsetime',
548  'IP.sys_language_uid',
549  'IP.item_crdate',
550  'IP.externalUrl',
551  'IP.recordUid',
552  'IP.freeIndexUid',
553  'IP.freeIndexSetId'
554  );
555 
556  return $queryBuilder->executeQuery();
557  }
558 
559  /***********************************
560  *
561  * Helper functions on searching (SQL)
562  *
563  ***********************************/
571  protected function ‪getPhashList(array $searchWords): string
572  {
573  // Initialize variables:
574  $c = 0;
575  // This array accumulates the phash-values
576  $totalHashList = [];
577  $this->wSelClauses = [];
578  // Traverse searchwords; for each, select all phash integers and merge/diff/intersect them with previous word (based on operator)
579  foreach ($searchWords as $v) {
580  // Making the query for a single search word based on the search-type
581  $sWord = (string)($v['sword'] ?? '');
582  $theType = ‪$this->searchType;
583  // If there are spaces in the search-word, make a full text search instead.
584  if (str_contains($sWord, ' ')) {
585  $theType = 20;
586  }
587  $this->‪getTimeTracker()->push('SearchWord "' . $sWord . '" - $theType=' . $theType);
588  // Perform search for word:
589  switch ($theType) {
590  case 1:
591  // Part of word
592  $res = $this->‪searchWord($sWord, LikeWildcard::BOTH);
593  break;
594  case 2:
595  // First part of word
596  $res = $this->‪searchWord($sWord, LikeWildcard::RIGHT);
597  break;
598  case 3:
599  // Last part of word
600  $res = $this->‪searchWord($sWord, LikeWildcard::LEFT);
601  break;
602  case 10:
603  // Sounds like
604  $indexerObj = GeneralUtility::makeInstance(Indexer::class);
605  // Perform metaphone search
606  $storeMetaphoneInfoAsWords = !‪IndexedSearchUtility::isTableUsed('index_words');
607  $res = $this->‪searchMetaphone((string)$indexerObj->metaphone($sWord, $storeMetaphoneInfoAsWords));
608  break;
609  case 20:
610  // Sentence
611  $res = $this->‪searchSentence($sWord);
612  // If there is a fulltext search for a sentence there is
613  // a likeliness that sorting cannot be done by the rankings
614  // from the rel-table (because no relations will exist for the
615  // sentence in the word-table). So therefore mtime is used instead.
616  // It is not required, but otherwise some hits may be left out.
617  $this->sortOrder = 'mtime';
618  break;
619  default:
620  // Distinct word
621  $res = $this->‪searchDistinct($sWord);
622  }
623  // If there was a query to do, then select all phash-integers which resulted from this.
624  // Get phash list by searching for it:
625  $phashList = [];
626  while ($row = $res->fetchAssociative()) {
627  $phashList[] = $row['phash'];
628  }
629  // Here the phash list are merged with the existing result based on whether we are dealing with OR, NOT or AND operations.
630  if ($c) {
631  switch ($v['oper']) {
632  case 'OR':
633  $totalHashList = array_unique(array_merge($phashList, $totalHashList));
634  break;
635  case 'AND NOT':
636  $totalHashList = array_diff($totalHashList, $phashList);
637  break;
638  default:
639  // AND...
640  $totalHashList = array_intersect($totalHashList, $phashList);
641  }
642  } else {
643  // First search
644  $totalHashList = $phashList;
645  }
646  $this->‪getTimeTracker()->pull();
647  $c++;
648  }
649  return implode(',', $totalHashList);
650  }
651 
658  protected function ‪execPHashListQuery(string $wordSel, string $additionalWhereClause): Result
659  {
660  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_words');
661  $queryBuilder->select('IR.phash')
662  ->from('index_words', 'IW')
663  ->from('index_rel', 'IR')
664  ->from('index_section', 'ISEC')
665  ->where(
667  $queryBuilder->expr()->eq('IW.wid', $queryBuilder->quoteIdentifier('IR.wid')),
668  $queryBuilder->expr()->eq('ISEC.phash', $queryBuilder->quoteIdentifier('IR.phash')),
669  ‪QueryHelper::stripLogicalOperatorPrefix($this->sectionTableWhere()),
670  ‪QueryHelper::stripLogicalOperatorPrefix($additionalWhereClause)
671  )
672  ->groupBy('IR.phash');
673 
674  return $queryBuilder->executeQuery();
675  }
676 
682  protected function ‪searchWord(string $sWord, ‪LikeWildcard $likeWildcard): Result
683  {
684  $wSel = $likeWildcard->getLikeQueryPart(
685  'index_words',
686  'IW.baseword',
687  $sWord
688  );
689  $this->wSelClauses[] = $wSel;
690  return $this->‪execPHashListQuery($wSel, ' AND is_stopword=0');
691  }
692 
698  protected function ‪searchDistinct(string $sWord): Result
699  {
700  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
701  ->getQueryBuilderForTable('index_words')
702  ->expr();
703  $wSel = $expressionBuilder->eq('IW.wid', ‪IndexedSearchUtility::md5inthash($sWord));
704  $this->wSelClauses[] = $wSel;
705  return $this->‪execPHashListQuery($wSel, $expressionBuilder->eq('is_stopword', 0));
706  }
707 
713  protected function ‪searchSentence(string $sWord): Result
714  {
715  $this->wSelClauses[] = '1=1';
716  $likeWildcard = LikeWildcard::BOTH;
717  $likePart = $likeWildcard->getLikeQueryPart(
718  'index_fulltext',
719  'IFT.fulltextdata',
720  $sWord
721  );
722 
723  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_section');
724  return $queryBuilder->select('ISEC.phash')
725  ->from('index_section', 'ISEC')
726  ->from('index_fulltext', 'IFT')
727  ->where(
729  $queryBuilder->expr()->eq('ISEC.phash', $queryBuilder->quoteIdentifier('IFT.phash')),
730  ‪QueryHelper::stripLogicalOperatorPrefix($this->sectionTableWhere())
731  )
732  ->groupBy('ISEC.phash')
733  ->executeQuery();
734  }
735 
741  protected function ‪searchMetaphone(string $sWord): Result
742  {
743  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
744  ->getQueryBuilderForTable('index_words')
745  ->expr();
746  $wSel = $expressionBuilder->eq('IW.metaphone', $expressionBuilder->literal($sWord));
747  $this->wSelClauses[] = $wSel;
748  return $this->‪execPHashListQuery($wSel, $expressionBuilder->eq('is_stopword', 0));
749  }
750 
756  protected function ‪sectionTableWhere(): string
757  {
758  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
759  ->getQueryBuilderForTable('index_section')
760  ->expr();
761 
762  $whereClause = $expressionBuilder->and();
763  $match = false;
764  if (!($this->searchRootPageIdList < 0)) {
765  $whereClause = $whereClause->with(
766  $expressionBuilder->in('ISEC.rl0', GeneralUtility::intExplode(',', $this->searchRootPageIdList, true))
767  );
768  }
769  if (str_starts_with($this->sections, 'rl1_')) {
770  $whereClause = $whereClause->with(
771  $expressionBuilder->in('ISEC.rl1', GeneralUtility::intExplode(',', substr($this->sections, 4)))
772  );
773  $match = true;
774  } elseif (str_starts_with($this->sections, 'rl2_')) {
775  $whereClause = $whereClause->with(
776  $expressionBuilder->in('ISEC.rl2', GeneralUtility::intExplode(',', substr($this->sections, 4)))
777  );
778  $match = true;
779  } else {
780  // Traversing user configured fields to see if any of those are used to limit search to a section:
781  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['addRootLineFields'] ?? [] as $fieldName => $rootLineLevel) {
782  if (str_starts_with($this->sections, $fieldName . '_')) {
783  $whereClause = $whereClause->with(
784  $expressionBuilder->in(
785  'ISEC.' . $fieldName,
786  GeneralUtility::intExplode(',', substr($this->sections, strlen($fieldName) + 1))
787  )
788  );
789  $match = true;
790  break;
791  }
792  }
793  }
794  // If no match above, test the static types:
795  if (!$match) {
796  switch ($this->sections) {
797  case '-1':
798  $whereClause = $whereClause->with(
799  $expressionBuilder->eq('ISEC.page_id', $this->getTypoScriptFrontendController()->id)
800  );
801  break;
802  case '-2':
803  $whereClause = $whereClause->with($expressionBuilder->eq('ISEC.rl2', 0));
804  break;
805  case '-3':
806  $whereClause = $whereClause->with($expressionBuilder->gt('ISEC.rl2', 0));
807  break;
808  }
809  }
810 
811  return $whereClause->count() ? ' AND ' . $whereClause : '';
812  }
813 
819  protected function ‪mediaTypeWhere(): string
820  {
821  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
822  ->getQueryBuilderForTable('index_phash')
823  ->expr();
824  switch ($this->mediaType) {
825  case '0':
826  // '0' => 'only TYPO3 pages',
827  $whereClause = $expressionBuilder->eq('IP.item_type', $expressionBuilder->literal('0'));
828  break;
829  case '-2':
830  // All external documents
831  $whereClause = $expressionBuilder->neq('IP.item_type', $expressionBuilder->literal('0'));
832  break;
833  case false:
834  // Intentional fall-through
835  case '-1':
836  // All content
837  $whereClause = '';
838  break;
839  default:
840  $whereClause = $expressionBuilder->eq('IP.item_type', $expressionBuilder->literal($this->mediaType));
841  }
842  return $whereClause ? ' AND ' . $whereClause : '';
843  }
844 
850  protected function ‪languageWhere(): string
851  {
852  // -1 is the same as ALL language.
853  if ($this->languageUid < 0) {
854  return '';
855  }
856 
857  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
858  ->getQueryBuilderForTable('index_phash')
859  ->expr();
860 
861  return ' AND ' . $expressionBuilder->eq('IP.sys_language_uid', $this->languageUid);
862  }
863 
870  protected function ‪freeIndexUidWhere(int $freeIndexUid): string
871  {
872  if ($freeIndexUid < 0) {
873  return '';
874  }
875  // First, look if the freeIndexUid is a meta configuration:
876  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
877  ->getQueryBuilderForTable('index_config');
878  $indexCfgRec = $queryBuilder->select('indexcfgs')
879  ->from('index_config')
880  ->where(
881  $queryBuilder->expr()->eq('type', $queryBuilder->createNamedParameter(5, ‪Connection::PARAM_INT)),
882  $queryBuilder->expr()->eq(
883  'uid',
884  $queryBuilder->createNamedParameter($freeIndexUid, ‪Connection::PARAM_INT)
885  )
886  )
887  ->executeQuery()
888  ->fetchAssociative();
889 
890  if (is_array($indexCfgRec)) {
891  $refs = GeneralUtility::trimExplode(',', $indexCfgRec['indexcfgs']);
892  // Default value to protect against empty array.
893  $list = [-99];
894  foreach ($refs as $ref) {
895  [$table, ‪$uid] = GeneralUtility::revExplode('_', $ref, 2);
896  ‪$uid = (int)‪$uid;
897  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
898  ->getQueryBuilderForTable('index_config');
899  $queryBuilder->select('uid')
900  ->from('index_config');
901  switch ($table) {
902  case 'index_config':
903  $idxRec = $queryBuilder
904  ->where(
905  $queryBuilder->expr()->eq(
906  'uid',
907  $queryBuilder->createNamedParameter(‪$uid, ‪Connection::PARAM_INT)
908  )
909  )
910  ->executeQuery()
911  ->fetchAssociative();
912  if ($idxRec) {
913  $list[] = ‪$uid;
914  }
915  break;
916  case 'pages':
917  $indexCfgRecordsFromPid = $queryBuilder
918  ->where(
919  $queryBuilder->expr()->eq(
920  'pid',
921  $queryBuilder->createNamedParameter(‪$uid, ‪Connection::PARAM_INT)
922  )
923  )
924  ->executeQuery();
925  while ($idxRec = $indexCfgRecordsFromPid->fetchAssociative()) {
926  $list[] = $idxRec['uid'];
927  }
928  break;
929  }
930  }
931  $list = array_unique($list);
932  } else {
933  $list = [$freeIndexUid];
934  }
935 
936  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
937  ->getQueryBuilderForTable('index_phash')
938  ->expr();
939  return ' AND ' . $expressionBuilder->in('IP.freeIndexUid', array_map('intval', $list));
940  }
941 
948  protected function ‪execFinalQuery(string $list, int $freeIndexUid): Result
949  {
950  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_words');
951  $queryBuilder->select('ISEC.*', 'IP.*')
952  ->from('index_phash', 'IP')
953  ->from('index_section', 'ISEC')
954  ->where(
955  $queryBuilder->expr()->in(
956  'IP.phash',
957  $queryBuilder->quoteArrayBasedValueListToIntegerList(
958  GeneralUtility::intExplode(',', $list, true)
959  )
960  ),
961  ‪QueryHelper::stripLogicalOperatorPrefix($this->mediaTypeWhere()),
962  ‪QueryHelper::stripLogicalOperatorPrefix($this->languageWhere()),
963  ‪QueryHelper::stripLogicalOperatorPrefix($this->freeIndexUidWhere($freeIndexUid)),
964  $queryBuilder->expr()->eq('ISEC.phash', $queryBuilder->quoteIdentifier('IP.phash'))
965  )
966  ->groupBy(
967  'IP.phash',
968  'ISEC.phash',
969  'ISEC.phash_t3',
970  'ISEC.rl0',
971  'ISEC.rl1',
972  'ISEC.rl2',
973  'ISEC.page_id',
974  'ISEC.uniqid',
975  'IP.phash_grouping',
976  'IP.data_filename',
977  'IP.data_page_id',
978  'IP.data_page_type',
979  'IP.data_page_mp',
980  'IP.gr_list',
981  'IP.item_type',
982  'IP.item_title',
983  'IP.item_description',
984  'IP.item_mtime',
985  'IP.tstamp',
986  'IP.item_size',
987  'IP.contentHash',
988  'IP.crdate',
989  'IP.parsetime',
990  'IP.sys_language_uid',
991  'IP.item_crdate',
992  'IP.externalUrl',
993  'IP.recordUid',
994  'IP.freeIndexUid',
995  'IP.freeIndexSetId',
996  'IP.static_page_arguments'
997  );
998 
999  // Setting up methods of filtering results
1000  // based on page types, access, etc.
1001  if ($hookObj = $this->‪hookRequest('execFinalQuery_idList')) {
1002  // Calling hook for alternative creation of page ID list
1003  $hookWhere = ‪QueryHelper::stripLogicalOperatorPrefix($hookObj->execFinalQuery_idList($list));
1004  if (!empty($hookWhere)) {
1005  $queryBuilder->andWhere($hookWhere);
1006  }
1007  } elseif ($this->searchRootPageIdList >= 0) {
1008  // Collecting all pages IDs in which to search,
1009  // filtering out ALL pages that are not accessible due to restriction containers.
1010  // Does NOT look for "no_search" field!
1011  $siteIdNumbers = GeneralUtility::intExplode(',', $this->searchRootPageIdList);
1012  $pageIdList = $this->‪getTypoScriptFrontendController()->sys_page->getPageIdsRecursive($siteIdNumbers, 9999);
1013  $queryBuilder->andWhere(
1014  $queryBuilder->expr()->in(
1015  'ISEC.page_id',
1016  $queryBuilder->quoteArrayBasedValueListToIntegerList($pageIdList)
1017  )
1018  );
1019  }
1020  // otherwise select all / disable everything
1021  // If any of the ranking sortings are selected, we must make a
1022  // join with the word/rel-table again, because we need to
1023  // calculate ranking based on all search-words found.
1024  if (str_starts_with($this->sortOrder, 'rank_')) {
1025  $queryBuilder
1026  ->from('index_words', 'IW')
1027  ->from('index_rel', 'IR')
1028  ->andWhere(
1029  $queryBuilder->expr()->eq('IW.wid', $queryBuilder->quoteIdentifier('IR.wid')),
1030  $queryBuilder->expr()->eq('ISEC.phash', $queryBuilder->quoteIdentifier('IR.phash'))
1031  );
1032  switch ($this->sortOrder) {
1033  case 'rank_flag':
1034  // This gives priority to word-position (max-value) so that words in title, keywords, description counts more than in content.
1035  // The ordering is refined with the frequency sum as well.
1036  $queryBuilder
1037  ->addSelectLiteral(
1038  $queryBuilder->expr()->max('IR.flags', 'order_val1'),
1039  $queryBuilder->expr()->sum('IR.freq', 'order_val2')
1040  )
1041  ->orderBy('order_val1', $this->‪getDescendingSortOrderFlag())
1042  ->addOrderBy('order_val2', $this->‪getDescendingSortOrderFlag());
1043  break;
1044  case 'rank_first':
1045  // Results in average position of search words on page.
1046  // Must be inversely sorted (low numbers are closer to top)
1047  $queryBuilder
1048  ->addSelectLiteral($queryBuilder->expr()->avg('IR.first', 'order_val'))
1049  ->orderBy('order_val', $this->‪getDescendingSortOrderFlag(true));
1050  break;
1051  case 'rank_count':
1052  // Number of words found
1053  $queryBuilder
1054  ->addSelectLiteral($queryBuilder->expr()->sum('IR.count', 'order_val'))
1055  ->orderBy('order_val', $this->‪getDescendingSortOrderFlag());
1056  break;
1057  default:
1058  // Frequency sum. I'm not sure if this is the best way to do
1059  // it (make a sum...). Or should it be the average?
1060  $queryBuilder
1061  ->addSelectLiteral($queryBuilder->expr()->sum('IR.freq', 'order_val'))
1062  ->orderBy('order_val', $this->‪getDescendingSortOrderFlag());
1063  }
1064 
1065  if (!empty($this->wSelClauses)) {
1066  // So, words are combined in an OR statement
1067  // (no "sentence search" should be done here - may deselect results)
1068  $wordSel = $queryBuilder->expr()->or();
1069  foreach ($this->wSelClauses as $wSelClause) {
1070  $wordSel = $wordSel->with(‪QueryHelper::stripLogicalOperatorPrefix($wSelClause));
1071  }
1072  $queryBuilder->andWhere($wordSel);
1073  }
1074  } else {
1075  // Otherwise, if sorting are done with the pages table or other fields,
1076  // there is no need for joining with the rel/word tables:
1077  switch ($this->sortOrder) {
1078  case 'title':
1079  $queryBuilder->orderBy('IP.item_title', $this->‪getDescendingSortOrderFlag());
1080  break;
1081  case 'crdate':
1082  $queryBuilder->orderBy('IP.item_crdate', $this->‪getDescendingSortOrderFlag());
1083  break;
1084  case 'mtime':
1085  $queryBuilder->orderBy('IP.item_mtime', $this->‪getDescendingSortOrderFlag());
1086  break;
1087  }
1088  }
1089 
1090  return $queryBuilder->executeQuery();
1091  }
1092 
1101  protected function ‪checkResume(array $row): bool
1102  {
1103  // If the record is indexed by an indexing configuration, just show it.
1104  // At least this is needed for external URLs and files.
1105  // For records we might need to extend this - for instance block display if record is access restricted.
1106  if ($row['freeIndexUid']) {
1107  return true;
1108  }
1109  // Evaluate regularly indexed pages based on item_type:
1110  // External media:
1111  $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('index_grlist');
1112  if ($row['item_type']) {
1113  // For external media we will check the access of the parent page on which the media was linked from.
1114  // "phash_t3" is the phash of the parent TYPO3 page row which initiated the indexing of the documents
1115  // in this section. So, selecting for the grlist records belonging to the parent phash-row where the
1116  // current users gr_list exists will help us to know. If this is NOT found, there is still a theoretical
1117  // possibility that another user accessible page would display a link, so maybe the resume of such a
1118  // document here may be unjustified hidden. But better safe than sorry.
1119  if (!‪IndexedSearchUtility::isTableUsed('index_grlist')) {
1120  return false;
1121  }
1122 
1123  return (bool)$connection->count(
1124  'phash',
1125  'index_grlist',
1126  [
1127  'phash' => (int)$row['phash_t3'],
1128  'gr_list' => $this->frontendUserGroupList,
1129  ]
1130  );
1131  }
1132  // Ordinary TYPO3 pages:
1133  if ((string)$row['gr_list'] !== $this->frontendUserGroupList) {
1134  // Selecting for the grlist records belonging to the phash-row where the current users gr_list exists.
1135  // If it is found it is proof that this user has direct access to the phash-rows content although
1136  // he did not himself initiate the indexing...
1137  if (!‪IndexedSearchUtility::isTableUsed('index_grlist')) {
1138  return false;
1139  }
1140 
1141  return (bool)$connection->count(
1142  'phash',
1143  'index_grlist',
1144  [
1145  'phash' => (int)$row['phash'],
1146  'gr_list' => $this->frontendUserGroupList,
1147  ]
1148  );
1149  }
1150  return true;
1151  }
1152 
1160  protected function ‪getDescendingSortOrderFlag(bool $inverse = false): string
1161  {
1163  if ($inverse) {
1164  $desc = !$desc;
1165  }
1166  return !$desc ? ' DESC' : '';
1167  }
1168 
1175  protected function ‪multiplePagesType(string $itemType): bool
1176  {
1178  $fileContentParser = $this->externalParsers[$itemType] ?? null;
1179  return is_object($fileContentParser) && $fileContentParser->isMultiplePageExtension($itemType);
1180  }
1181 
1188  protected function ‪hookRequest(string $functionName): ?object
1189  {
1190  // Hook: menuConfig_preProcessModMenu
1191  if (‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['pi1_hooks'][$functionName] ?? false) {
1192  $hookObj = GeneralUtility::makeInstance(‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['pi1_hooks'][$functionName]);
1193  if (method_exists($hookObj, $functionName)) {
1194  $hookObj->pObj = $this;
1195  return $hookObj;
1196  }
1197  }
1198  return null;
1199  }
1200 
1205  protected function ‪getSearchType(): int
1206  {
1207  return ‪$this->searchType;
1208  }
1209 
1215  protected function ‪getSearchRootPageIdList(): array
1216  {
1217  return GeneralUtility::intExplode(',', $this->searchRootPageIdList);
1218  }
1219 
1221  {
1222  return ‪$GLOBALS['TSFE'];
1223  }
1224 
1225  protected function ‪getTimeTracker(): ‪TimeTracker
1226  {
1227  return GeneralUtility::makeInstance(TimeTracker::class);
1228  }
1229 }
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\searchDistinct
‪searchDistinct(string $sWord)
Definition: IndexSearchRepository.php:698
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\getSearchType
‪getSearchType()
Definition: IndexSearchRepository.php:1205
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\searchWord
‪searchWord(string $sWord, LikeWildcard $likeWildcard)
Definition: IndexSearchRepository.php:682
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\getResultRows_SQLpointerMysqlFulltext
‪getResultRows_SQLpointerMysqlFulltext(array $searchWordsArray, int $freeIndexUid)
Definition: IndexSearchRepository.php:359
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$searchType
‪int $searchType
Definition: IndexSearchRepository.php:64
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$sortOrder
‪string $sortOrder
Definition: IndexSearchRepository.php:82
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:50
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$languageUid
‪int $languageUid
Definition: IndexSearchRepository.php:70
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\getSearchString
‪array getSearchString(array $searchWordArray)
Definition: IndexSearchRepository.php:392
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$numberOfResults
‪int $numberOfResults
Definition: IndexSearchRepository.php:100
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\writeSearchStat
‪writeSearchStat(int $pageId, array $searchWords)
Definition: IndexSearchRepository.php:271
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\searchSentence
‪searchSentence(string $sWord)
Definition: IndexSearchRepository.php:713
‪TYPO3\CMS\Core\Configuration\ExtensionConfiguration
Definition: ExtensionConfiguration.php:47
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\freeIndexUidWhere
‪string freeIndexUidWhere(int $freeIndexUid)
Definition: IndexSearchRepository.php:870
‪TYPO3\CMS\IndexedSearch\Domain\Repository
Definition: AdministrationRepository.php:16
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository
Definition: IndexSearchRepository.php:43
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\getIndexConfigurationById
‪getIndexConfigurationById(int $id)
Definition: IndexSearchRepository.php:311
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\getSearchRootPageIdList
‪int[] getSearchRootPageIdList()
Definition: IndexSearchRepository.php:1215
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\mediaTypeWhere
‪string mediaTypeWhere()
Definition: IndexSearchRepository.php:819
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$resultpagePointer
‪int $resultpagePointer
Definition: IndexSearchRepository.php:94
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\checkResume
‪bool checkResume(array $row)
Definition: IndexSearchRepository.php:1101
‪TYPO3\CMS\Core\Database\Connection\PARAM_STR
‪const PARAM_STR
Definition: Connection.php:55
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$mediaType
‪int $mediaType
Definition: IndexSearchRepository.php:76
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:54
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\getTypoScriptFrontendController
‪getTypoScriptFrontendController()
Definition: IndexSearchRepository.php:1220
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$displayForbiddenRecords
‪bool $displayForbiddenRecords
Definition: IndexSearchRepository.php:131
‪TYPO3\CMS\IndexedSearch\Utility\LikeWildcard
‪LikeWildcard
Definition: LikeWildcard.php:28
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\doSearch
‪array false doSearch(array $searchWords, int $freeIndexUid)
Definition: IndexSearchRepository.php:169
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\execPHashListQuery
‪execPHashListQuery(string $wordSel, string $additionalWhereClause)
Definition: IndexSearchRepository.php:658
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\getPhashList
‪string getPhashList(array $searchWords)
Definition: IndexSearchRepository.php:571
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\searchMetaphone
‪searchMetaphone(string $sWord)
Definition: IndexSearchRepository.php:741
‪TYPO3\CMS\IndexedSearch\Utility\IndexedSearchUtility\isTableUsed
‪static bool isTableUsed(string $tableName)
Definition: IndexedSearchUtility.php:37
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\initialize
‪initialize(array $settings, array $searchData, array $externalParsers, int|string $searchRootPageIdList)
Definition: IndexSearchRepository.php:139
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\hookRequest
‪object null hookRequest(string $functionName)
Definition: IndexSearchRepository.php:1188
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$wSelClauses
‪array $wSelClauses
Definition: IndexSearchRepository.php:112
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\getDescendingSortOrderFlag
‪string getDescendingSortOrderFlag(bool $inverse=false)
Definition: IndexSearchRepository.php:1160
‪TYPO3\CMS\Core\Database\Query\QueryHelper
Definition: QueryHelper.php:32
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$descendingSortOrderFlag
‪bool $descendingSortOrderFlag
Definition: IndexSearchRepository.php:88
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\getFullTextRowByPhash
‪getFullTextRowByPhash(int $phash)
Definition: IndexSearchRepository.php:294
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$searchRootPageIdList
‪string $searchRootPageIdList
Definition: IndexSearchRepository.php:107
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\sectionTableWhere
‪string sectionTableWhere()
Definition: IndexSearchRepository.php:756
‪TYPO3\CMS\IndexedSearch\FileContentParser
Definition: FileContentParser.php:33
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$sections
‪string $sections
Definition: IndexSearchRepository.php:58
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$useExactCount
‪bool $useExactCount
Definition: IndexSearchRepository.php:123
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\getTimeTracker
‪getTimeTracker()
Definition: IndexSearchRepository.php:1225
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$externalParsers
‪array $externalParsers
Definition: IndexSearchRepository.php:47
‪TYPO3\CMS\IndexedSearch\Utility\IndexedSearchUtility\md5inthash
‪static int md5inthash(string $stringToHash)
Definition: IndexedSearchUtility.php:50
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\execFinalQuery_fulltext
‪execFinalQuery_fulltext(array $searchData, int $freeIndexUid)
Definition: IndexSearchRepository.php:469
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:39
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
Definition: TypoScriptFrontendController.php:103
‪TYPO3\CMS\Webhooks\Message\$uid
‪identifier readonly int $uid
Definition: PageModificationMessage.php:35
‪TYPO3\CMS\Core\Database\Query\QueryHelper\stripLogicalOperatorPrefix
‪static string stripLogicalOperatorPrefix(string $constraint)
Definition: QueryHelper.php:171
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Core\Domain\Repository\PageRepository
Definition: PageRepository.php:61
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:48
‪TYPO3\CMS\Core\Utility\MathUtility\forceIntegerInRange
‪static int forceIntegerInRange(mixed $theInt, int $min, int $max=2000000000, int $defaultValue=0)
Definition: MathUtility.php:34
‪TYPO3\CMS\IndexedSearch\Indexer
Definition: Indexer.php:39
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:51
‪TYPO3\CMS\Core\TimeTracker\TimeTracker
Definition: TimeTracker.php:32
‪TYPO3\CMS\IndexedSearch\Utility\IndexedSearchUtility
Definition: IndexedSearchUtility.php:28
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\languageWhere
‪string languageWhere()
Definition: IndexSearchRepository.php:850
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\execFinalQuery
‪execFinalQuery(string $list, int $freeIndexUid)
Definition: IndexSearchRepository.php:948
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\multiplePagesType
‪bool multiplePagesType(string $itemType)
Definition: IndexSearchRepository.php:1175
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$frontendUserGroupList
‪string $frontendUserGroupList
Definition: IndexSearchRepository.php:52
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\getResultRows_SQLpointer
‪getResultRows_SQLpointer(array $searchWords, int $freeIndexUid)
Definition: IndexSearchRepository.php:335