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.
12 * FusionForge is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published
14 * by the Free Software Foundation; either version 2 of the License,
15 * or (at your option) any later version.
17 * FusionForge is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with FusionForge; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
28 class SearchQuery extends Error {
30 * the operator between each part of the query. Can be AND or OR.
32 * @var string $operator
36 * Number of rows per page
38 * @var int $rowsPerPage
42 * Number of rows we will display on the page
48 * Number of rows returned by the query
50 * @var int $rowsTotalCount
52 var $rowsTotalCount = 0;
62 * @var resource $result
66 * When search by id is enabled, the id to search for
70 var $searchId = false;
72 * if we want to search for all the words or if only one is sufficient
74 * @var boolean $isExact
78 * sections to search in
80 * @var array $sections
82 var $sections = SEARCH__ALL_SECTIONS;
86 var $phrases = array();
91 * @param string $words words we are searching for
92 * @param int $offset offset
93 * @param boolean $isExact if we want to search for all the words or if only one is sufficient
94 * @param int $rowsPerPage number of rows per page
96 function SearchQuery($words, $offset, $isExact, $rowsPerPage = SEARCH__DEFAULT_ROWS_PER_PAGE) {
97 if (get_magic_quotes_gpc()) {
98 $words = stripslashes($words);
100 $this->cleanSearchWords($words);
101 //We manual escap because every Query in Search escap parameters
102 $words = addslashes($words);
103 $this->words = array_map('addslashes',$this->words);
104 $this->phrases = array_map('addslashes',$this->phrases);
105 $this->rowsPerPage = $rowsPerPage;
106 $this->offset = $offset;
107 $this->isExact = $isExact;
108 $this->operator = $this->getOperator();
112 * cleanSearchWords - clean the words we are searching for
114 * @param string $words words we are searching for
116 function cleanSearchWords($words) {
117 $words = trim($words);
119 $this->setError(_('Error: criteria not specified'));
122 if(is_numeric($words) && $this->implementsSearchById()) {
123 $this->searchId = (int) $words;
125 $words = preg_replace("/[ \t]+/", ' ', $words);
126 if(strlen($words) < 3) {
127 $this->setError(_('Error: search query too short'));
130 $words = htmlspecialchars($words);
131 $words = strtr($words, array('%' => '\%', '_' => '\_'));
132 $this->words = array();
133 $this->phrases = array();
136 foreach(explode(' ', quotemeta($words)) as $word) {
138 if(substr($word, -3) == "\\\\'") {
139 $word = substr($word, 0, -3);
141 $phrase .= ' '.$word;
142 $this->phrases[] = $phrase;
144 $phrase .= ' '.$word;
147 if(substr($word, 0, 3) == "\\\\'") {
148 $word = substr($word, 3);
150 if(substr($word, -3) == "\\\\'") {
151 // This is a special case where the phrase is just one word
152 $word = substr($word, 0, -3);
154 $this->phrases[] = $word;
159 $this->words[] = $word;
168 * executeQuery - execute the SQL query to get the results
170 function executeQuery() {
172 if($this->searchId) {
173 $qpa = $this->getSearchByIdQuery();
175 $qpa = $this->getQuery();
178 if (forge_get_config('use_fti')) {
179 db_query_params ('select set_curcfg($1)',
182 $this->result = db_query_qpa (
184 $this->rowsPerPage + 1,
189 $this->rowsTotalCount = db_numrows($this->result);
190 $this->rowsCount = min($this->rowsPerPage, $this->rowsTotalCount);
194 * getQuery - returns the query built to get the search results
195 * This is an abstract method. It _MUST_ be implemented in children classes.
197 * @return array query+params array
199 function getQuery() {
203 function addMatchCondition($qpa, $fieldName) {
205 $qpa = db_construct_qpa ($qpa, 'TRUE') ;
207 $regexs = str_replace(' ', "\\\s+", $arr);
208 for ($i = 0; $i < count ($regexs); $i++) {
210 $qpa = db_construct_qpa ($qpa,
213 $qpa = db_construct_qpa ($qpa,
215 array ($regexs[$i])) ;
221 function addIlikeCondition($qpa, $fieldName) {
222 $wordArgs = array_map ('strtolower',
223 array_merge($this->words, str_replace(' ', "\\\s+", $this->phrases)));
225 for ($i = 0; $i < count ($wordArgs); $i++) {
227 $qpa = db_construct_qpa ($qpa,
230 $qpa = db_construct_qpa ($qpa,
231 'lower ('.$fieldName.') LIKE $1',
232 array ('%'.$wordArgs[$i].'%')) ;
238 * getOperator - get the operator we have to use in ILIKE condition
240 * @return string AND if it is an exact search, OR otherwise
242 function getOperator() {
251 * implementsSearchById - check if the current object implements the search by id feature by having a getSearchByIdQuery method
253 * @return boolean true if our object implements search by id, false otherwise.
255 function implementsSearchById() {
256 return method_exists($this, 'getSearchByIdQuery');
260 * getResult - returns the result set
262 * @return resource result set
264 function & getResult() {
265 return $this->result;
269 * getRowsCount - returns number of rows for the current page
271 * @return int rows count for the current page
273 function getRowsCount() {
274 return $this->rowsCount;
278 * getRowsTotalCount - returns total number of rows
280 * @return int rows count
282 function getRowsTotalCount() {
283 return $this->rowsTotalCount;
287 * getOffset - returns the offset
291 function getOffset() {
292 return $this->offset;
296 * getRowsPerPage - returns number of rows per page
298 * @return int number of rows per page
300 function getRowsPerPage() {
301 return $this->rowsPerPage;
305 * getWords - returns the array containing words we are searching for
307 * @return array words we are searching for
309 function getWords() {
314 * setSections - set the sections list
316 * @param $sections mixed array of sections or SEARCH__ALL_SECTIONS
318 function setSections($sections) {
319 if(is_array($sections)) {
320 $this->sections = array_keys ($sections) ;
322 $this->sections = $sections;
327 * getFormattedWords - get words formatted in order to be used in the FTI stored procedures
329 * @return string words we are searching for, separated by a pipe
331 function getFormattedWords() {
332 if ($this->isExact) {
333 $words = implode('&', $this->words);
335 $words = implode('|', $this->words);
342 // c-file-style: "bsd"