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 $this->cleanSearchWords($words);
98 //We manual escap because every Query in Search escap parameters
99 $words = addslashes($words);
100 if (is_array ($this->words)){
101 $this->words = array_map ('addslashes',$this->words);
103 $this->words = array();
105 if (is_array ($this->phrases)){
106 $this->phrases = array_map ('addslashes',$this->phrases);
108 $this->phrases = array();
110 $this->rowsPerPage = $rowsPerPage;
111 $this->offset = $offset;
112 $this->isExact = $isExact;
113 $this->operator = $this->getOperator();
117 * cleanSearchWords - clean the words we are searching for
119 * @param string $words words we are searching for
121 function cleanSearchWords($words) {
122 $words = trim($words);
124 $this->setError(_('Error: criteria not specified'));
127 if(is_numeric($words) && $this->implementsSearchById()) {
128 $this->searchId = (int) $words;
130 $words = preg_replace("/[ \t]+/", ' ', $words);
131 if(strlen($words) < 3) {
132 $this->setError(_('Error: search query too short'));
135 $words = htmlspecialchars($words);
136 $words = strtr($words, array('%' => '\%', '_' => '\_'));
139 foreach(explode(' ', quotemeta($words)) as $word) {
141 if(substr($word, -3) == "\\\\'") {
142 $word = substr($word, 0, -3);
144 $phrase .= ' '.$word;
145 $this->phrases[] = $phrase;
147 $phrase .= ' '.$word;
150 if(substr($word, 0, 3) == "\\\\'") {
151 $word = substr($word, 3);
153 if(substr($word, -3) == "\\\\'") {
154 // This is a special case where the phrase is just one word
155 $word = substr($word, 0, -3);
157 $this->phrases[] = $word;
162 $this->words[] = $word;
171 * executeQuery - execute the SQL query to get the results
173 function executeQuery() {
175 if($this->searchId) {
176 $qpa = $this->getSearchByIdQuery();
178 $qpa = $this->getQuery();
181 if (forge_get_config('use_fti')) {
182 db_query_params ('select set_curcfg($1)',
185 $this->result = db_query_qpa (
187 $this->rowsPerPage + 1,
192 $this->rowsTotalCount = db_numrows($this->result);
193 $this->rowsCount = min($this->rowsPerPage, $this->rowsTotalCount);
197 * getQuery - returns the query built to get the search results
198 * This is an abstract method. It _MUST_ be implemented in children classes.
200 * @return array query+params array
202 function getQuery() {
206 function addMatchCondition($qpa, $fieldName) {
208 $qpa = db_construct_qpa ($qpa, 'TRUE') ;
210 $regexs = str_replace(' ', "\\\s+", $arr);
211 for ($i = 0; $i < count ($regexs); $i++) {
213 $qpa = db_construct_qpa ($qpa,
216 $qpa = db_construct_qpa ($qpa,
218 array ($regexs[$i])) ;
224 function addIlikeCondition($qpa, $fieldName) {
225 $wordArgs = array_map ('strtolower',
226 array_merge($this->words, str_replace(' ', "\\\s+", $this->phrases)));
228 for ($i = 0; $i < count ($wordArgs); $i++) {
230 $qpa = db_construct_qpa ($qpa,
233 $qpa = db_construct_qpa ($qpa,
234 'lower ('.$fieldName.') LIKE $1',
235 array ('%'.$wordArgs[$i].'%')) ;
241 * getOperator - get the operator we have to use in ILIKE condition
243 * @return string AND if it is an exact search, OR otherwise
245 function getOperator() {
254 * implementsSearchById - check if the current object implements the search by id feature by having a getSearchByIdQuery method
256 * @return boolean true if our object implements search by id, false otherwise.
258 function implementsSearchById() {
259 return method_exists($this, 'getSearchByIdQuery');
263 * getResult - returns the result set
265 * @return resource result set
267 function & getResult() {
268 return $this->result;
272 * getRowsCount - returns number of rows for the current page
274 * @return int rows count for the current page
276 function getRowsCount() {
277 return $this->rowsCount;
281 * getRowsTotalCount - returns total number of rows
283 * @return int rows count
285 function getRowsTotalCount() {
286 return $this->rowsTotalCount;
290 * getOffset - returns the offset
294 function getOffset() {
295 return $this->offset;
299 * getRowsPerPage - returns number of rows per page
301 * @return int number of rows per page
303 function getRowsPerPage() {
304 return $this->rowsPerPage;
308 * getWords - returns the array containing words we are searching for
310 * @return array words we are searching for
312 function getWords() {
317 * setSections - set the sections list
319 * @param $sections mixed array of sections or SEARCH__ALL_SECTIONS
321 function setSections($sections) {
322 if(is_array($sections)) {
323 $this->sections = array_keys ($sections) ;
325 $this->sections = $sections;
330 * getFormattedWords - get words formatted in order to be used in the FTI stored procedures
332 * @return string words we are searching for, separated by a pipe
334 function getFormattedWords() {
335 if ($this->isExact) {
336 $words = implode('&', $this->words);
338 $words = implode('|', $this->words);
345 // c-file-style: "bsd"