Search.php 8.68 KB
Newer Older
zYne's avatar
zYne committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
<?php
/*
 *  $Id$
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
 * <http://www.phpdoctrine.com>.
 */

/**
 * Doctrine_Search
 *
 * @package     Doctrine
26 27
 * @subpackage  Search
 * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
zYne's avatar
zYne committed
28 29 30 31 32
 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
 * @version     $Revision$
 * @link        www.phpdoctrine.com
 * @since       1.0
 */
zYne's avatar
zYne committed
33
class Doctrine_Search extends Doctrine_Record_Generator
zYne's avatar
zYne committed
34
{
zYne's avatar
zYne committed
35 36
    const INDEX_FILES = 0;

zYne's avatar
zYne committed
37
    const INDEX_TABLES = 1;
zYne's avatar
zYne committed
38

39
    protected $_options = array('generateFiles' => false,
zYne's avatar
zYne committed
40
                                'type'          => self::INDEX_TABLES,
zYne's avatar
zYne committed
41
                                'className'     => '%CLASS%Index',
zYne's avatar
zYne committed
42
                                'generatePath'  => false,
43
                                'table'         => null,
zYne's avatar
zYne committed
44
                                'batchUpdates'  => false,
zYne's avatar
zYne committed
45
                                'pluginTable'   => false,
zYne's avatar
zYne committed
46
                                'fields'        => array(),
47 48
                                'connection'    => null,
                                'children'      => array());
49 50 51 52 53 54
    /**
     * __construct 
     * 
     * @param array $options 
     * @return void
     */
zYne's avatar
zYne committed
55 56
    public function __construct(array $options)
    {
57
        $this->_options = Doctrine_Lib::arrayDeepMerge($this->_options, $options);
58 59 60 61
        
        if ( ! isset($this->_options['analyzer'])) {
            $this->_options['analyzer'] = new Doctrine_Search_Analyzer_Standard();
        }
zYne's avatar
zYne committed
62 63 64
        if ( ! isset($this->_options['connection'])) {
            $this->_options['connection'] = Doctrine_Manager::connection();
        }
zYne's avatar
zYne committed
65 66
    }

zYne's avatar
zYne committed
67

68 69 70
    /**
     * search 
     * 
71
     * @param string $query
72 73
     * @return Doctrine_Collection The collection of search results
     */
zYne's avatar
zYne committed
74 75
    public function search($query)
    {
76
        $q = new Doctrine_Search_Query($this->_table);
zYne's avatar
zYne committed
77
        
zYne's avatar
zYne committed
78
        $q->query($query);
zYne's avatar
zYne committed
79
        
zYne's avatar
zYne committed
80
        return $this->_options['connection']->fetchAll($q->getSql(), $q->getParams());;
zYne's avatar
zYne committed
81
    }
zYne's avatar
zYne committed
82
    
83 84 85 86 87 88
    /**
     * analyze 
     * 
     * @param string $text 
     * @return void
     */
89 90
    public function analyze($text)
    {
zYne's avatar
zYne committed
91
        return $this->_options['analyzer']->analyze($text);
92 93
    }

zYne's avatar
zYne committed
94 95 96 97 98 99 100
    /**
     * updateIndex
     * updates the index
     *
     * @param Doctrine_Record $record
     * @return integer
     */
101
    public function updateIndex(array $data)
zYne's avatar
zYne committed
102
    {
103
        $this->initialize($this->_options['table']);
104

105
        $fields = $this->getOption('fields');
zYne's avatar
zYne committed
106
        $class  = $this->getOption('className');
107 108 109 110
        $name   = $this->getOption('table')->getComponentName();
        $conn   = $this->getOption('table')->getConnection();
        $identifier = $this->_options['table']->getIdentifier();

zYne's avatar
zYne committed
111 112 113 114 115 116
        $q = Doctrine_Query::create()->delete()
                                     ->from($class);
        foreach ((array) $identifier as $id) {
            $q->addWhere($id . ' = ?', array($data[$id]));
        }
        $q->execute();
zYne's avatar
zYne committed
117

zYne's avatar
zYne committed
118
        if ($this->_options['batchUpdates'] === true) {
119
            $index = new $class(); 
zYne's avatar
zYne committed
120

121
            foreach ((array) $this->_options['table']->getIdentifier() as $id) {
122
                $index->$id = $data[$id];
123
            }
124

125
            $index->save();
zYne's avatar
zYne committed
126 127
        } else {
            foreach ($fields as $field) {
128 129 130 131

                $value = $data[$field];

                $terms = $this->analyze($value);
132

zYne's avatar
zYne committed
133 134
                foreach ($terms as $pos => $term) {
                    $index = new $class();
135

zYne's avatar
zYne committed
136 137 138
                    $index->keyword = $term;
                    $index->position = $pos;
                    $index->field = $field;
139
                    foreach ((array) $this->_options['table']->getIdentifier() as $id) {
140 141
                        $index->$id = $data[$id];
                    }
142

zYne's avatar
zYne committed
143 144
                    $index->save();
                }
zYne's avatar
zYne committed
145 146 147
            }
        }
    }
zYne's avatar
zYne committed
148

149 150 151 152 153 154 155
    /**
     * readTableData 
     * 
     * @param mixed $limit 
     * @param mixed $offset 
     * @return Doctrine_Collection The collection of results
     */
zYne's avatar
zYne committed
156 157
    public function readTableData($limit = null, $offset = null)
    {
158
        $this->initialize($this->_options['table']);
159

160 161 162
        $conn      = $this->_options['table']->getConnection();
        $tableName = $this->_options['table']->getTableName();
        $id        = $this->_options['table']->getIdentifier();
zYne's avatar
zYne committed
163 164 165 166

        $query = 'SELECT * FROM ' . $conn->quoteIdentifier($tableName)
               . ' WHERE ' . $conn->quoteIdentifier($id)
               . ' IN (SELECT ' . $conn->quoteIdentifier($id)
167
               . ' FROM ' . $conn->quoteIdentifier($this->_table->getTableName())
zYne's avatar
zYne committed
168 169 170 171 172
               . ' WHERE keyword IS NULL)';

        $query = $conn->modifyLimitQuery($query, $limit, $offset);

        return $conn->fetchAll($query);
zYne's avatar
zYne committed
173
    }
174 175
    

zYne's avatar
zYne committed
176

177 178 179 180 181 182 183
    /**
     * batchUpdateIndex 
     * 
     * @param mixed $limit 
     * @param mixed $offset 
     * @return void
     */
zYne's avatar
zYne committed
184
    public function batchUpdateIndex($limit = null, $offset = null)
zYne's avatar
zYne committed
185
    {
186
        $this->initialize($this->_options['table']);
187

188
        $id        = $this->_options['table']->getIdentifier();
zYne's avatar
zYne committed
189 190 191
        $class     = $this->_options['className'];
        $fields    = $this->_options['fields'];
        $conn      = $this->_options['connection'];
zYne's avatar
zYne committed
192
        try {
zYne's avatar
zYne committed
193

zYne's avatar
zYne committed
194
            $conn->beginTransaction();
zYne's avatar
zYne committed
195 196 197

            $rows = $this->readTableData($limit, $offset);

zYne's avatar
zYne committed
198 199 200 201 202
            foreach ($rows as $row) {
                $ids[] = $row[$id];
            }

            $conn->exec('DELETE FROM ' 
203
                        . $conn->quoteIdentifier($this->_table->getTableName())
zYne's avatar
zYne committed
204 205
                        . ' WHERE ' . $conn->quoteIdentifier($id) . ' IN (' . implode(', ', $ids) . ')');
                        
zYne's avatar
zYne committed
206 207 208 209 210 211 212 213 214 215 216 217 218
            foreach ($rows as $row) {
                foreach ($fields as $field) {
                    $data  = $row[$field];
        
                    $terms = $this->analyze($data);
        
                    foreach ($terms as $pos => $term) {
                        $index = new $class();
        
                        $index->keyword = $term;
                        $index->position = $pos;
                        $index->field = $field;
                        
zYne's avatar
zYne committed
219 220
                        foreach ((array) $id as $identifier) {
                            $index->$identifier = $row[$identifier];
zYne's avatar
zYne committed
221 222 223 224
                        }
    
                        $index->save();
                    }
zYne's avatar
zYne committed
225 226
                }
            }
zYne's avatar
zYne committed
227 228 229 230

            $conn->commit();
        } catch (Doctrine_Exception $e) {
            $conn->rollback();
zYne's avatar
zYne committed
231 232
        }
    }
zYne's avatar
zYne committed
233

234 235 236 237 238
    /**
     * buildDefinition 
     * 
     * @return void
     */
239
    public function setTableDefinition()
zYne's avatar
zYne committed
240
    {
241 242
    	if ( ! isset($this->_options['table'])) {
    	    throw new Doctrine_Plugin_Exception("Unknown option 'table'.");
243 244
    	}

245
        $componentName = $this->_options['table']->getComponentName();
246

zYne's avatar
zYne committed
247
        $className = $this->getOption('className');
zYne's avatar
zYne committed
248

zYne's avatar
zYne committed
249 250 251
        if (class_exists($className)) {
            return false;
        }
zYne's avatar
zYne committed
252

zYne's avatar
zYne committed
253 254
        $columns = array('keyword'  => array('type'    => 'string',
                                             'length'  => 200,
zYne's avatar
zYne committed
255 256
                                             'primary' => true,
                                             ),
zYne's avatar
zYne committed
257 258
                         'field'    => array('type'    => 'string',
                                             'length'  => 50,
zYne's avatar
zYne committed
259
                                             'primary' => true),
zYne's avatar
zYne committed
260
                         'position' => array('type'    => 'integer',
zYne's avatar
zYne committed
261 262 263
                                             'length'  => 8,
                                             'primary' => true,
                                             ));
zYne's avatar
zYne committed
264

265
        $this->hasColumns($columns);
zYne's avatar
zYne committed
266
    }
zYne's avatar
zYne committed
267
}