_encoding = $encoding; $this->_defaultField = $defaultField; } /** * Get context default field * * @return string|null */ public function getField() { return ($this->_nextEntryField !== null) ? $this->_nextEntryField : $this->_defaultField; } /** * Set field for next entry * * @param string $field */ public function setNextEntryField($field) { $this->_nextEntryField = $field; } /** * Set sign for next entry * * @param integer $sign * @throws Zend_Search_Lucene_Exception */ public function setNextEntrySign($sign) { if ($this->_mode === self::GM_BOOLEAN) { #require_once 'Zend/Search/Lucene/Search/QueryParserException.php'; throw new Zend_Search_Lucene_Search_QueryParserException('It\'s not allowed to mix boolean and signs styles in the same subquery.'); } $this->_mode = self::GM_SIGNS; if ($sign == Zend_Search_Lucene_Search_QueryToken::TT_REQUIRED) { $this->_nextEntrySign = true; } else if ($sign == Zend_Search_Lucene_Search_QueryToken::TT_PROHIBITED) { $this->_nextEntrySign = false; } else { #require_once 'Zend/Search/Lucene/Exception.php'; throw new Zend_Search_Lucene_Exception('Unrecognized sign type.'); } } /** * Add entry to a query * * @param Zend_Search_Lucene_Search_QueryEntry $entry */ public function addEntry(Zend_Search_Lucene_Search_QueryEntry $entry) { if ($this->_mode !== self::GM_BOOLEAN) { $this->_signs[] = $this->_nextEntrySign; } $this->_entries[] = $entry; $this->_nextEntryField = null; $this->_nextEntrySign = null; } /** * Process fuzzy search or proximity search modifier * * @throws Zend_Search_Lucene_Search_QueryParserException */ public function processFuzzyProximityModifier($parameter = null) { // Check, that modifier has came just after word or phrase if ($this->_nextEntryField !== null || $this->_nextEntrySign !== null) { #require_once 'Zend/Search/Lucene/Search/QueryParserException.php'; throw new Zend_Search_Lucene_Search_QueryParserException('\'~\' modifier must follow word or phrase.'); } $lastEntry = array_pop($this->_entries); if (!$lastEntry instanceof Zend_Search_Lucene_Search_QueryEntry) { // there are no entries or last entry is boolean operator #require_once 'Zend/Search/Lucene/Search/QueryParserException.php'; throw new Zend_Search_Lucene_Search_QueryParserException('\'~\' modifier must follow word or phrase.'); } $lastEntry->processFuzzyProximityModifier($parameter); $this->_entries[] = $lastEntry; } /** * Set boost factor to the entry * * @param float $boostFactor */ public function boost($boostFactor) { // Check, that modifier has came just after word or phrase if ($this->_nextEntryField !== null || $this->_nextEntrySign !== null) { #require_once 'Zend/Search/Lucene/Search/QueryParserException.php'; throw new Zend_Search_Lucene_Search_QueryParserException('\'^\' modifier must follow word, phrase or subquery.'); } $lastEntry = array_pop($this->_entries); if (!$lastEntry instanceof Zend_Search_Lucene_Search_QueryEntry) { // there are no entries or last entry is boolean operator #require_once 'Zend/Search/Lucene/Search/QueryParserException.php'; throw new Zend_Search_Lucene_Search_QueryParserException('\'^\' modifier must follow word, phrase or subquery.'); } $lastEntry->boost($boostFactor); $this->_entries[] = $lastEntry; } /** * Process logical operator * * @param integer $operator */ public function addLogicalOperator($operator) { if ($this->_mode === self::GM_SIGNS) { #require_once 'Zend/Search/Lucene/Search/QueryParserException.php'; throw new Zend_Search_Lucene_Search_QueryParserException('It\'s not allowed to mix boolean and signs styles in the same subquery.'); } $this->_mode = self::GM_BOOLEAN; $this->_entries[] = $operator; } /** * Generate 'signs style' query from the context * '+term1 term2 -term3 +() ...' * * @return Zend_Search_Lucene_Search_Query */ public function _signStyleExpressionQuery() { #require_once 'Zend/Search/Lucene/Search/Query/Boolean.php'; $query = new Zend_Search_Lucene_Search_Query_Boolean(); #require_once 'Zend/Search/Lucene/Search/QueryParser.php'; if (Zend_Search_Lucene_Search_QueryParser::getDefaultOperator() == Zend_Search_Lucene_Search_QueryParser::B_AND) { $defaultSign = true; // required } else { // Zend_Search_Lucene_Search_QueryParser::B_OR $defaultSign = null; // optional } foreach ($this->_entries as $entryId => $entry) { $sign = ($this->_signs[$entryId] !== null) ? $this->_signs[$entryId] : $defaultSign; $query->addSubquery($entry->getQuery($this->_encoding), $sign); } return $query; } /** * Generate 'boolean style' query from the context * 'term1 and term2 or term3 and () and not ()' * * @return Zend_Search_Lucene_Search_Query * @throws Zend_Search_Lucene */ private function _booleanExpressionQuery() { /** * We treat each level of an expression as a boolean expression in * a Disjunctive Normal Form * * AND operator has higher precedence than OR * * Thus logical query is a disjunction of one or more conjunctions of * one or more query entries */ #require_once 'Zend/Search/Lucene/Search/BooleanExpressionRecognizer.php'; $expressionRecognizer = new Zend_Search_Lucene_Search_BooleanExpressionRecognizer(); #require_once 'Zend/Search/Lucene/Exception.php'; try { foreach ($this->_entries as $entry) { if ($entry instanceof Zend_Search_Lucene_Search_QueryEntry) { $expressionRecognizer->processLiteral($entry); } else { switch ($entry) { case Zend_Search_Lucene_Search_QueryToken::TT_AND_LEXEME: $expressionRecognizer->processOperator(Zend_Search_Lucene_Search_BooleanExpressionRecognizer::IN_AND_OPERATOR); break; case Zend_Search_Lucene_Search_QueryToken::TT_OR_LEXEME: $expressionRecognizer->processOperator(Zend_Search_Lucene_Search_BooleanExpressionRecognizer::IN_OR_OPERATOR); break; case Zend_Search_Lucene_Search_QueryToken::TT_NOT_LEXEME: $expressionRecognizer->processOperator(Zend_Search_Lucene_Search_BooleanExpressionRecognizer::IN_NOT_OPERATOR); break; default: throw new Zend_Search_Lucene('Boolean expression error. Unknown operator type.'); } } } $conjuctions = $expressionRecognizer->finishExpression(); } catch (Zend_Search_Exception $e) { // throw new Zend_Search_Lucene_Search_QueryParserException('Boolean expression error. Error message: \'' . // $e->getMessage() . '\'.' ); // It's query syntax error message and it should be user friendly. So FSM message is omitted #require_once 'Zend/Search/Lucene/Search/QueryParserException.php'; throw new Zend_Search_Lucene_Search_QueryParserException('Boolean expression error.', 0, $e); } // Remove 'only negative' conjunctions foreach ($conjuctions as $conjuctionId => $conjuction) { $nonNegativeEntryFound = false; foreach ($conjuction as $conjuctionEntry) { if ($conjuctionEntry[1]) { $nonNegativeEntryFound = true; break; } } if (!$nonNegativeEntryFound) { unset($conjuctions[$conjuctionId]); } } $subqueries = array(); foreach ($conjuctions as $conjuction) { // Check, if it's a one term conjuction if (count($conjuction) == 1) { $subqueries[] = $conjuction[0][0]->getQuery($this->_encoding); } else { #require_once 'Zend/Search/Lucene/Search/Query/Boolean.php'; $subquery = new Zend_Search_Lucene_Search_Query_Boolean(); foreach ($conjuction as $conjuctionEntry) { $subquery->addSubquery($conjuctionEntry[0]->getQuery($this->_encoding), $conjuctionEntry[1]); } $subqueries[] = $subquery; } } if (count($subqueries) == 0) { #require_once 'Zend/Search/Lucene/Search/Query/Insignificant.php'; return new Zend_Search_Lucene_Search_Query_Insignificant(); } if (count($subqueries) == 1) { return $subqueries[0]; } #require_once 'Zend/Search/Lucene/Search/Query/Boolean.php'; $query = new Zend_Search_Lucene_Search_Query_Boolean(); foreach ($subqueries as $subquery) { // Non-requirered entry/subquery $query->addSubquery($subquery); } return $query; } /** * Generate query from current context * * @return Zend_Search_Lucene_Search_Query */ public function getQuery() { if ($this->_mode === self::GM_BOOLEAN) { return $this->_booleanExpressionQuery(); } else { return $this->_signStyleExpressionQuery(); } } }