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 $this->words = array_map('addslashes',$this->words);
101 $this->phrases = array_map('addslashes',$this->phrases);
102 $this->rowsPerPage = $rowsPerPage;
103 $this->offset = $offset;
104 $this->isExact = $isExact;
105 $this->operator = $this->getOperator();
109 * cleanSearchWords - clean the words we are searching for
111 * @param string $words words we are searching for
113 function cleanSearchWords($words) {
114 $words = trim($words);
116 $this->setError(_('Error: criteria not specified'));
119 if(is_numeric($words) && $this->implementsSearchById()) {
120 $this->searchId = (int) $words;
122 $words = preg_replace("/[ \t]+/", ' ', $words);
123 if(strlen($words) < 3) {
124 $this->setError(_('Error: search query too short'));
127 $words = htmlspecialchars($words);
128 $words = strtr($words, array('%' => '\%', '_' => '\_'));
129 $this->words = array();
130 $this->phrases = array();
133 foreach(explode(' ', quotemeta($words)) as $word) {
135 if(substr($word, -3) == "\\\\'") {
136 $word = substr($word, 0, -3);
138 $phrase .= ' '.$word;
139 $this->phrases[] = $phrase;
141 $phrase .= ' '.$word;
144 if(substr($word, 0, 3) == "\\\\'") {
145 $word = substr($word, 3);
147 if(substr($word, -3) == "\\\\'") {
148 // This is a special case where the phrase is just one word
149 $word = substr($word, 0, -3);
151 $this->phrases[] = $word;
156 $this->words[] = $word;
165 * executeQuery - execute the SQL query to get the results
167 function executeQuery() {
169 if($this->searchId) {
170 $qpa = $this->getSearchByIdQuery();
172 $qpa = $this->getQuery();
175 if (forge_get_config('use_fti')) {
176 db_query_params ('select set_curcfg($1)',
179 $this->result = db_query_qpa (
181 $this->rowsPerPage + 1,
186 $this->rowsTotalCount = db_numrows($this->result);
187 $this->rowsCount = min($this->rowsPerPage, $this->rowsTotalCount);
191 * getQuery - returns the query built to get the search results
192 * This is an abstract method. It _MUST_ be implemented in children classes.
194 * @return array query+params array
196 function getQuery() {
200 function addMatchCondition($qpa, $fieldName) {
202 $qpa = db_construct_qpa ($qpa, 'TRUE') ;
204 $regexs = str_replace(' ', "\\\s+", $arr);
205 for ($i = 0; $i < count ($regexs); $i++) {
207 $qpa = db_construct_qpa ($qpa,
210 $qpa = db_construct_qpa ($qpa,
212 array ($regexs[$i])) ;
218 function addIlikeCondition($qpa, $fieldName) {
219 $wordArgs = array_map ('strtolower',
220 array_merge($this->words, str_replace(' ', "\\\s+", $this->phrases)));
222 for ($i = 0; $i < count ($wordArgs); $i++) {
224 $qpa = db_construct_qpa ($qpa,
227 $qpa = db_construct_qpa ($qpa,
228 'lower ('.$fieldName.') LIKE $1',
229 array ('%'.$wordArgs[$i].'%')) ;
235 * getOperator - get the operator we have to use in ILIKE condition
237 * @return string AND if it is an exact search, OR otherwise
239 function getOperator() {
248 * implementsSearchById - check if the current object implements the search by id feature by having a getSearchByIdQuery method
250 * @return boolean true if our object implements search by id, false otherwise.
252 function implementsSearchById() {
253 return method_exists($this, 'getSearchByIdQuery');
257 * getResult - returns the result set
259 * @return resource result set
261 function & getResult() {
262 return $this->result;
266 * getRowsCount - returns number of rows for the current page
268 * @return int rows count for the current page
270 function getRowsCount() {
271 return $this->rowsCount;
275 * getRowsTotalCount - returns total number of rows
277 * @return int rows count
279 function getRowsTotalCount() {
280 return $this->rowsTotalCount;
284 * getOffset - returns the offset
288 function getOffset() {
289 return $this->offset;
293 * getRowsPerPage - returns number of rows per page
295 * @return int number of rows per page
297 function getRowsPerPage() {
298 return $this->rowsPerPage;
302 * getWords - returns the array containing words we are searching for
304 * @return array words we are searching for
306 function getWords() {
311 * setSections - set the sections list
313 * @param $sections mixed array of sections or SEARCH__ALL_SECTIONS
315 function setSections($sections) {
316 if(is_array($sections)) {
317 $this->sections = array_keys ($sections) ;
319 $this->sections = $sections;
324 * getFormattedWords - get words formatted in order to be used in the FTI stored procedures
326 * @return string words we are searching for, separated by a pipe
328 function getFormattedWords() {
329 if ($this->isExact) {
330 $words = implode('&', $this->words);
332 $words = implode('|', $this->words);
339 // c-file-style: "bsd"