Coverage for Doctrine_Search_Query

Back to coverage report

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  */
21
22 /**
23  * Doctrine_Search_Query
24  *
25  * @package     Doctrine
26  * @subpackage  Search
27  * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
28  * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
29  * @version     $Revision$
30  * @link        www.phpdoctrine.com
31  * @since       1.0
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();
43     
44     protected $_sql = '';
45     
46     protected $_params = array();
47     
48
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         }
58
59         $this->_table = $table;
60
61         $this->_query = new Doctrine_Query();
62         $foreignId = current(array_diff($this->_table->getColumnNames(), array('keyword', 'field', 'position')));
63
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     }
75
76     public function search($text)
77     {
78         $text = trim($text);
79         
80         $foreignId = current(array_diff($this->_table->getColumnNames(), array('keyword', 'field', 'position')));
81
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;
89
90             $select = 'SELECT SUM(sub_relevance) AS relevance, ' . $foreignId;
91             $from = 'FROM ' ;
92         }
93         
94         $where = 'WHERE ';
95         $where .= $this->parseClause($text);
96
97         $groupby = 'GROUP BY ' . $foreignId;
98         $orderby = 'ORDER BY relevance';
99
100         $this->_sql = $select . ' ' . $from . ' ' . $where . ' ' . $groupby . ' ' . $orderby;
101     }
102
103     public function parseClause($originalClause, $recursive = false)
104     {
105         $clause = Doctrine_Tokenizer::bracketTrim($originalClause);
106         
107         $brackets = false;
108
109         if ($clause !== $originalClause) {
110             $brackets = true;
111         }
112
113         $foreignId = current(array_diff($this->_table->getColumnNames(), array('keyword', 'field', 'position')));
114         
115         $terms = Doctrine_Tokenizer::sqlExplode($clause, ' OR ', '(', ')');
116
117         $ret = array();
118
119         if (count($terms) > 1) {
120             $leavesOnly = true;
121
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             }
130
131             $return = implode(' OR ', $ret);
132
133             if ($leavesOnly && $recursive) {
134                 $return = sprintf($this->_condition, 'IN') . $return . ')';
135                 $brackets = false;
136             }
137         } else {
138             $terms = Doctrine_Tokenizer::sqlExplode($clause, ' ', '(', ')');
139             
140             if (count($terms) === 1 && ! $recursive) {
141                 $return = $this->parseTerm($clause);
142             } else {
143                 foreach ($terms as $k => $term) {
144                     $term = trim($term);
145     
146                     if ($term === 'AND') {
147                         continue;
148                     }
149     
150                     if (substr($term, 0, 1) === '-') {
151                         $operator = 'NOT IN';
152                         $term = substr($term, 1);
153                     } else {
154                         $operator = 'IN';
155                     }
156     
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         }
166
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);
179             
180             return (count($terms) > 1);
181         }
182     }
183
184     public function parseTerm($term)
185     {
186         $negation = false;
187
188         if (strpos($term, "'") === false) {
189             $where = $this->parseWord($term);
190         } else {
191             $term = trim($term, "' ");
192
193             $terms = Doctrine_Tokenizer::quoteExplode($term);
194             $where = $this->parseWord($terms[0]);
195
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) {
209             
210             $word = str_replace('*', '%', $word);
211
212             $where = 'keyword LIKE ?';
213             
214             $params = array($word);
215         } else {
216             $where = 'keyword = ?';
217         }
218         
219         $this->_params[] = $word;
220
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(); 
234         
235         return $resultSet;
236     }
237 }