TYPO3  7.6
MysqlFulltextIndexHook.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\IndexedSearchMysql\Hook;
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
21 {
25  public $pObj;
26 
27  const ANY_PART_OF_THE_WORD = '1';
28  const LAST_PART_OF_THE_WORD = '2';
30  const SOUNDS_LIKE = '10';
31  const SENTENCE = '20';
39  public function getResultRows_SQLpointer($searchWordsArray, $freeIndexUid = -1)
40  {
41  // Build the search string, detect which fulltext index to use, and decide whether boolean search is needed or not
42  $searchData = $this->getSearchString($searchWordsArray);
43  // Perform SQL Search / collection of result rows array:
44  $resource = false;
45  if ($searchData) {
46  // Do the search:
47  $GLOBALS['TT']->push('execFinalQuery');
48  $resource = $this->execFinalQuery_fulltext($searchData, $freeIndexUid);
49  $GLOBALS['TT']->pull();
50  }
51  return $resource;
52  }
53 
60  public function getSearchString($searchWordArray)
61  {
62  // Initialize variables:
63  $count = 0;
64  // Change this to TRUE to force BOOLEAN SEARCH MODE (useful if fulltext index is still empty)
65  $searchBoolean = false;
66  $fulltextIndex = 'index_fulltext.fulltextdata';
67  // This holds the result if the search is natural (doesn't contain any boolean operators)
68  $naturalSearchString = '';
69  // This holds the result if the search is boolen (contains +/-/| operators)
70  $booleanSearchString = '';
71 
72  $searchType = (string)$this->pObj->getSearchType();
73 
74  // Traverse searchwords and prefix them with corresponding operator
75  foreach ($searchWordArray as $searchWordData) {
76  // Making the query for a single search word based on the search-type
77  $searchWord = $searchWordData['sword'];
78  $wildcard = '';
79  if (strstr($searchWord, ' ')) {
80  $searchType = self::SENTENCE;
81  }
82  switch ($searchType) {
83  case self::ANY_PART_OF_THE_WORD:
84 
85  case self::LAST_PART_OF_THE_WORD:
86 
87  case self::FIRST_PART_OF_THE_WORD:
88  // First part of word
89  $wildcard = '*';
90  // Part-of-word search requires boolean mode!
91  $searchBoolean = true;
92  break;
93  case self::SOUNDS_LIKE:
94  $indexerObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\IndexedSearch\Indexer::class);
95  // Initialize the indexer-class
97  $searchWord = $indexerObj->metaphone($searchWord, $indexerObj->storeMetaphoneInfoAsWords);
98  unset($indexerObj);
99  $fulltextIndex = 'index_fulltext.metaphonedata';
100  break;
101  case self::SENTENCE:
102  $searchBoolean = true;
103  // Remove existing quotes and fix misplaced quotes.
104  $searchWord = trim(str_replace('"', ' ', $searchWord));
105  break;
106  }
107  // Perform search for word:
108  switch ($searchWordData['oper']) {
109  case 'AND NOT':
110  $booleanSearchString .= ' -' . $searchWord . $wildcard;
111  $searchBoolean = true;
112  break;
113  case 'OR':
114  $booleanSearchString .= ' ' . $searchWord . $wildcard;
115  $searchBoolean = true;
116  break;
117  default:
118  $booleanSearchString .= ' +' . $searchWord . $wildcard;
119  $naturalSearchString .= ' ' . $searchWord;
120  }
121  $count++;
122  }
123  if ($searchType == self::SENTENCE) {
124  $searchString = '"' . trim($naturalSearchString) . '"';
125  } elseif ($searchBoolean) {
126  $searchString = trim($booleanSearchString);
127  } else {
128  $searchString = trim($naturalSearchString);
129  }
130  return array(
131  'searchBoolean' => $searchBoolean,
132  'searchString' => $searchString,
133  'fulltextIndex' => $fulltextIndex
134  );
135  }
136 
144  protected function execFinalQuery_fulltext($searchData, $freeIndexUid = -1)
145  {
146  // Setting up methods of filtering results based on page types, access, etc.
147  $pageJoin = '';
148  // Indexing configuration clause:
149  $freeIndexUidClause = $this->pObj->freeIndexUidWhere($freeIndexUid);
150  // Calling hook for alternative creation of page ID list
151  $searchRootPageIdList = $this->pObj->getSearchRootPageIdList();
152  if ($hookObj = &$this->pObj->hookRequest('execFinalQuery_idList')) {
153  $pageWhere = $hookObj->execFinalQuery_idList('');
154  } elseif ($this->pObj->getJoinPagesForQuery()) {
155  // Alternative to getting all page ids by ->getTreeList() where "excludeSubpages" is NOT respected.
156  $pageJoin = ',
157  pages';
158  $pageWhere = 'pages.uid = ISEC.page_id
159  ' . $GLOBALS['TSFE']->cObj->enableFields('pages') . '
160  AND pages.no_search=0
161  AND pages.doktype<200
162  ';
163  } elseif ($searchRootPageIdList[0] >= 0) {
164 
165  // Collecting all pages IDs in which to search;
166  // filtering out ALL pages that are not accessible due to enableFields. Does NOT look for "no_search" field!
167  $idList = array();
168  foreach ($searchRootPageIdList as $rootId) {
170  $cObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::class);
171  $idList[] = $cObj->getTreeList(-1 * $rootId, 9999);
172  }
173  $pageWhere = ' ISEC.page_id IN (' . implode(',', $idList) . ')';
174  } else {
175  // Disable everything... (select all)
176  $pageWhere = ' 1=1';
177  }
178  $searchBoolean = '';
179  if ($searchData['searchBoolean']) {
180  $searchBoolean = ' IN BOOLEAN MODE';
181  }
182  $resource = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
183  'index_fulltext.*, ISEC.*, IP.*',
184  'index_fulltext, index_section ISEC, index_phash IP' . $pageJoin,
185  'MATCH (' . $searchData['fulltextIndex'] . ')
186  AGAINST (' . $GLOBALS['TYPO3_DB']->fullQuoteStr($searchData['searchString'], 'index_fulltext') . $searchBoolean . ') ' .
187  $this->pObj->mediaTypeWhere() . ' ' . $this->pObj->languageWhere() . $freeIndexUidClause . '
188  AND index_fulltext.phash = IP.phash
189  AND ISEC.phash = IP.phash
190  AND ' . $pageWhere,
191  'IP.phash,ISEC.phash,ISEC.phash_t3,ISEC.rl0,ISEC.rl1,ISEC.rl2,ISEC.page_id,ISEC.uniqid,IP.phash_grouping,IP.data_filename ,IP.data_page_id ,IP.data_page_reg1,IP.data_page_type,IP.data_page_mp,IP.gr_list,IP.item_type,IP.item_title,IP.item_description,IP.item_mtime,IP.tstamp,IP.item_size,IP.contentHash,IP.crdate,IP.parsetime,IP.sys_language_uid,IP.item_crdate,IP.cHashParams,IP.externalUrl,IP.recordUid,IP.freeIndexUid,IP.freeIndexSetId'
192  );
193  return $resource;
194  }
195 }