Coverage for Doctrine_Search

Back to coverage report

1 <?php
2 /*
3  *  $Id$
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.org>.
20  */
21 Doctrine::autoload('Doctrine_Record_Generator');
22 /**
23  * Doctrine_Search
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.org
31  * @since       1.0
32  */
33 class Doctrine_Search extends Doctrine_Record_Generator
34 {
35     const INDEX_FILES = 0;
36
37     const INDEX_TABLES = 1;
38
39     protected $_options = array('generateFiles' => false,
40                                 'type'          => self::INDEX_TABLES,
41                                 'className'     => '%CLASS%Index',
42                                 'generatePath'  => false,
43                                 'table'         => null,
44                                 'batchUpdates'  => false,
45                                 'pluginTable'   => false,
46                                 'fields'        => array(),
47                                 'connection'    => null,
48                                 'children'      => array());
49     /**
50      * __construct 
51      * 
52      * @param array $options 
53      * @return void
54      */
55     public function __construct(array $options)
56     {
57         $this->_options = Doctrine_Lib::arrayDeepMerge($this->_options, $options);
58         
59         if ( ! isset($this->_options['analyzer'])) {
60             $this->_options['analyzer'] = new Doctrine_Search_Analyzer_Standard();
61         }
62         if ( ! isset($this->_options['connection'])) {
63             $this->_options['connection'] = Doctrine_Manager::connection();
64         }
65     }
66
67
68     /**
69      * search 
70      * 
71      * @param string $query
72      * @return Doctrine_Collection The collection of search results
73      */
74     public function search($query)
75     {
76         $q = new Doctrine_Search_Query($this->_table);
77         
78         $q->query($query);
79         
80         return $this->_options['connection']->fetchAll($q->getSql(), $q->getParams());;
81     }
82     
83     /**
84      * analyze 
85      * 
86      * @param string $text 
87      * @return void
88      */
89     public function analyze($text)
90     {
91         return $this->_options['analyzer']->analyze($text);
92     }
93
94     /**
95      * updateIndex
96      * updates the index
97      *
98      * @param Doctrine_Record $record
99      * @return integer
100      */
101     public function updateIndex(array $data)
102     {
103         $this->initialize($this->_options['table']);
104
105         $fields = $this->getOption('fields');
106         $class  = $this->getOption('className');
107         $name   = $this->getOption('table')->getComponentName();
108         $conn   = $this->getOption('table')->getConnection();
109         $identifier = $this->_options['table']->getIdentifier();
110
111         $q = Doctrine_Query::create()->delete()
112                                      ->from($class);
113         foreach ((array) $identifier as $id) {
114             $q->addWhere($id . ' = ?', array($data[$id]));
115         }
116         $q->execute();
117
118         if ($this->_options['batchUpdates'] === true) {
119             $index = new $class(); 
120
121             foreach ((array) $this->_options['table']->getIdentifier() as $id) {
122                 $index->$id = $data[$id];
123             }
124
125             $index->save();
126         } else {
127             foreach ($fields as $field) {
128
129                 $value = $data[$field];
130
131                 $terms = $this->analyze($value);
132
133                 foreach ($terms as $pos => $term) {
134                     $index = new $class();
135
136                     $index->keyword = $term;
137                     $index->position = $pos;
138                     $index->field = $field;
139                     foreach ((array) $this->_options['table']->getIdentifier() as $id) {
140                         $index->$id = $data[$id];
141                     }
142
143                     $index->save();
144                 }
145             }
146         }
147     }
148
149     /**
150      * readTableData 
151      * 
152      * @param mixed $limit 
153      * @param mixed $offset 
154      * @return Doctrine_Collection The collection of results
155      */
156     public function readTableData($limit = null, $offset = null)
157     {
158         $this->initialize($this->_options['table']);
159
160         $conn      = $this->_options['table']->getConnection();
161         $tableName = $this->_options['table']->getTableName();
162         $id        = $this->_options['table']->getIdentifier();
163
164         $query = 'SELECT * FROM ' . $conn->quoteIdentifier($tableName)
165                . ' WHERE ' . $conn->quoteIdentifier($id)
166                . ' IN (SELECT ' . $conn->quoteIdentifier($id)
167                . ' FROM ' . $conn->quoteIdentifier($this->_table->getTableName())
168                . ' WHERE keyword IS NULL)';
169
170         $query = $conn->modifyLimitQuery($query, $limit, $offset);
171
172         return $conn->fetchAll($query);
173     }
174     
175
176
177     /**
178      * batchUpdateIndex 
179      * 
180      * @param mixed $limit 
181      * @param mixed $offset 
182      * @return void
183      */
184     public function batchUpdateIndex($limit = null, $offset = null)
185     {
186         $this->initialize($this->_options['table']);
187
188         $id        = $this->_options['table']->getIdentifier();
189         $class     = $this->_options['className'];
190         $fields    = $this->_options['fields'];
191         $conn      = $this->_options['connection'];
192         try {
193
194             $conn->beginTransaction();
195
196             $rows = $this->readTableData($limit, $offset);
197
198             foreach ($rows as $row) {
199                 $ids[] = $row[$id];
200             }
201
202             $conn->exec('DELETE FROM ' 
203                         . $conn->quoteIdentifier($this->_table->getTableName())
204                         . ' WHERE ' . $conn->quoteIdentifier($id) . ' IN (' . implode(', ', $ids) . ')');
205                         
206             foreach ($rows as $row) {
207                 foreach ($fields as $field) {
208                     $data  = $row[$field];
209         
210                     $terms = $this->analyze($data);
211         
212                     foreach ($terms as $pos => $term) {
213                         $index = new $class();
214         
215                         $index->keyword = $term;
216                         $index->position = $pos;
217                         $index->field = $field;
218                         
219                         foreach ((array) $id as $identifier) {
220                             $index->$identifier = $row[$identifier];
221                         }
222     
223                         $index->save();
224                     }
225                 }
226             }
227
228             $conn->commit();
229         } catch (Doctrine_Exception $e) {
230             $conn->rollback();
231         }
232     }
233
234     /**
235      * buildDefinition 
236      * 
237      * @return void
238      */
239     public function setTableDefinition()
240     {
241      if ( ! isset($this->_options['table'])) {
242          throw new Doctrine_Plugin_Exception("Unknown option 'table'.");
243      }
244
245         $componentName = $this->_options['table']->getComponentName();
246
247         $className = $this->getOption('className');
248
249         if (class_exists($className)) {
250             return false;
251         }
252
253         $columns = array('keyword'  => array('type'    => 'string',
254                                              'length'  => 200,
255                                              'primary' => true,
256                                              ),
257                          'field'    => array('type'    => 'string',
258                                              'length'  => 50,
259                                              'primary' => true),
260                          'position' => array('type'    => 'integer',
261                                              'length'  => 8,
262                                              'primary' => true,
263                                              ));
264
265         $this->hasColumns($columns);
266     }
267 }