upgrades Zend engine to version 1.5.0
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/**
|
||||
* Zend Framework
|
||||
*
|
||||
* LICENSE
|
||||
*
|
||||
* This source file is subject to the new BSD license that is bundled
|
||||
* with this package in the file LICENSE.txt.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* http://framework.zend.com/license/new-bsd
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to [email protected] so we can send you a copy immediately.
|
||||
*
|
||||
* @category Zend
|
||||
* @package Zend_Search_Lucene
|
||||
* @subpackage Analysis
|
||||
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
|
||||
|
||||
/** Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8 */
|
||||
require_once 'Zend/Search/Lucene/Analysis/Analyzer/Common/Utf8.php';
|
||||
|
||||
/** Zend_Search_Lucene_Analysis_TokenFilter_LowerCaseUtf8 */
|
||||
require_once 'Zend/Search/Lucene/Analysis/TokenFilter/LowerCaseUtf8.php';
|
||||
|
||||
|
||||
/**
|
||||
* @category Zend
|
||||
* @package Zend_Search_Lucene
|
||||
* @subpackage Analysis
|
||||
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
|
||||
|
||||
class Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8_CaseInsensitive extends Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->addFilter(new Zend_Search_Lucene_Analysis_TokenFilter_LowerCaseUtf8());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/**
|
||||
* Zend Framework
|
||||
*
|
||||
* LICENSE
|
||||
*
|
||||
* This source file is subject to the new BSD license that is bundled
|
||||
* with this package in the file LICENSE.txt.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* http://framework.zend.com/license/new-bsd
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to [email protected] so we can send you a copy immediately.
|
||||
*
|
||||
* @category Zend
|
||||
* @package Zend_Search_Lucene
|
||||
* @subpackage Analysis
|
||||
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
|
||||
|
||||
/** Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num */
|
||||
require_once 'Zend/Search/Lucene/Analysis/Analyzer/Common/Utf8Num.php';
|
||||
|
||||
/** Zend_Search_Lucene_Analysis_TokenFilter_LowerCaseUtf8 */
|
||||
require_once 'Zend/Search/Lucene/Analysis/TokenFilter/LowerCaseUtf8.php';
|
||||
|
||||
|
||||
/**
|
||||
* @category Zend
|
||||
* @package Zend_Search_Lucene
|
||||
* @subpackage Analysis
|
||||
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
|
||||
|
||||
class Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num_CaseInsensitive extends Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->addFilter(new Zend_Search_Lucene_Analysis_TokenFilter_LowerCaseUtf8());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/**
|
||||
* Zend Framework
|
||||
*
|
||||
* LICENSE
|
||||
*
|
||||
* This source file is subject to the new BSD license that is bundled
|
||||
* with this package in the file LICENSE.txt.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* http://framework.zend.com/license/new-bsd
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to [email protected] so we can send you a copy immediately.
|
||||
*
|
||||
* @category Zend
|
||||
* @package Zend_Search_Lucene
|
||||
* @subpackage Analysis
|
||||
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
|
||||
|
||||
/** Zend_Search_Lucene_Analysis_TokenFilter */
|
||||
require_once 'Zend/Search/Lucene/Analysis/TokenFilter.php';
|
||||
|
||||
|
||||
/**
|
||||
* Lower case Token filter.
|
||||
*
|
||||
* @category Zend
|
||||
* @package Zend_Search_Lucene
|
||||
* @subpackage Analysis
|
||||
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
|
||||
class Zend_Search_Lucene_Analysis_TokenFilter_LowerCaseUtf8 extends Zend_Search_Lucene_Analysis_TokenFilter
|
||||
{
|
||||
/**
|
||||
* Object constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if (!function_exists('mb_strtolower')) {
|
||||
// mbstring extension is disabled
|
||||
require_once 'Zend/Search/Lucene/Exception.php';
|
||||
throw new Zend_Search_Lucene_Exception('Utf8 compatible lower case filter needs mbstring extension to be enabled.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize Token or remove it (if null is returned)
|
||||
*
|
||||
* @param Zend_Search_Lucene_Analysis_Token $srcToken
|
||||
* @return Zend_Search_Lucene_Analysis_Token
|
||||
*/
|
||||
public function normalize(Zend_Search_Lucene_Analysis_Token $srcToken)
|
||||
{
|
||||
$newToken = new Zend_Search_Lucene_Analysis_Token(
|
||||
mb_strtolower($srcToken->getTermText(), 'UTF-8'),
|
||||
$srcToken->getStartOffset(),
|
||||
$srcToken->getEndOffset());
|
||||
|
||||
$newToken->setPositionIncrement($srcToken->getPositionIncrement());
|
||||
|
||||
return $newToken;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
class EncodingConverter {
|
||||
private $last_error,
|
||||
$in_encoding,
|
||||
$out_encoding;
|
||||
|
||||
function __construct($in_encoding, $out_encoding) {
|
||||
$this->in_encoding = $in_encoding;
|
||||
$this->out_encoding = $out_encoding;
|
||||
} //constructor
|
||||
|
||||
function handleError($err, $msg) {
|
||||
$this->last_error = $msg;
|
||||
} //handleError
|
||||
|
||||
function convert($str) {
|
||||
$this->last_error = FALSE;
|
||||
|
||||
set_error_handler(array(&$this, 'handleError'));
|
||||
$ret = iconv($this->in_encoding, $this->out_encoding, $str);
|
||||
restore_error_handler();
|
||||
|
||||
return $ret;
|
||||
} //convert
|
||||
|
||||
function getLastError() {
|
||||
return $this->last_error;
|
||||
} //getLastError
|
||||
} //EncodingConverter
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
/**
|
||||
* Zend Framework
|
||||
*
|
||||
* LICENSE
|
||||
*
|
||||
* This source file is subject to the new BSD license that is bundled
|
||||
* with this package in the file LICENSE.txt.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* http://framework.zend.com/license/new-bsd
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to [email protected] so we can send you a copy immediately.
|
||||
*
|
||||
* @category Zend
|
||||
* @package Zend_Search_Lucene
|
||||
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
|
||||
|
||||
/** Zend_Search_Lucene_Exception */
|
||||
require_once 'Zend/Search/Lucene/Exception.php';
|
||||
|
||||
/** Zend_Search_Lucene_Storage_Directory */
|
||||
require_once 'Zend/Search/Lucene/Storage/Directory.php';
|
||||
|
||||
/** Zend_Search_Lucene_Storage_File */
|
||||
require_once 'Zend/Search/Lucene/Storage/File.php';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This is an utility class which provides index locks processing functionality
|
||||
*
|
||||
* @category Zend
|
||||
* @package Zend_Search_Lucene
|
||||
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
class Zend_Search_Lucene_LockManager
|
||||
{
|
||||
const WRITE_LOCK_FILE = 'write.lock.file';
|
||||
const READ_LOCK_FILE = 'read.lock.file';
|
||||
const OPTIMIZATION_LOCK_FILE = 'optimization.lock.file';
|
||||
|
||||
/**
|
||||
* Obtain exclusive write lock on the index
|
||||
*
|
||||
* @param Zend_Search_Lucene_Storage_Directory $lockDirectory
|
||||
* @return Zend_Search_Lucene_Storage_File
|
||||
* @throws Zend_Search_Lucene_Exception
|
||||
*/
|
||||
public static function obtainWriteLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
|
||||
{
|
||||
$lock = $lockDirectory->createFile(self::WRITE_LOCK_FILE);
|
||||
if (!$lock->lock(LOCK_EX)) {
|
||||
throw new Zend_Search_Lucene_Exception('Can\'t obtain exclusive index lock');
|
||||
}
|
||||
return $lock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release exclusive write lock
|
||||
*
|
||||
* @param Zend_Search_Lucene_Storage_Directory $lockDirectory
|
||||
*/
|
||||
public static function releaseWriteLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
|
||||
{
|
||||
$lock = $lockDirectory->getFileObject(self::WRITE_LOCK_FILE);
|
||||
$lock->unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain shared read lock on the index
|
||||
*
|
||||
* It doesn't block other read or update processes, but prevent index from the premature cleaning-up
|
||||
*
|
||||
* @param Zend_Search_Lucene_Storage_Directory $defaultLockDirectory
|
||||
* @return Zend_Search_Lucene_Storage_File
|
||||
* @throws Zend_Search_Lucene_Exception
|
||||
*/
|
||||
public static function obtainReadLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
|
||||
{
|
||||
$lock = $lockDirectory->createFile(self::READ_LOCK_FILE);
|
||||
if (!$lock->lock(LOCK_SH)) {
|
||||
throw new Zend_Search_Lucene_Exception('Can\'t obtain shared reading index lock');
|
||||
}
|
||||
return $lock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release shared read lock
|
||||
*
|
||||
* @param Zend_Search_Lucene_Storage_Directory $lockDirectory
|
||||
*/
|
||||
public static function releaseReadLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
|
||||
{
|
||||
$lock = $lockDirectory->getFileObject(self::READ_LOCK_FILE);
|
||||
$lock->unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Escalate Read lock to exclusive level
|
||||
*
|
||||
* @param Zend_Search_Lucene_Storage_Directory $lockDirectory
|
||||
* @return boolean
|
||||
*/
|
||||
public static function escalateReadLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
|
||||
{
|
||||
$lock = $lockDirectory->getFileObject(self::READ_LOCK_FILE);
|
||||
|
||||
// Try to escalate read lock
|
||||
if (!$lock->lock(LOCK_EX, true)) {
|
||||
// Restore lock state
|
||||
$lock->lock(LOCK_SH);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* De-escalate Read lock to shared level
|
||||
*
|
||||
* @param Zend_Search_Lucene_Storage_Directory $lockDirectory
|
||||
*/
|
||||
public static function deEscalateReadLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
|
||||
{
|
||||
$lock = $lockDirectory->getFileObject(self::READ_LOCK_FILE);
|
||||
$lock->lock(LOCK_SH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain exclusive optimization lock on the index
|
||||
*
|
||||
* Returns lock object on success and false otherwise (doesn't block execution)
|
||||
*
|
||||
* @param Zend_Search_Lucene_Storage_Directory $lockDirectory
|
||||
* @return mixed
|
||||
*/
|
||||
public static function obtainOptimizationLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
|
||||
{
|
||||
$lock = $lockDirectory->createFile(self::OPTIMIZATION_LOCK_FILE);
|
||||
if (!$lock->lock(LOCK_EX, true)) {
|
||||
return false;
|
||||
}
|
||||
return $lock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release exclusive optimization lock
|
||||
*
|
||||
* @param Zend_Search_Lucene_Storage_Directory $lockDirectory
|
||||
*/
|
||||
public static function releaseOptimizationLock(Zend_Search_Lucene_Storage_Directory $lockDirectory)
|
||||
{
|
||||
$lock = $lockDirectory->getFileObject(self::OPTIMIZATION_LOCK_FILE);
|
||||
$lock->unlock();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
<?php
|
||||
/**
|
||||
* Zend Framework
|
||||
*
|
||||
* LICENSE
|
||||
*
|
||||
* This source file is subject to the new BSD license that is bundled
|
||||
* with this package in the file LICENSE.txt.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* http://framework.zend.com/license/new-bsd
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to [email protected] so we can send you a copy immediately.
|
||||
*
|
||||
* @category Zend
|
||||
* @package Zend_Search_Lucene
|
||||
* @subpackage Search
|
||||
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
|
||||
|
||||
/** Zend_Search_Lucene_Search_Query */
|
||||
require_once 'Zend/Search/Lucene/Search/Query.php';
|
||||
|
||||
/** Zend_Search_Lucene_Search_Query_MultiTerm */
|
||||
require_once 'Zend/Search/Lucene/Search/Query/MultiTerm.php';
|
||||
|
||||
|
||||
/**
|
||||
* @category Zend
|
||||
* @package Zend_Search_Lucene
|
||||
* @subpackage Search
|
||||
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
class Zend_Search_Lucene_Search_Query_Fuzzy extends Zend_Search_Lucene_Search_Query
|
||||
{
|
||||
/** Default minimum similarity */
|
||||
const DEFAULT_MIN_SIMILARITY = 0.5;
|
||||
|
||||
/**
|
||||
* Maximum number of matched terms.
|
||||
* Apache Lucene defines this limitation as boolean query maximum number of clauses:
|
||||
* org.apache.lucene.search.BooleanQuery.getMaxClauseCount()
|
||||
*/
|
||||
const MAX_CLAUSE_COUNT = 1024;
|
||||
|
||||
/**
|
||||
* Array of precalculated max distances
|
||||
*
|
||||
* keys are integers representing a word size
|
||||
*/
|
||||
private $_maxDistances = array();
|
||||
|
||||
/**
|
||||
* Base searching term.
|
||||
*
|
||||
* @var Zend_Search_Lucene_Index_Term
|
||||
*/
|
||||
private $_term;
|
||||
|
||||
/**
|
||||
* A value between 0 and 1 to set the required similarity
|
||||
* between the query term and the matching terms. For example, for a
|
||||
* _minimumSimilarity of 0.5 a term of the same length
|
||||
* as the query term is considered similar to the query term if the edit distance
|
||||
* between both terms is less than length(term)*0.5
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
private $_minimumSimilarity;
|
||||
|
||||
/**
|
||||
* The length of common (non-fuzzy) prefix
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $_prefixLength;
|
||||
|
||||
/**
|
||||
* Matched terms.
|
||||
*
|
||||
* Matched terms list.
|
||||
* It's filled during the search (rewrite operation) and may be used for search result
|
||||
* post-processing
|
||||
*
|
||||
* Array of Zend_Search_Lucene_Index_Term objects
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_matches = null;
|
||||
|
||||
/**
|
||||
* Matched terms scores
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_scores = null;
|
||||
|
||||
/**
|
||||
* Array of the term keys.
|
||||
* Used to sort terms in alphabetical order if terms have the same socres
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_termKeys = null;
|
||||
|
||||
/**
|
||||
* Zend_Search_Lucene_Search_Query_Wildcard constructor.
|
||||
*
|
||||
* @param Zend_Search_Lucene_Index_Term $pattern
|
||||
* @throws Zend_Search_Lucene_Exception
|
||||
*/
|
||||
public function __construct(Zend_Search_Lucene_Index_Term $term, $minimumSimilarity = self::DEFAULT_MIN_SIMILARITY, $prefixLength = 0)
|
||||
{
|
||||
if ($minimumSimilarity < 0) {
|
||||
throw new Zend_Search_Lucene_Exception('minimumSimilarity cannot be less than 0');
|
||||
}
|
||||
if ($minimumSimilarity >= 1) {
|
||||
throw new Zend_Search_Lucene_Exception('minimumSimilarity cannot be greater than or equal to 1');
|
||||
}
|
||||
if ($prefixLength < 0) {
|
||||
throw new Zend_Search_Lucene_Exception('prefixLength cannot be less than 0');
|
||||
}
|
||||
|
||||
$this->_term = $term;
|
||||
$this->_minimumSimilarity = $minimumSimilarity;
|
||||
$this->_prefixLength = $prefixLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate maximum distance for specified word length
|
||||
*
|
||||
* @param integer $prefixLength
|
||||
* @param integer $termLength
|
||||
* @param integer $length
|
||||
* @return integer
|
||||
*/
|
||||
private function _calculateMaxDistance($prefixLength, $termLength, $length)
|
||||
{
|
||||
$this->_maxDistances[$length] = (int) ((1 - $this->_minimumSimilarity)*(min($termLength, $length) + $prefixLength));
|
||||
return $this->_maxDistances[$length];
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-write query into primitive queries in the context of specified index
|
||||
*
|
||||
* @param Zend_Search_Lucene_Interface $index
|
||||
* @return Zend_Search_Lucene_Search_Query
|
||||
*/
|
||||
public function rewrite(Zend_Search_Lucene_Interface $index)
|
||||
{
|
||||
$this->_matches = array();
|
||||
$this->_scores = array();
|
||||
$this->_termKeys = array();
|
||||
|
||||
if ($this->_term->field === null) {
|
||||
// Search through all fields
|
||||
$fields = $index->getFieldNames(true /* indexed fields list */);
|
||||
} else {
|
||||
$fields = array($this->_term->field);
|
||||
}
|
||||
|
||||
$prefix = Zend_Search_Lucene_Index_Term::getPrefix($this->_term->text, $this->_prefixLength);
|
||||
$prefixByteLength = strlen($prefix);
|
||||
$prefixUtf8Length = Zend_Search_Lucene_Index_Term::getLength($prefix);
|
||||
|
||||
$termLength = Zend_Search_Lucene_Index_Term::getLength($this->_term->text);
|
||||
|
||||
$termRest = substr($this->_term->text, $prefixByteLength);
|
||||
// we calculate length of the rest in bytes since levenshtein() is not UTF-8 compatible
|
||||
$termRestLength = strlen($termRest);
|
||||
|
||||
$scaleFactor = 1/(1 - $this->_minimumSimilarity);
|
||||
|
||||
foreach ($fields as $field) {
|
||||
$index->resetTermsStream();
|
||||
|
||||
if ($prefix != '') {
|
||||
$index->skipTo(new Zend_Search_Lucene_Index_Term($prefix, $field));
|
||||
|
||||
while ($index->currentTerm() !== null &&
|
||||
$index->currentTerm()->field == $field &&
|
||||
substr($index->currentTerm()->text, 0, $prefixByteLength) == $prefix) {
|
||||
// Calculate similarity
|
||||
$target = substr($index->currentTerm()->text, $prefixByteLength);
|
||||
|
||||
$maxDistance = isset($this->_maxDistances[strlen($target)])?
|
||||
$this->_maxDistances[strlen($target)] :
|
||||
$this->_calculateMaxDistance($prefixUtf8Length, $termRestLength, strlen($target));
|
||||
|
||||
if ($termRestLength == 0) {
|
||||
// we don't have anything to compare. That means if we just add
|
||||
// the letters for current term we get the new word
|
||||
$similarity = (($prefixUtf8Length == 0)? 0 : 1 - strlen($target)/$prefixUtf8Length);
|
||||
} else if (strlen($target) == 0) {
|
||||
$similarity = (($prefixUtf8Length == 0)? 0 : 1 - $termRestLength/$prefixUtf8Length);
|
||||
} else if ($maxDistance < abs($termRestLength - strlen($target))){
|
||||
//just adding the characters of term to target or vice-versa results in too many edits
|
||||
//for example "pre" length is 3 and "prefixes" length is 8. We can see that
|
||||
//given this optimal circumstance, the edit distance cannot be less than 5.
|
||||
//which is 8-3 or more precisesly abs(3-8).
|
||||
//if our maximum edit distance is 4, then we can discard this word
|
||||
//without looking at it.
|
||||
$similarity = 0;
|
||||
} else {
|
||||
$similarity = 1 - levenshtein($termRest, $target)/($prefixUtf8Length + min($termRestLength, strlen($target)));
|
||||
}
|
||||
|
||||
if ($similarity > $this->_minimumSimilarity) {
|
||||
$this->_matches[] = $index->currentTerm();
|
||||
$this->_termKeys[] = $index->currentTerm()->key();
|
||||
$this->_scores[] = ($similarity - $this->_minimumSimilarity)*$scaleFactor;
|
||||
}
|
||||
|
||||
$index->nextTerm();
|
||||
}
|
||||
} else {
|
||||
$index->skipTo(new Zend_Search_Lucene_Index_Term('', $field));
|
||||
|
||||
while ($index->currentTerm() !== null && $index->currentTerm()->field == $field) {
|
||||
// Calculate similarity
|
||||
$target = $index->currentTerm()->text;
|
||||
|
||||
$maxDistance = isset($this->_maxDistances[strlen($target)])?
|
||||
$this->_maxDistances[strlen($target)] :
|
||||
$this->_calculateMaxDistance(0, $termRestLength, strlen($target));
|
||||
|
||||
if ($maxDistance < abs($termRestLength - strlen($target))){
|
||||
//just adding the characters of term to target or vice-versa results in too many edits
|
||||
//for example "pre" length is 3 and "prefixes" length is 8. We can see that
|
||||
//given this optimal circumstance, the edit distance cannot be less than 5.
|
||||
//which is 8-3 or more precisesly abs(3-8).
|
||||
//if our maximum edit distance is 4, then we can discard this word
|
||||
//without looking at it.
|
||||
$similarity = 0;
|
||||
} else {
|
||||
$similarity = 1 - levenshtein($termRest, $target)/min($termRestLength, strlen($target));
|
||||
}
|
||||
|
||||
if ($similarity > $this->_minimumSimilarity) {
|
||||
$this->_matches[] = $index->currentTerm();
|
||||
$this->_termKeys[] = $index->currentTerm()->key();
|
||||
$this->_scores[] = ($similarity - $this->_minimumSimilarity)*$scaleFactor;
|
||||
}
|
||||
|
||||
$index->nextTerm();
|
||||
}
|
||||
}
|
||||
|
||||
$index->closeTermsStream();
|
||||
}
|
||||
|
||||
if (count($this->_matches) == 0) {
|
||||
return new Zend_Search_Lucene_Search_Query_Empty();
|
||||
} else if (count($this->_matches) == 1) {
|
||||
return new Zend_Search_Lucene_Search_Query_Term(reset($this->_matches));
|
||||
} else {
|
||||
$rewrittenQuery = new Zend_Search_Lucene_Search_Query_Boolean();
|
||||
|
||||
array_multisort($this->_scores, SORT_DESC, SORT_NUMERIC,
|
||||
$this->_termKeys, SORT_ASC, SORT_STRING,
|
||||
$this->_matches);
|
||||
|
||||
$termCount = 0;
|
||||
foreach ($this->_matches as $id => $matchedTerm) {
|
||||
$subquery = new Zend_Search_Lucene_Search_Query_Term($matchedTerm);
|
||||
$subquery->setBoost($this->_scores[$id]);
|
||||
|
||||
$rewrittenQuery->addSubquery($subquery);
|
||||
|
||||
$termCount++;
|
||||
if ($termCount >= self::MAX_CLAUSE_COUNT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $rewrittenQuery;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize query in the context of specified index
|
||||
*
|
||||
* @param Zend_Search_Lucene_Interface $index
|
||||
* @return Zend_Search_Lucene_Search_Query
|
||||
*/
|
||||
public function optimize(Zend_Search_Lucene_Interface $index)
|
||||
{
|
||||
throw new Zend_Search_Lucene_Exception('Wildcard query should not be directly used for search. Use $query->rewrite($index)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return query terms
|
||||
*
|
||||
* @return array
|
||||
* @throws Zend_Search_Lucene_Exception
|
||||
*/
|
||||
public function getQueryTerms()
|
||||
{
|
||||
if ($this->_matches === null) {
|
||||
throw new Zend_Search_Lucene_Exception('Search has to be performed first to get matched terms');
|
||||
}
|
||||
|
||||
return $this->_matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an appropriate Weight implementation for this query.
|
||||
*
|
||||
* @param Zend_Search_Lucene_Interface $reader
|
||||
* @return Zend_Search_Lucene_Search_Weight
|
||||
* @throws Zend_Search_Lucene_Exception
|
||||
*/
|
||||
public function createWeight(Zend_Search_Lucene_Interface $reader)
|
||||
{
|
||||
throw new Zend_Search_Lucene_Exception('Wildcard query should not be directly used for search. Use $query->rewrite($index)');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute query in context of index reader
|
||||
* It also initializes necessary internal structures
|
||||
*
|
||||
* @param Zend_Search_Lucene_Interface $reader
|
||||
* @throws Zend_Search_Lucene_Exception
|
||||
*/
|
||||
public function execute(Zend_Search_Lucene_Interface $reader)
|
||||
{
|
||||
throw new Zend_Search_Lucene_Exception('Wildcard query should not be directly used for search. Use $query->rewrite($index)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get document ids likely matching the query
|
||||
*
|
||||
* It's an array with document ids as keys (performance considerations)
|
||||
*
|
||||
* @return array
|
||||
* @throws Zend_Search_Lucene_Exception
|
||||
*/
|
||||
public function matchedDocs()
|
||||
{
|
||||
throw new Zend_Search_Lucene_Exception('Wildcard query should not be directly used for search. Use $query->rewrite($index)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Score specified document
|
||||
*
|
||||
* @param integer $docId
|
||||
* @param Zend_Search_Lucene_Interface $reader
|
||||
* @return float
|
||||
* @throws Zend_Search_Lucene_Exception
|
||||
*/
|
||||
public function score($docId, Zend_Search_Lucene_Interface $reader)
|
||||
{
|
||||
throw new Zend_Search_Lucene_Exception('Wildcard query should not be directly used for search. Use $query->rewrite($index)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight query terms
|
||||
*
|
||||
* @param integer &$colorIndex
|
||||
* @param Zend_Search_Lucene_Document_Html $doc
|
||||
*/
|
||||
public function highlightMatchesDOM(Zend_Search_Lucene_Document_Html $doc, &$colorIndex)
|
||||
{
|
||||
$words = array();
|
||||
|
||||
foreach ($this->_matches as $term) {
|
||||
$words[] = $term->text;
|
||||
}
|
||||
|
||||
$doc->highlight($words, $this->_getHighlightColor($colorIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a query
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
// It's used only for query visualisation, so we don't care about characters escaping
|
||||
return (($this->_term->field === null)? '' : $this->_term->field . ':')
|
||||
. $this->_term->text . '~'
|
||||
. (($this->_minimumSimilarity != self::DEFAULT_MIN_SIMILARITY)? round($this->_minimumSimilarity, 4) : '');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
/**
|
||||
* Zend Framework
|
||||
*
|
||||
* LICENSE
|
||||
*
|
||||
* This source file is subject to the new BSD license that is bundled
|
||||
* with this package in the file LICENSE.txt.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* http://framework.zend.com/license/new-bsd
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to [email protected] so we can send you a copy immediately.
|
||||
*
|
||||
* @category Zend
|
||||
* @package Zend_Search_Lucene
|
||||
* @subpackage Search
|
||||
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
|
||||
|
||||
/** Zend_Search_Lucene_Search_Query */
|
||||
require_once 'Zend/Search/Lucene/Search/Query.php';
|
||||
|
||||
/** Zend_Search_Lucene_Search_Weight_Empty */
|
||||
require_once 'Zend/Search/Lucene/Search/Weight/Empty.php';
|
||||
|
||||
|
||||
/**
|
||||
* The insignificant query returns empty result, but doesn't limit result set as a part of other queries
|
||||
*
|
||||
* @category Zend
|
||||
* @package Zend_Search_Lucene
|
||||
* @subpackage Search
|
||||
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
class Zend_Search_Lucene_Search_Query_Insignificant extends Zend_Search_Lucene_Search_Query
|
||||
{
|
||||
/**
|
||||
* Re-write query into primitive queries in the context of specified index
|
||||
*
|
||||
* @param Zend_Search_Lucene_Interface $index
|
||||
* @return Zend_Search_Lucene_Search_Query
|
||||
*/
|
||||
public function rewrite(Zend_Search_Lucene_Interface $index)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize query in the context of specified index
|
||||
*
|
||||
* @param Zend_Search_Lucene_Interface $index
|
||||
* @return Zend_Search_Lucene_Search_Query
|
||||
*/
|
||||
public function optimize(Zend_Search_Lucene_Interface $index)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an appropriate Weight implementation for this query.
|
||||
*
|
||||
* @param Zend_Search_Lucene_Interface $reader
|
||||
* @return Zend_Search_Lucene_Search_Weight
|
||||
*/
|
||||
public function createWeight(Zend_Search_Lucene_Interface $reader)
|
||||
{
|
||||
return new Zend_Search_Lucene_Search_Weight_Empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute query in context of index reader
|
||||
* It also initializes necessary internal structures
|
||||
*
|
||||
* @param Zend_Search_Lucene_Interface $reader
|
||||
*/
|
||||
public function execute(Zend_Search_Lucene_Interface $reader)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Get document ids likely matching the query
|
||||
*
|
||||
* It's an array with document ids as keys (performance considerations)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function matchedDocs()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Score specified document
|
||||
*
|
||||
* @param integer $docId
|
||||
* @param Zend_Search_Lucene_Interface $reader
|
||||
* @return float
|
||||
*/
|
||||
public function score($docId, Zend_Search_Lucene_Interface $reader)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return query terms
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getQueryTerms()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight query terms
|
||||
*
|
||||
* @param integer &$colorIndex
|
||||
* @param Zend_Search_Lucene_Document_Html $doc
|
||||
*/
|
||||
public function highlightMatchesDOM(Zend_Search_Lucene_Document_Html $doc, &$colorIndex)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a query
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return '<InsignificantQuery>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,331 @@
|
||||
<?php
|
||||
/**
|
||||
* Zend Framework
|
||||
*
|
||||
* LICENSE
|
||||
*
|
||||
* This source file is subject to the new BSD license that is bundled
|
||||
* with this package in the file LICENSE.txt.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* http://framework.zend.com/license/new-bsd
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to [email protected] so we can send you a copy immediately.
|
||||
*
|
||||
* @category Zend
|
||||
* @package Zend_Search_Lucene
|
||||
* @subpackage Search
|
||||
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
|
||||
|
||||
/** Zend_Search_Lucene_Search_Query */
|
||||
require_once 'Zend/Search/Lucene/Search/Query.php';
|
||||
|
||||
/** Zend_Search_Lucene_Search_Query_MultiTerm */
|
||||
require_once 'Zend/Search/Lucene/Search/Query/MultiTerm.php';
|
||||
|
||||
|
||||
/**
|
||||
* @category Zend
|
||||
* @package Zend_Search_Lucene
|
||||
* @subpackage Search
|
||||
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
class Zend_Search_Lucene_Search_Query_Range extends Zend_Search_Lucene_Search_Query
|
||||
{
|
||||
/**
|
||||
* Lower term.
|
||||
*
|
||||
* @var Zend_Search_Lucene_Index_Term
|
||||
*/
|
||||
private $_lowerTerm;
|
||||
|
||||
/**
|
||||
* Upper term.
|
||||
*
|
||||
* @var Zend_Search_Lucene_Index_Term
|
||||
*/
|
||||
private $_upperTerm;
|
||||
|
||||
|
||||
/**
|
||||
* Search field
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_field;
|
||||
|
||||
/**
|
||||
* Inclusive
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $_inclusive;
|
||||
|
||||
/**
|
||||
* Matched terms.
|
||||
*
|
||||
* Matched terms list.
|
||||
* It's filled during the search (rewrite operation) and may be used for search result
|
||||
* post-processing
|
||||
*
|
||||
* Array of Zend_Search_Lucene_Index_Term objects
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_matches;
|
||||
|
||||
|
||||
/**
|
||||
* Zend_Search_Lucene_Search_Query_Range constructor.
|
||||
*
|
||||
* @param Zend_Search_Lucene_Index_Term|null $lowerTerm
|
||||
* @param Zend_Search_Lucene_Index_Term|null $upperTerm
|
||||
* @param boolean $inclusive
|
||||
* @throws Zend_Search_Lucene_Exception
|
||||
*/
|
||||
public function __construct($lowerTerm, $upperTerm, $inclusive)
|
||||
{
|
||||
if ($lowerTerm === null && $upperTerm === null) {
|
||||
throw new Zend_Search_Lucene_Exception('At least one term must be non-null');
|
||||
}
|
||||
if ($lowerTerm !== null && $upperTerm !== null && $lowerTerm->field != $upperTerm->field) {
|
||||
throw new Zend_Search_Lucene_Exception('Both terms must be for the same field');
|
||||
}
|
||||
|
||||
$this->_field = ($lowerTerm !== null)? $lowerTerm->field : $upperTerm->field;
|
||||
$this->_lowerTerm = $lowerTerm;
|
||||
$this->_upperTerm = $upperTerm;
|
||||
$this->_inclusive = $inclusive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query field name
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getField()
|
||||
{
|
||||
return $this->_field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get lower term
|
||||
*
|
||||
* @return Zend_Search_Lucene_Index_Term|null
|
||||
*/
|
||||
public function getLowerTerm()
|
||||
{
|
||||
return $this->_lowerTerm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get upper term
|
||||
*
|
||||
* @return Zend_Search_Lucene_Index_Term|null
|
||||
*/
|
||||
public function getUpperTerm()
|
||||
{
|
||||
return $this->_upperTerm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get upper term
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isInclusive()
|
||||
{
|
||||
return $this->_inclusive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-write query into primitive queries in the context of specified index
|
||||
*
|
||||
* @param Zend_Search_Lucene_Interface $index
|
||||
* @return Zend_Search_Lucene_Search_Query
|
||||
*/
|
||||
public function rewrite(Zend_Search_Lucene_Interface $index)
|
||||
{
|
||||
$this->_matches = array();
|
||||
|
||||
if ($this->_field === null) {
|
||||
// Search through all fields
|
||||
$fields = $index->getFieldNames(true /* indexed fields list */);
|
||||
} else {
|
||||
$fields = array($this->_field);
|
||||
}
|
||||
|
||||
foreach ($fields as $field) {
|
||||
$index->resetTermsStream();
|
||||
|
||||
if ($this->_lowerTerm !== null) {
|
||||
$lowerTerm = new Zend_Search_Lucene_Index_Term($this->_lowerTerm->text, $field);
|
||||
|
||||
$index->skipTo($lowerTerm);
|
||||
|
||||
if (!$this->_inclusive &&
|
||||
$index->currentTerm() == $lowerTerm) {
|
||||
// Skip lower term
|
||||
$index->nextTerm();
|
||||
}
|
||||
} else {
|
||||
$index->skipTo(new Zend_Search_Lucene_Index_Term('', $field));
|
||||
}
|
||||
|
||||
|
||||
if ($this->_upperTerm !== null) {
|
||||
// Walk up to the upper term
|
||||
$upperTerm = new Zend_Search_Lucene_Index_Term($this->_upperTerm->text, $field);
|
||||
|
||||
while ($index->currentTerm() !== null &&
|
||||
$index->currentTerm()->field == $field &&
|
||||
$index->currentTerm()->text < $upperTerm->text) {
|
||||
$this->_matches[] = $index->currentTerm();
|
||||
$index->nextTerm();
|
||||
}
|
||||
|
||||
if ($this->_inclusive && $index->currentTerm() == $upperTerm) {
|
||||
// Include upper term into result
|
||||
$this->_matches[] = $upperTerm;
|
||||
}
|
||||
} else {
|
||||
// Walk up to the end of field data
|
||||
while ($index->currentTerm() !== null && $index->currentTerm()->field == $field) {
|
||||
$this->_matches[] = $index->currentTerm();
|
||||
$index->nextTerm();
|
||||
}
|
||||
}
|
||||
|
||||
$index->closeTermsStream();
|
||||
}
|
||||
|
||||
if (count($this->_matches) == 0) {
|
||||
return new Zend_Search_Lucene_Search_Query_Empty();
|
||||
} else if (count($this->_matches) == 1) {
|
||||
return new Zend_Search_Lucene_Search_Query_Term(reset($this->_matches));
|
||||
} else {
|
||||
$rewrittenQuery = new Zend_Search_Lucene_Search_Query_MultiTerm();
|
||||
|
||||
foreach ($this->_matches as $matchedTerm) {
|
||||
$rewrittenQuery->addTerm($matchedTerm);
|
||||
}
|
||||
|
||||
return $rewrittenQuery;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize query in the context of specified index
|
||||
*
|
||||
* @param Zend_Search_Lucene_Interface $index
|
||||
* @return Zend_Search_Lucene_Search_Query
|
||||
*/
|
||||
public function optimize(Zend_Search_Lucene_Interface $index)
|
||||
{
|
||||
throw new Zend_Search_Lucene_Exception('Range query should not be directly used for search. Use $query->rewrite($index)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return query terms
|
||||
*
|
||||
* @return array
|
||||
* @throws Zend_Search_Lucene_Exception
|
||||
*/
|
||||
public function getQueryTerms()
|
||||
{
|
||||
if ($this->_matches === null) {
|
||||
throw new Zend_Search_Lucene_Exception('Search has to be performed first to get matched terms');
|
||||
}
|
||||
|
||||
return $this->_matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an appropriate Weight implementation for this query.
|
||||
*
|
||||
* @param Zend_Search_Lucene_Interface $reader
|
||||
* @return Zend_Search_Lucene_Search_Weight
|
||||
* @throws Zend_Search_Lucene_Exception
|
||||
*/
|
||||
public function createWeight(Zend_Search_Lucene_Interface $reader)
|
||||
{
|
||||
throw new Zend_Search_Lucene_Exception('Range query should not be directly used for search. Use $query->rewrite($index)');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute query in context of index reader
|
||||
* It also initializes necessary internal structures
|
||||
*
|
||||
* @param Zend_Search_Lucene_Interface $reader
|
||||
* @throws Zend_Search_Lucene_Exception
|
||||
*/
|
||||
public function execute(Zend_Search_Lucene_Interface $reader)
|
||||
{
|
||||
throw new Zend_Search_Lucene_Exception('Range query should not be directly used for search. Use $query->rewrite($index)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get document ids likely matching the query
|
||||
*
|
||||
* It's an array with document ids as keys (performance considerations)
|
||||
*
|
||||
* @return array
|
||||
* @throws Zend_Search_Lucene_Exception
|
||||
*/
|
||||
public function matchedDocs()
|
||||
{
|
||||
throw new Zend_Search_Lucene_Exception('Range query should not be directly used for search. Use $query->rewrite($index)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Score specified document
|
||||
*
|
||||
* @param integer $docId
|
||||
* @param Zend_Search_Lucene_Interface $reader
|
||||
* @return float
|
||||
* @throws Zend_Search_Lucene_Exception
|
||||
*/
|
||||
public function score($docId, Zend_Search_Lucene_Interface $reader)
|
||||
{
|
||||
throw new Zend_Search_Lucene_Exception('Range query should not be directly used for search. Use $query->rewrite($index)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight query terms
|
||||
*
|
||||
* @param integer &$colorIndex
|
||||
* @param Zend_Search_Lucene_Document_Html $doc
|
||||
*/
|
||||
public function highlightMatchesDOM(Zend_Search_Lucene_Document_Html $doc, &$colorIndex)
|
||||
{
|
||||
$words = array();
|
||||
|
||||
foreach ($this->_matches as $term) {
|
||||
$words[] = $term->text;
|
||||
}
|
||||
|
||||
$doc->highlight($words, $this->_getHighlightColor($colorIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a query
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
// It's used only for query visualisation, so we don't care about characters escaping
|
||||
return (($this->_field === null)? '' : $this->_field . ':')
|
||||
. (($this->_inclusive)? '[' : '{')
|
||||
. (($this->_lowerTerm !== null)? $this->_lowerTerm->text : 'null')
|
||||
. ' TO '
|
||||
. (($this->_upperTerm !== null)? $this->_upperTerm->text : 'null')
|
||||
. (($this->_inclusive)? ']' : '}');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,297 @@
|
||||
<?php
|
||||
/**
|
||||
* Zend Framework
|
||||
*
|
||||
* LICENSE
|
||||
*
|
||||
* This source file is subject to the new BSD license that is bundled
|
||||
* with this package in the file LICENSE.txt.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* http://framework.zend.com/license/new-bsd
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to [email protected] so we can send you a copy immediately.
|
||||
*
|
||||
* @category Zend
|
||||
* @package Zend_Search_Lucene
|
||||
* @subpackage Search
|
||||
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
|
||||
|
||||
/** Zend_Search_Lucene_Search_Query */
|
||||
require_once 'Zend/Search/Lucene/Search/Query.php';
|
||||
|
||||
/** Zend_Search_Lucene_Search_Query_MultiTerm */
|
||||
require_once 'Zend/Search/Lucene/Search/Query/MultiTerm.php';
|
||||
|
||||
|
||||
/**
|
||||
* @category Zend
|
||||
* @package Zend_Search_Lucene
|
||||
* @subpackage Search
|
||||
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
class Zend_Search_Lucene_Search_Query_Wildcard extends Zend_Search_Lucene_Search_Query
|
||||
{
|
||||
/**
|
||||
* Search pattern.
|
||||
*
|
||||
* Field has to be fully specified or has to be null
|
||||
* Text may contain '*' or '?' symbols
|
||||
*
|
||||
* @var Zend_Search_Lucene_Index_Term
|
||||
*/
|
||||
private $_pattern;
|
||||
|
||||
/**
|
||||
* Matched terms.
|
||||
*
|
||||
* Matched terms list.
|
||||
* It's filled during the search (rewrite operation) and may be used for search result
|
||||
* post-processing
|
||||
*
|
||||
* Array of Zend_Search_Lucene_Index_Term objects
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_matches = null;
|
||||
|
||||
/**
|
||||
* Zend_Search_Lucene_Search_Query_Wildcard constructor.
|
||||
*
|
||||
* @param Zend_Search_Lucene_Index_Term $pattern
|
||||
*/
|
||||
public function __construct(Zend_Search_Lucene_Index_Term $pattern)
|
||||
{
|
||||
$this->_pattern = $pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get terms prefix
|
||||
*
|
||||
* @param string $word
|
||||
* @return string
|
||||
*/
|
||||
private static function _getPrefix($word)
|
||||
{
|
||||
$questionMarkPosition = strpos($word, '?');
|
||||
$astrericPosition = strpos($word, '*');
|
||||
|
||||
if ($questionMarkPosition !== false) {
|
||||
if ($astrericPosition !== false) {
|
||||
return substr($word, 0, min($questionMarkPosition, $astrericPosition));
|
||||
}
|
||||
|
||||
return substr($word, 0, $questionMarkPosition);
|
||||
} else if ($astrericPosition !== false) {
|
||||
return substr($word, 0, $astrericPosition);
|
||||
}
|
||||
|
||||
return $word;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-write query into primitive queries in the context of specified index
|
||||
*
|
||||
* @param Zend_Search_Lucene_Interface $index
|
||||
* @return Zend_Search_Lucene_Search_Query
|
||||
*/
|
||||
public function rewrite(Zend_Search_Lucene_Interface $index)
|
||||
{
|
||||
$this->_matches = array();
|
||||
|
||||
if ($this->_pattern->field === null) {
|
||||
// Search through all fields
|
||||
$fields = $index->getFieldNames(true /* indexed fields list */);
|
||||
} else {
|
||||
$fields = array($this->_pattern->field);
|
||||
}
|
||||
|
||||
$prefix = self::_getPrefix($this->_pattern->text);
|
||||
$prefixLength = strlen($prefix);
|
||||
$matchExpression = '/^' . str_replace(array('\\?', '\\*'), array('.', '.*') , preg_quote($this->_pattern->text, '/')) . '$/';
|
||||
|
||||
/** @todo check for PCRE unicode support may be performed through Zend_Environment in some future */
|
||||
if (@preg_match('/\pL/u', 'a') == 1) {
|
||||
// PCRE unicode support is turned on
|
||||
// add Unicode modifier to the match expression
|
||||
$matchExpression .= 'u';
|
||||
}
|
||||
|
||||
|
||||
foreach ($fields as $field) {
|
||||
$index->resetTermsStream();
|
||||
|
||||
if ($prefix != '') {
|
||||
$index->skipTo(new Zend_Search_Lucene_Index_Term($prefix, $field));
|
||||
|
||||
while ($index->currentTerm() !== null &&
|
||||
$index->currentTerm()->field == $field &&
|
||||
substr($index->currentTerm()->text, 0, $prefixLength) == $prefix) {
|
||||
if (preg_match($matchExpression, $index->currentTerm()->text) === 1) {
|
||||
$this->_matches[] = $index->currentTerm();
|
||||
}
|
||||
|
||||
$index->nextTerm();
|
||||
}
|
||||
} else {
|
||||
$index->skipTo(new Zend_Search_Lucene_Index_Term('', $field));
|
||||
|
||||
while ($index->currentTerm() !== null && $index->currentTerm()->field == $field) {
|
||||
if (preg_match($matchExpression, $index->currentTerm()->text) === 1) {
|
||||
$this->_matches[] = $index->currentTerm();
|
||||
}
|
||||
|
||||
$index->nextTerm();
|
||||
}
|
||||
}
|
||||
|
||||
$index->closeTermsStream();
|
||||
}
|
||||
|
||||
if (count($this->_matches) == 0) {
|
||||
return new Zend_Search_Lucene_Search_Query_Empty();
|
||||
} else if (count($this->_matches) == 1) {
|
||||
return new Zend_Search_Lucene_Search_Query_Term(reset($this->_matches));
|
||||
} else {
|
||||
$rewrittenQuery = new Zend_Search_Lucene_Search_Query_MultiTerm();
|
||||
|
||||
foreach ($this->_matches as $matchedTerm) {
|
||||
$rewrittenQuery->addTerm($matchedTerm);
|
||||
}
|
||||
|
||||
return $rewrittenQuery;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize query in the context of specified index
|
||||
*
|
||||
* @param Zend_Search_Lucene_Interface $index
|
||||
* @return Zend_Search_Lucene_Search_Query
|
||||
*/
|
||||
public function optimize(Zend_Search_Lucene_Interface $index)
|
||||
{
|
||||
throw new Zend_Search_Lucene_Exception('Wildcard query should not be directly used for search. Use $query->rewrite($index)');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns query pattern
|
||||
*
|
||||
* @return Zend_Search_Lucene_Index_Term
|
||||
*/
|
||||
public function getPattern()
|
||||
{
|
||||
return $this->_pattern;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return query terms
|
||||
*
|
||||
* @return array
|
||||
* @throws Zend_Search_Lucene_Exception
|
||||
*/
|
||||
public function getQueryTerms()
|
||||
{
|
||||
if ($this->_matches === null) {
|
||||
throw new Zend_Search_Lucene_Exception('Search has to be performed first to get matched terms');
|
||||
}
|
||||
|
||||
return $this->_matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an appropriate Weight implementation for this query.
|
||||
*
|
||||
* @param Zend_Search_Lucene_Interface $reader
|
||||
* @return Zend_Search_Lucene_Search_Weight
|
||||
* @throws Zend_Search_Lucene_Exception
|
||||
*/
|
||||
public function createWeight(Zend_Search_Lucene_Interface $reader)
|
||||
{
|
||||
throw new Zend_Search_Lucene_Exception('Wildcard query should not be directly used for search. Use $query->rewrite($index)');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute query in context of index reader
|
||||
* It also initializes necessary internal structures
|
||||
*
|
||||
* @param Zend_Search_Lucene_Interface $reader
|
||||
* @throws Zend_Search_Lucene_Exception
|
||||
*/
|
||||
public function execute(Zend_Search_Lucene_Interface $reader)
|
||||
{
|
||||
throw new Zend_Search_Lucene_Exception('Wildcard query should not be directly used for search. Use $query->rewrite($index)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get document ids likely matching the query
|
||||
*
|
||||
* It's an array with document ids as keys (performance considerations)
|
||||
*
|
||||
* @return array
|
||||
* @throws Zend_Search_Lucene_Exception
|
||||
*/
|
||||
public function matchedDocs()
|
||||
{
|
||||
throw new Zend_Search_Lucene_Exception('Wildcard query should not be directly used for search. Use $query->rewrite($index)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Score specified document
|
||||
*
|
||||
* @param integer $docId
|
||||
* @param Zend_Search_Lucene_Interface $reader
|
||||
* @return float
|
||||
* @throws Zend_Search_Lucene_Exception
|
||||
*/
|
||||
public function score($docId, Zend_Search_Lucene_Interface $reader)
|
||||
{
|
||||
throw new Zend_Search_Lucene_Exception('Wildcard query should not be directly used for search. Use $query->rewrite($index)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight query terms
|
||||
*
|
||||
* @param integer &$colorIndex
|
||||
* @param Zend_Search_Lucene_Document_Html $doc
|
||||
*/
|
||||
public function highlightMatchesDOM(Zend_Search_Lucene_Document_Html $doc, &$colorIndex)
|
||||
{
|
||||
$words = array();
|
||||
|
||||
$matchExpression = '/^' . str_replace(array('\\?', '\\*'), array('.', '.*') , preg_quote($this->_pattern->text, '/')) . '$/';
|
||||
if (@preg_match('/\pL/u', 'a') == 1) {
|
||||
// PCRE unicode support is turned on
|
||||
// add Unicode modifier to the match expression
|
||||
$matchExpression .= 'u';
|
||||
}
|
||||
|
||||
$tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($doc->getFieldUtf8Value('body'), 'UTF-8');
|
||||
foreach ($tokens as $token) {
|
||||
if (preg_match($matchExpression, $token->getTermText()) === 1) {
|
||||
$words[] = $token->getTermText();
|
||||
}
|
||||
}
|
||||
|
||||
$doc->highlight($words, $this->_getHighlightColor($colorIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a query
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
// It's used only for query visualisation, so we don't care about characters escaping
|
||||
return (($this->_pattern->field === null)? '' : $this->_pattern->field . ':') . $this->_pattern->text;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user