QueryParser.php 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Search_Lucene
  17. * @subpackage Search
  18. * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. */
  21. /** Zend_Search_Lucene_Index_Term */
  22. require_once 'Zend/Search/Lucene/Index/Term.php';
  23. /** Zend_Search_Lucene_Search_Query_Term */
  24. require_once 'Zend/Search/Lucene/Search/Query/Term.php';
  25. /** Zend_Search_Lucene_Search_Query_MultiTerm */
  26. require_once 'Zend/Search/Lucene/Search/Query/MultiTerm.php';
  27. /** Zend_Search_Lucene_Search_Query_Boolean */
  28. require_once 'Zend/Search/Lucene/Search/Query/Boolean.php';
  29. /** Zend_Search_Lucene_Search_Query_Preprocessing_Phrase */
  30. require_once 'Zend/Search/Lucene/Search/Query/Preprocessing/Phrase.php';
  31. /** Zend_Search_Lucene_Search_Query_Preprocessing_Term */
  32. require_once 'Zend/Search/Lucene/Search/Query/Preprocessing/Term.php';
  33. /** Zend_Search_Lucene_Search_Query_Preprocessing_Fuzzy */
  34. require_once 'Zend/Search/Lucene/Search/Query/Preprocessing/Fuzzy.php';
  35. /** Zend_Search_Lucene_Search_Query_Wildcard */
  36. require_once 'Zend/Search/Lucene/Search/Query/Wildcard.php';
  37. /** Zend_Search_Lucene_Search_Query_Range */
  38. require_once 'Zend/Search/Lucene/Search/Query/Range.php';
  39. /** Zend_Search_Lucene_Search_Query_Fuzzy */
  40. require_once 'Zend/Search/Lucene/Search/Query/Fuzzy.php';
  41. /** Zend_Search_Lucene_Search_Query_Empty */
  42. require_once 'Zend/Search/Lucene/Search/Query/Empty.php';
  43. /** Zend_Search_Lucene_Search_Query_Insignificant */
  44. require_once 'Zend/Search/Lucene/Search/Query/Insignificant.php';
  45. /** Zend_Search_Lucene_Search_QueryLexer */
  46. require_once 'Zend/Search/Lucene/Search/QueryLexer.php';
  47. /** Zend_Search_Lucene_Search_QueryParserContext */
  48. require_once 'Zend/Search/Lucene/Search/QueryParserContext.php';
  49. /** Zend_Search_Lucene_FSM */
  50. require_once 'Zend/Search/Lucene/FSM.php';
  51. /**
  52. * @category Zend
  53. * @package Zend_Search_Lucene
  54. * @subpackage Search
  55. * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
  56. * @license http://framework.zend.com/license/new-bsd New BSD License
  57. */
  58. class Zend_Search_Lucene_Search_QueryParser extends Zend_Search_Lucene_FSM
  59. {
  60. /**
  61. * Parser instance
  62. *
  63. * @var Zend_Search_Lucene_Search_QueryParser
  64. */
  65. private static $_instance = null;
  66. /**
  67. * Query lexer
  68. *
  69. * @var Zend_Search_Lucene_Search_QueryLexer
  70. */
  71. private $_lexer;
  72. /**
  73. * Tokens list
  74. * Array of Zend_Search_Lucene_Search_QueryToken objects
  75. *
  76. * @var array
  77. */
  78. private $_tokens;
  79. /**
  80. * Current token
  81. *
  82. * @var integer|string
  83. */
  84. private $_currentToken;
  85. /**
  86. * Last token
  87. *
  88. * It can be processed within FSM states, but this addirional state simplifies FSM
  89. *
  90. * @var Zend_Search_Lucene_Search_QueryToken
  91. */
  92. private $_lastToken = null;
  93. /**
  94. * Range query first term
  95. *
  96. * @var string
  97. */
  98. private $_rqFirstTerm = null;
  99. /**
  100. * Current query parser context
  101. *
  102. * @var Zend_Search_Lucene_Search_QueryParserContext
  103. */
  104. private $_context;
  105. /**
  106. * Context stack
  107. *
  108. * @var array
  109. */
  110. private $_contextStack;
  111. /**
  112. * Query string encoding
  113. *
  114. * @var string
  115. */
  116. private $_encoding;
  117. /**
  118. * Query string default encoding
  119. *
  120. * @var string
  121. */
  122. private $_defaultEncoding = '';
  123. /**
  124. * Defines query parsing mode.
  125. *
  126. * If this option is turned on, then query parser suppress query parser exceptions
  127. * and constructs multi-term query using all words from a query.
  128. *
  129. * That helps to avoid exceptions caused by queries, which don't conform to query language,
  130. * but limits possibilities to check, that query entered by user has some inconsistencies.
  131. *
  132. *
  133. * Default is true.
  134. *
  135. * Use {@link Zend_Search_Lucene::suppressQueryParsingExceptions()},
  136. * {@link Zend_Search_Lucene::dontSuppressQueryParsingExceptions()} and
  137. * {@link Zend_Search_Lucene::checkQueryParsingExceptionsSuppressMode()} to operate
  138. * with this setting.
  139. *
  140. * @var boolean
  141. */
  142. private $_suppressQueryParsingExceptions = true;
  143. /**
  144. * Boolean operators constants
  145. */
  146. const B_OR = 0;
  147. const B_AND = 1;
  148. /**
  149. * Default boolean queries operator
  150. *
  151. * @var integer
  152. */
  153. private $_defaultOperator = self::B_OR;
  154. /** Query parser State Machine states */
  155. const ST_COMMON_QUERY_ELEMENT = 0; // Terms, phrases, operators
  156. const ST_CLOSEDINT_RQ_START = 1; // Range query start (closed interval) - '['
  157. const ST_CLOSEDINT_RQ_FIRST_TERM = 2; // First term in '[term1 to term2]' construction
  158. const ST_CLOSEDINT_RQ_TO_TERM = 3; // 'TO' lexeme in '[term1 to term2]' construction
  159. const ST_CLOSEDINT_RQ_LAST_TERM = 4; // Second term in '[term1 to term2]' construction
  160. const ST_CLOSEDINT_RQ_END = 5; // Range query end (closed interval) - ']'
  161. const ST_OPENEDINT_RQ_START = 6; // Range query start (opened interval) - '{'
  162. const ST_OPENEDINT_RQ_FIRST_TERM = 7; // First term in '{term1 to term2}' construction
  163. const ST_OPENEDINT_RQ_TO_TERM = 8; // 'TO' lexeme in '{term1 to term2}' construction
  164. const ST_OPENEDINT_RQ_LAST_TERM = 9; // Second term in '{term1 to term2}' construction
  165. const ST_OPENEDINT_RQ_END = 10; // Range query end (opened interval) - '}'
  166. /**
  167. * Parser constructor
  168. */
  169. public function __construct()
  170. {
  171. parent::__construct(array(self::ST_COMMON_QUERY_ELEMENT,
  172. self::ST_CLOSEDINT_RQ_START,
  173. self::ST_CLOSEDINT_RQ_FIRST_TERM,
  174. self::ST_CLOSEDINT_RQ_TO_TERM,
  175. self::ST_CLOSEDINT_RQ_LAST_TERM,
  176. self::ST_CLOSEDINT_RQ_END,
  177. self::ST_OPENEDINT_RQ_START,
  178. self::ST_OPENEDINT_RQ_FIRST_TERM,
  179. self::ST_OPENEDINT_RQ_TO_TERM,
  180. self::ST_OPENEDINT_RQ_LAST_TERM,
  181. self::ST_OPENEDINT_RQ_END
  182. ),
  183. Zend_Search_Lucene_Search_QueryToken::getTypes());
  184. $this->addRules(
  185. array(array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_WORD, self::ST_COMMON_QUERY_ELEMENT),
  186. array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_PHRASE, self::ST_COMMON_QUERY_ELEMENT),
  187. array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_FIELD, self::ST_COMMON_QUERY_ELEMENT),
  188. array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_REQUIRED, self::ST_COMMON_QUERY_ELEMENT),
  189. array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_PROHIBITED, self::ST_COMMON_QUERY_ELEMENT),
  190. array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_FUZZY_PROX_MARK, self::ST_COMMON_QUERY_ELEMENT),
  191. array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_BOOSTING_MARK, self::ST_COMMON_QUERY_ELEMENT),
  192. array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_RANGE_INCL_START, self::ST_CLOSEDINT_RQ_START),
  193. array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_RANGE_EXCL_START, self::ST_OPENEDINT_RQ_START),
  194. array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_SUBQUERY_START, self::ST_COMMON_QUERY_ELEMENT),
  195. array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_SUBQUERY_END, self::ST_COMMON_QUERY_ELEMENT),
  196. array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_AND_LEXEME, self::ST_COMMON_QUERY_ELEMENT),
  197. array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_OR_LEXEME, self::ST_COMMON_QUERY_ELEMENT),
  198. array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_NOT_LEXEME, self::ST_COMMON_QUERY_ELEMENT),
  199. array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_NUMBER, self::ST_COMMON_QUERY_ELEMENT)
  200. ));
  201. $this->addRules(
  202. array(array(self::ST_CLOSEDINT_RQ_START, Zend_Search_Lucene_Search_QueryToken::TT_WORD, self::ST_CLOSEDINT_RQ_FIRST_TERM),
  203. array(self::ST_CLOSEDINT_RQ_FIRST_TERM, Zend_Search_Lucene_Search_QueryToken::TT_TO_LEXEME, self::ST_CLOSEDINT_RQ_TO_TERM),
  204. array(self::ST_CLOSEDINT_RQ_TO_TERM, Zend_Search_Lucene_Search_QueryToken::TT_WORD, self::ST_CLOSEDINT_RQ_LAST_TERM),
  205. array(self::ST_CLOSEDINT_RQ_LAST_TERM, Zend_Search_Lucene_Search_QueryToken::TT_RANGE_INCL_END, self::ST_COMMON_QUERY_ELEMENT)
  206. ));
  207. $this->addRules(
  208. array(array(self::ST_OPENEDINT_RQ_START, Zend_Search_Lucene_Search_QueryToken::TT_WORD, self::ST_OPENEDINT_RQ_FIRST_TERM),
  209. array(self::ST_OPENEDINT_RQ_FIRST_TERM, Zend_Search_Lucene_Search_QueryToken::TT_TO_LEXEME, self::ST_OPENEDINT_RQ_TO_TERM),
  210. array(self::ST_OPENEDINT_RQ_TO_TERM, Zend_Search_Lucene_Search_QueryToken::TT_WORD, self::ST_OPENEDINT_RQ_LAST_TERM),
  211. array(self::ST_OPENEDINT_RQ_LAST_TERM, Zend_Search_Lucene_Search_QueryToken::TT_RANGE_EXCL_END, self::ST_COMMON_QUERY_ELEMENT)
  212. ));
  213. $addTermEntryAction = new Zend_Search_Lucene_FSMAction($this, 'addTermEntry');
  214. $addPhraseEntryAction = new Zend_Search_Lucene_FSMAction($this, 'addPhraseEntry');
  215. $setFieldAction = new Zend_Search_Lucene_FSMAction($this, 'setField');
  216. $setSignAction = new Zend_Search_Lucene_FSMAction($this, 'setSign');
  217. $setFuzzyProxAction = new Zend_Search_Lucene_FSMAction($this, 'processFuzzyProximityModifier');
  218. $processModifierParameterAction = new Zend_Search_Lucene_FSMAction($this, 'processModifierParameter');
  219. $subqueryStartAction = new Zend_Search_Lucene_FSMAction($this, 'subqueryStart');
  220. $subqueryEndAction = new Zend_Search_Lucene_FSMAction($this, 'subqueryEnd');
  221. $logicalOperatorAction = new Zend_Search_Lucene_FSMAction($this, 'logicalOperator');
  222. $openedRQFirstTermAction = new Zend_Search_Lucene_FSMAction($this, 'openedRQFirstTerm');
  223. $openedRQLastTermAction = new Zend_Search_Lucene_FSMAction($this, 'openedRQLastTerm');
  224. $closedRQFirstTermAction = new Zend_Search_Lucene_FSMAction($this, 'closedRQFirstTerm');
  225. $closedRQLastTermAction = new Zend_Search_Lucene_FSMAction($this, 'closedRQLastTerm');
  226. $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_WORD, $addTermEntryAction);
  227. $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_PHRASE, $addPhraseEntryAction);
  228. $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_FIELD, $setFieldAction);
  229. $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_REQUIRED, $setSignAction);
  230. $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_PROHIBITED, $setSignAction);
  231. $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_FUZZY_PROX_MARK, $setFuzzyProxAction);
  232. $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_NUMBER, $processModifierParameterAction);
  233. $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_SUBQUERY_START, $subqueryStartAction);
  234. $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_SUBQUERY_END, $subqueryEndAction);
  235. $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_AND_LEXEME, $logicalOperatorAction);
  236. $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_OR_LEXEME, $logicalOperatorAction);
  237. $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_NOT_LEXEME, $logicalOperatorAction);
  238. $this->addEntryAction(self::ST_OPENEDINT_RQ_FIRST_TERM, $openedRQFirstTermAction);
  239. $this->addEntryAction(self::ST_OPENEDINT_RQ_LAST_TERM, $openedRQLastTermAction);
  240. $this->addEntryAction(self::ST_CLOSEDINT_RQ_FIRST_TERM, $closedRQFirstTermAction);
  241. $this->addEntryAction(self::ST_CLOSEDINT_RQ_LAST_TERM, $closedRQLastTermAction);
  242. $this->_lexer = new Zend_Search_Lucene_Search_QueryLexer();
  243. }
  244. /**
  245. * Get query parser instance
  246. *
  247. * @return Zend_Search_Lucene_Search_QueryParser
  248. */
  249. private static function _getInstance()
  250. {
  251. if (self::$_instance === null) {
  252. self::$_instance = new self();
  253. }
  254. return self::$_instance;
  255. }
  256. /**
  257. * Set query string default encoding
  258. *
  259. * @param string $encoding
  260. */
  261. public static function setDefaultEncoding($encoding)
  262. {
  263. self::_getInstance()->_defaultEncoding = $encoding;
  264. }
  265. /**
  266. * Get query string default encoding
  267. *
  268. * @return string
  269. */
  270. public static function getDefaultEncoding()
  271. {
  272. return self::_getInstance()->_defaultEncoding;
  273. }
  274. /**
  275. * Set default boolean operator
  276. *
  277. * @param integer $operator
  278. */
  279. public static function setDefaultOperator($operator)
  280. {
  281. self::_getInstance()->_defaultOperator = $operator;
  282. }
  283. /**
  284. * Get default boolean operator
  285. *
  286. * @return integer
  287. */
  288. public static function getDefaultOperator()
  289. {
  290. return self::_getInstance()->_defaultOperator;
  291. }
  292. /**
  293. * Turn on 'suppress query parser exceptions' mode.
  294. */
  295. public static function suppressQueryParsingExceptions()
  296. {
  297. self::_getInstance()->_suppressQueryParsingExceptions = true;
  298. }
  299. /**
  300. * Turn off 'suppress query parser exceptions' mode.
  301. */
  302. public static function dontSuppressQueryParsingExceptions()
  303. {
  304. self::_getInstance()->_suppressQueryParsingExceptions = false;
  305. }
  306. /**
  307. * Check 'suppress query parser exceptions' mode.
  308. * @return boolean
  309. */
  310. public static function queryParsingExceptionsSuppressed()
  311. {
  312. return self::_getInstance()->_suppressQueryParsingExceptions;
  313. }
  314. /**
  315. * Parses a query string
  316. *
  317. * @param string $strQuery
  318. * @param string $encoding
  319. * @return Zend_Search_Lucene_Search_Query
  320. * @throws Zend_Search_Lucene_Search_QueryParserException
  321. */
  322. public static function parse($strQuery, $encoding = null)
  323. {
  324. self::_getInstance();
  325. // Reset FSM if previous parse operation didn't return it into a correct state
  326. self::$_instance->reset();
  327. require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
  328. try {
  329. self::$_instance->_encoding = ($encoding !== null) ? $encoding : self::$_instance->_defaultEncoding;
  330. self::$_instance->_lastToken = null;
  331. self::$_instance->_context = new Zend_Search_Lucene_Search_QueryParserContext(self::$_instance->_encoding);
  332. self::$_instance->_contextStack = array();
  333. self::$_instance->_tokens = self::$_instance->_lexer->tokenize($strQuery, self::$_instance->_encoding);
  334. // Empty query
  335. if (count(self::$_instance->_tokens) == 0) {
  336. return new Zend_Search_Lucene_Search_Query_Insignificant();
  337. }
  338. foreach (self::$_instance->_tokens as $token) {
  339. try {
  340. self::$_instance->_currentToken = $token;
  341. self::$_instance->process($token->type);
  342. self::$_instance->_lastToken = $token;
  343. } catch (Exception $e) {
  344. if (strpos($e->getMessage(), 'There is no any rule for') !== false) {
  345. throw new Zend_Search_Lucene_Search_QueryParserException( 'Syntax error at char position ' . $token->position . '.' );
  346. }
  347. throw $e;
  348. }
  349. }
  350. if (count(self::$_instance->_contextStack) != 0) {
  351. throw new Zend_Search_Lucene_Search_QueryParserException('Syntax Error: mismatched parentheses, every opening must have closing.' );
  352. }
  353. return self::$_instance->_context->getQuery();
  354. } catch (Zend_Search_Lucene_Search_QueryParserException $e) {
  355. if (self::$_instance->_suppressQueryParsingExceptions) {
  356. $queryTokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($strQuery, self::$_instance->_encoding);
  357. $query = new Zend_Search_Lucene_Search_Query_MultiTerm();
  358. $termsSign = (self::$_instance->_defaultOperator == self::B_AND) ? true /* required term */ :
  359. null /* optional term */;
  360. foreach ($queryTokens as $token) {
  361. $query->addTerm(new Zend_Search_Lucene_Index_Term($token->getTermText()), $termsSign);
  362. }
  363. return $query;
  364. } else {
  365. throw $e;
  366. }
  367. }
  368. }
  369. /*********************************************************************
  370. * Actions implementation
  371. *
  372. * Actions affect on recognized lexemes list
  373. *********************************************************************/
  374. /**
  375. * Add term to a query
  376. */
  377. public function addTermEntry()
  378. {
  379. $entry = new Zend_Search_Lucene_Search_QueryEntry_Term($this->_currentToken->text, $this->_context->getField());
  380. $this->_context->addEntry($entry);
  381. }
  382. /**
  383. * Add phrase to a query
  384. */
  385. public function addPhraseEntry()
  386. {
  387. $entry = new Zend_Search_Lucene_Search_QueryEntry_Phrase($this->_currentToken->text, $this->_context->getField());
  388. $this->_context->addEntry($entry);
  389. }
  390. /**
  391. * Set entry field
  392. */
  393. public function setField()
  394. {
  395. $this->_context->setNextEntryField($this->_currentToken->text);
  396. }
  397. /**
  398. * Set entry sign
  399. */
  400. public function setSign()
  401. {
  402. $this->_context->setNextEntrySign($this->_currentToken->type);
  403. }
  404. /**
  405. * Process fuzzy search/proximity modifier - '~'
  406. */
  407. public function processFuzzyProximityModifier()
  408. {
  409. $this->_context->processFuzzyProximityModifier();
  410. }
  411. /**
  412. * Process modifier parameter
  413. *
  414. * @throws Zend_Search_Lucene_Exception
  415. */
  416. public function processModifierParameter()
  417. {
  418. if ($this->_lastToken === null) {
  419. require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
  420. throw new Zend_Search_Lucene_Search_QueryParserException('Lexeme modifier parameter must follow lexeme modifier. Char position 0.' );
  421. }
  422. switch ($this->_lastToken->type) {
  423. case Zend_Search_Lucene_Search_QueryToken::TT_FUZZY_PROX_MARK:
  424. $this->_context->processFuzzyProximityModifier($this->_currentToken->text);
  425. break;
  426. case Zend_Search_Lucene_Search_QueryToken::TT_BOOSTING_MARK:
  427. $this->_context->boost($this->_currentToken->text);
  428. break;
  429. default:
  430. // It's not a user input exception
  431. require_once 'Zend/Search/Lucene/Exception.php';
  432. throw new Zend_Search_Lucene_Exception('Lexeme modifier parameter must follow lexeme modifier. Char position 0.' );
  433. }
  434. }
  435. /**
  436. * Start subquery
  437. */
  438. public function subqueryStart()
  439. {
  440. $this->_contextStack[] = $this->_context;
  441. $this->_context = new Zend_Search_Lucene_Search_QueryParserContext($this->_encoding, $this->_context->getField());
  442. }
  443. /**
  444. * End subquery
  445. */
  446. public function subqueryEnd()
  447. {
  448. if (count($this->_contextStack) == 0) {
  449. require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
  450. throw new Zend_Search_Lucene_Search_QueryParserException('Syntax Error: mismatched parentheses, every opening must have closing. Char position ' . $this->_currentToken->position . '.' );
  451. }
  452. $query = $this->_context->getQuery();
  453. $this->_context = array_pop($this->_contextStack);
  454. $this->_context->addEntry(new Zend_Search_Lucene_Search_QueryEntry_Subquery($query));
  455. }
  456. /**
  457. * Process logical operator
  458. */
  459. public function logicalOperator()
  460. {
  461. $this->_context->addLogicalOperator($this->_currentToken->type);
  462. }
  463. /**
  464. * Process first range query term (opened interval)
  465. */
  466. public function openedRQFirstTerm()
  467. {
  468. $this->_rqFirstTerm = $this->_currentToken->text;
  469. }
  470. /**
  471. * Process last range query term (opened interval)
  472. *
  473. * @throws Zend_Search_Lucene_Search_QueryParserException
  474. */
  475. public function openedRQLastTerm()
  476. {
  477. $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_rqFirstTerm, $this->_encoding);
  478. if (count($tokens) > 1) {
  479. require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
  480. throw new Zend_Search_Lucene_Search_QueryParserException('Range query boundary terms must be non-multiple word terms');
  481. } else if (count($tokens) == 1) {
  482. $from = new Zend_Search_Lucene_Index_Term(reset($tokens)->getTermText(), $this->_context->getField());
  483. } else {
  484. $from = null;
  485. }
  486. $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_currentToken->text, $this->_encoding);
  487. if (count($tokens) > 1) {
  488. require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
  489. throw new Zend_Search_Lucene_Search_QueryParserException('Range query boundary terms must be non-multiple word terms');
  490. } else if (count($tokens) == 1) {
  491. $to = new Zend_Search_Lucene_Index_Term(reset($tokens)->getTermText(), $this->_context->getField());
  492. } else {
  493. $to = null;
  494. }
  495. if ($from === null && $to === null) {
  496. require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
  497. throw new Zend_Search_Lucene_Search_QueryParserException('At least one range query boundary term must be non-empty term');
  498. }
  499. $rangeQuery = new Zend_Search_Lucene_Search_Query_Range($from, $to, false);
  500. $entry = new Zend_Search_Lucene_Search_QueryEntry_Subquery($rangeQuery);
  501. $this->_context->addEntry($entry);
  502. }
  503. /**
  504. * Process first range query term (closed interval)
  505. */
  506. public function closedRQFirstTerm()
  507. {
  508. $this->_rqFirstTerm = $this->_currentToken->text;
  509. }
  510. /**
  511. * Process last range query term (closed interval)
  512. *
  513. * @throws Zend_Search_Lucene_Search_QueryParserException
  514. */
  515. public function closedRQLastTerm()
  516. {
  517. $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_rqFirstTerm, $this->_encoding);
  518. if (count($tokens) > 1) {
  519. require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
  520. throw new Zend_Search_Lucene_Search_QueryParserException('Range query boundary terms must be non-multiple word terms');
  521. } else if (count($tokens) == 1) {
  522. $from = new Zend_Search_Lucene_Index_Term(reset($tokens)->getTermText(), $this->_context->getField());
  523. } else {
  524. $from = null;
  525. }
  526. $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_currentToken->text, $this->_encoding);
  527. if (count($tokens) > 1) {
  528. require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
  529. throw new Zend_Search_Lucene_Search_QueryParserException('Range query boundary terms must be non-multiple word terms');
  530. } else if (count($tokens) == 1) {
  531. $to = new Zend_Search_Lucene_Index_Term(reset($tokens)->getTermText(), $this->_context->getField());
  532. } else {
  533. $to = null;
  534. }
  535. if ($from === null && $to === null) {
  536. require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
  537. throw new Zend_Search_Lucene_Search_QueryParserException('At least one range query boundary term must be non-empty term');
  538. }
  539. $rangeQuery = new Zend_Search_Lucene_Search_Query_Range($from, $to, true);
  540. $entry = new Zend_Search_Lucene_Search_QueryEntry_Subquery($rangeQuery);
  541. $this->_context->addEntry($entry);
  542. }
  543. }