[TASK] Remove commented-out code
[Packages/TYPO3.CMS.git] / typo3 / sysext / indexed_search_mysql / class.tx_indexedsearch_mysql.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2011 Michael Stucki (michael@typo3.org)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27
28 /**
29 * Class that hooks into Indexed Search and replaces standard SQL queries with MySQL fulltext index queries.
30 *
31 * @author Michael Stucki <michael@typo3.org>
32 * @package TYPO3
33 * @subpackage tx_indexedsearch_mysql
34 */
35 class tx_indexedsearch_mysql {
36 /** @var tx_indexedsearch */
37 public $pObj;
38
39 const ANY_PART_OF_THE_WORD = '1';
40 const LAST_PART_OF_THE_WORD = '2';
41 const FIRST_PART_OF_THE_WORD = '3';
42 const SOUNDS_LIKE = '10';
43 const SENTENCE = '20';
44
45 /**
46 * Gets a SQL result pointer to traverse for the search records.
47 *
48 * @param array $searchWordsArray Search words
49 * @param int $freeIndexUid Pointer to which indexing configuration you want to search in. -1 means no filtering. 0 means only regular indexed content.
50 * @return resource|false
51 */
52 public function getResultRows_SQLpointer($searchWordsArray, $freeIndexUid = -1) {
53 // Build the search string, detect which fulltext index to use, and decide whether boolean search is needed or not
54 $searchData = $this->getSearchString($searchWordsArray);
55
56 // Perform SQL Search / collection of result rows array:
57 $resource = FALSE;
58 if ($searchData) {
59 // Do the search:
60 $GLOBALS['TT']->push('execFinalQuery');
61 $resource = $this->execFinalQuery_fulltext($searchData, $freeIndexUid);
62 $GLOBALS['TT']->pull();
63 }
64 return $resource;
65 }
66
67 /**
68 * Returns a search string for use with MySQL FULLTEXT query
69 *
70 * @param array $searchWordArray Search word array
71 * @return string Search string
72 */
73 public function getSearchString($searchWordArray) {
74
75 // Initialize variables:
76 $count = 0;
77
78 $searchBoolean = FALSE; // Change this to TRUE to force BOOLEAN SEARCH MODE (useful if fulltext index is still empty)
79 $fulltextIndex = 'index_fulltext.fulltextdata';
80
81 $naturalSearchString = ''; // This holds the result if the search is natural (doesn't contain any boolean operators)
82 $booleanSearchString = ''; // This holds the result if the search is boolen (contains +/-/| operators)
83
84 $searchType = (string)$this->pObj->piVars['type'];
85
86 // Traverse searchwords and prefix them with corresponding operator
87 foreach ($searchWordArray as $searchWordData) {
88 // Making the query for a single search word based on the search-type
89 $searchWord = $searchWordData['sword'];
90 $wildcard = '';
91
92 if (strstr($searchWord, ' ')) {
93 $searchType = self::SENTENCE; // If there are spaces in the search-word, make a full text search instead.
94 }
95
96 switch ($searchType) {
97 case self::ANY_PART_OF_THE_WORD:
98 case self::LAST_PART_OF_THE_WORD:
99 // Both options above are both not possible with fulltext indexing! Therefore, fallback to first-part-of-word search
100 case self::FIRST_PART_OF_THE_WORD:
101 // First part of word
102 $wildcard = '*';
103 // Part-of-word search requires boolean mode!
104 $searchBoolean = TRUE;
105 break;
106 case self::SOUNDS_LIKE:
107 $indexerObj = t3lib_div::makeInstance('tx_indexedsearch_indexer'); // Initialize the indexer-class
108 /** @var tx_indexedsearch_indexer $indexerObj */
109 $searchWord = $indexerObj->metaphone($searchWord, $indexerObj->storeMetaphoneInfoAsWords);
110 unset($indexerObj);
111 $fulltextIndex = 'index_fulltext.metaphonedata';
112 break;
113 case self::SENTENCE:
114 $searchBoolean = TRUE;
115 // Remove existing quotes and fix misplaced quotes.
116 $searchWord = trim(str_replace('"', ' ', $searchWord));
117 break;
118 }
119
120 // Perform search for word:
121 switch ($searchWordData['oper']) {
122 case 'AND NOT':
123 $booleanSearchString .= ' -' . $searchWord . $wildcard;
124 $searchBoolean = TRUE;
125 break;
126 case 'OR':
127 $booleanSearchString .= ' ' . $searchWord . $wildcard;
128 $searchBoolean = TRUE;
129 break;
130 default:
131 $booleanSearchString .= ' +' . $searchWord . $wildcard;
132 $naturalSearchString .= ' ' . $searchWord;
133 }
134
135 $count++;
136 }
137
138 if ($searchType == self::SENTENCE) {
139 $searchString = '"' . trim($naturalSearchString) . '"';
140 } elseif ($searchBoolean) {
141 $searchString = trim($booleanSearchString);
142 } else {
143 $searchString = trim($naturalSearchString);
144 }
145
146 return array(
147 'searchBoolean' => $searchBoolean,
148 'searchString' => $searchString,
149 'fulltextIndex' => $fulltextIndex
150 );
151 }
152
153 /**
154 * Execute final query, based on phash integer list. The main point is sorting the result in the right order.
155 *
156 * @param array $searchData Array with search string, boolean indicator, and fulltext index reference
157 * @param int $freeIndexUid Pointer to which indexing configuration you want to search in. -1 means no filtering. 0 means only regular indexed content.
158 * @return resource Query result
159 */
160 protected function execFinalQuery_fulltext($searchData, $freeIndexUid = -1) {
161
162 // Setting up methods of filtering results based on page types, access, etc.
163 $pageJoin = '';
164
165 // Indexing configuration clause:
166 $freeIndexUidClause = $this->pObj->freeIndexUidWhere($freeIndexUid);
167
168 // Calling hook for alternative creation of page ID list
169 if (($hookObj = &$this->pObj->hookRequest('execFinalQuery_idList'))) {
170 $pageWhere = $hookObj->execFinalQuery_idList(''); // Originally this hook expects a list of page IDs, so since we don't know them yet, just send an empty string. Users of this hook need to adjust their hook to this!
171 } elseif ($this->pObj->join_pages) { // Alternative to getting all page ids by ->getTreeList() where "excludeSubpages" is NOT respected.
172 $pageJoin = ',
173 pages';
174 $pageWhere = 'pages.uid = ISEC.page_id
175 '.$this->pObj->cObj->enableFields('pages').'
176 AND pages.no_search=0
177 AND pages.doktype<200
178 ';
179 } elseif ($this->pObj->wholeSiteIdList >= 0) { // Collecting all pages IDs in which to search; filtering out ALL pages that are not accessible due to enableFields. Does NOT look for "no_search" field!
180 $siteIdNumbers = t3lib_div::intExplode(',', $this->pObj->wholeSiteIdList);
181 $idList = array();
182 foreach ($siteIdNumbers as $rootId) {
183 $cObj = t3lib_div::makeInstance('tslib_cObj');
184 /** @var tslib_cObj $cObj */
185 $idList[] = $cObj->getTreeList($rootId, 9999, 0, 0, '', '') . $rootId;
186 }
187 $pageWhere = ' ISEC.page_id IN (' . implode(',', $idList) . ')';
188 } else {
189 // Disable everything... (select all)
190 $pageWhere = ' 1=1';
191 }
192
193 $searchBoolean = '';
194 if ($searchData['searchBoolean']) {
195 $searchBoolean = ' IN BOOLEAN MODE';
196 }
197
198 $resource = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
199 'index_fulltext.*, ISEC.*, IP.*',
200 'index_fulltext, index_section ISEC, index_phash IP' . $pageJoin,
201 'MATCH ('.$searchData['fulltextIndex'].') AGAINST (' . $GLOBALS['TYPO3_DB']->fullQuoteStr($searchData['searchString'],'index_fulltext') . $searchBoolean . ') '.
202 $this->pObj->mediaTypeWhere() . ' ' .
203 $this->pObj->languageWhere() .
204 $freeIndexUidClause . '
205 AND index_fulltext.phash = IP.phash
206 AND ISEC.phash = IP.phash
207 AND ' . $pageWhere,
208 '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'
209 );
210
211 return $resource;
212 }
213 }
214 ?>