PageRenderTime 37ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/servers/urlcatcher.org/htdocs/lib/vendor/symfony/lib/plugins/sfDoctrinePlugin/lib/vendor/doctrine/Doctrine/Search.php

https://github.com/bigcalm/urlcatcher
PHP | 307 lines | 182 code | 49 blank | 76 comment | 11 complexity | ad6b0242a58233519d0b40dbfeb8e5b9 MD5 | raw file
  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. /**
  22. * Doctrine_Search
  23. *
  24. * @package Doctrine
  25. * @subpackage Search
  26. * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
  27. * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
  28. * @version $Revision$
  29. * @link www.phpdoctrine.org
  30. * @since 1.0
  31. */
  32. class Doctrine_Search extends Doctrine_Record_Generator
  33. {
  34. const INDEX_FILES = 0;
  35. const INDEX_TABLES = 1;
  36. protected $_options = array('generateFiles' => false,
  37. 'analyzer' => 'Doctrine_Search_Analyzer_Standard',
  38. 'analyzer_options' => array(),
  39. 'type' => self::INDEX_TABLES,
  40. 'className' => '%CLASS%Index',
  41. 'generatePath' => false,
  42. 'table' => null,
  43. 'batchUpdates' => false,
  44. 'pluginTable' => false,
  45. 'fields' => array(),
  46. 'connection' => null,
  47. 'children' => array(),
  48. 'cascadeDelete' => true,
  49. 'appLevelDelete' => false);
  50. /**
  51. * __construct
  52. *
  53. * @param array $options
  54. * @return void
  55. */
  56. public function __construct(array $options)
  57. {
  58. $this->_options = Doctrine_Lib::arrayDeepMerge($this->_options, $options);
  59. if ( ! isset($this->_options['analyzer'])) {
  60. $this->_options['analyzer'] = 'Doctrine_Search_Analyzer_Standard';
  61. }
  62. if ( ! isset($this->_options['analyzer_options'])) {
  63. $this->_options['analyzer_options'] = array();
  64. }
  65. $this->_options['analyzer'] = new $this->_options['analyzer']($this->_options['analyzer_options']);
  66. if ( ! isset($this->_options['connection'])) {
  67. $this->_options['connection'] = Doctrine_Manager::connection();
  68. }
  69. }
  70. /**
  71. * Searchable keyword search
  72. *
  73. * @param string $string Keyword string to search for
  74. * @param Doctrine_Query $query Query object to alter. Adds where condition to limit the results using the search index
  75. * @return array ids and relevancy
  76. */
  77. public function search($string, $query = null)
  78. {
  79. $q = new Doctrine_Search_Query($this->_table);
  80. if ($query instanceof Doctrine_Query) {
  81. $q->query($string, false);
  82. $newQuery = $query->copy();
  83. $query->getSqlQuery();
  84. $key = (array) $this->getOption('table')->getIdentifier();
  85. $newQuery->addWhere($query->getRootAlias() . '.'.current($key).' IN (SQL:' . $q->getSqlQuery() . ')', $q->getParams());
  86. return $newQuery;
  87. } else {
  88. $q->query($string);
  89. return $this->_options['connection']->fetchAll($q->getSqlQuery(), $q->getParams());
  90. }
  91. }
  92. /**
  93. * analyze a text in the encoding format
  94. *
  95. * @param string $text
  96. * @param string $encoding
  97. * @return void
  98. */
  99. public function analyze($text, $encoding = null)
  100. {
  101. return $this->_options['analyzer']->analyze($text, $encoding);
  102. }
  103. /**
  104. * updateIndex
  105. * updates the index
  106. *
  107. * @param Doctrine_Record $record
  108. * @return integer
  109. */
  110. public function updateIndex(array $data, $encoding = null)
  111. {
  112. $this->initialize($this->_options['table']);
  113. $fields = $this->getOption('fields');
  114. $class = $this->getOption('className');
  115. $name = $this->getOption('table')->getComponentName();
  116. $conn = $this->getOption('table')->getConnection();
  117. $identifier = $this->_options['table']->getIdentifier();
  118. $q = Doctrine_Query::create()->delete()
  119. ->from($class);
  120. foreach ((array) $identifier as $id) {
  121. $q->addWhere($id . ' = ?', array($data[$id]));
  122. }
  123. $q->execute();
  124. if ($this->_options['batchUpdates'] === true) {
  125. $index = new $class();
  126. foreach ((array) $this->_options['table']->getIdentifier() as $id) {
  127. $index->$id = $data[$id];
  128. }
  129. $index->save();
  130. } else {
  131. foreach ($fields as $field) {
  132. $value = isset($data[$field]) ? $data[$field] : null;
  133. $terms = $this->analyze($value, $encoding);
  134. foreach ($terms as $pos => $term) {
  135. $index = new $class();
  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. $index->save();
  143. $index->free(true);
  144. }
  145. }
  146. }
  147. }
  148. /**
  149. * readTableData
  150. *
  151. * @param mixed $limit
  152. * @param mixed $offset
  153. * @return Doctrine_Collection The collection of results
  154. */
  155. public function readTableData($limit = null, $offset = null)
  156. {
  157. $this->initialize($this->_options['table']);
  158. $conn = $this->_options['table']->getConnection();
  159. $tableName = $this->_options['table']->getTableName();
  160. $id = current($this->_options['table']->getIdentifierColumnNames());
  161. $tableId = current($this->_table->getIdentifierColumnNames());
  162. $query = 'SELECT * FROM ' . $conn->quoteIdentifier($tableName)
  163. . ' WHERE ' . $conn->quoteIdentifier($id)
  164. . ' IN (SELECT ' . $conn->quoteIdentifier($tableId)
  165. . ' FROM ' . $conn->quoteIdentifier($this->_table->getTableName())
  166. . ' WHERE keyword = \'\') OR ' . $conn->quoteIdentifier($id)
  167. . ' NOT IN (SELECT ' . $conn->quoteIdentifier($tableId)
  168. . ' FROM ' . $conn->quoteIdentifier($this->_table->getTableName()) . ')';
  169. $query = $conn->modifyLimitQuery($query, $limit, $offset);
  170. return $conn->fetchAll($query);
  171. }
  172. /**
  173. * batchUpdateIndex
  174. *
  175. * @param mixed $limit
  176. * @param mixed $offset
  177. * @return void
  178. */
  179. public function batchUpdateIndex($limit = null, $offset = null, $encoding = null)
  180. {
  181. $table = $this->_options['table'];
  182. $this->initialize($table);
  183. $id = current($table->getIdentifierColumnNames());
  184. $class = $this->_options['className'];
  185. $fields = $this->_options['fields'];
  186. $conn = $this->_options['connection'];
  187. for ($i = 0; $i < count($fields); $i++) {
  188. $fields[$i] = $table->getColumnName($fields[$i], $fields[$i]);
  189. }
  190. $rows = $this->readTableData($limit, $offset);
  191. $ids = array();
  192. foreach ($rows as $row) {
  193. $ids[] = $row[$id];
  194. }
  195. if (count($ids) > 0)
  196. {
  197. $placeholders = str_repeat('?, ', count($ids));
  198. $placeholders = substr($placeholders, 0, strlen($placeholders) - 2);
  199. $sql = 'DELETE FROM '
  200. . $conn->quoteIdentifier($this->_table->getTableName())
  201. . ' WHERE ' . $conn->quoteIdentifier($table->getIdentifier()) . ' IN (' . substr($placeholders, 0) . ')';
  202. $conn->exec($sql, $ids);
  203. }
  204. foreach ($rows as $row) {
  205. $conn->beginTransaction();
  206. try {
  207. foreach ($fields as $field) {
  208. $data = $row[$field];
  209. $terms = $this->analyze($data, $encoding);
  210. foreach ($terms as $pos => $term) {
  211. $index = new $class();
  212. $index->keyword = $term;
  213. $index->position = $pos;
  214. $index->field = $field;
  215. foreach ((array) $table->getIdentifier() as $identifier) {
  216. $index->$identifier = $row[$table->getColumnName($identifier, $identifier)];
  217. }
  218. $index->save();
  219. $index->free(true);
  220. }
  221. }
  222. $conn->commit();
  223. } catch (Doctrine_Exception $e) {
  224. $conn->rollback();
  225. throw $e;
  226. }
  227. }
  228. }
  229. /**
  230. * buildDefinition
  231. *
  232. * @return void
  233. */
  234. public function setTableDefinition()
  235. {
  236. if ( ! isset($this->_options['table'])) {
  237. throw new Doctrine_Record_Exception("Unknown option 'table'.");
  238. }
  239. $componentName = $this->_options['table']->getComponentName();
  240. $className = $this->getOption('className');
  241. $autoLoad = (bool) ($this->_options['generateFiles']);
  242. if (class_exists($className, $autoLoad)) {
  243. return false;
  244. }
  245. $columns = array('keyword' => array('type' => 'string',
  246. 'length' => 200,
  247. 'primary' => true,
  248. ),
  249. 'field' => array('type' => 'string',
  250. 'length' => 50,
  251. 'primary' => true),
  252. 'position' => array('type' => 'integer',
  253. 'length' => 8,
  254. 'primary' => true,
  255. ));
  256. $this->hasColumns($columns);
  257. }
  258. }