3 * FusionForge search engine
5 * Copyright 1999-2001, VA Linux Systems, Inc
6 * Copyright 2004, Guillaume Smet/Open Wide
7 * Copyright 2009, Roland Mas
8 * Copyright 2010 (c) Capgemini - Franck Villaume
10 * This file is part of FusionForge. FusionForge is free software;
11 * you can redistribute it and/or modify it under the terms of the
12 * GNU General Public License as published by the Free Software
13 * Foundation; either version 2 of the Licence, or (at your option)
16 * FusionForge is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License along
22 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 class SearchQuery extends Error {
28 * the operator between each part of the query. Can be AND or OR.
30 * @var string $operator
34 * Number of rows per page
36 * @var int $rowsPerPage
40 * Number of rows we will display on the page
46 * Number of rows returned by the query
48 * @var int $rowsTotalCount
50 var $rowsTotalCount = 0;
60 * @var resource $result
64 * When search by id is enabled, the id to search for
68 var $searchId = false;
70 * if we want to search for all the words or if only one is sufficient
72 * @var boolean $isExact
76 * sections to search in
78 * @var array $sections
80 var $sections = SEARCH__ALL_SECTIONS;
84 var $phrases = array();
89 * @param string $words words we are searching for
90 * @param int $offset offset
91 * @param boolean $isExact if we want to search for all the words or if only one is sufficient
92 * @param int $rowsPerPage number of rows per page
94 function SearchQuery($words, $offset, $isExact, $rowsPerPage = SEARCH__DEFAULT_ROWS_PER_PAGE) {
95 $this->cleanSearchWords($words);
96 //We manual escap because every Query in Search escap parameters
97 $words = addslashes($words);
98 if (is_array ($this->words)){
99 $this->words = array_map ('addslashes',$this->words);
101 $this->words = array();
103 if (is_array ($this->phrases)){
104 $this->phrases = array_map ('addslashes',$this->phrases);
106 $this->phrases = array();
108 $this->rowsPerPage = $rowsPerPage;
109 $this->offset = $offset;
110 $this->isExact = $isExact;
111 $this->operator = $this->getOperator();
115 * cleanSearchWords - clean the words we are searching for
117 * @param string $words words we are searching for
119 function cleanSearchWords($words) {
120 $words = trim($words);
122 $this->setError(_('Error: criteria not specified'));
125 if(is_numeric($words) && $this->implementsSearchById()) {
126 $this->searchId = (int) $words;
128 $words = preg_replace("/[ \t]+/", ' ', $words);
129 if(strlen($words) < 3) {
130 $this->setError(_('Error: search query too short'));
133 $words = htmlspecialchars($words);
134 $words = strtr($words, array('%' => '\%', '_' => '\_'));
137 foreach(explode(' ', quotemeta($words)) as $word) {
139 if(substr($word, -3) == "\\\\'") {
140 $word = substr($word, 0, -3);
142 $phrase .= ' '.$word;
143 $this->phrases[] = $phrase;
145 $phrase .= ' '.$word;
148 if(substr($word, 0, 3) == "\\\\'") {
149 $word = substr($word, 3);
151 if(substr($word, -3) == "\\\\'") {
152 // This is a special case where the phrase is just one word
153 $word = substr($word, 0, -3);
155 $this->phrases[] = $word;
160 $this->words[] = $word;
169 * executeQuery - execute the SQL query to get the results
171 function executeQuery() {
173 if($this->searchId) {
174 $qpa = $this->getSearchByIdQuery();
176 $qpa = $this->getQuery();
179 if (forge_get_config('use_fti')) {
180 db_query_params ('select set_curcfg($1)',
183 $this->result = db_query_qpa (
185 $this->rowsPerPage + 1,
190 $this->rowsTotalCount = db_numrows($this->result);
191 $this->rowsCount = min($this->rowsPerPage, $this->rowsTotalCount);
195 * getQuery - returns the query built to get the search results
196 * This is an abstract method. It _MUST_ be implemented in children classes.
198 * @return array query+params array
200 function getQuery() {
204 function addMatchCondition($qpa, $fieldName) {
206 $qpa = db_construct_qpa ($qpa, 'TRUE') ;
208 $regexs = str_replace(' ', "\\\s+", $arr);
209 for ($i = 0; $i < count ($regexs); $i++) {
211 $qpa = db_construct_qpa ($qpa,
214 $qpa = db_construct_qpa ($qpa,
216 array ($regexs[$i])) ;
222 function addIlikeCondition($qpa, $fieldName) {
223 $wordArgs = array_map ('strtolower',
224 array_merge($this->words, str_replace(' ', "\\\s+", $this->phrases)));
226 for ($i = 0; $i < count ($wordArgs); $i++) {
228 $qpa = db_construct_qpa ($qpa,
231 $qpa = db_construct_qpa ($qpa,
232 'lower ('.$fieldName.') LIKE $1',
233 array ('%'.$wordArgs[$i].'%')) ;
239 * getOperator - get the operator we have to use in ILIKE condition
241 * @return string AND if it is an exact search, OR otherwise
243 function getOperator() {
252 * implementsSearchById - check if the current object implements the search by id feature by having a getSearchByIdQuery method
254 * @return boolean true if our object implements search by id, false otherwise.
256 function implementsSearchById() {
257 return method_exists($this, 'getSearchByIdQuery');
261 * getResult - returns the result set
263 * @return resource result set
265 function & getResult() {
266 return $this->result;
270 * getRowsCount - returns number of rows for the current page
272 * @return int rows count for the current page
274 function getRowsCount() {
275 return $this->rowsCount;
279 * getRowsTotalCount - returns total number of rows
281 * @return int rows count
283 function getRowsTotalCount() {
284 return $this->rowsTotalCount;
288 * getOffset - returns the offset
292 function getOffset() {
293 return $this->offset;
297 * getRowsPerPage - returns number of rows per page
299 * @return int number of rows per page
301 function getRowsPerPage() {
302 return $this->rowsPerPage;
306 * getWords - returns the array containing words we are searching for
308 * @return array words we are searching for
310 function getWords() {
315 * setSections - set the sections list
317 * @param $sections mixed array of sections or SEARCH__ALL_SECTIONS
319 function setSections($sections) {
320 if(is_array($sections)) {
321 $this->sections = array_keys ($sections) ;
323 $this->sections = $sections;
328 * getFormattedWords - get words formatted in order to be used in the FTI stored procedures
330 * @return string words we are searching for, separated by a pipe
332 function getFormattedWords() {
333 if ($this->isExact) {
334 $words = implode('&', $this->words);
336 $words = implode('|', $this->words);
343 // c-file-style: "bsd"