PageRenderTime 46ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/gforge/common/search/SearchQuery.class.php

https://github.com/neymanna/fusionforge
PHP | 332 lines | 154 code | 26 blank | 152 comment | 30 complexity | 5457695b7bf60d15adc87a7cdd75127f MD5 | raw file
Possible License(s): GPL-2.0, MPL-2.0-no-copyleft-exception
  1. <?php
  2. /**
  3. * FusionForge search engine
  4. *
  5. * Copyright 1999-2001, VA Linux Systems, Inc
  6. * Copyright 2004, Guillaume Smet/Open Wide
  7. *
  8. * This file is part of FusionForge.
  9. *
  10. * FusionForge is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published
  12. * by the Free Software Foundation; either version 2 of the License,
  13. * or (at your option) any later version.
  14. *
  15. * FusionForge is distributed in the hope that it will be useful, but
  16. * WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  18. * General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with FusionForge; if not, write to the Free Software
  22. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  23. * USA
  24. */
  25. class SearchQuery extends Error {
  26. /**
  27. * the operator between each part of the query. Can be AND or OR.
  28. *
  29. * @var string $operator
  30. */
  31. var $operator;
  32. /**
  33. * Number of rows per page
  34. *
  35. * @var int $rowsPerPage
  36. */
  37. var $rowsPerPage;
  38. /**
  39. * Number of rows we will display on the page
  40. *
  41. * @var int $rowsCount
  42. */
  43. var $rowsCount = 0;
  44. /**
  45. * Number of rows returned by the query
  46. *
  47. * @var int $rowsTotalCount
  48. */
  49. var $rowsTotalCount = 0;
  50. /**
  51. * Offset
  52. *
  53. * @var int $offset
  54. */
  55. var $offset = 0;
  56. /**
  57. * Result handle
  58. *
  59. * @var resource $result
  60. */
  61. var $result;
  62. /**
  63. * When search by id is enabled, the id to search for
  64. *
  65. * @var int $searchId
  66. */
  67. var $searchId = false;
  68. /**
  69. * if we want to search for all the words or if only one is sufficient
  70. *
  71. * @var boolean $isExact
  72. */
  73. var $isExact = false;
  74. /**
  75. * sections to search in
  76. *
  77. * @var array $sections
  78. */
  79. var $sections = SEARCH__ALL_SECTIONS;
  80. var $words;
  81. var $phrases;
  82. /**
  83. * Constructor
  84. *
  85. * @param string $words words we are searching for
  86. * @param int $offset offset
  87. * @param boolean $isExact if we want to search for all the words or if only one is sufficient
  88. * @param int $rowsPerPage number of rows per page
  89. */
  90. function SearchQuery($words, $offset, $isExact, $rowsPerPage = SEARCH__DEFAULT_ROWS_PER_PAGE) {
  91. $this->cleanSearchWords($words);
  92. $this->rowsPerPage = $rowsPerPage;
  93. $this->offset = $offset;
  94. $this->isExact = $isExact;
  95. $this->operator = $this->getOperator();
  96. }
  97. /**
  98. * cleanSearchWords - clean the words we are searching for
  99. *
  100. * @param string $words words we are searching for
  101. */
  102. function cleanSearchWords($words) {
  103. $words = trim($words);
  104. if(!$words) {
  105. $this->setError(_('Error: criteria not specified'));
  106. return;
  107. }
  108. if(is_numeric($words) && $this->implementsSearchById()) {
  109. $this->searchId = (int) $words;
  110. } else {
  111. $words = htmlspecialchars($words);
  112. $words = strtr($words, array('%' => '', '_' => ''));
  113. $words = preg_replace("/[ \t]+/", ' ', $words);
  114. if(strlen($words) < 3) {
  115. $this->setError(_('Error: search query too short'));
  116. return;
  117. }
  118. $this->words = array();
  119. $this->phrases = array();
  120. $phrase = '';
  121. $inQuote = false;
  122. foreach(explode(' ', quotemeta($words)) as $word) {
  123. if($inQuote) {
  124. if(substr($word, -3) == "\\\\'") {
  125. $word = substr($word, 0, -3);
  126. $inQuote = false;
  127. $phrase .= ' '.$word;
  128. $this->phrases[] = $phrase;
  129. } else {
  130. $phrase .= ' '.$word;
  131. }
  132. } else {
  133. if(substr($word, 0, 3) == "\\\\'") {
  134. $word = substr($word, 3);
  135. $inQuote = true;
  136. if(substr($word, -3) == "\\\\'") {
  137. // This is a special case where the phrase is just one word
  138. $word = substr($word, 0, -3);
  139. $inQuote = false;
  140. $this->phrases[] = $word;
  141. } else {
  142. $phrase = $word;
  143. }
  144. } else {
  145. $this->words[] = $word;
  146. }
  147. }
  148. }
  149. }
  150. }
  151. /**
  152. * executeQuery - execute the SQL query to get the results
  153. */
  154. function executeQuery() {
  155. global $sys_use_fti;
  156. if($this->searchId) {
  157. $query = $this->getSearchByIdQuery();
  158. } else {
  159. $query = $this->getQuery();
  160. }
  161. if ($sys_use_fti) {
  162. db_query("select set_curcfg('default')");
  163. }
  164. $this->result = db_query(
  165. $query,
  166. $this->rowsPerPage + 1,
  167. $this->offset,
  168. SYS_DB_SEARCH
  169. );
  170. $this->rowsTotalCount = db_numrows($this->result);
  171. $this->rowsCount = min($this->rowsPerPage, $this->rowsTotalCount);
  172. }
  173. /**
  174. * getQuery - returns the sql query built to get the search results
  175. * This is an abstract method. It _MUST_ be implemented in children classes.
  176. *
  177. * @return string sql query to execute
  178. */
  179. function getQuery() {
  180. return;
  181. }
  182. /**
  183. * getIlikeCondition - build the ILIKE condition of the SQL query for a given field name
  184. *
  185. * @param string $fieldName name of the field in the ILIKE condition
  186. * @return string the condition
  187. */
  188. function getIlikeCondition($fieldName) {
  189. global $sys_database_type;
  190. $wordArgs = array_merge($this->words, str_replace(' ', "\\\s+",$this->phrases));
  191. if ( $sys_database_type == "mysql" ) {
  192. return $fieldName." LIKE '%" . implode("%' ".$this->operator." ".$fieldName." ILIKE '%", $wordArgs) ."%'";
  193. } else {
  194. return $fieldName." ILIKE '%" . implode("%' ".$this->operator." ".$fieldName." ILIKE '%", $wordArgs) ."%'";
  195. }
  196. }
  197. function getMatchCond($fieldName, $arr) {
  198. if(!count($arr)) {
  199. $result = 'TRUE';
  200. } else {
  201. $regexs = str_replace(' ', "\\\s+",$arr);
  202. $result = $fieldName." ~* '" . implode("' ".$this->operator." ".$fieldName." ~* '", $regexs) ."'";
  203. }
  204. return $result;
  205. }
  206. /**
  207. * getOperator - get the operator we have to use in ILIKE condition
  208. *
  209. * @return string AND if it is an exact search, OR otherwise
  210. */
  211. function getOperator() {
  212. if($this->isExact) {
  213. return 'AND';
  214. } else {
  215. return 'OR';
  216. }
  217. }
  218. /**
  219. * implementsSearchById - check if the current object implements the search by id feature by having a getSearchByIdQuery method
  220. *
  221. * @return boolean true if our object implements search by id, false otherwise.
  222. */
  223. function implementsSearchById() {
  224. return method_exists($this, 'getSearchByIdQuery');
  225. }
  226. /**
  227. * getResult - returns the result set
  228. *
  229. * @return resource result set
  230. */
  231. function & getResult() {
  232. return $this->result;
  233. }
  234. /**
  235. * getRowsCount - returns number of rows for the current page
  236. *
  237. * @return int rows count for the current page
  238. */
  239. function getRowsCount() {
  240. return $this->rowsCount;
  241. }
  242. /**
  243. * getRowsTotalCount - returns total number of rows
  244. *
  245. * @return int rows count
  246. */
  247. function getRowsTotalCount() {
  248. return $this->rowsTotalCount;
  249. }
  250. /**
  251. * getOffset - returns the offset
  252. *
  253. * @return int offset
  254. */
  255. function getOffset() {
  256. return $this->offset;
  257. }
  258. /**
  259. * getRowsPerPage - returns number of rows per page
  260. *
  261. * @return int number of rows per page
  262. */
  263. function getRowsPerPage() {
  264. return $this->rowsPerPage;
  265. }
  266. /**
  267. * getWords - returns the array containing words we are searching for
  268. *
  269. * @return array words we are searching for
  270. */
  271. function getWords() {
  272. return $this->words;
  273. }
  274. /**
  275. * setSections - set the sections list
  276. *
  277. * @param $sections mixed array of sections or SEARCH__ALL_SECTIONS
  278. */
  279. function setSections($sections) {
  280. if(is_array($sections)) {
  281. //make a comma separated string from the sections array
  282. foreach($sections as $key => $section)
  283. $sections[$key] = '\''.$section.'\'';
  284. $this->sections = implode(', ', $sections);
  285. } else {
  286. $this->sections = $sections;
  287. }
  288. }
  289. /**
  290. * getFormattedWords - get words formatted in order to be used in the FTI stored procedures
  291. *
  292. * @return string words we are searching for, separated by a pipe
  293. */
  294. function getFormattedWords() {
  295. if ($this->isExact) {
  296. $words = implode('&', $this->words);
  297. } else {
  298. $words = implode('|', $this->words);
  299. }
  300. }
  301. }
  302. // Local Variables:
  303. // mode: php
  304. // c-file-style: "bsd"
  305. // End:
  306. ?>