‪TYPO3CMS  11.5
IndexSearchRepository.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
27 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
32 
38 {
44  protected ‪$externalParsers = [];
45 
51  protected ‪$frontendUserGroupList = '';
52 
59  protected ‪$sections;
60 
67  protected int ‪$searchType = 0;
68 
75  protected ‪$languageUid;
76 
83  protected ‪$mediaType;
84 
91  protected ‪$sortOrder = '';
92 
100 
107  protected ‪$resultpagePointer = 0;
108 
115  protected ‪$numberOfResults = 10;
116 
124  protected ‪$searchRootPageIdList = '';
125 
132  protected ‪$joinPagesForQuery = false;
133 
139  protected ‪$wSelClauses = [];
140 
152  protected ‪$useExactCount = false;
153 
162  protected ‪$displayForbiddenRecords = false;
163 
172  public function ‪initialize($settings, $searchData, ‪$externalParsers, ‪$searchRootPageIdList)
173  {
174  $this->externalParsers = ‪$externalParsers;
175  $this->searchRootPageIdList = (string)‪$searchRootPageIdList;
176  $this->frontendUserGroupList = implode(',', GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('frontend.user', 'groupIds', [0, -1]));
177  // Should we use joinPagesForQuery instead of long lists of uids?
178  if ($settings['searchSkipExtendToSubpagesChecking'] ?? false) {
179  $this->joinPagesForQuery = true;
180  }
181  if ($settings['exactCount'] ?? false) {
182  $this->useExactCount = true;
183  }
184  if ($settings['displayForbiddenRecords'] ?? false) {
185  $this->displayForbiddenRecords = true;
186  }
187  $this->sections = (string)($searchData['sections'] ?? '');
188  $this->searchType = (int)($searchData['searchType'] ?? 0);
189  $this->languageUid = (int)($searchData['languageUid'] ?? 0);
190  $this->mediaType = $searchData['mediaType'] ?? 0;
191  $this->sortOrder = (string)($searchData['sortOrder'] ?? '');
192  $this->descendingSortOrderFlag = $searchData['desc'] ?? false;
193  $this->resultpagePointer = (int)($searchData['pointer'] ?? 0);
194  if (is_numeric($searchData['numberOfResults'] ?? null)) {
195  $this->numberOfResults = (int)$searchData['numberOfResults'];
196  }
197  }
198 
206  public function ‪doSearch($searchWords, $freeIndexUid = -1)
207  {
208  $useMysqlFulltext = (bool)GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('indexed_search', 'useMysqlFulltext');
209  // Getting SQL result pointer:
210  $this->‪getTimeTracker()->‪push('Searching result');
211  // @todo Change hook and method signatures to return the QueryBuilder instead the Result. Consider to move
212  // from hook to a proper PSR-14 event.
213  if ($hookObj = $this->‪hookRequest('getResultRows_SQLpointer')) {
214  $result = $hookObj->getResultRows_SQLpointer($searchWords, $freeIndexUid);
215  } elseif ($useMysqlFulltext) {
216  $result = $this->‪getResultRows_SQLpointerMysqlFulltext($searchWords, $freeIndexUid);
217  } else {
218  $result = $this->‪getResultRows_SQLpointer($searchWords, $freeIndexUid);
219  }
220  $this->‪getTimeTracker()->‪pull();
221  // Organize and process result:
222  if ($result) {
223  // We need the result row count beforehand for the pointer calculation. Using $result->rowCount() for
224  // select queries is not reliable across dbms systems, and in case of sqlite this will return 0 here,
225  // even if there is a result set, we need to retrieve all rows and doing a count on the array.
226  // @todo Change this to second count() query call, after getResultRows_SQLpointer() signatures/hook has
227  // been changed to return QueryBuilder instead of the Result.
228  $rows = $result->fetchAllAssociative();
229  // Total search-result count
230  $count = count($rows);
231  // The pointer is set to the result page that is currently being viewed
232  $pointer = ‪MathUtility::forceIntegerInRange($this->resultpagePointer, 0, (int)floor($count / $this->numberOfResults));
233  // Initialize result accumulation variables:
234  $c = 0;
235  // Result pointer: Counts up the position in the current search-result
236  $grouping_phashes = [];
237  // Used to filter out duplicates.
238  $grouping_chashes = [];
239  // Used to filter out duplicates BASED ON cHash.
240  $firstRow = [];
241  // Will hold the first row in result - used to calculate relative hit-ratings.
242  $resultRows = [];
243  // Will hold the results rows for display.
244  // Now, traverse result and put the rows to be displayed into an array
245  // Each row should contain the fields from 'ISEC.*, IP.*' combined
246  // + artificial fields "show_resume" (bool) and "result_number" (counter)
247  // @todo Change this back to while($row = $result->fetchAssociative()) after changing
248  // getResultRows_SQLpointer() returning QueryBuilder instead of a Result.
249  foreach ($rows as $row) {
250  // Set first row
251  if (!$c) {
252  $firstRow = $row;
253  }
254  // Tells whether we can link directly to a document
255  // or not (depends on possible right problems)
256  $row['show_resume'] = $this->‪checkResume($row);
257  $phashGr = !in_array($row['phash_grouping'], $grouping_phashes);
258  $chashGr = !in_array($row['contentHash'] . '.' . $row['data_page_id'], $grouping_chashes);
259  if ($phashGr && $chashGr) {
260  // Only if the resume may be shown are we going to filter out duplicates...
261  if ($row['show_resume'] || $this->displayForbiddenRecords) {
262  // Only on documents which are not multiple pages documents
263  if (!$this->‪multiplePagesType($row['item_type'] ?? '')) {
264  $grouping_phashes[] = $row['phash_grouping'];
265  }
266  $grouping_chashes[] = $row['contentHash'] . '.' . $row['data_page_id'];
267  // Increase the result pointer
268  $c++;
269  // All rows for display is put into resultRows[]
270  if ($c > $pointer * $this->numberOfResults && $c <= $pointer * $this->numberOfResults + $this->numberOfResults) {
271  $row['result_number'] = $c;
272  $resultRows[] = $row;
273  // This may lead to a problem: If the result check is not stopped here, the search will take longer.
274  // However the result counter will not filter out grouped cHashes/pHashes that were not processed yet.
275  // You can change this behavior using the "settings.exactCount" property (see above).
276  if (!$this->useExactCount && $c + 1 > ($pointer + 1) * $this->numberOfResults) {
277  break;
278  }
279  }
280  } else {
281  // Skip this row if the user cannot
282  // view it (missing permission)
283  $count--;
284  }
285  } else {
286  // For each time a phash_grouping document is found
287  // (which is thus not displayed) the search-result count is reduced,
288  // so that it matches the number of rows displayed.
289  $count--;
290  }
291  }
292 
293  return [
294  'resultRows' => $resultRows,
295  'firstRow' => $firstRow,
296  'count' => $count,
297  ];
298  }
299  // No results found
300  return false;
301  }
302 
310  protected function ‪getResultRows_SQLpointer($searchWords, $freeIndexUid = -1)
311  {
312  // This SEARCHES for the searchwords in $searchWords AND returns a
313  // COMPLETE list of phash-integers of the matches.
314  $list = $this->‪getPhashList($searchWords);
315  // Perform SQL Search / collection of result rows array:
316  if ($list) {
317  // Do the search:
318  $this->‪getTimeTracker()->‪push('execFinalQuery');
319  $res = $this->‪execFinalQuery($list, $freeIndexUid);
320  $this->‪getTimeTracker()->‪pull();
321  return $res;
322  }
323  return false;
324  }
325 
335  protected function ‪getResultRows_SQLpointerMysqlFulltext($searchWordsArray, $freeIndexUid = -1)
336  {
337  $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('index_fulltext');
338  if (strpos($connection->getServerVersion(), 'MySQL') !== 0) {
339  throw new \RuntimeException(
340  'Extension indexed_search is configured to use mysql fulltext, but table \'index_fulltext\''
341  . ' is running on a different DBMS.',
342  1472585525
343  );
344  }
345  // Build the search string, detect which fulltext index to use, and decide whether boolean search is needed or not
346  $searchData = $this->‪getSearchString($searchWordsArray);
347  // Perform SQL Search / collection of result rows array:
348  $resource = false;
349  if ($searchData) {
350  $timeTracker = GeneralUtility::makeInstance(TimeTracker::class);
351  // Do the search:
352  $timeTracker->push('execFinalQuery');
353  $resource = $this->‪execFinalQuery_fulltext($searchData, $freeIndexUid);
354  $timeTracker->pull();
355  }
356  return $resource;
357  }
358 
367  protected function ‪getSearchString($searchWordArray)
368  {
369  // Change this to TRUE to force BOOLEAN SEARCH MODE (useful if fulltext index is still empty)
370  $searchBoolean = false;
371  $fulltextIndex = 'index_fulltext.fulltextdata';
372  // This holds the result if the search is natural (doesn't contain any boolean operators)
373  $naturalSearchString = '';
374  // This holds the result if the search is boolean (contains +/-/| operators)
375  $booleanSearchString = '';
376 
377  ‪$searchType = $this->‪getSearchType();
378 
379  // Traverse searchwords and prefix them with corresponding operator
380  foreach ($searchWordArray as $searchWordData) {
381  // Making the query for a single search word based on the search-type
382  $searchWord = $searchWordData['sword'];
383  $wildcard = '';
384  if (str_contains($searchWord, ' ')) {
385  ‪$searchType = 20;
386  }
387  switch (‪$searchType) {
388  case 1:
389  case 2:
390  case 3:
391  // First part of word
392  $wildcard = '*';
393  // Part-of-word search requires boolean mode!
394  $searchBoolean = true;
395  break;
396  case 10:
397  $indexerObj = GeneralUtility::makeInstance(Indexer::class);
398  $searchWord = $indexerObj->metaphone($searchWord, $indexerObj->storeMetaphoneInfoAsWords);
399  $fulltextIndex = 'index_fulltext.metaphonedata';
400  break;
401  case 20:
402  $searchBoolean = true;
403  // Remove existing quotes and fix misplaced quotes.
404  $searchWord = trim(str_replace('"', ' ', $searchWord));
405  break;
406  }
407  // Perform search for word:
408  switch ($searchWordData['oper']) {
409  case 'AND NOT':
410  $booleanSearchString .= ' -' . $searchWord . $wildcard;
411  $searchBoolean = true;
412  break;
413  case 'OR':
414  $booleanSearchString .= ' ' . $searchWord . $wildcard;
415  $searchBoolean = true;
416  break;
417  default:
418  $booleanSearchString .= ' +' . $searchWord . $wildcard;
419  $naturalSearchString .= ' ' . $searchWord;
420  }
421  }
422  if (‪$searchType === 20) {
423  $searchString = '"' . trim($naturalSearchString) . '"';
424  } elseif ($searchBoolean) {
425  $searchString = trim($booleanSearchString);
426  } else {
427  $searchString = trim($naturalSearchString);
428  }
429  return [
430  'searchBoolean' => $searchBoolean,
431  'searchString' => $searchString,
432  'fulltextIndex' => $fulltextIndex,
433  ];
434  }
435 
445  protected function ‪execFinalQuery_fulltext($searchData, $freeIndexUid = -1)
446  {
447  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_fulltext');
448  $queryBuilder->getRestrictions()->removeAll();
449  $queryBuilder->select('index_fulltext.*', 'ISEC.*', 'IP.*')
450  ->from('index_fulltext')
451  ->join(
452  'index_fulltext',
453  'index_phash',
454  'IP',
455  $queryBuilder->expr()->eq('index_fulltext.phash', $queryBuilder->quoteIdentifier('IP.phash'))
456  )
457  ->join(
458  'IP',
459  'index_section',
460  'ISEC',
461  $queryBuilder->expr()->eq('IP.phash', $queryBuilder->quoteIdentifier('ISEC.phash'))
462  );
463 
464  // Calling hook for alternative creation of page ID list
466  if ($hookObj = $this->‪hookRequest('execFinalQuery_idList')) {
467  $pageWhere = $hookObj->execFinalQuery_idList('');
468  $queryBuilder->andWhere(‪QueryHelper::stripLogicalOperatorPrefix($pageWhere));
469  } elseif ($this->joinPagesForQuery) {
470  // Alternative to getting all page ids by ->getTreeList() where "excludeSubpages" is NOT respected.
471  $queryBuilder
472  ->join(
473  'ISEC',
474  'pages',
475  'pages',
476  $queryBuilder->expr()->eq('ISEC.page_id', $queryBuilder->quoteIdentifier('pages.uid'))
477  )
478  ->andWhere(
479  $queryBuilder->expr()->eq(
480  'pages.no_search',
481  $queryBuilder->createNamedParameter(0, ‪Connection::PARAM_INT)
482  )
483  )
484  ->andWhere(
485  $queryBuilder->expr()->lt(
486  'pages.doktype',
487  $queryBuilder->createNamedParameter(200, ‪Connection::PARAM_INT)
488  )
489  );
490  $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
491  } elseif (‪$searchRootPageIdList[0] >= 0) {
492  // Collecting all pages IDs in which to search;
493  // filtering out ALL pages that are not accessible due to restriction containers. Does NOT look for "no_search" field!
494  $idList = [];
495  foreach (‪$searchRootPageIdList as $rootId) {
496  $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
497  $idList[] = $cObj->getTreeList(-1 * $rootId, 9999);
498  }
499  $idList = ‪GeneralUtility::intExplode(',', implode(',', $idList));
500  $queryBuilder->andWhere(
501  $queryBuilder->expr()->in(
502  'ISEC.page_id',
503  $queryBuilder->quoteArrayBasedValueListToIntegerList($idList)
504  )
505  );
506  }
507 
508  $searchBoolean = '';
509  if ($searchData['searchBoolean']) {
510  $searchBoolean = ' IN BOOLEAN MODE';
511  }
512  $queryBuilder->andWhere(
513  'MATCH (' . $queryBuilder->quoteIdentifier($searchData['fulltextIndex']) . ')'
514  . ' AGAINST (' . $queryBuilder->createNamedParameter($searchData['searchString'])
515  . $searchBoolean
516  . ')'
517  );
518 
519  $queryBuilder->andWhere(
524  );
525 
526  $queryBuilder->groupBy(
527  'IP.phash',
528  'ISEC.phash',
529  'ISEC.phash_t3',
530  'ISEC.rl0',
531  'ISEC.rl1',
532  'ISEC.rl2',
533  'ISEC.page_id',
534  'ISEC.uniqid',
535  'IP.phash_grouping',
536  'IP.data_filename',
537  'IP.data_page_id',
538  'IP.data_page_type',
539  'IP.data_page_mp',
540  'IP.gr_list',
541  'IP.item_type',
542  'IP.item_title',
543  'IP.item_description',
544  'IP.item_mtime',
545  'IP.tstamp',
546  'IP.item_size',
547  'IP.contentHash',
548  'IP.crdate',
549  'IP.parsetime',
550  'IP.sys_language_uid',
551  'IP.item_crdate',
552  'IP.externalUrl',
553  'IP.recordUid',
554  'IP.freeIndexUid',
555  'IP.freeIndexSetId'
556  );
557 
558  return $queryBuilder->executeQuery();
559  }
560 
561  /***********************************
562  *
563  * Helper functions on searching (SQL)
564  *
565  ***********************************/
573  protected function ‪getPhashList($searchWords)
574  {
575  // Initialize variables:
576  $c = 0;
577  // This array accumulates the phash-values
578  $totalHashList = [];
579  $this->wSelClauses = [];
580  // Traverse searchwords; for each, select all phash integers and merge/diff/intersect them with previous word (based on operator)
581  foreach ($searchWords as $v) {
582  // Making the query for a single search word based on the search-type
583  $sWord = $v['sword'] ?? '';
584  $theType = ‪$this->searchType;
585  // If there are spaces in the search-word, make a full text search instead.
586  if (str_contains($sWord, ' ')) {
587  $theType = 20;
588  }
589  $this->‪getTimeTracker()->‪push('SearchWord "' . $sWord . '" - $theType=' . (string)$theType);
590  // Perform search for word:
591  switch ($theType) {
592  case 1:
593  // Part of word
594  $res = $this->‪searchWord($sWord, ‪LikeWildcard::BOTH);
595  break;
596  case 2:
597  // First part of word
598  $res = $this->‪searchWord($sWord, ‪LikeWildcard::RIGHT);
599  break;
600  case 3:
601  // Last part of word
602  $res = $this->‪searchWord($sWord, ‪LikeWildcard::LEFT);
603  break;
604  case 10:
605  // Sounds like
606  $indexerObj = GeneralUtility::makeInstance(Indexer::class);
607  // Perform metaphone search
608  $storeMetaphoneInfoAsWords = !$this->‪isTableUsed('index_words');
609  $res = $this->‪searchMetaphone($indexerObj->metaphone($sWord, $storeMetaphoneInfoAsWords));
610  break;
611  case 20:
612  // Sentence
613  $res = $this->‪searchSentence($sWord);
614  // If there is a fulltext search for a sentence there is
615  // a likeliness that sorting cannot be done by the rankings
616  // from the rel-table (because no relations will exist for the
617  // sentence in the word-table). So therefore mtime is used instead.
618  // It is not required, but otherwise some hits may be left out.
619  $this->sortOrder = 'mtime';
620  break;
621  default:
622  // Distinct word
623  $res = $this->‪searchDistinct($sWord);
624  }
625  // If there was a query to do, then select all phash-integers which resulted from this.
626  // Get phash list by searching for it:
627  $phashList = [];
628  while ($row = $res->fetchAssociative()) {
629  $phashList[] = $row['phash'];
630  }
631  // Here the phash list are merged with the existing result based on whether we are dealing with OR, NOT or AND operations.
632  if ($c) {
633  switch ($v['oper']) {
634  case 'OR':
635  $totalHashList = array_unique(array_merge($phashList, $totalHashList));
636  break;
637  case 'AND NOT':
638  $totalHashList = array_diff($totalHashList, $phashList);
639  break;
640  default:
641  // AND...
642  $totalHashList = array_intersect($totalHashList, $phashList);
643  }
644  } else {
645  // First search
646  $totalHashList = $phashList;
647  }
648  $this->‪getTimeTracker()->‪pull();
649  $c++;
650  }
651  return implode(',', $totalHashList);
652  }
653 
661  protected function ‪execPHashListQuery($wordSel, $additionalWhereClause = '')
662  {
663  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_words');
664  $queryBuilder->select('IR.phash')
665  ->from('index_words', 'IW')
666  ->from('index_rel', 'IR')
667  ->from('index_section', 'ISEC')
668  ->where(
670  $queryBuilder->expr()->eq('IW.wid', $queryBuilder->quoteIdentifier('IR.wid')),
671  $queryBuilder->expr()->eq('ISEC.phash', $queryBuilder->quoteIdentifier('IR.phash')),
672  ‪QueryHelper::stripLogicalOperatorPrefix($this->sectionTableWhere()),
674  )
675  ->groupBy('IR.phash');
676 
677  return $queryBuilder->executeQuery();
678  }
679 
687  protected function ‪searchWord($sWord, $wildcard)
688  {
689  $likeWildcard = ‪LikeWildcard::cast($wildcard);
690  $wSel = $likeWildcard->getLikeQueryPart(
691  'index_words',
692  'IW.baseword',
693  $sWord
694  );
695  $this->wSelClauses[] = $wSel;
696  return $this->‪execPHashListQuery($wSel, ' AND is_stopword=0');
697  }
698 
705  protected function ‪searchDistinct($sWord)
706  {
707  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
708  ->getQueryBuilderForTable('index_words')
709  ->expr();
710  $wSel = $expressionBuilder->eq('IW.wid', $this->‪md5inthash($sWord));
711  $this->wSelClauses[] = $wSel;
712  return $this->‪execPHashListQuery($wSel, $expressionBuilder->eq('is_stopword', 0));
713  }
714 
721  protected function ‪searchSentence($sWord)
722  {
723  $this->wSelClauses[] = '1=1';
725  $likePart = $likeWildcard->getLikeQueryPart(
726  'index_fulltext',
727  'IFT.fulltextdata',
728  $sWord
729  );
730 
731  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_section');
732  return $queryBuilder->select('ISEC.phash')
733  ->from('index_section', 'ISEC')
734  ->from('index_fulltext', 'IFT')
735  ->where(
737  $queryBuilder->expr()->eq('ISEC.phash', $queryBuilder->quoteIdentifier('IFT.phash')),
738  ‪QueryHelper::stripLogicalOperatorPrefix($this->sectionTableWhere())
739  )
740  ->groupBy('ISEC.phash')
741  ->executeQuery();
742  }
743 
750  protected function ‪searchMetaphone($sWord)
751  {
752  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
753  ->getQueryBuilderForTable('index_words')
754  ->expr();
755  $wSel = $expressionBuilder->eq('IW.metaphone', $expressionBuilder->literal($sWord));
756  $this->wSelClauses[] = $wSel;
757  return $this->‪execPHashListQuery($wSel, $expressionBuilder->eq('is_stopword', 0));
758  }
759 
765  public function ‪sectionTableWhere()
766  {
767  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
768  ->getQueryBuilderForTable('index_section')
769  ->expr();
770 
771  $whereClause = $expressionBuilder->andX();
772  $match = false;
773  if (!($this->searchRootPageIdList < 0)) {
774  $whereClause->add(
775  $expressionBuilder->in('ISEC.rl0', ‪GeneralUtility::intExplode(',', $this->searchRootPageIdList, true))
776  );
777  }
778  if (strpos($this->sections, 'rl1_') === 0) {
779  $whereClause->add(
780  $expressionBuilder->in('ISEC.rl1', ‪GeneralUtility::intExplode(',', substr($this->sections, 4)))
781  );
782  $match = true;
783  } elseif (strpos($this->sections, 'rl2_') === 0) {
784  $whereClause->add(
785  $expressionBuilder->in('ISEC.rl2', ‪GeneralUtility::intExplode(',', substr($this->sections, 4)))
786  );
787  $match = true;
788  } else {
789  // Traversing user configured fields to see if any of those are used to limit search to a section:
790  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['addRootLineFields'] ?? [] as $fieldName => $rootLineLevel) {
791  if (strpos($this->sections, $fieldName . '_') === 0) {
792  $whereClause->add(
793  $expressionBuilder->in(
794  'ISEC.' . $fieldName,
795  ‪GeneralUtility::intExplode(',', substr($this->sections, strlen($fieldName) + 1))
796  )
797  );
798  $match = true;
799  break;
800  }
801  }
802  }
803  // If no match above, test the static types:
804  if (!$match) {
805  switch ((string)$this->sections) {
806  case '-1':
807  $whereClause->add(
808  $expressionBuilder->eq('ISEC.page_id', (int)$this->getTypoScriptFrontendController()->id)
809  );
810  break;
811  case '-2':
812  $whereClause->add($expressionBuilder->eq('ISEC.rl2', 0));
813  break;
814  case '-3':
815  $whereClause->add($expressionBuilder->gt('ISEC.rl2', 0));
816  break;
817  }
818  }
819 
820  return $whereClause->count() ? ' AND ' . $whereClause : '';
821  }
822 
828  public function ‪mediaTypeWhere()
829  {
830  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
831  ->getQueryBuilderForTable('index_phash')
832  ->expr();
833  switch ($this->mediaType) {
834  case '0':
835  // '0' => 'only TYPO3 pages',
836  $whereClause = $expressionBuilder->eq('IP.item_type', $expressionBuilder->literal('0'));
837  break;
838  case '-2':
839  // All external documents
840  $whereClause = $expressionBuilder->neq('IP.item_type', $expressionBuilder->literal('0'));
841  break;
842  case false:
843  // Intentional fall-through
844  case '-1':
845  // All content
846  $whereClause = '';
847  break;
848  default:
849  $whereClause = $expressionBuilder->eq('IP.item_type', $expressionBuilder->literal($this->mediaType));
850  }
851  return $whereClause ? ' AND ' . $whereClause : '';
852  }
853 
859  public function ‪languageWhere()
860  {
861  // -1 is the same as ALL language.
862  if ($this->languageUid < 0) {
863  return '';
864  }
865 
866  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
867  ->getQueryBuilderForTable('index_phash')
868  ->expr();
869 
870  return ' AND ' . $expressionBuilder->eq('IP.sys_language_uid', $this->languageUid);
871  }
872 
879  public function ‪freeIndexUidWhere($freeIndexUid)
880  {
881  $freeIndexUid = (int)$freeIndexUid;
882  if ($freeIndexUid < 0) {
883  return '';
884  }
885  // First, look if the freeIndexUid is a meta configuration:
886  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
887  ->getQueryBuilderForTable('index_config');
888  $indexCfgRec = $queryBuilder->select('indexcfgs')
889  ->from('index_config')
890  ->where(
891  $queryBuilder->expr()->eq('type', $queryBuilder->createNamedParameter(5, ‪Connection::PARAM_INT)),
892  $queryBuilder->expr()->eq(
893  'uid',
894  $queryBuilder->createNamedParameter($freeIndexUid, ‪Connection::PARAM_INT)
895  )
896  )
897  ->executeQuery()
898  ->fetchAssociative();
899 
900  if (is_array($indexCfgRec)) {
901  $refs = ‪GeneralUtility::trimExplode(',', $indexCfgRec['indexcfgs']);
902  // Default value to protect against empty array.
903  $list = [-99];
904  foreach ($refs as $ref) {
905  [$table, $uid] = ‪GeneralUtility::revExplode('_', $ref, 2);
906  $uid = (int)$uid;
907  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
908  ->getQueryBuilderForTable('index_config');
909  $queryBuilder->select('uid')
910  ->from('index_config');
911  switch ($table) {
912  case 'index_config':
913  $idxRec = $queryBuilder
914  ->where(
915  $queryBuilder->expr()->eq(
916  'uid',
917  $queryBuilder->createNamedParameter($uid, ‪Connection::PARAM_INT)
918  )
919  )
920  ->executeQuery()
921  ->fetchAssociative();
922  if ($idxRec) {
923  $list[] = $uid;
924  }
925  break;
926  case 'pages':
927  $indexCfgRecordsFromPid = $queryBuilder
928  ->where(
929  $queryBuilder->expr()->eq(
930  'pid',
931  $queryBuilder->createNamedParameter($uid, ‪Connection::PARAM_INT)
932  )
933  )
934  ->executeQuery();
935  while ($idxRec = $indexCfgRecordsFromPid->fetchAssociative()) {
936  $list[] = $idxRec['uid'];
937  }
938  break;
939  }
940  }
941  $list = array_unique($list);
942  } else {
943  $list = [$freeIndexUid];
944  }
945 
946  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
947  ->getQueryBuilderForTable('index_phash')
948  ->expr();
949  return ' AND ' . $expressionBuilder->in('IP.freeIndexUid', array_map('intval', $list));
950  }
951 
959  protected function ‪execFinalQuery($list, $freeIndexUid = -1)
960  {
961  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_words');
962  $queryBuilder->select('ISEC.*', 'IP.*')
963  ->from('index_phash', 'IP')
964  ->from('index_section', 'ISEC')
965  ->where(
966  $queryBuilder->expr()->in(
967  'IP.phash',
968  $queryBuilder->quoteArrayBasedValueListToIntegerList(
969  ‪GeneralUtility::intExplode(',', $list, true)
970  )
971  ),
972  ‪QueryHelper::stripLogicalOperatorPrefix($this->mediaTypeWhere()),
973  ‪QueryHelper::stripLogicalOperatorPrefix($this->languageWhere()),
974  ‪QueryHelper::stripLogicalOperatorPrefix($this->freeIndexUidWhere($freeIndexUid)),
975  $queryBuilder->expr()->eq('ISEC.phash', $queryBuilder->quoteIdentifier('IP.phash'))
976  )
977  ->groupBy(
978  'IP.phash',
979  'ISEC.phash',
980  'ISEC.phash_t3',
981  'ISEC.rl0',
982  'ISEC.rl1',
983  'ISEC.rl2',
984  'ISEC.page_id',
985  'ISEC.uniqid',
986  'IP.phash_grouping',
987  'IP.data_filename',
988  'IP.data_page_id',
989  'IP.data_page_type',
990  'IP.data_page_mp',
991  'IP.gr_list',
992  'IP.item_type',
993  'IP.item_title',
994  'IP.item_description',
995  'IP.item_mtime',
996  'IP.tstamp',
997  'IP.item_size',
998  'IP.contentHash',
999  'IP.crdate',
1000  'IP.parsetime',
1001  'IP.sys_language_uid',
1002  'IP.item_crdate',
1003  'IP.externalUrl',
1004  'IP.recordUid',
1005  'IP.freeIndexUid',
1006  'IP.freeIndexSetId',
1007  'IP.static_page_arguments'
1008  );
1009 
1010  // Setting up methods of filtering results
1011  // based on page types, access, etc.
1012  if ($hookObj = $this->‪hookRequest('execFinalQuery_idList')) {
1013  // Calling hook for alternative creation of page ID list
1014  $hookWhere = ‪QueryHelper::stripLogicalOperatorPrefix($hookObj->execFinalQuery_idList($list));
1015  if (!empty($hookWhere)) {
1016  $queryBuilder->andWhere($hookWhere);
1017  }
1018  } elseif ($this->joinPagesForQuery) {
1019  // Alternative to getting all page ids by ->getTreeList() where
1020  // "excludeSubpages" is NOT respected.
1021  $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
1022  $queryBuilder->from('pages');
1023  $queryBuilder->andWhere(
1024  $queryBuilder->expr()->eq('pages.uid', $queryBuilder->quoteIdentifier('ISEC.page_id')),
1025  $queryBuilder->expr()->eq(
1026  'pages.no_search',
1027  $queryBuilder->createNamedParameter(0, ‪Connection::PARAM_INT)
1028  ),
1029  $queryBuilder->expr()->lt(
1030  'pages.doktype',
1031  $queryBuilder->createNamedParameter(200, ‪Connection::PARAM_INT)
1032  )
1033  );
1034  } elseif ($this->searchRootPageIdList >= 0) {
1035  // Collecting all pages IDs in which to search;
1036  // filtering out ALL pages that are not accessible due to restriction containers.
1037  // Does NOT look for "no_search" field!
1038  $siteIdNumbers = ‪GeneralUtility::intExplode(',', $this->searchRootPageIdList);
1039  $pageIdList = [];
1040  foreach ($siteIdNumbers as $rootId) {
1041  $pageIdList[] = $this->‪getTypoScriptFrontendController()->cObj->getTreeList(-1 * $rootId, 9999);
1042  }
1043  $queryBuilder->andWhere(
1044  $queryBuilder->expr()->in(
1045  'ISEC.page_id',
1046  $queryBuilder->quoteArrayBasedValueListToIntegerList($pageIdList)
1047  )
1048  );
1049  }
1050  // otherwise select all / disable everything
1051  // If any of the ranking sortings are selected, we must make a
1052  // join with the word/rel-table again, because we need to
1053  // calculate ranking based on all search-words found.
1054  if (strpos($this->sortOrder, 'rank_') === 0) {
1055  $queryBuilder
1056  ->from('index_words', 'IW')
1057  ->from('index_rel', 'IR')
1058  ->andWhere(
1059  $queryBuilder->expr()->eq('IW.wid', $queryBuilder->quoteIdentifier('IR.wid')),
1060  $queryBuilder->expr()->eq('ISEC.phash', $queryBuilder->quoteIdentifier('IR.phash'))
1061  );
1062  switch ($this->sortOrder) {
1063  case 'rank_flag':
1064  // This gives priority to word-position (max-value) so that words in title, keywords, description counts more than in content.
1065  // The ordering is refined with the frequency sum as well.
1066  $queryBuilder
1067  ->addSelectLiteral(
1068  $queryBuilder->expr()->max('IR.flags', 'order_val1'),
1069  $queryBuilder->expr()->sum('IR.freq', 'order_val2')
1070  )
1071  ->orderBy('order_val1', $this->‪getDescendingSortOrderFlag())
1072  ->addOrderBy('order_val2', $this->‪getDescendingSortOrderFlag());
1073  break;
1074  case 'rank_first':
1075  // Results in average position of search words on page.
1076  // Must be inversely sorted (low numbers are closer to top)
1077  $queryBuilder
1078  ->addSelectLiteral($queryBuilder->expr()->avg('IR.first', 'order_val'))
1079  ->orderBy('order_val', $this->‪getDescendingSortOrderFlag(true));
1080  break;
1081  case 'rank_count':
1082  // Number of words found
1083  $queryBuilder
1084  ->addSelectLiteral($queryBuilder->expr()->sum('IR.count', 'order_val'))
1085  ->orderBy('order_val', $this->‪getDescendingSortOrderFlag());
1086  break;
1087  default:
1088  // Frequency sum. I'm not sure if this is the best way to do
1089  // it (make a sum...). Or should it be the average?
1090  $queryBuilder
1091  ->addSelectLiteral($queryBuilder->expr()->sum('IR.freq', 'order_val'))
1092  ->orderBy('order_val', $this->‪getDescendingSortOrderFlag());
1093  }
1094 
1095  if (!empty($this->wSelClauses)) {
1096  // So, words are combined in an OR statement
1097  // (no "sentence search" should be done here - may deselect results)
1098  $wordSel = $queryBuilder->expr()->orX();
1099  foreach ($this->wSelClauses as $wSelClause) {
1100  $wordSel->add(‪QueryHelper::stripLogicalOperatorPrefix($wSelClause));
1101  }
1102  $queryBuilder->andWhere($wordSel);
1103  }
1104  } else {
1105  // Otherwise, if sorting are done with the pages table or other fields,
1106  // there is no need for joining with the rel/word tables:
1107  switch ($this->sortOrder) {
1108  case 'title':
1109  $queryBuilder->orderBy('IP.item_title', $this->‪getDescendingSortOrderFlag());
1110  break;
1111  case 'crdate':
1112  $queryBuilder->orderBy('IP.item_crdate', $this->‪getDescendingSortOrderFlag());
1113  break;
1114  case 'mtime':
1115  $queryBuilder->orderBy('IP.item_mtime', $this->‪getDescendingSortOrderFlag());
1116  break;
1117  }
1118  }
1119 
1120  return $queryBuilder->executeQuery();
1121  }
1122 
1131  protected function ‪checkResume($row)
1132  {
1133  // If the record is indexed by an indexing configuration, just show it.
1134  // At least this is needed for external URLs and files.
1135  // For records we might need to extend this - for instance block display if record is access restricted.
1136  if ($row['freeIndexUid']) {
1137  return true;
1138  }
1139  // Evaluate regularly indexed pages based on item_type:
1140  // External media:
1141  $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('index_grlist');
1142  if ($row['item_type']) {
1143  // For external media we will check the access of the parent page on which the media was linked from.
1144  // "phash_t3" is the phash of the parent TYPO3 page row which initiated the indexing of the documents
1145  // in this section. So, selecting for the grlist records belonging to the parent phash-row where the
1146  // current users gr_list exists will help us to know. If this is NOT found, there is still a theoretical
1147  // possibility that another user accessible page would display a link, so maybe the resume of such a
1148  // document here may be unjustified hidden. But better safe than sorry.
1149  if (!$this->‪isTableUsed('index_grlist')) {
1150  return false;
1151  }
1152 
1153  return (bool)$connection->count(
1154  'phash',
1155  'index_grlist',
1156  [
1157  'phash' => (int)$row['phash_t3'],
1158  'gr_list' => $this->frontendUserGroupList,
1159  ]
1160  );
1161  }
1162  // Ordinary TYPO3 pages:
1163  if ((string)$row['gr_list'] !== (string)$this->frontendUserGroupList) {
1164  // Selecting for the grlist records belonging to the phash-row where the current users gr_list exists.
1165  // If it is found it is proof that this user has direct access to the phash-rows content although
1166  // he did not himself initiate the indexing...
1167  if (!$this->‪isTableUsed('index_grlist')) {
1168  return false;
1169  }
1170 
1171  return (bool)$connection->count(
1172  'phash',
1173  'index_grlist',
1174  [
1175  'phash' => (int)$row['phash'],
1176  'gr_list' => $this->frontendUserGroupList,
1177  ]
1178  );
1179  }
1180  return true;
1181  }
1182 
1190  protected function ‪getDescendingSortOrderFlag($inverse = false)
1191  {
1193  if ($inverse) {
1194  $desc = !$desc;
1195  }
1196  return !$desc ? ' DESC' : '';
1197  }
1198 
1205  protected function ‪multiplePagesType($itemType)
1206  {
1208  $fileContentParser = $this->externalParsers[$itemType] ?? null;
1209  return is_object($fileContentParser) && $fileContentParser->isMultiplePageExtension($itemType);
1210  }
1211 
1221  protected function ‪md5inthash($str)
1222  {
1224  }
1225 
1234  protected function ‪isTableUsed($table_list)
1235  {
1236  return ‪IndexedSearchUtility::isTableUsed($table_list);
1237  }
1238 
1245  public function ‪hookRequest($functionName)
1246  {
1247  // Hook: menuConfig_preProcessModMenu
1248  if (‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['pi1_hooks'][$functionName] ?? false) {
1249  $hookObj = GeneralUtility::makeInstance(‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['indexed_search']['pi1_hooks'][$functionName]);
1250  if (method_exists($hookObj, $functionName)) {
1251  $hookObj->pObj = $this;
1252  return $hookObj;
1253  }
1254  }
1255  return null;
1256  }
1257 
1264  public function ‪getSearchType(): int
1265  {
1266  return ‪$this->searchType;
1267  }
1268 
1274  public function ‪getSearchRootPageIdList()
1275  {
1276  return ‪GeneralUtility::intExplode(',', $this->searchRootPageIdList);
1277  }
1278 
1285  public function ‪getJoinPagesForQuery()
1286  {
1288  }
1289 
1293  protected function ‪getTypoScriptFrontendController()
1294  {
1295  return ‪$GLOBALS['TSFE'];
1296  }
1297 
1301  protected function ‪getTimeTracker()
1302  {
1303  return GeneralUtility::makeInstance(TimeTracker::class);
1304  }
1305 }
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:999
‪TYPO3\CMS\Core\Utility\GeneralUtility\revExplode
‪static list< string > revExplode($delimiter, $string, $count=0)
Definition: GeneralUtility.php:964
‪TYPO3\CMS\IndexedSearch\Utility\IndexedSearchUtility\md5inthash
‪static int md5inthash($stringToHash)
Definition: IndexedSearchUtility.php:48
‪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:85
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:49
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$languageUid
‪int $languageUid
Definition: IndexSearchRepository.php:71
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$numberOfResults
‪int $numberOfResults
Definition: IndexSearchRepository.php:106
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\getJoinPagesForQuery
‪bool getJoinPagesForQuery()
Definition: IndexSearchRepository.php:1271
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\getTimeTracker
‪TimeTracker getTimeTracker()
Definition: IndexSearchRepository.php:1287
‪TYPO3\CMS\Core\Configuration\ExtensionConfiguration
Definition: ExtensionConfiguration.php:45
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\md5inthash
‪int md5inthash($str)
Definition: IndexSearchRepository.php:1207
‪TYPO3\CMS\IndexedSearch\Domain\Repository
Definition: AdministrationRepository.php:16
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository
Definition: IndexSearchRepository.php:38
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\getResultRows_SQLpointer
‪Doctrine DBAL Result int bool getResultRows_SQLpointer($searchWords, $freeIndexUid=-1)
Definition: IndexSearchRepository.php:296
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\getSearchRootPageIdList
‪int[] getSearchRootPageIdList()
Definition: IndexSearchRepository.php:1260
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\mediaTypeWhere
‪string mediaTypeWhere()
Definition: IndexSearchRepository.php:814
‪TYPO3\CMS\IndexedSearch\Utility\LikeWildcard
Definition: LikeWildcard.php:27
‪TYPO3\CMS\Core\Utility\MathUtility\forceIntegerInRange
‪static int forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:32
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\getTypoScriptFrontendController
‪TYPO3 CMS Frontend Controller TypoScriptFrontendController getTypoScriptFrontendController()
Definition: IndexSearchRepository.php:1279
‪TYPO3\CMS\IndexedSearch\Utility\IndexedSearchUtility\isTableUsed
‪static bool isTableUsed($tableName)
Definition: IndexedSearchUtility.php:35
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$resultpagePointer
‪int $resultpagePointer
Definition: IndexSearchRepository.php:99
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\execFinalQuery_fulltext
‪Doctrine DBAL Result execFinalQuery_fulltext($searchData, $freeIndexUid=-1)
Definition: IndexSearchRepository.php:431
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\getDescendingSortOrderFlag
‪string getDescendingSortOrderFlag($inverse=false)
Definition: IndexSearchRepository.php:1176
‪TYPO3\CMS\IndexedSearch\Utility\LikeWildcard\LEFT
‪const LEFT
Definition: LikeWildcard.php:34
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\initialize
‪initialize($settings, $searchData, $externalParsers, $searchRootPageIdList)
Definition: IndexSearchRepository.php:158
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$mediaType
‪int $mediaType
Definition: IndexSearchRepository.php:78
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:53
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$displayForbiddenRecords
‪bool $displayForbiddenRecords
Definition: IndexSearchRepository.php:148
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\execFinalQuery
‪Doctrine DBAL Result execFinalQuery($list, $freeIndexUid=-1)
Definition: IndexSearchRepository.php:945
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\doSearch
‪bool array doSearch($searchWords, $freeIndexUid=-1)
Definition: IndexSearchRepository.php:192
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\searchDistinct
‪Doctrine DBAL Result searchDistinct($sWord)
Definition: IndexSearchRepository.php:691
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\hookRequest
‪object null hookRequest($functionName)
Definition: IndexSearchRepository.php:1231
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\getSearchType
‪int getSearchType()
Definition: IndexSearchRepository.php:1250
‪TYPO3\CMS\Core\Type\Enumeration\cast
‪static static cast($value)
Definition: Enumeration.php:186
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$wSelClauses
‪array $wSelClauses
Definition: IndexSearchRepository.php:127
‪TYPO3\CMS\Core\Database\Query\QueryHelper
Definition: QueryHelper.php:32
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$descendingSortOrderFlag
‪bool $descendingSortOrderFlag
Definition: IndexSearchRepository.php:92
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\searchMetaphone
‪Doctrine DBAL Result searchMetaphone($sWord)
Definition: IndexSearchRepository.php:736
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\multiplePagesType
‪bool multiplePagesType($itemType)
Definition: IndexSearchRepository.php:1191
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\isTableUsed
‪bool isTableUsed($table_list)
Definition: IndexSearchRepository.php:1220
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\pull
‪pull($content='')
Definition: TimeTracker.php:206
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$searchRootPageIdList
‪string $searchRootPageIdList
Definition: IndexSearchRepository.php:114
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\execPHashListQuery
‪Doctrine DBAL Result int execPHashListQuery($wordSel, $additionalWhereClause='')
Definition: IndexSearchRepository.php:647
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\sectionTableWhere
‪string sectionTableWhere()
Definition: IndexSearchRepository.php:751
‪TYPO3\CMS\IndexedSearch\FileContentParser
Definition: FileContentParser.php:33
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$sections
‪string $sections
Definition: IndexSearchRepository.php:56
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$useExactCount
‪bool $useExactCount
Definition: IndexSearchRepository.php:139
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$externalParsers
‪array $externalParsers
Definition: IndexSearchRepository.php:43
‪TYPO3\CMS\IndexedSearch\Utility\LikeWildcard\BOTH
‪const BOTH
Definition: LikeWildcard.php:40
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$joinPagesForQuery
‪bool $joinPagesForQuery
Definition: IndexSearchRepository.php:121
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\freeIndexUidWhere
‪string freeIndexUidWhere($freeIndexUid)
Definition: IndexSearchRepository.php:865
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:38
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\searchWord
‪Doctrine DBAL Result searchWord($sWord, $wildcard)
Definition: IndexSearchRepository.php:673
‪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\IndexedSearch\Domain\Repository\IndexSearchRepository\getPhashList
‪string getPhashList($searchWords)
Definition: IndexSearchRepository.php:559
‪TYPO3\CMS\Core\Utility\GeneralUtility\intExplode
‪static int[] intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:927
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:22
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\searchSentence
‪Doctrine DBAL Result searchSentence($sWord)
Definition: IndexSearchRepository.php:707
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\checkResume
‪bool checkResume($row)
Definition: IndexSearchRepository.php:1117
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\IndexedSearch\Indexer
Definition: Indexer.php:39
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer
Definition: FrontendRestrictionContainer.php:31
‪TYPO3\CMS\Core\TimeTracker\TimeTracker
Definition: TimeTracker.php:31
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\getSearchString
‪array getSearchString($searchWordArray)
Definition: IndexSearchRepository.php:353
‪TYPO3\CMS\IndexedSearch\Utility\IndexedSearchUtility
Definition: IndexedSearchUtility.php:26
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\languageWhere
‪string languageWhere()
Definition: IndexSearchRepository.php:845
‪TYPO3\CMS\IndexedSearch\Utility\LikeWildcard\RIGHT
‪const RIGHT
Definition: LikeWildcard.php:37
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\$frontendUserGroupList
‪string $frontendUserGroupList
Definition: IndexSearchRepository.php:49
‪TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository\getResultRows_SQLpointerMysqlFulltext
‪Doctrine DBAL Result int bool getResultRows_SQLpointerMysqlFulltext($searchWordsArray, $freeIndexUid=-1)
Definition: IndexSearchRepository.php:321
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\push
‪push($tslabel, $value='')
Definition: TimeTracker.php:179