# Source for file Query.php

Documentation is available at Query.php

1. <?php
2. /*
3. * $Id: Hook.php 1939 2007-07-05 23:47:48Z zYne $
4. *
5. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
6. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
7. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
8. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
9. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
10. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
11. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
12. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
13. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16. *
17. * This software consists of voluntary contributions made by many individuals
18. * and is licensed under the LGPL. For more information, see
19. * <http://www.phpdoctrine.com>.
20. */
22. /**
23. * Doctrine_Search_Query
24. *
25. * @package Doctrine
26. * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
27. * @category Object Relational Mapping
28. * @link www.phpdoctrine.com
29. * @since 1.0
30. * @version $Revision: 1939 $
31. * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
32. */
33. class Doctrine_Search_Query
34. {
35. /**
36. * @var Doctrine_Query $query the base query
37. */
38. protected $_query;
39. /**
40. * @var Doctrine_Table $_table the index table
41. */
42. protected $_table = array();
44. protected $_sql = '';
46. protected $_params = array();
49. protected $_condition;
50. /**
51. * @param octrine_Table $_table the index table
52. */
53. public function __construct($table)
54. {
55. if (is_string($table)) {
56. $table = Doctrine_Manager::table($table);
57. }
59. $this->_table = $table;
61. $this->_query = new Doctrine_Query();
62. $foreignId = current(array_diff($this->_table->getColumnNames(), array('keyword', 'field', 'position')));
64. $this->_condition = $foreignId . ' %s (SELECT ' . $foreignId . ' FROM ' . $this->_table->getTableName() . ' WHERE ';
65. }
66. /**
67. * getQuery
68. *
69. * @return Doctrine_Query returns the query object associated with this object
70. */
71. public function getQuery()
72. {
73. return $this->_query;
74. }
76. public function search($text)
77. {
78. $text = trim($text);
80. $foreignId = current(array_diff($this->_table->getColumnNames(), array('keyword', 'field', 'position')));
82. $weighted = false;
83. if (strpos($text, '^') === false) {
84. $select = 'SELECT COUNT(keyword) AS relevance, ' . $foreignId;
85. $from = 'FROM ' . $this->_table->getTableName();
86. } else {
87. // organize terms according weights
88. $weighted = true;
90. $select = 'SELECT SUM(sub_relevance) AS relevance, ' . $foreignId;
91. $from = 'FROM ' ;
92. }
94. $where = 'WHERE ';
95. $where .= $this->parseClause($text);
97. $groupby = 'GROUP BY ' . $foreignId;
98. $orderby = 'ORDER BY relevance';
100. $this->_sql = $select . ' ' . $from . ' ' . $where . ' ' . $groupby . ' ' . $orderby;
101. }
103. public function parseClause($originalClause, $recursive = false)
104. {
105. $clause = Doctrine_Tokenizer::bracketTrim($originalClause);
107. $brackets = false;
109. if ($clause !== $originalClause) {
110. $brackets = true;
111. }
113. $foreignId = current(array_diff($this->_table->getColumnNames(), array('keyword', 'field', 'position')));
115. $terms = Doctrine_Tokenizer::sqlExplode($clause, ' OR ', '(', ')');
117. $ret = array();
119. if (count($terms) > 1) {
120. $leavesOnly = true;
122. foreach ($terms as $k => $term) {
123. if ($this->isExpression($term)) {
124. $ret[$k] = $this->parseClause($term, true);
125. $leavesOnly = false;
126. } else {
127. $ret[$k] = $this->parseTerm($term);
128. }
129. }
131. $return = implode(' OR ', $ret);
133. if ($leavesOnly && $recursive) {
134. $return = sprintf($this->_condition, 'IN') . $return . ')';
135. $brackets = false;
136. }
137. } else {
138. $terms = Doctrine_Tokenizer::sqlExplode($clause, ' ', '(', ')');
140. if (count($terms) === 1 && ! $recursive) {
141. $return = $this->parseTerm($clause);
142. } else {
143. foreach ($terms as $k => $term) {
144. $term = trim($term);
146. if ($term === 'AND') {
147. continue;
148. }
150. if (substr($term, 0, 1) === '-') {
151. $operator = 'NOT IN';
152. $term = substr($term, 1);
153. } else {
154. $operator = 'IN';
155. }
157. if ($this->isExpression($term)) {
158. $ret[$k] = $this->parseClause($term, true);
159. } else {
160. $ret[$k] = sprintf($this->_condition, $operator) . $this->parseTerm($term) . ')';
161. }
162. }
163. $return = implode(' AND ', $ret);
164. }
165. }
167. if ($brackets) {
168. return '(' . $return . ')';
169. } else {
170. return $return;
171. }
172. }
173. public function isExpression($term)
174. {
175. if (strpos($term, '(') !== false) {
176. return true;
177. } else {
178. $terms = Doctrine_Tokenizer::quoteExplode($term);
180. return (count($terms) > 1);
181. }
182. }
184. public function parseTerm($term)
185. {
186. $negation = false;
188. if (strpos($term, "'") === false) {
189. $where = $this->parseWord($term);
190. } else {
191. $term = trim($term, "' ");
193. $terms = Doctrine_Tokenizer::quoteExplode($term);
194. $where = $this->parseWord($terms[0]);
196. foreach ($terms as $k => $word) {
197. if ($k === 0) {
198. continue;
199. }
200. $where .= ' AND (position + ' . $k . ') = (SELECT position FROM ' . $this->_table->getTableName() . ' WHERE ' . $this->parseWord($word) . ')';
201. }
202. }
203. return $where;
204. }
205. public function parseWord($word)
206. {
207. if (strpos($word, '?') !== false ||
208. strpos($word, '*') !== false) {
210. $word = str_replace('*', '%', $word);
212. $where = 'keyword LIKE ?';
214. $params = array($word);
215. } else {
216. $where = 'keyword = ?';
217. }
219. $this->_params[] = $word;
221. return $where;
222. }
223. public function getParams()
224. {
225. return $this->_params;
226. }
227. public function getSql()
228. {
229. return $this->_sql;
230. }
231. public function execute()
232. {
233. $resultSet = $this->_query->execute();
235. return $resultSet;
236. }
237. }