17 use Doctrine\DBAL\Driver\Statement;
179 $this->indexerObj = GeneralUtility::makeInstance(Indexer::class);
182 $this->frontendUserGroupList = implode(
',', GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect(
'frontend.user',
'groupIds', [0, -1]));
184 if ($settings[
'searchSkipExtendToSubpagesChecking']) {
185 $this->joinPagesForQuery = 1;
187 if ($settings[
'exactCount']) {
188 $this->useExactCount =
true;
190 if ($settings[
'displayForbiddenRecords']) {
191 $this->displayForbiddenRecords =
true;
193 $this->sections = $searchData[
'sections'];
194 $this->searchType = $searchData[
'searchType'];
195 $this->languageUid = $searchData[
'languageUid'];
196 $this->mediaType = $searchData[
'mediaType'] ??
false;
197 $this->sortOrder = $searchData[
'sortOrder'];
198 $this->descendingSortOrderFlag = $searchData[
'desc'];
199 $this->resultpagePointer = $searchData[
'pointer'];
200 if (isset($searchData[
'numberOfResults']) && is_numeric($searchData[
'numberOfResults'])) {
201 $this->numberOfResults = (int)$searchData[
'numberOfResults'];
212 public function doSearch($searchWords, $freeIndexUid = -1)
214 $useMysqlFulltext = (bool)GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(
'indexed_search',
'useMysqlFulltext');
217 if ($hookObj = &$this->
hookRequest(
'getResultRows_SQLpointer')) {
218 $result = $hookObj->getResultRows_SQLpointer($searchWords, $freeIndexUid);
219 } elseif ($useMysqlFulltext) {
228 $count = $result->rowCount();
234 $grouping_phashes = [];
236 $grouping_chashes = [];
245 while ($row = $result->fetch()) {
253 $phashGr = !in_array($row[
'phash_grouping'], $grouping_phashes);
254 $chashGr = !in_array($row[
'contentHash'] .
'.' . $row[
'data_page_id'], $grouping_chashes);
255 if ($phashGr && $chashGr) {
257 if ($row[
'show_resume'] || $this->displayForbiddenRecords) {
260 $grouping_phashes[] = $row[
'phash_grouping'];
262 $grouping_chashes[] = $row[
'contentHash'] .
'.' . $row[
'data_page_id'];
266 if ($c > $pointer * $this->numberOfResults && $c <= $pointer * $this->numberOfResults + $this->numberOfResults) {
267 $row[
'result_number'] = $c;
268 $resultRows[] = $row;
272 if (!$this->useExactCount && $c + 1 > ($pointer + 1) * $this->numberOfResults) {
289 $result->closeCursor();
292 'resultRows' => $resultRows,
293 'firstRow' => $firstRow,
312 $list = $this->getPhashList($searchWords);
335 $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable(
'index_fulltext');
336 if (strpos($connection->getServerVersion(),
'MySQL') !== 0) {
337 throw new \RuntimeException(
338 'Extension indexed_search is configured to use mysql fulltext, but table \'index_fulltext\''
339 .
' is running on a different DBMS.',
349 $timeTracker = GeneralUtility::makeInstance(TimeTracker::class);
351 $timeTracker->push(
'execFinalQuery');
353 $timeTracker->pull();
371 $searchBoolean =
false;
372 $fulltextIndex =
'index_fulltext.fulltextdata';
374 $naturalSearchString =
'';
376 $booleanSearchString =
'';
381 foreach ($searchWordArray as $searchWordData) {
383 $searchWord = $searchWordData[
'sword'];
385 if (strstr($searchWord,
' ')) {
395 $searchBoolean =
true;
398 $indexerObj = GeneralUtility::makeInstance(Indexer::class);
403 $fulltextIndex =
'index_fulltext.metaphonedata';
406 $searchBoolean =
true;
408 $searchWord = trim(str_replace(
'"',
' ', $searchWord));
412 switch ($searchWordData[
'oper']) {
414 $booleanSearchString .=
' -' . $searchWord . $wildcard;
415 $searchBoolean =
true;
418 $booleanSearchString .=
' ' . $searchWord . $wildcard;
419 $searchBoolean =
true;
422 $booleanSearchString .=
' +' . $searchWord . $wildcard;
423 $naturalSearchString .=
' ' . $searchWord;
428 $searchString =
'"' . trim($naturalSearchString) .
'"';
429 } elseif ($searchBoolean) {
430 $searchString = trim($booleanSearchString);
432 $searchString = trim($naturalSearchString);
435 'searchBoolean' => $searchBoolean,
436 'searchString' => $searchString,
437 'fulltextIndex' => $fulltextIndex
452 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(
'index_fulltext');
453 $queryBuilder->getRestrictions()->removeAll();
454 $queryBuilder->select(
'index_fulltext.*',
'ISEC.*',
'IP.*')
455 ->from(
'index_fulltext')
460 $queryBuilder->expr()->eq(
'index_fulltext.phash', $queryBuilder->quoteIdentifier(
'IP.phash'))
466 $queryBuilder->expr()->eq(
'IP.phash', $queryBuilder->quoteIdentifier(
'ISEC.phash'))
471 if ($hookObj = &$this->
hookRequest(
'execFinalQuery_idList')) {
472 $pageWhere = $hookObj->execFinalQuery_idList(
'');
474 } elseif ($this->joinPagesForQuery) {
481 $queryBuilder->expr()->eq(
'ISEC.page_id', $queryBuilder->quoteIdentifier(
'pages.uid'))
484 $queryBuilder->expr()->eq(
486 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
490 $queryBuilder->expr()->lt(
492 $queryBuilder->createNamedParameter(200, \PDO::PARAM_INT)
495 $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
502 $cObj = GeneralUtility::makeInstance(\
TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::class);
503 $idList[] = $cObj->getTreeList(-1 * $rootId, 9999);
505 $idList = GeneralUtility::intExplode(
',', implode(
',', $idList));
506 $queryBuilder->andWhere(
507 $queryBuilder->expr()->in(
509 $queryBuilder->createNamedParameter($idList, Connection::PARAM_INT_ARRAY)
515 if ($searchData[
'searchBoolean']) {
516 $searchBoolean =
' IN BOOLEAN MODE';
518 $queryBuilder->andWhere(
519 'MATCH (' . $queryBuilder->quoteIdentifier($searchData[
'fulltextIndex']) .
')'
520 .
' AGAINST (' . $queryBuilder->createNamedParameter($searchData[
'searchString'])
525 $queryBuilder->andWhere(
532 $queryBuilder->groupBy(
551 'IP.item_description',
558 'IP.sys_language_uid',
567 return $queryBuilder->execute();
582 protected function getPhashList($searchWords)
588 $this->wSelClauses = [];
590 foreach ($searchWords as $k => $v) {
592 $sWord = $v[
'sword'];
593 $theType = (string)$this->searchType;
595 if (strstr($sWord,
' ')) {
620 $indexerObj = GeneralUtility::makeInstance(Indexer::class);
622 $storeMetaphoneInfoAsWords = !$this->
isTableUsed(
'index_words');
634 $this->sortOrder =
'mtime';
644 while ($row = $res->fetch()) {
645 $phashList[] = $row[
'phash'];
649 switch ($v[
'oper']) {
651 $totalHashList = array_unique(array_merge($phashList, $totalHashList));
654 $totalHashList = array_diff($totalHashList, $phashList);
658 $totalHashList = array_intersect($totalHashList, $phashList);
662 $totalHashList = $phashList;
668 return implode(
',', $totalHashList);
680 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(
'index_words');
681 $queryBuilder->select(
'IR.phash')
682 ->from(
'index_words',
'IW')
683 ->from(
'index_rel',
'IR')
684 ->from(
'index_section',
'ISEC')
687 $queryBuilder->expr()->eq(
'IW.wid', $queryBuilder->quoteIdentifier(
'IR.wid')),
688 $queryBuilder->expr()->eq(
'ISEC.phash', $queryBuilder->quoteIdentifier(
'IR.phash')),
692 ->groupBy(
'IR.phash');
694 return $queryBuilder->execute();
707 $wSel = $likeWildcard->getLikeQueryPart(
712 $this->wSelClauses[] = $wSel;
724 $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
725 ->getQueryBuilderForTable(
'index_words')
727 $wSel = $expressionBuilder->eq(
'IW.wid', $this->
md5inthash($sWord));
728 $this->wSelClauses[] = $wSel;
740 $this->wSelClauses[] =
'1=1';
742 $likePart = $likeWildcard->getLikeQueryPart(
748 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(
'index_section');
749 return $queryBuilder->select(
'ISEC.phash')
750 ->from(
'index_section',
'ISEC')
751 ->from(
'index_fulltext',
'IFT')
754 $queryBuilder->expr()->eq(
'ISEC.phash', $queryBuilder->quoteIdentifier(
'IFT.phash')),
757 ->groupBy(
'ISEC.phash')
769 $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
770 ->getQueryBuilderForTable(
'index_words')
772 $wSel = $expressionBuilder->eq(
'IW.metaphone', $expressionBuilder->literal($sWord));
773 $this->wSelClauses[] = $wSel;
784 $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
785 ->getQueryBuilderForTable(
'index_section')
788 $whereClause = $expressionBuilder->andX();
790 if (!($this->searchRootPageIdList < 0)) {
792 $expressionBuilder->in(
'ISEC.rl0', GeneralUtility::intExplode(
',', $this->searchRootPageIdList,
true))
795 if (strpos($this->sections,
'rl1_') === 0) {
797 $expressionBuilder->in(
'ISEC.rl1', GeneralUtility::intExplode(
',', substr($this->sections, 4)))
800 } elseif (strpos($this->sections,
'rl2_') === 0) {
802 $expressionBuilder->in(
'ISEC.rl2', GeneralUtility::intExplode(
',', substr($this->sections, 4)))
807 foreach (
$GLOBALS[
'TYPO3_CONF_VARS'][
'EXTCONF'][
'indexed_search'][
'addRootLineFields'] ?? [] as $fieldName => $rootLineLevel) {
808 if (strpos($this->sections, $fieldName .
'_') === 0) {
810 $expressionBuilder->in(
811 'ISEC.' . $fieldName,
812 GeneralUtility::intExplode(
',', substr($this->sections, strlen($fieldName) + 1))
822 switch ((
string)$this->sections) {
825 $expressionBuilder->eq(
'ISEC.page_id', (
int)$this->getTypoScriptFrontendController()->id)
829 $whereClause->add($expressionBuilder->eq(
'ISEC.rl2', 0));
832 $whereClause->add($expressionBuilder->gt(
'ISEC.rl2', 0));
837 return $whereClause->count() ?
' AND ' . $whereClause :
'';
847 $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
848 ->getQueryBuilderForTable(
'index_phash')
850 switch ($this->mediaType) {
853 $whereClause = $expressionBuilder->eq(
'IP.item_type', $expressionBuilder->literal(
'0'));
857 $whereClause = $expressionBuilder->neq(
'IP.item_type', $expressionBuilder->literal(
'0'));
866 $whereClause = $expressionBuilder->eq(
'IP.item_type', $expressionBuilder->literal($this->mediaType));
868 return $whereClause ?
' AND ' . $whereClause :
'';
879 if ($this->languageUid < 0) {
883 $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
884 ->getQueryBuilderForTable(
'index_phash')
887 return ' AND ' . $expressionBuilder->eq(
'IP.sys_language_uid', (
int)$this->languageUid);
898 $freeIndexUid = (int)$freeIndexUid;
899 if ($freeIndexUid < 0) {
903 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
904 ->getQueryBuilderForTable(
'index_config');
905 $indexCfgRec = $queryBuilder->select(
'indexcfgs')
906 ->from(
'index_config')
908 $queryBuilder->expr()->eq(
'type', $queryBuilder->createNamedParameter(5, \PDO::PARAM_INT)),
909 $queryBuilder->expr()->eq(
911 $queryBuilder->createNamedParameter($freeIndexUid, \PDO::PARAM_INT)
917 if (is_array($indexCfgRec)) {
918 $refs = GeneralUtility::trimExplode(
',', $indexCfgRec[
'indexcfgs']);
921 foreach ($refs as $ref) {
922 list($table, $uid) = GeneralUtility::revExplode(
'_', $ref, 2);
924 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
925 ->getQueryBuilderForTable(
'index_config');
926 $queryBuilder->select(
'uid')
927 ->from(
'index_config');
930 $idxRec = $queryBuilder
932 $queryBuilder->expr()->eq(
934 $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
944 $indexCfgRecordsFromPid = $queryBuilder
946 $queryBuilder->expr()->eq(
948 $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
952 while ($idxRec = $indexCfgRecordsFromPid->fetch()) {
953 $list[] = $idxRec[
'uid'];
958 $list = array_unique($list);
960 $list = [$freeIndexUid];
963 $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
964 ->getQueryBuilderForTable(
'index_phash')
966 return ' AND ' . $expressionBuilder->in(
'IP.freeIndexUid', array_map(
'intval', $list));
978 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(
'index_words');
979 $queryBuilder->select(
'ISEC.*',
'IP.*')
980 ->from(
'index_phash',
'IP')
981 ->from(
'index_section',
'ISEC')
983 $queryBuilder->expr()->in(
985 $queryBuilder->createNamedParameter(
986 GeneralUtility::intExplode(
',', $list,
true),
987 Connection::PARAM_INT_ARRAY
993 $queryBuilder->expr()->eq(
'ISEC.phash', $queryBuilder->quoteIdentifier(
'IP.phash'))
1004 'IP.phash_grouping',
1008 'IP.data_page_reg1',
1009 'IP.data_page_type',
1014 'IP.item_description',
1021 'IP.sys_language_uid',
1027 'IP.freeIndexSetId',
1028 'IP.static_page_arguments'
1033 if ($hookObj = $this->
hookRequest(
'execFinalQuery_idList')) {
1036 if (!empty($hookWhere)) {
1037 $queryBuilder->andWhere($hookWhere);
1039 } elseif ($this->joinPagesForQuery) {
1042 $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
1043 $queryBuilder->from(
'pages');
1044 $queryBuilder->andWhere(
1045 $queryBuilder->expr()->eq(
'pages.uid', $queryBuilder->quoteIdentifier(
'ISEC.page_id')),
1046 $queryBuilder->expr()->eq(
1048 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
1050 $queryBuilder->expr()->lt(
1052 $queryBuilder->createNamedParameter(200, \PDO::PARAM_INT)
1055 } elseif ($this->searchRootPageIdList >= 0) {
1059 $siteIdNumbers = GeneralUtility::intExplode(
',', $this->searchRootPageIdList);
1061 foreach ($siteIdNumbers as $rootId) {
1064 $queryBuilder->andWhere(
1065 $queryBuilder->expr()->in(
1067 $queryBuilder->createNamedParameter(
1068 array_unique(GeneralUtility::intExplode(
',', implode(
',', $pageIdList),
true)),
1069 Connection::PARAM_INT_ARRAY
1078 if (strpos($this->sortOrder,
'rank_') === 0) {
1080 ->from(
'index_words',
'IW')
1081 ->from(
'index_rel',
'IR')
1083 $queryBuilder->expr()->eq(
'IW.wid', $queryBuilder->quoteIdentifier(
'IR.wid')),
1084 $queryBuilder->expr()->eq(
'ISEC.phash', $queryBuilder->quoteIdentifier(
'IR.phash'))
1086 switch ($this->sortOrder) {
1092 $queryBuilder->expr()->max(
'IR.flags',
'order_val1'),
1093 $queryBuilder->expr()->sum(
'IR.freq',
'order_val2')
1102 ->addSelectLiteral($queryBuilder->expr()->avg(
'IR.first',
'order_val'))
1108 ->addSelectLiteral($queryBuilder->expr()->sum(
'IR.count',
'order_val'))
1115 ->addSelectLiteral($queryBuilder->expr()->sum(
'IR.freq',
'order_val'))
1119 if (!empty($this->wSelClauses)) {
1122 $wordSel = $queryBuilder->expr()->orX();
1123 foreach ($this->wSelClauses as $wSelClause) {
1126 $queryBuilder->andWhere($wordSel);
1131 switch ((
string)$this->sortOrder) {
1144 return $queryBuilder->execute();
1160 if ($row[
'freeIndexUid']) {
1165 $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable(
'index_grlist');
1166 if ($row[
'item_type']) {
1177 return (
bool)$connection->count(
1181 'phash' => (
int)$row[
'phash_t3'],
1182 'gr_list' => $this->frontendUserGroupList
1187 if ((
string)$row[
'gr_list'] !== (
string)$this->frontendUserGroupList) {
1195 return (
bool)$connection->count(
1199 'phash' => (
int)$row[
'phash'],
1200 'gr_list' => $this->frontendUserGroupList
1220 return !$desc ?
' DESC' :
'';
1232 $fileContentParser = $this->externalParsers[$itemType];
1233 return is_object($fileContentParser) && $fileContentParser->isMultiplePageExtension($itemType);
1272 if (
$GLOBALS[
'TYPO3_CONF_VARS'][
'EXTCONF'][
'indexed_search'][
'pi1_hooks'][$functionName]) {
1273 $hookObj = GeneralUtility::makeInstance(
$GLOBALS[
'TYPO3_CONF_VARS'][
'EXTCONF'][
'indexed_search'][
'pi1_hooks'][$functionName]);
1274 if (method_exists($hookObj, $functionName)) {
1275 $hookObj->pObj = $this;
1300 return GeneralUtility::intExplode(
',', $this->searchRootPageIdList);
1327 return GeneralUtility::makeInstance(TimeTracker::class);