3 * FusionForge search engine
5 * Copyright 1999-2001, VA Linux Systems, Inc
6 * Copyright 2004, Guillaume Smet/Open Wide
7 * Copyright 2009, Roland Mas
9 * This file is part of FusionForge.
11 * FusionForge is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published
13 * by the Free Software Foundation; either version 2 of the License,
14 * or (at your option) any later version.
16 * FusionForge is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with FusionForge; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 class SearchQuery extends Error {
29 * the operator between each part of the query. Can be AND or OR.
31 * @var string $operator
35 * Number of rows per page
37 * @var int $rowsPerPage
41 * Number of rows we will display on the page
47 * Number of rows returned by the query
49 * @var int $rowsTotalCount
51 var $rowsTotalCount = 0;
61 * @var resource $result
65 * When search by id is enabled, the id to search for
69 var $searchId = false;
71 * if we want to search for all the words or if only one is sufficient
73 * @var boolean $isExact
77 * sections to search in
79 * @var array $sections
81 var $sections = SEARCH__ALL_SECTIONS;
90 * @param string $words words we are searching for
91 * @param int $offset offset
92 * @param boolean $isExact if we want to search for all the words or if only one is sufficient
93 * @param int $rowsPerPage number of rows per page
95 function SearchQuery($words, $offset, $isExact, $rowsPerPage = SEARCH__DEFAULT_ROWS_PER_PAGE) {
96 if (get_magic_quotes_gpc()) {
97 $words = stripslashes($words);
99 $this->cleanSearchWords($words);
100 //We manual escap because every Query in Search escap parameters
101 $words = addslashes($words);
102 $this->words = array_map('addslashes',$this->words);
103 $this->phrases = array_map('addslashes',$this->phrases);
104 $this->rowsPerPage = $rowsPerPage;
105 $this->offset = $offset;
106 $this->isExact = $isExact;
107 $this->operator = $this->getOperator();
111 * cleanSearchWords - clean the words we are searching for
113 * @param string $words words we are searching for
115 function cleanSearchWords($words) {
116 $words = trim($words);
118 $this->setError(_('Error: criteria not specified'));
121 if(is_numeric($words) && $this->implementsSearchById()) {
122 $this->searchId = (int) $words;
124 $words = preg_replace("/[ \t]+/", ' ', $words);
125 if(strlen($words) < 3) {
126 $this->setError(_('Error: search query too short'));
129 $words = htmlspecialchars($words);
130 $words = strtr($words, array('%' => '\%', '_' => '\_'));
131 $this->words = array();
132 $this->phrases = array();
135 foreach(explode(' ', quotemeta($words)) as $word) {
137 if(substr($word, -3) == "\\\\'") {
138 $word = substr($word, 0, -3);
140 $phrase .= ' '.$word;
141 $this->phrases[] = $phrase;
143 $phrase .= ' '.$word;
146 if(substr($word, 0, 3) == "\\\\'") {
147 $word = substr($word, 3);
149 if(substr($word, -3) == "\\\\'") {
150 // This is a special case where the phrase is just one word
151 $word = substr($word, 0, -3);
153 $this->phrases[] = $word;
158 $this->words[] = $word;
167 * executeQuery - execute the SQL query to get the results
169 function executeQuery() {
171 if($this->searchId) {
172 $qpa = $this->getSearchByIdQuery();
174 $qpa = $this->getQuery();
178 db_query_params ('select set_curcfg($1)',
181 $this->result = db_query_qpa (
183 $this->rowsPerPage + 1,
188 $this->rowsTotalCount = db_numrows($this->result);
189 $this->rowsCount = min($this->rowsPerPage, $this->rowsTotalCount);
193 * getQuery - returns the query built to get the search results
194 * This is an abstract method. It _MUST_ be implemented in children classes.
196 * @return array query+params array
198 function getQuery() {
202 function addMatchCondition($qpa, $fieldName) {
204 $qpa = db_construct_qpa ($qpa, 'TRUE') ;
206 $regexs = str_replace(' ', "\\\s+", $arr);
207 for ($i = 0; $i < count ($regexs); $i++) {
209 $qpa = db_construct_qpa ($qpa,
212 $qpa = db_construct_qpa ($qpa,
220 function addIlikeCondition($qpa, $fieldName) {
221 $wordArgs = array_map ('strtolower',
222 array_merge($this->words, str_replace(' ', "\\\s+", $this->phrases)));
224 for ($i = 0; $i < count ($wordArgs); $i++) {
226 $qpa = db_construct_qpa ($qpa,
229 $qpa = db_construct_qpa ($qpa,
230 'lower ('.$fieldName.' LIKE $1',
237 * getOperator - get the operator we have to use in ILIKE condition
239 * @return string AND if it is an exact search, OR otherwise
241 function getOperator() {
250 * implementsSearchById - check if the current object implements the search by id feature by having a getSearchByIdQuery method
252 * @return boolean true if our object implements search by id, false otherwise.
254 function implementsSearchById() {
255 return method_exists($this, 'getSearchByIdQuery');
259 * getResult - returns the result set
261 * @return resource result set
263 function & getResult() {
264 return $this->result;
268 * getRowsCount - returns number of rows for the current page
270 * @return int rows count for the current page
272 function getRowsCount() {
273 return $this->rowsCount;
277 * getRowsTotalCount - returns total number of rows
279 * @return int rows count
281 function getRowsTotalCount() {
282 return $this->rowsTotalCount;
286 * getOffset - returns the offset
290 function getOffset() {
291 return $this->offset;
295 * getRowsPerPage - returns number of rows per page
297 * @return int number of rows per page
299 function getRowsPerPage() {
300 return $this->rowsPerPage;
304 * getWords - returns the array containing words we are searching for
306 * @return array words we are searching for
308 function getWords() {
313 * setSections - set the sections list
315 * @param $sections mixed array of sections or SEARCH__ALL_SECTIONS
317 function setSections($sections) {
318 if(is_array($sections)) {
319 $this->sections = array_keys ($sections) ;
321 $this->sections = $sections;
326 * getFormattedWords - get words formatted in order to be used in the FTI stored procedures
328 * @return string words we are searching for, separated by a pipe
330 function getFormattedWords() {
331 if ($this->isExact) {
332 $words = implode('&', $this->words);
334 $words = implode('|', $this->words);
341 // c-file-style: "bsd"