PageRenderTime 55ms CodeModel.GetById 32ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/doctrine/Doctrine/Search/Query.php

https://github.com/digitarald/redracer
PHP | 245 lines | 165 code | 44 blank | 36 comment | 36 complexity | d38158774187987bc800d32e78c5b966 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1, GPL-2.0, BSD-3-Clause, Apache-2.0
  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.org>.
  20. */
  21. /**
  22. * Doctrine_Search_Query
  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_Query
  33. {
  34. /**
  35. * @var Doctrine_Table $_table the index table
  36. */
  37. protected $_table = array();
  38. protected $_sql = '';
  39. protected $_params = array();
  40. protected $_words = array();
  41. protected $_tokenizer;
  42. protected $_condition;
  43. /**
  44. * @param Doctrine_Table $_table the index table
  45. */
  46. public function __construct($table)
  47. {
  48. if (is_string($table)) {
  49. $table = Doctrine::getTable($table);
  50. } else {
  51. if ( ! $table instanceof Doctrine_Table) {
  52. throw new Doctrine_Search_Exception('Invalid argument type. Expected instance of Doctrine_Table.');
  53. }
  54. }
  55. $this->_tokenizer = new Doctrine_Query_Tokenizer();
  56. $this->_table = $table;
  57. $foreignId = current(array_diff($this->_table->getColumnNames(), array('keyword', 'field', 'position')));
  58. $this->_condition = $foreignId . ' %s (SELECT ' . $foreignId . ' FROM ' . $this->_table->getTableName() . ' WHERE ';
  59. }
  60. public function query($text, $includeRelevance = true)
  61. {
  62. $text = trim($text);
  63. $foreignId = current(array_diff($this->_table->getColumnNames(), array('keyword', 'field', 'position')));
  64. $weighted = false;
  65. if (strpos($text, '^') === false) {
  66. if ($includeRelevance) {
  67. $select = 'SELECT COUNT(keyword) AS relevance, ' . $foreignId;
  68. } else {
  69. $select = 'SELECT ' . $foreignId;
  70. }
  71. } else {
  72. if ($includeRelevance) {
  73. $select = 'SELECT SUM(sub_relevance) AS relevance, ' . $foreignId;
  74. } else {
  75. $select = 'SELECT ' . $foreignId;
  76. }
  77. }
  78. $from = 'FROM ' . $this->_table->getTableName();
  79. $where = 'WHERE ';
  80. $where .= $this->parseClause($text);
  81. $groupby = 'GROUP BY ' . $foreignId;
  82. if ($includeRelevance) {
  83. $orderBy = 'ORDER BY relevance DESC';
  84. } else {
  85. $orderBy = null;
  86. }
  87. $this->_sql = $select . ' ' . $from . ' ' . $where . ' ' . $groupby;
  88. if (isset($orderBy) && $orderBy !== null) {
  89. $this->_sql .= ' ' . $orderBy;
  90. }
  91. }
  92. public function parseClause($originalClause, $recursive = false)
  93. {
  94. $clause = $this->_tokenizer->bracketTrim($originalClause);
  95. $brackets = false;
  96. if ($clause !== $originalClause) {
  97. $brackets = true;
  98. }
  99. $foreignId = current(array_diff($this->_table->getColumnNames(), array('keyword', 'field', 'position')));
  100. $terms = $this->_tokenizer->sqlExplode($clause, ' OR ', '(', ')');
  101. $ret = array();
  102. if (count($terms) > 1) {
  103. $leavesOnly = true;
  104. foreach ($terms as $k => $term) {
  105. if ($this->isExpression($term)) {
  106. $ret[$k] = $this->parseClause($term, true);
  107. $leavesOnly = false;
  108. } else {
  109. $ret[$k] = $this->parseTerm($term);
  110. }
  111. }
  112. $return = implode(' OR ', $ret);
  113. if ($leavesOnly && $recursive) {
  114. $return = sprintf($this->_condition, 'IN') . $return . ')';
  115. $brackets = false;
  116. }
  117. } else {
  118. $terms = $this->_tokenizer->sqlExplode($clause, ' ', '(', ')');
  119. if (count($terms) === 1 && ! $recursive) {
  120. $return = $this->parseTerm($clause);
  121. } else {
  122. foreach ($terms as $k => $term) {
  123. $term = trim($term);
  124. if ($term === 'AND') {
  125. continue;
  126. }
  127. if (substr($term, 0, 1) === '-') {
  128. $operator = 'NOT IN';
  129. $term = substr($term, 1);
  130. } else {
  131. $operator = 'IN';
  132. }
  133. if ($this->isExpression($term)) {
  134. $ret[$k] = $this->parseClause($term, true);
  135. } else {
  136. $ret[$k] = sprintf($this->_condition, $operator) . $this->parseTerm($term) . ')';
  137. }
  138. }
  139. $return = implode(' AND ', $ret);
  140. }
  141. }
  142. if ($brackets) {
  143. return '(' . $return . ')';
  144. } else {
  145. return $return;
  146. }
  147. }
  148. public function isExpression($term)
  149. {
  150. if (strpos($term, '(') !== false) {
  151. return true;
  152. } else {
  153. $terms = $this->_tokenizer->quoteExplode($term);
  154. return (count($terms) > 1);
  155. }
  156. }
  157. public function parseTerm($term)
  158. {
  159. $negation = false;
  160. if (strpos($term, "'") === false) {
  161. $where = $this->parseWord($term);
  162. } else {
  163. $term = trim($term, "' ");
  164. $terms = $this->_tokenizer->quoteExplode($term);
  165. $where = $this->parseWord($terms[0]);
  166. foreach ($terms as $k => $word) {
  167. if ($k === 0) {
  168. continue;
  169. }
  170. $where .= ' AND (position + ' . $k . ') = (SELECT position FROM ' . $this->_table->getTableName() . ' WHERE ' . $this->parseWord($word) . ')';
  171. }
  172. }
  173. return $where;
  174. }
  175. public function parseWord($word)
  176. {
  177. $this->_words[] = str_replace('*', '', $word);
  178. if (strpos($word, '?') !== false ||
  179. strpos($word, '*') !== false) {
  180. $word = str_replace('*', '%', $word);
  181. $where = 'keyword LIKE ?';
  182. $params = array($word);
  183. } else {
  184. $where = 'keyword = ?';
  185. }
  186. $this->_params[] = $word;
  187. return $where;
  188. }
  189. public function getWords()
  190. {
  191. return $this->_words;
  192. }
  193. public function getParams()
  194. {
  195. return $this->_params;
  196. }
  197. public function getSql()
  198. {
  199. return $this->_sql;
  200. }
  201. }