Sfoglia il codice sorgente

[ZF-7530]Line endings in Zend/Search/Lucene

git-svn-id: http://framework.zend.com/svn/framework/standard/trunk@17617 44c647ce-9c0f-0410-b52a-842ac1e357ba
yoshida@zend.co.jp 16 anni fa
parent
commit
d4947b5d01

+ 66 - 66
library/Zend/Search/Lucene/Index/TermsStream/Interface.php

@@ -1,66 +1,66 @@
-<?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 license@zend.com so we can send you a copy immediately.
- *
- * @category   Zend
- * @package    Zend_Search_Lucene
- * @subpackage Index
- * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
- * @license    http://framework.zend.com/license/new-bsd     New BSD License
- * @version    $Id$
- */
-
-/**
- * @category   Zend
- * @package    Zend_Search_Lucene
- * @subpackage Index
- * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
- * @license    http://framework.zend.com/license/new-bsd     New BSD License
- */
-interface Zend_Search_Lucene_Index_TermsStream_Interface
-{
-    /**
-     * Reset terms stream.
-     */
-	public function resetTermsStream();
-
-    /**
-     * Skip terms stream up to specified term preffix.
-     *
-     * Prefix contains fully specified field info and portion of searched term
-     *
-     * @param Zend_Search_Lucene_Index_Term $prefix
-     */
-    public function skipTo(Zend_Search_Lucene_Index_Term $prefix);
-
-    /**
-     * Scans terms dictionary and returns next term
-     *
-     * @return Zend_Search_Lucene_Index_Term|null
-     */
-    public function nextTerm();
-
-    /**
-     * Returns term in current position
-     *
-     * @return Zend_Search_Lucene_Index_Term|null
-     */
-    public function currentTerm();
-
-    /**
-     * Close terms stream
-     *
-     * Should be used for resources clean up if stream is not read up to the end
-     */
-    public function closeTermsStream();
-}
+<?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 license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Search_Lucene
+ * @subpackage Index
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/**
+ * @category   Zend
+ * @package    Zend_Search_Lucene
+ * @subpackage Index
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+interface Zend_Search_Lucene_Index_TermsStream_Interface
+{
+    /**
+     * Reset terms stream.
+     */
+    public function resetTermsStream();
+
+    /**
+     * Skip terms stream up to specified term preffix.
+     *
+     * Prefix contains fully specified field info and portion of searched term
+     *
+     * @param Zend_Search_Lucene_Index_Term $prefix
+     */
+    public function skipTo(Zend_Search_Lucene_Index_Term $prefix);
+
+    /**
+     * Scans terms dictionary and returns next term
+     *
+     * @return Zend_Search_Lucene_Index_Term|null
+     */
+    public function nextTerm();
+
+    /**
+     * Returns term in current position
+     *
+     * @return Zend_Search_Lucene_Index_Term|null
+     */
+    public function currentTerm();
+
+    /**
+     * Close terms stream
+     *
+     * Should be used for resources clean up if stream is not read up to the end
+     */
+    public function closeTermsStream();
+}

+ 963 - 963
library/Zend/Search/Lucene/MultiSearcher.php

@@ -1,963 +1,963 @@
-<?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 license@zend.com so we can send you a copy immediately.
- *
- * @category   Zend
- * @package    Zend_Search_Lucene
- * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
- * @license    http://framework.zend.com/license/new-bsd     New BSD License
- * @version    $Id$
- */
-
-/** Zend_Search_Lucene_TermStreamsPriorityQueue */
-require_once 'Zend/Search/Lucene/TermStreamsPriorityQueue.php';
-
-/** Zend_Search_Lucene_Interface */
-require_once 'Zend/Search/Lucene/Interface.php';
-
-/**
- * Multisearcher allows to search through several independent indexes.
- *
- * @category   Zend
- * @package    Zend_Search_Lucene
- * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
- * @license    http://framework.zend.com/license/new-bsd     New BSD License
- */
-class Zend_Search_Lucene_Interface_MultiSearcher implements Zend_Search_Lucene_Interface
-{
-	/**
-	 * List of indices for searching.
-	 * Array of Zend_Search_Lucene_Interface objects
-	 *
-	 * @var array
-	 */
-	protected $_indices;
-
-	/**
-	 * Object constructor.
-	 *
-	 * @param array $indices   Arrays of indices for search
-	 * @throws Zend_Search_Lucene_Exception
-	 */
-	public function __construct($indices = array())
-	{
-		$this->_indices = $indices;
-
-		foreach ($this->_indices as $index) {
-			if (!$index instanceof Zend_Search_Lucene_Interface) {
-                require_once 'Zend/Search/Lucene/Exception.php';
-                throw new Zend_Search_Lucene_Exception('sub-index objects have to implement Zend_Search_Lucene_Interface.');
-			}
-		}
-	}
-
-    /**
-     * Add index for searching.
-     *
-     * @param Zend_Search_Lucene_Interface $index
-     */
-    public function addIndex(Zend_Search_Lucene_Interface $index)
-    {
-        $this->_indices[] = $index;
-    }
-
-
-    /**
-     * Get current generation number
-     *
-     * Returns generation number
-     * 0 means pre-2.1 index format
-     * -1 means there are no segments files.
-     *
-     * @param Zend_Search_Lucene_Storage_Directory $directory
-     * @return integer
-     * @throws Zend_Search_Lucene_Exception
-     */
-    public static function getActualGeneration(Zend_Search_Lucene_Storage_Directory $directory)
-    {
-        require_once 'Zend/Search/Lucene/Exception.php';
-        throw new Zend_Search_Lucene_Exception("Generation number can't be retrieved for multi-searcher");
-    }
-
-    /**
-     * Get segments file name
-     *
-     * @param integer $generation
-     * @return string
-     */
-    public static function getSegmentFileName($generation)
-    {
-        return Zend_Search_Lucene::getSegmentFileName($generation);
-    }
-
-    /**
-     * Get index format version
-     *
-     * @return integer
-     * @throws Zend_Search_Lucene_Exception
-     */
-    public function getFormatVersion()
-    {
-        require_once 'Zend/Search/Lucene/Exception.php';
-        throw new Zend_Search_Lucene_Exception("Format version can't be retrieved for multi-searcher");
-    }
-
-    /**
-     * Set index format version.
-     * Index is converted to this format at the nearest upfdate time
-     *
-     * @param int $formatVersion
-     */
-    public function setFormatVersion($formatVersion)
-    {
-    	foreach ($this->_indices as $index) {
-    		$index->setFormatVersion($formatVersion);
-    	}
-    }
-
-    /**
-     * Returns the Zend_Search_Lucene_Storage_Directory instance for this index.
-     *
-     * @return Zend_Search_Lucene_Storage_Directory
-     */
-    public function getDirectory()
-    {
-        require_once 'Zend/Search/Lucene/Exception.php';
-        throw new Zend_Search_Lucene_Exception("Index directory can't be retrieved for multi-searcher");
-    }
-
-    /**
-     * Returns the total number of documents in this index (including deleted documents).
-     *
-     * @return integer
-     */
-    public function count()
-    {
-    	$count = 0;
-
-    	foreach ($this->_indices as $index) {
-    		$count += $this->_indices->count();
-    	}
-
-    	return $count;
-    }
-
-    /**
-     * Returns one greater than the largest possible document number.
-     * This may be used to, e.g., determine how big to allocate a structure which will have
-     * an element for every document number in an index.
-     *
-     * @return integer
-     */
-    public function maxDoc()
-    {
-        return $this->count();
-    }
-
-    /**
-     * Returns the total number of non-deleted documents in this index.
-     *
-     * @return integer
-     */
-    public function numDocs()
-    {
-        $docs = 0;
-
-        foreach ($this->_indices as $index) {
-            $docs += $this->_indices->numDocs();
-        }
-
-        return $docs;
-    }
-
-    /**
-     * Checks, that document is deleted
-     *
-     * @param integer $id
-     * @return boolean
-     * @throws Zend_Search_Lucene_Exception    Exception is thrown if $id is out of the range
-     */
-    public function isDeleted($id)
-    {
-        foreach ($this->_indices as $index) {
-        	$indexCount = $index->count();
-
-        	if ($indexCount > $id) {
-            	return $index->isDeleted($id);
-            }
-
-            $id -= $indexCount;
-        }
-
-        require_once 'Zend/Search/Lucene/Exception.php';
-        throw new Zend_Search_Lucene_Exception('Document id is out of the range.');
-    }
-
-    /**
-     * Set default search field.
-     *
-     * Null means, that search is performed through all fields by default
-     *
-     * Default value is null
-     *
-     * @param string $fieldName
-     */
-    public static function setDefaultSearchField($fieldName)
-    {
-        foreach ($this->_indices as $index) {
-        	$index->setDefaultSearchField($fieldName);
-        }
-    }
-
-
-    /**
-     * Get default search field.
-     *
-     * Null means, that search is performed through all fields by default
-     *
-     * @return string
-     * @throws Zend_Search_Lucene_Exception
-     */
-    public static function getDefaultSearchField()
-    {
-    	if (count($this->_indices) == 0) {
-            require_once 'Zend/Search/Lucene/Exception.php';
-            throw new Zend_Search_Lucene_Exception('Indices list is empty');
-    	}
-
-    	$defaultSearchField = reset($this->_indices)->getDefaultSearchField();
-
-    	foreach ($this->_indices as $index) {
-    		if ($index->getDefaultSearchField() !== $defaultSearchField) {
-                require_once 'Zend/Search/Lucene/Exception.php';
-                throw new Zend_Search_Lucene_Exception('Indices have different default search field.');
-    		}
-    	}
-
-    	return $defaultSearchField;
-    }
-
-    /**
-     * Set result set limit.
-     *
-     * 0 (default) means no limit
-     *
-     * @param integer $limit
-     */
-    public static function setResultSetLimit($limit)
-    {
-        foreach ($this->_indices as $index) {
-            $index->setResultSetLimit($limit);
-        }
-    }
-
-    /**
-     * Set result set limit.
-     *
-     * 0 means no limit
-     *
-     * @return integer
-     * @throws Zend_Search_Lucene_Exception
-     */
-    public static function getResultSetLimit()
-    {
-        if (count($this->_indices) == 0) {
-            require_once 'Zend/Search/Lucene/Exception.php';
-            throw new Zend_Search_Lucene_Exception('Indices list is empty');
-        }
-
-        $defaultResultSetLimit = reset($this->_indices)->getResultSetLimit();
-
-        foreach ($this->_indices as $index) {
-            if ($index->getResultSetLimit() !== $defaultResultSetLimit) {
-                require_once 'Zend/Search/Lucene/Exception.php';
-                throw new Zend_Search_Lucene_Exception('Indices have different default search field.');
-            }
-        }
-
-        return $defaultResultSetLimit;
-    }
-
-    /**
-     * Retrieve index maxBufferedDocs option
-     *
-     * maxBufferedDocs is a minimal number of documents required before
-     * the buffered in-memory documents are written into a new Segment
-     *
-     * Default value is 10
-     *
-     * @return integer
-     * @throws Zend_Search_Lucene_Exception
-     */
-    public function getMaxBufferedDocs()
-    {
-        if (count($this->_indices) == 0) {
-            require_once 'Zend/Search/Lucene/Exception.php';
-            throw new Zend_Search_Lucene_Exception('Indices list is empty');
-        }
-
-        $maxBufferedDocs = reset($this->_indices)->getMaxBufferedDocs();
-
-        foreach ($this->_indices as $index) {
-            if ($index->getMaxBufferedDocs() !== $maxBufferedDocs) {
-                require_once 'Zend/Search/Lucene/Exception.php';
-                throw new Zend_Search_Lucene_Exception('Indices have different default search field.');
-            }
-        }
-
-        return $maxBufferedDocs;
-    }
-
-    /**
-     * Set index maxBufferedDocs option
-     *
-     * maxBufferedDocs is a minimal number of documents required before
-     * the buffered in-memory documents are written into a new Segment
-     *
-     * Default value is 10
-     *
-     * @param integer $maxBufferedDocs
-     */
-    public function setMaxBufferedDocs($maxBufferedDocs)
-    {
-        foreach ($this->_indices as $index) {
-            $index->setMaxBufferedDocs($maxBufferedDocs);
-        }
-    }
-
-    /**
-     * Retrieve index maxMergeDocs option
-     *
-     * maxMergeDocs is a largest number of documents ever merged by addDocument().
-     * Small values (e.g., less than 10,000) are best for interactive indexing,
-     * as this limits the length of pauses while indexing to a few seconds.
-     * Larger values are best for batched indexing and speedier searches.
-     *
-     * Default value is PHP_INT_MAX
-     *
-     * @return integer
-     * @throws Zend_Search_Lucene_Exception
-     */
-    public function getMaxMergeDocs()
-    {
-        if (count($this->_indices) == 0) {
-            require_once 'Zend/Search/Lucene/Exception.php';
-            throw new Zend_Search_Lucene_Exception('Indices list is empty');
-        }
-
-        $maxMergeDocs = reset($this->_indices)->getMaxMergeDocs();
-
-        foreach ($this->_indices as $index) {
-            if ($index->getMaxMergeDocs() !== $maxMergeDocs) {
-                require_once 'Zend/Search/Lucene/Exception.php';
-                throw new Zend_Search_Lucene_Exception('Indices have different default search field.');
-            }
-        }
-
-        return $maxMergeDocs;
-    }
-
-    /**
-     * Set index maxMergeDocs option
-     *
-     * maxMergeDocs is a largest number of documents ever merged by addDocument().
-     * Small values (e.g., less than 10,000) are best for interactive indexing,
-     * as this limits the length of pauses while indexing to a few seconds.
-     * Larger values are best for batched indexing and speedier searches.
-     *
-     * Default value is PHP_INT_MAX
-     *
-     * @param integer $maxMergeDocs
-     */
-    public function setMaxMergeDocs($maxMergeDocs)
-    {
-        foreach ($this->_indices as $index) {
-            $index->setMaxMergeDocs($maxMergeDocs);
-        }
-    }
-
-    /**
-     * Retrieve index mergeFactor option
-     *
-     * mergeFactor determines how often segment indices are merged by addDocument().
-     * With smaller values, less RAM is used while indexing,
-     * and searches on unoptimized indices are faster,
-     * but indexing speed is slower.
-     * With larger values, more RAM is used during indexing,
-     * and while searches on unoptimized indices are slower,
-     * indexing is faster.
-     * Thus larger values (> 10) are best for batch index creation,
-     * and smaller values (< 10) for indices that are interactively maintained.
-     *
-     * Default value is 10
-     *
-     * @return integer
-     * @throws Zend_Search_Lucene_Exception
-     */
-    public function getMergeFactor()
-    {
-        if (count($this->_indices) == 0) {
-            require_once 'Zend/Search/Lucene/Exception.php';
-            throw new Zend_Search_Lucene_Exception('Indices list is empty');
-        }
-
-        $mergeFactor = reset($this->_indices)->getMergeFactor();
-
-        foreach ($this->_indices as $index) {
-            if ($index->getMergeFactor() !== $mergeFactor) {
-                require_once 'Zend/Search/Lucene/Exception.php';
-                throw new Zend_Search_Lucene_Exception('Indices have different default search field.');
-            }
-        }
-
-        return $mergeFactor;
-    }
-
-    /**
-     * Set index mergeFactor option
-     *
-     * mergeFactor determines how often segment indices are merged by addDocument().
-     * With smaller values, less RAM is used while indexing,
-     * and searches on unoptimized indices are faster,
-     * but indexing speed is slower.
-     * With larger values, more RAM is used during indexing,
-     * and while searches on unoptimized indices are slower,
-     * indexing is faster.
-     * Thus larger values (> 10) are best for batch index creation,
-     * and smaller values (< 10) for indices that are interactively maintained.
-     *
-     * Default value is 10
-     *
-     * @param integer $maxMergeDocs
-     */
-    public function setMergeFactor($mergeFactor)
-    {
-        foreach ($this->_indices as $index) {
-            $index->setMaxMergeDocs($maxMergeDocs);
-        }
-    }
-
-    /**
-     * Performs a query against the index and returns an array
-     * of Zend_Search_Lucene_Search_QueryHit objects.
-     * Input is a string or Zend_Search_Lucene_Search_Query.
-     *
-     * @param mixed $query
-     * @return array Zend_Search_Lucene_Search_QueryHit
-     * @throws Zend_Search_Lucene_Exception
-     */
-    public function find($query)
-    {
-    	$hitsList = array();
-
-    	$indexShift = 0;
-    	foreach ($this->_indices as $index) {
-    		$hits = $index->find($query);
-
-    		if ($indexShift != 0) {
-                foreach ($hits as $hit) {
-                    $hit->id += $indexShift;
-                }
-    		}
-
-    		$indexShift += $index->count();
-    		$hitsList[] = $hits;
-    	}
-
-    	/** @todo Implement advanced sorting */
-
-    	return call_user_func_array('array_merge', $hitsList);
-    }
-
-    /**
-     * Returns a list of all unique field names that exist in this index.
-     *
-     * @param boolean $indexed
-     * @return array
-     */
-    public function getFieldNames($indexed = false)
-    {
-    	$fieldNamesList = array();
-
-    	foreach ($this->_indices as $index) {
-    		$fieldNamesList[] = $index->getFieldNames($indexed);
-    	}
-
-    	return array_unique(call_user_func_array('array_merge', $fieldNamesList));
-    }
-
-    /**
-     * Returns a Zend_Search_Lucene_Document object for the document
-     * number $id in this index.
-     *
-     * @param integer|Zend_Search_Lucene_Search_QueryHit $id
-     * @return Zend_Search_Lucene_Document
-     * @throws Zend_Search_Lucene_Exception    Exception is thrown if $id is out of the range
-     */
-    public function getDocument($id)
-    {
-        if ($id instanceof Zend_Search_Lucene_Search_QueryHit) {
-            /* @var $id Zend_Search_Lucene_Search_QueryHit */
-            $id = $id->id;
-        }
-
-    	foreach ($this->_indices as $index) {
-            $indexCount = $index->count();
-
-            if ($indexCount > $id) {
-                return $index->getDocument($id);
-            }
-
-            $id -= $indexCount;
-        }
-
-        require_once 'Zend/Search/Lucene/Exception.php';
-        throw new Zend_Search_Lucene_Exception('Document id is out of the range.');
-    }
-
-    /**
-     * Returns true if index contain documents with specified term.
-     *
-     * Is used for query optimization.
-     *
-     * @param Zend_Search_Lucene_Index_Term $term
-     * @return boolean
-     */
-    public function hasTerm(Zend_Search_Lucene_Index_Term $term)
-    {
-        foreach ($this->_indices as $index) {
-        	if ($index->hasTerm($term)) {
-        		return true;
-        	}
-        }
-
-        return false;
-    }
-
-    /**
-     * Returns IDs of all the documents containing term.
-     *
-     * @param Zend_Search_Lucene_Index_Term $term
-     * @param Zend_Search_Lucene_Index_DocsFilter|null $docsFilter
-     * @return array
-     * @throws Zend_Search_Lucene_Exception
-     */
-    public function termDocs(Zend_Search_Lucene_Index_Term $term, $docsFilter = null)
-    {
-    	if ($docsFilter != null) {
-            require_once 'Zend/Search/Lucene/Exception.php';
-            throw new Zend_Search_Lucene_Exception('Document filters could not used with multi-searcher');
-    	}
-
-        $docsList = array();
-
-        $indexShift = 0;
-        foreach ($this->_indices as $index) {
-            $docs = $index->termDocs($term);
-
-            if ($indexShift != 0) {
-                foreach ($docs as $id => $docId) {
-                    $docs[$id] += $indexShift;
-                }
-            }
-
-            $indexShift += $index->count();
-            $docsList[] = $docs;
-        }
-
-        return call_user_func_array('array_merge', $docsList);
-    }
-
-    /**
-     * Returns documents filter for all documents containing term.
-     *
-     * It performs the same operation as termDocs, but return result as
-     * Zend_Search_Lucene_Index_DocsFilter object
-     *
-     * @param Zend_Search_Lucene_Index_Term $term
-     * @param Zend_Search_Lucene_Index_DocsFilter|null $docsFilter
-     * @return Zend_Search_Lucene_Index_DocsFilter
-     * @throws Zend_Search_Lucene_Exception
-     */
-    public function termDocsFilter(Zend_Search_Lucene_Index_Term $term, $docsFilter = null)
-    {
-        require_once 'Zend/Search/Lucene/Exception.php';
-        throw new Zend_Search_Lucene_Exception('Document filters could not used with multi-searcher');
-    }
-
-    /**
-     * Returns an array of all term freqs.
-     * Return array structure: array( docId => freq, ...)
-     *
-     * @param Zend_Search_Lucene_Index_Term $term
-     * @param Zend_Search_Lucene_Index_DocsFilter|null $docsFilter
-     * @return integer
-     * @throws Zend_Search_Lucene_Exception
-     */
-    public function termFreqs(Zend_Search_Lucene_Index_Term $term, $docsFilter = null)
-    {
-        if ($docsFilter != null) {
-            require_once 'Zend/Search/Lucene/Exception.php';
-            throw new Zend_Search_Lucene_Exception('Document filters could not used with multi-searcher');
-        }
-
-        $freqsList = array();
-
-        $indexShift = 0;
-        foreach ($this->_indices as $index) {
-            $freqs = $index->termFreqs($term);
-
-            if ($indexShift != 0) {
-            	$freqsShifted = array();
-
-                foreach ($freqs as $docId => $freq) {
-                	$freqsShifted[$docId + $indexShift] = $freq;
-                }
-                $freqs = $freqsShifted;
-            }
-
-            $indexShift += $index->count();
-            $freqsList[] = $freqs;
-        }
-
-        return call_user_func_array('array_merge', $freqsList);
-    }
-
-    /**
-     * Returns an array of all term positions in the documents.
-     * Return array structure: array( docId => array( pos1, pos2, ...), ...)
-     *
-     * @param Zend_Search_Lucene_Index_Term $term
-     * @param Zend_Search_Lucene_Index_DocsFilter|null $docsFilter
-     * @return array
-     * @throws Zend_Search_Lucene_Exception
-     */
-    public function termPositions(Zend_Search_Lucene_Index_Term $term, $docsFilter = null)
-    {
-        if ($docsFilter != null) {
-            require_once 'Zend/Search/Lucene/Exception.php';
-            throw new Zend_Search_Lucene_Exception('Document filters could not used with multi-searcher');
-        }
-
-        $termPositionsList = array();
-
-        $indexShift = 0;
-        foreach ($this->_indices as $index) {
-            $termPositions = $index->termPositions($term);
-
-            if ($indexShift != 0) {
-                $termPositionsShifted = array();
-
-                foreach ($termPositions as $docId => $positions) {
-                    $termPositions[$docId + $indexShift] = $positions;
-                }
-                $termPositions = $termPositionsShifted;
-            }
-
-            $indexShift += $index->count();
-            $termPositionsList[] = $termPositions;
-        }
-
-        return call_user_func_array('array_merge', $termPositions);
-    }
-
-    /**
-     * Returns the number of documents in this index containing the $term.
-     *
-     * @param Zend_Search_Lucene_Index_Term $term
-     * @return integer
-     */
-    public function docFreq(Zend_Search_Lucene_Index_Term $term)
-    {
-    	$docFreq = 0;
-
-    	foreach ($this->_indices as $index) {
-    		$docFreq += $index->docFreq($term);
-    	}
-
-    	return $docFreq;
-    }
-
-    /**
-     * Retrive similarity used by index reader
-     *
-     * @return Zend_Search_Lucene_Search_Similarity
-     * @throws Zend_Search_Lucene_Exception
-     */
-    public function getSimilarity()
-    {
-        if (count($this->_indices) == 0) {
-            require_once 'Zend/Search/Lucene/Exception.php';
-            throw new Zend_Search_Lucene_Exception('Indices list is empty');
-        }
-
-        $similarity = reset($this->_indices)->getSimilarity();
-
-        foreach ($this->_indices as $index) {
-            if ($index->getSimilarity() !== $similarity) {
-                require_once 'Zend/Search/Lucene/Exception.php';
-                throw new Zend_Search_Lucene_Exception('Indices have different similarity.');
-            }
-        }
-
-        return $similarity;
-    }
-
-    /**
-     * Returns a normalization factor for "field, document" pair.
-     *
-     * @param integer $id
-     * @param string $fieldName
-     * @return float
-     */
-    public function norm($id, $fieldName)
-    {
-        foreach ($this->_indices as $index) {
-            $indexCount = $index->count();
-
-            if ($indexCount > $id) {
-                return $index->norm($id, $fieldName);
-            }
-
-            $id -= $indexCount;
-        }
-
-        return null;
-    }
-
-    /**
-     * Returns true if any documents have been deleted from this index.
-     *
-     * @return boolean
-     */
-    public function hasDeletions()
-    {
-    	foreach ($this->_indices as $index) {
-    		if ($index->hasDeletions()) {
-    			return true;
-    		}
-    	}
-
-    	return false;
-    }
-
-    /**
-     * Deletes a document from the index.
-     * $id is an internal document id
-     *
-     * @param integer|Zend_Search_Lucene_Search_QueryHit $id
-     * @throws Zend_Search_Lucene_Exception
-     */
-    public function delete($id)
-    {
-        foreach ($this->_indices as $index) {
-            $indexCount = $index->count();
-
-            if ($indexCount > $id) {
-                $index->delete($id);
-                return;
-            }
-
-            $id -= $indexCount;
-        }
-
-        require_once 'Zend/Search/Lucene/Exception.php';
-        throw new Zend_Search_Lucene_Exception('Document id is out of the range.');
-    }
-
-
-    /**
-     * Callback used to choose target index for new documents
-     *
-     * Function/method signature:
-     *    Zend_Search_Lucene_Interface  callbackFunction(Zend_Search_Lucene_Document $document, array $indices);
-     *
-     * null means "default documents distributing algorithm"
-     *
-     * @var callback
-     */
-    protected $_documentDistributorCallBack = null;
-
-    /**
-     * Set callback for choosing target index.
-     *
-     * @param callback $callback
-     */
-    public function setDocumentDistributorCallback($callback)
-    {
-    	if ($callback !== null  &&  !is_callable($callback))
-    	$this->_documentDistributorCallBack = $callback;
-    }
-
-    /**
-     * Get callback for choosing target index.
-     *
-     * @return callback
-     */
-    public function getDocumentDistributorCallback()
-    {
-        return $this->_documentDistributorCallBack;
-    }
-
-    /**
-     * Adds a document to this index.
-     *
-     * @param Zend_Search_Lucene_Document $document
-     * @throws Zend_Search_Lucene_Exception
-     */
-    public function addDocument(Zend_Search_Lucene_Document $document)
-    {
-    	if ($this->_documentDistributorCallBack !== null) {
-    		$index = call_user_func($this->_documentDistributorCallBack, $document, $this->_indices);
-    	} else {
-    		$index = $this->_indices[ array_rand($this->_indices) ];
-    	}
-
-    	$index->addDocument($document);
-    }
-
-    /**
-     * Commit changes resulting from delete() or undeleteAll() operations.
-     */
-    public function commit()
-    {
-        foreach ($this->_indices as $index) {
-        	$index->commit();
-        }
-    }
-
-    /**
-     * Optimize index.
-     *
-     * Merges all segments into one
-     */
-    public function optimize()
-    {
-    	foreach ($this->_indices as $index) {
-    		$index->_optimise();
-    	}
-    }
-
-    /**
-     * Returns an array of all terms in this index.
-     *
-     * @return array
-     */
-    public function terms()
-    {
-    	$termsList = array();
-
-    	foreach ($this->_indices as $index) {
-    		$termsList[] = $index->terms();
-    	}
-
-    	return array_unique(call_user_func_array('array_merge', $termsList));
-    }
-
-
-    /**
-     * Terms stream priority queue object
-     *
-     * @var Zend_Search_Lucene_TermStreamsPriorityQueue
-     */
-    private $_termsStream = null;
-
-    /**
-     * Reset terms stream.
-     */
-    public function resetTermsStream()
-    {
-        if ($this->_termsStream === null) {
-            $this->_termsStream = new Zend_Search_Lucene_TermStreamsPriorityQueue($this->_indices);
-        } else {
-            $this->_termsStream->resetTermsStream();
-        }
-    }
-
-    /**
-     * Skip terms stream up to specified term preffix.
-     *
-     * Prefix contains fully specified field info and portion of searched term
-     *
-     * @param Zend_Search_Lucene_Index_Term $prefix
-     */
-    public function skipTo(Zend_Search_Lucene_Index_Term $prefix)
-    {
-        $this->_termsStream->skipTo($prefix);
-    }
-
-    /**
-     * Scans terms dictionary and returns next term
-     *
-     * @return Zend_Search_Lucene_Index_Term|null
-     */
-    public function nextTerm()
-    {
-        return $this->_termsStream->nextTerm();
-    }
-
-    /**
-     * Returns term in current position
-     *
-     * @return Zend_Search_Lucene_Index_Term|null
-     */
-    public function currentTerm()
-    {
-        return $this->_termsStream->currentTerm();
-    }
-
-    /**
-     * Close terms stream
-     *
-     * Should be used for resources clean up if stream is not read up to the end
-     */
-    public function closeTermsStream()
-    {
-        $this->_termsStream->closeTermsStream();
-        $this->_termsStream = null;
-    }
-
-
-    /**
-     * Undeletes all documents currently marked as deleted in this index.
-     */
-    public function undeleteAll()
-    {
-        foreach ($this->_indices as $index) {
-            $index->undeleteAll();
-        }
-    }
-
-
-    /**
-     * Add reference to the index object
-     *
-     * @internal
-     */
-    public function addReference()
-    {
-    	// Do nothing, since it's never referenced by indices
-    }
-
-    /**
-     * Remove reference from the index object
-     *
-     * When reference count becomes zero, index is closed and resources are cleaned up
-     *
-     * @internal
-     */
-    public function removeReference()
-    {
-    	// Do nothing, since it's never referenced by indices
-    }
-}
+<?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 license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Search_Lucene
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/** @see Zend_Search_Lucene_TermStreamsPriorityQueue */
+require_once 'Zend/Search/Lucene/TermStreamsPriorityQueue.php';
+
+/** @see Zend_Search_Lucene_Interface */
+require_once 'Zend/Search/Lucene/Interface.php';
+
+/**
+ * Multisearcher allows to search through several independent indexes.
+ *
+ * @category   Zend
+ * @package    Zend_Search_Lucene
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Search_Lucene_Interface_MultiSearcher implements Zend_Search_Lucene_Interface
+{
+    /**
+     * List of indices for searching.
+     * Array of Zend_Search_Lucene_Interface objects
+     *
+     * @var array
+     */
+    protected $_indices;
+
+    /**
+     * Object constructor.
+     *
+     * @param array $indices   Arrays of indices for search
+     * @throws Zend_Search_Lucene_Exception
+     */
+    public function __construct($indices = array())
+    {
+        $this->_indices = $indices;
+
+        foreach ($this->_indices as $index) {
+            if (!$index instanceof Zend_Search_Lucene_Interface) {
+                require_once 'Zend/Search/Lucene/Exception.php';
+                throw new Zend_Search_Lucene_Exception('sub-index objects have to implement Zend_Search_Lucene_Interface.');
+            }
+        }
+    }
+
+    /**
+     * Add index for searching.
+     *
+     * @param Zend_Search_Lucene_Interface $index
+     */
+    public function addIndex(Zend_Search_Lucene_Interface $index)
+    {
+        $this->_indices[] = $index;
+    }
+
+
+    /**
+     * Get current generation number
+     *
+     * Returns generation number
+     * 0 means pre-2.1 index format
+     * -1 means there are no segments files.
+     *
+     * @param Zend_Search_Lucene_Storage_Directory $directory
+     * @return integer
+     * @throws Zend_Search_Lucene_Exception
+     */
+    public static function getActualGeneration(Zend_Search_Lucene_Storage_Directory $directory)
+    {
+        require_once 'Zend/Search/Lucene/Exception.php';
+        throw new Zend_Search_Lucene_Exception("Generation number can't be retrieved for multi-searcher");
+    }
+
+    /**
+     * Get segments file name
+     *
+     * @param integer $generation
+     * @return string
+     */
+    public static function getSegmentFileName($generation)
+    {
+        return Zend_Search_Lucene::getSegmentFileName($generation);
+    }
+
+    /**
+     * Get index format version
+     *
+     * @return integer
+     * @throws Zend_Search_Lucene_Exception
+     */
+    public function getFormatVersion()
+    {
+        require_once 'Zend/Search/Lucene/Exception.php';
+        throw new Zend_Search_Lucene_Exception("Format version can't be retrieved for multi-searcher");
+    }
+
+    /**
+     * Set index format version.
+     * Index is converted to this format at the nearest upfdate time
+     *
+     * @param int $formatVersion
+     */
+    public function setFormatVersion($formatVersion)
+    {
+        foreach ($this->_indices as $index) {
+            $index->setFormatVersion($formatVersion);
+        }
+    }
+
+    /**
+     * Returns the Zend_Search_Lucene_Storage_Directory instance for this index.
+     *
+     * @return Zend_Search_Lucene_Storage_Directory
+     */
+    public function getDirectory()
+    {
+        require_once 'Zend/Search/Lucene/Exception.php';
+        throw new Zend_Search_Lucene_Exception("Index directory can't be retrieved for multi-searcher");
+    }
+
+    /**
+     * Returns the total number of documents in this index (including deleted documents).
+     *
+     * @return integer
+     */
+    public function count()
+    {
+        $count = 0;
+
+        foreach ($this->_indices as $index) {
+            $count += $this->_indices->count();
+        }
+
+        return $count;
+    }
+
+    /**
+     * Returns one greater than the largest possible document number.
+     * This may be used to, e.g., determine how big to allocate a structure which will have
+     * an element for every document number in an index.
+     *
+     * @return integer
+     */
+    public function maxDoc()
+    {
+        return $this->count();
+    }
+
+    /**
+     * Returns the total number of non-deleted documents in this index.
+     *
+     * @return integer
+     */
+    public function numDocs()
+    {
+        $docs = 0;
+
+        foreach ($this->_indices as $index) {
+            $docs += $this->_indices->numDocs();
+        }
+
+        return $docs;
+    }
+
+    /**
+     * Checks, that document is deleted
+     *
+     * @param integer $id
+     * @return boolean
+     * @throws Zend_Search_Lucene_Exception    Exception is thrown if $id is out of the range
+     */
+    public function isDeleted($id)
+    {
+        foreach ($this->_indices as $index) {
+            $indexCount = $index->count();
+
+            if ($indexCount > $id) {
+                return $index->isDeleted($id);
+            }
+
+            $id -= $indexCount;
+        }
+
+        require_once 'Zend/Search/Lucene/Exception.php';
+        throw new Zend_Search_Lucene_Exception('Document id is out of the range.');
+    }
+
+    /**
+     * Set default search field.
+     *
+     * Null means, that search is performed through all fields by default
+     *
+     * Default value is null
+     *
+     * @param string $fieldName
+     */
+    public static function setDefaultSearchField($fieldName)
+    {
+        foreach ($this->_indices as $index) {
+            $index->setDefaultSearchField($fieldName);
+        }
+    }
+
+
+    /**
+     * Get default search field.
+     *
+     * Null means, that search is performed through all fields by default
+     *
+     * @return string
+     * @throws Zend_Search_Lucene_Exception
+     */
+    public static function getDefaultSearchField()
+    {
+        if (count($this->_indices) == 0) {
+            require_once 'Zend/Search/Lucene/Exception.php';
+            throw new Zend_Search_Lucene_Exception('Indices list is empty');
+        }
+
+        $defaultSearchField = reset($this->_indices)->getDefaultSearchField();
+
+        foreach ($this->_indices as $index) {
+            if ($index->getDefaultSearchField() !== $defaultSearchField) {
+                require_once 'Zend/Search/Lucene/Exception.php';
+                throw new Zend_Search_Lucene_Exception('Indices have different default search field.');
+            }
+        }
+
+        return $defaultSearchField;
+    }
+
+    /**
+     * Set result set limit.
+     *
+     * 0 (default) means no limit
+     *
+     * @param integer $limit
+     */
+    public static function setResultSetLimit($limit)
+    {
+        foreach ($this->_indices as $index) {
+            $index->setResultSetLimit($limit);
+        }
+    }
+
+    /**
+     * Set result set limit.
+     *
+     * 0 means no limit
+     *
+     * @return integer
+     * @throws Zend_Search_Lucene_Exception
+     */
+    public static function getResultSetLimit()
+    {
+        if (count($this->_indices) == 0) {
+            require_once 'Zend/Search/Lucene/Exception.php';
+            throw new Zend_Search_Lucene_Exception('Indices list is empty');
+        }
+
+        $defaultResultSetLimit = reset($this->_indices)->getResultSetLimit();
+
+        foreach ($this->_indices as $index) {
+            if ($index->getResultSetLimit() !== $defaultResultSetLimit) {
+                require_once 'Zend/Search/Lucene/Exception.php';
+                throw new Zend_Search_Lucene_Exception('Indices have different default search field.');
+            }
+        }
+
+        return $defaultResultSetLimit;
+    }
+
+    /**
+     * Retrieve index maxBufferedDocs option
+     *
+     * maxBufferedDocs is a minimal number of documents required before
+     * the buffered in-memory documents are written into a new Segment
+     *
+     * Default value is 10
+     *
+     * @return integer
+     * @throws Zend_Search_Lucene_Exception
+     */
+    public function getMaxBufferedDocs()
+    {
+        if (count($this->_indices) == 0) {
+            require_once 'Zend/Search/Lucene/Exception.php';
+            throw new Zend_Search_Lucene_Exception('Indices list is empty');
+        }
+
+        $maxBufferedDocs = reset($this->_indices)->getMaxBufferedDocs();
+
+        foreach ($this->_indices as $index) {
+            if ($index->getMaxBufferedDocs() !== $maxBufferedDocs) {
+                require_once 'Zend/Search/Lucene/Exception.php';
+                throw new Zend_Search_Lucene_Exception('Indices have different default search field.');
+            }
+        }
+
+        return $maxBufferedDocs;
+    }
+
+    /**
+     * Set index maxBufferedDocs option
+     *
+     * maxBufferedDocs is a minimal number of documents required before
+     * the buffered in-memory documents are written into a new Segment
+     *
+     * Default value is 10
+     *
+     * @param integer $maxBufferedDocs
+     */
+    public function setMaxBufferedDocs($maxBufferedDocs)
+    {
+        foreach ($this->_indices as $index) {
+            $index->setMaxBufferedDocs($maxBufferedDocs);
+        }
+    }
+
+    /**
+     * Retrieve index maxMergeDocs option
+     *
+     * maxMergeDocs is a largest number of documents ever merged by addDocument().
+     * Small values (e.g., less than 10,000) are best for interactive indexing,
+     * as this limits the length of pauses while indexing to a few seconds.
+     * Larger values are best for batched indexing and speedier searches.
+     *
+     * Default value is PHP_INT_MAX
+     *
+     * @return integer
+     * @throws Zend_Search_Lucene_Exception
+     */
+    public function getMaxMergeDocs()
+    {
+        if (count($this->_indices) == 0) {
+            require_once 'Zend/Search/Lucene/Exception.php';
+            throw new Zend_Search_Lucene_Exception('Indices list is empty');
+        }
+
+        $maxMergeDocs = reset($this->_indices)->getMaxMergeDocs();
+
+        foreach ($this->_indices as $index) {
+            if ($index->getMaxMergeDocs() !== $maxMergeDocs) {
+                require_once 'Zend/Search/Lucene/Exception.php';
+                throw new Zend_Search_Lucene_Exception('Indices have different default search field.');
+            }
+        }
+
+        return $maxMergeDocs;
+    }
+
+    /**
+     * Set index maxMergeDocs option
+     *
+     * maxMergeDocs is a largest number of documents ever merged by addDocument().
+     * Small values (e.g., less than 10,000) are best for interactive indexing,
+     * as this limits the length of pauses while indexing to a few seconds.
+     * Larger values are best for batched indexing and speedier searches.
+     *
+     * Default value is PHP_INT_MAX
+     *
+     * @param integer $maxMergeDocs
+     */
+    public function setMaxMergeDocs($maxMergeDocs)
+    {
+        foreach ($this->_indices as $index) {
+            $index->setMaxMergeDocs($maxMergeDocs);
+        }
+    }
+
+    /**
+     * Retrieve index mergeFactor option
+     *
+     * mergeFactor determines how often segment indices are merged by addDocument().
+     * With smaller values, less RAM is used while indexing,
+     * and searches on unoptimized indices are faster,
+     * but indexing speed is slower.
+     * With larger values, more RAM is used during indexing,
+     * and while searches on unoptimized indices are slower,
+     * indexing is faster.
+     * Thus larger values (> 10) are best for batch index creation,
+     * and smaller values (< 10) for indices that are interactively maintained.
+     *
+     * Default value is 10
+     *
+     * @return integer
+     * @throws Zend_Search_Lucene_Exception
+     */
+    public function getMergeFactor()
+    {
+        if (count($this->_indices) == 0) {
+            require_once 'Zend/Search/Lucene/Exception.php';
+            throw new Zend_Search_Lucene_Exception('Indices list is empty');
+        }
+
+        $mergeFactor = reset($this->_indices)->getMergeFactor();
+
+        foreach ($this->_indices as $index) {
+            if ($index->getMergeFactor() !== $mergeFactor) {
+                require_once 'Zend/Search/Lucene/Exception.php';
+                throw new Zend_Search_Lucene_Exception('Indices have different default search field.');
+            }
+        }
+
+        return $mergeFactor;
+    }
+
+    /**
+     * Set index mergeFactor option
+     *
+     * mergeFactor determines how often segment indices are merged by addDocument().
+     * With smaller values, less RAM is used while indexing,
+     * and searches on unoptimized indices are faster,
+     * but indexing speed is slower.
+     * With larger values, more RAM is used during indexing,
+     * and while searches on unoptimized indices are slower,
+     * indexing is faster.
+     * Thus larger values (> 10) are best for batch index creation,
+     * and smaller values (< 10) for indices that are interactively maintained.
+     *
+     * Default value is 10
+     *
+     * @param integer $maxMergeDocs
+     */
+    public function setMergeFactor($mergeFactor)
+    {
+        foreach ($this->_indices as $index) {
+            $index->setMaxMergeDocs($maxMergeDocs);
+        }
+    }
+
+    /**
+     * Performs a query against the index and returns an array
+     * of Zend_Search_Lucene_Search_QueryHit objects.
+     * Input is a string or Zend_Search_Lucene_Search_Query.
+     *
+     * @param mixed $query
+     * @return array Zend_Search_Lucene_Search_QueryHit
+     * @throws Zend_Search_Lucene_Exception
+     */
+    public function find($query)
+    {
+        $hitsList = array();
+
+        $indexShift = 0;
+        foreach ($this->_indices as $index) {
+            $hits = $index->find($query);
+
+            if ($indexShift != 0) {
+                foreach ($hits as $hit) {
+                    $hit->id += $indexShift;
+                }
+            }
+
+            $indexShift += $index->count();
+            $hitsList[] = $hits;
+        }
+
+        /** @todo Implement advanced sorting */
+
+        return call_user_func_array('array_merge', $hitsList);
+    }
+
+    /**
+     * Returns a list of all unique field names that exist in this index.
+     *
+     * @param boolean $indexed
+     * @return array
+     */
+    public function getFieldNames($indexed = false)
+    {
+        $fieldNamesList = array();
+
+        foreach ($this->_indices as $index) {
+            $fieldNamesList[] = $index->getFieldNames($indexed);
+        }
+
+        return array_unique(call_user_func_array('array_merge', $fieldNamesList));
+    }
+
+    /**
+     * Returns a Zend_Search_Lucene_Document object for the document
+     * number $id in this index.
+     *
+     * @param integer|Zend_Search_Lucene_Search_QueryHit $id
+     * @return Zend_Search_Lucene_Document
+     * @throws Zend_Search_Lucene_Exception    Exception is thrown if $id is out of the range
+     */
+    public function getDocument($id)
+    {
+        if ($id instanceof Zend_Search_Lucene_Search_QueryHit) {
+            /* @var $id Zend_Search_Lucene_Search_QueryHit */
+            $id = $id->id;
+        }
+
+        foreach ($this->_indices as $index) {
+            $indexCount = $index->count();
+
+            if ($indexCount > $id) {
+                return $index->getDocument($id);
+            }
+
+            $id -= $indexCount;
+        }
+
+        require_once 'Zend/Search/Lucene/Exception.php';
+        throw new Zend_Search_Lucene_Exception('Document id is out of the range.');
+    }
+
+    /**
+     * Returns true if index contain documents with specified term.
+     *
+     * Is used for query optimization.
+     *
+     * @param Zend_Search_Lucene_Index_Term $term
+     * @return boolean
+     */
+    public function hasTerm(Zend_Search_Lucene_Index_Term $term)
+    {
+        foreach ($this->_indices as $index) {
+            if ($index->hasTerm($term)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns IDs of all the documents containing term.
+     *
+     * @param Zend_Search_Lucene_Index_Term $term
+     * @param Zend_Search_Lucene_Index_DocsFilter|null $docsFilter
+     * @return array
+     * @throws Zend_Search_Lucene_Exception
+     */
+    public function termDocs(Zend_Search_Lucene_Index_Term $term, $docsFilter = null)
+    {
+        if ($docsFilter != null) {
+            require_once 'Zend/Search/Lucene/Exception.php';
+            throw new Zend_Search_Lucene_Exception('Document filters could not used with multi-searcher');
+        }
+
+        $docsList = array();
+
+        $indexShift = 0;
+        foreach ($this->_indices as $index) {
+            $docs = $index->termDocs($term);
+
+            if ($indexShift != 0) {
+                foreach ($docs as $id => $docId) {
+                    $docs[$id] += $indexShift;
+                }
+            }
+
+            $indexShift += $index->count();
+            $docsList[] = $docs;
+        }
+
+        return call_user_func_array('array_merge', $docsList);
+    }
+
+    /**
+     * Returns documents filter for all documents containing term.
+     *
+     * It performs the same operation as termDocs, but return result as
+     * Zend_Search_Lucene_Index_DocsFilter object
+     *
+     * @param Zend_Search_Lucene_Index_Term $term
+     * @param Zend_Search_Lucene_Index_DocsFilter|null $docsFilter
+     * @return Zend_Search_Lucene_Index_DocsFilter
+     * @throws Zend_Search_Lucene_Exception
+     */
+    public function termDocsFilter(Zend_Search_Lucene_Index_Term $term, $docsFilter = null)
+    {
+        require_once 'Zend/Search/Lucene/Exception.php';
+        throw new Zend_Search_Lucene_Exception('Document filters could not used with multi-searcher');
+    }
+
+    /**
+     * Returns an array of all term freqs.
+     * Return array structure: array( docId => freq, ...)
+     *
+     * @param Zend_Search_Lucene_Index_Term $term
+     * @param Zend_Search_Lucene_Index_DocsFilter|null $docsFilter
+     * @return integer
+     * @throws Zend_Search_Lucene_Exception
+     */
+    public function termFreqs(Zend_Search_Lucene_Index_Term $term, $docsFilter = null)
+    {
+        if ($docsFilter != null) {
+            require_once 'Zend/Search/Lucene/Exception.php';
+            throw new Zend_Search_Lucene_Exception('Document filters could not used with multi-searcher');
+        }
+
+        $freqsList = array();
+
+        $indexShift = 0;
+        foreach ($this->_indices as $index) {
+            $freqs = $index->termFreqs($term);
+
+            if ($indexShift != 0) {
+                $freqsShifted = array();
+
+                foreach ($freqs as $docId => $freq) {
+                    $freqsShifted[$docId + $indexShift] = $freq;
+                }
+                $freqs = $freqsShifted;
+            }
+
+            $indexShift += $index->count();
+            $freqsList[] = $freqs;
+        }
+
+        return call_user_func_array('array_merge', $freqsList);
+    }
+
+    /**
+     * Returns an array of all term positions in the documents.
+     * Return array structure: array( docId => array( pos1, pos2, ...), ...)
+     *
+     * @param Zend_Search_Lucene_Index_Term $term
+     * @param Zend_Search_Lucene_Index_DocsFilter|null $docsFilter
+     * @return array
+     * @throws Zend_Search_Lucene_Exception
+     */
+    public function termPositions(Zend_Search_Lucene_Index_Term $term, $docsFilter = null)
+    {
+        if ($docsFilter != null) {
+            require_once 'Zend/Search/Lucene/Exception.php';
+            throw new Zend_Search_Lucene_Exception('Document filters could not used with multi-searcher');
+        }
+
+        $termPositionsList = array();
+
+        $indexShift = 0;
+        foreach ($this->_indices as $index) {
+            $termPositions = $index->termPositions($term);
+
+            if ($indexShift != 0) {
+                $termPositionsShifted = array();
+
+                foreach ($termPositions as $docId => $positions) {
+                    $termPositions[$docId + $indexShift] = $positions;
+                }
+                $termPositions = $termPositionsShifted;
+            }
+
+            $indexShift += $index->count();
+            $termPositionsList[] = $termPositions;
+        }
+
+        return call_user_func_array('array_merge', $termPositions);
+    }
+
+    /**
+     * Returns the number of documents in this index containing the $term.
+     *
+     * @param Zend_Search_Lucene_Index_Term $term
+     * @return integer
+     */
+    public function docFreq(Zend_Search_Lucene_Index_Term $term)
+    {
+        $docFreq = 0;
+
+        foreach ($this->_indices as $index) {
+            $docFreq += $index->docFreq($term);
+        }
+
+        return $docFreq;
+    }
+
+    /**
+     * Retrive similarity used by index reader
+     *
+     * @return Zend_Search_Lucene_Search_Similarity
+     * @throws Zend_Search_Lucene_Exception
+     */
+    public function getSimilarity()
+    {
+        if (count($this->_indices) == 0) {
+            require_once 'Zend/Search/Lucene/Exception.php';
+            throw new Zend_Search_Lucene_Exception('Indices list is empty');
+        }
+
+        $similarity = reset($this->_indices)->getSimilarity();
+
+        foreach ($this->_indices as $index) {
+            if ($index->getSimilarity() !== $similarity) {
+                require_once 'Zend/Search/Lucene/Exception.php';
+                throw new Zend_Search_Lucene_Exception('Indices have different similarity.');
+            }
+        }
+
+        return $similarity;
+    }
+
+    /**
+     * Returns a normalization factor for "field, document" pair.
+     *
+     * @param integer $id
+     * @param string $fieldName
+     * @return float
+     */
+    public function norm($id, $fieldName)
+    {
+        foreach ($this->_indices as $index) {
+            $indexCount = $index->count();
+
+            if ($indexCount > $id) {
+                return $index->norm($id, $fieldName);
+            }
+
+            $id -= $indexCount;
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns true if any documents have been deleted from this index.
+     *
+     * @return boolean
+     */
+    public function hasDeletions()
+    {
+        foreach ($this->_indices as $index) {
+            if ($index->hasDeletions()) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Deletes a document from the index.
+     * $id is an internal document id
+     *
+     * @param integer|Zend_Search_Lucene_Search_QueryHit $id
+     * @throws Zend_Search_Lucene_Exception
+     */
+    public function delete($id)
+    {
+        foreach ($this->_indices as $index) {
+            $indexCount = $index->count();
+
+            if ($indexCount > $id) {
+                $index->delete($id);
+                return;
+            }
+
+            $id -= $indexCount;
+        }
+
+        require_once 'Zend/Search/Lucene/Exception.php';
+        throw new Zend_Search_Lucene_Exception('Document id is out of the range.');
+    }
+
+
+    /**
+     * Callback used to choose target index for new documents
+     *
+     * Function/method signature:
+     *    Zend_Search_Lucene_Interface  callbackFunction(Zend_Search_Lucene_Document $document, array $indices);
+     *
+     * null means "default documents distributing algorithm"
+     *
+     * @var callback
+     */
+    protected $_documentDistributorCallBack = null;
+
+    /**
+     * Set callback for choosing target index.
+     *
+     * @param callback $callback
+     */
+    public function setDocumentDistributorCallback($callback)
+    {
+        if ($callback !== null  &&  !is_callable($callback))
+        $this->_documentDistributorCallBack = $callback;
+    }
+
+    /**
+     * Get callback for choosing target index.
+     *
+     * @return callback
+     */
+    public function getDocumentDistributorCallback()
+    {
+        return $this->_documentDistributorCallBack;
+    }
+
+    /**
+     * Adds a document to this index.
+     *
+     * @param Zend_Search_Lucene_Document $document
+     * @throws Zend_Search_Lucene_Exception
+     */
+    public function addDocument(Zend_Search_Lucene_Document $document)
+    {
+        if ($this->_documentDistributorCallBack !== null) {
+            $index = call_user_func($this->_documentDistributorCallBack, $document, $this->_indices);
+        } else {
+            $index = $this->_indices[ array_rand($this->_indices) ];
+        }
+
+        $index->addDocument($document);
+    }
+
+    /**
+     * Commit changes resulting from delete() or undeleteAll() operations.
+     */
+    public function commit()
+    {
+        foreach ($this->_indices as $index) {
+            $index->commit();
+        }
+    }
+
+    /**
+     * Optimize index.
+     *
+     * Merges all segments into one
+     */
+    public function optimize()
+    {
+        foreach ($this->_indices as $index) {
+            $index->_optimise();
+        }
+    }
+
+    /**
+     * Returns an array of all terms in this index.
+     *
+     * @return array
+     */
+    public function terms()
+    {
+        $termsList = array();
+
+        foreach ($this->_indices as $index) {
+            $termsList[] = $index->terms();
+        }
+
+        return array_unique(call_user_func_array('array_merge', $termsList));
+    }
+
+
+    /**
+     * Terms stream priority queue object
+     *
+     * @var Zend_Search_Lucene_TermStreamsPriorityQueue
+     */
+    private $_termsStream = null;
+
+    /**
+     * Reset terms stream.
+     */
+    public function resetTermsStream()
+    {
+        if ($this->_termsStream === null) {
+            $this->_termsStream = new Zend_Search_Lucene_TermStreamsPriorityQueue($this->_indices);
+        } else {
+            $this->_termsStream->resetTermsStream();
+        }
+    }
+
+    /**
+     * Skip terms stream up to specified term preffix.
+     *
+     * Prefix contains fully specified field info and portion of searched term
+     *
+     * @param Zend_Search_Lucene_Index_Term $prefix
+     */
+    public function skipTo(Zend_Search_Lucene_Index_Term $prefix)
+    {
+        $this->_termsStream->skipTo($prefix);
+    }
+
+    /**
+     * Scans terms dictionary and returns next term
+     *
+     * @return Zend_Search_Lucene_Index_Term|null
+     */
+    public function nextTerm()
+    {
+        return $this->_termsStream->nextTerm();
+    }
+
+    /**
+     * Returns term in current position
+     *
+     * @return Zend_Search_Lucene_Index_Term|null
+     */
+    public function currentTerm()
+    {
+        return $this->_termsStream->currentTerm();
+    }
+
+    /**
+     * Close terms stream
+     *
+     * Should be used for resources clean up if stream is not read up to the end
+     */
+    public function closeTermsStream()
+    {
+        $this->_termsStream->closeTermsStream();
+        $this->_termsStream = null;
+    }
+
+
+    /**
+     * Undeletes all documents currently marked as deleted in this index.
+     */
+    public function undeleteAll()
+    {
+        foreach ($this->_indices as $index) {
+            $index->undeleteAll();
+        }
+    }
+
+
+    /**
+     * Add reference to the index object
+     *
+     * @internal
+     */
+    public function addReference()
+    {
+        // Do nothing, since it's never referenced by indices
+    }
+
+    /**
+     * Remove reference from the index object
+     *
+     * When reference count becomes zero, index is closed and resources are cleaned up
+     *
+     * @internal
+     */
+    public function removeReference()
+    {
+        // Do nothing, since it's never referenced by indices
+    }
+}

+ 94 - 94
library/Zend/Search/Lucene/Search/Highlighter/Default.php

@@ -1,94 +1,94 @@
-<?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 license@zend.com so we can send you a copy immediately.
- *
- * @category   Zend
- * @package    Zend_Search_Lucene
- * @subpackage Search
- * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
- * @license    http://framework.zend.com/license/new-bsd     New BSD License
- * @version    $Id$
- */
-
-/** Zend_Search_Lucene_Search_Highlighter_Interface */
-require_once 'Zend/Search/Lucene/Search/Highlighter/Interface.php';
-/**
- * @category   Zend
- * @package    Zend_Search_Lucene
- * @subpackage Search
- * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
- * @license    http://framework.zend.com/license/new-bsd     New BSD License
- */
-class Zend_Search_Lucene_Search_Highlighter_Default implements Zend_Search_Lucene_Search_Highlighter_Interface
-{
-    /**
-     * List of colors for text highlighting
-     *
-     * @var array
-     */
-    protected $_highlightColors = array('#66ffff', '#ff66ff', '#ffff66',
-                                        '#ff8888', '#88ff88', '#8888ff',
-                                        '#88dddd', '#dd88dd', '#dddd88',
-                                        '#aaddff', '#aaffdd', '#ddaaff',
-                                        '#ddffaa', '#ffaadd', '#ffddaa');
-
-    /**
-     * Index of current color for highlighting
-     *
-     * Index is increased at each highlight() call, so terms matching different queries are highlighted using different colors.
-     *
-     * @var integer
-     */
-    protected $_currentColorIndex = 0;
-
-    /**
-     * HTML document for highlighting
-     *
-     * @var Zend_Search_Lucene_Document_Html
-     */
-    protected $_doc;
-
-    /**
-     * Set document for highlighting.
-     *
-     * @param Zend_Search_Lucene_Document_Html $document
-     */
-    public function setDocument(Zend_Search_Lucene_Document_Html $document)
-    {
-    	$this->_doc = $document;
-    }
-
-    /**
-     * Get document for highlighting.
-     *
-     * @return Zend_Search_Lucene_Document_Html $document
-     */
-    public function getDocument()
-    {
-    	return $this->_doc;
-    }
-
-    /**
-     * Highlight specified words
-     *
-     * @param string|array $words  Words to highlight. They could be organized using the array or string.
-     */
-    public function highlight($words)
-    {
-    	$color = $this->_highlightColors[$this->_currentColorIndex];
-    	$this->_currentColorIndex = ($this->_currentColorIndex + 1) % count($this->_highlightColors);
-
-    	$this->_doc->highlight($words, $color);
-    }
-
-}
+<?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 license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/** @see Zend_Search_Lucene_Search_Highlighter_Interface */
+require_once 'Zend/Search/Lucene/Search/Highlighter/Interface.php';
+/**
+ * @category   Zend
+ * @package    Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Search_Lucene_Search_Highlighter_Default implements Zend_Search_Lucene_Search_Highlighter_Interface
+{
+    /**
+     * List of colors for text highlighting
+     *
+     * @var array
+     */
+    protected $_highlightColors = array('#66ffff', '#ff66ff', '#ffff66',
+                                        '#ff8888', '#88ff88', '#8888ff',
+                                        '#88dddd', '#dd88dd', '#dddd88',
+                                        '#aaddff', '#aaffdd', '#ddaaff',
+                                        '#ddffaa', '#ffaadd', '#ffddaa');
+
+    /**
+     * Index of current color for highlighting
+     *
+     * Index is increased at each highlight() call, so terms matching different queries are highlighted using different colors.
+     *
+     * @var integer
+     */
+    protected $_currentColorIndex = 0;
+
+    /**
+     * HTML document for highlighting
+     *
+     * @var Zend_Search_Lucene_Document_Html
+     */
+    protected $_doc;
+
+    /**
+     * Set document for highlighting.
+     *
+     * @param Zend_Search_Lucene_Document_Html $document
+     */
+    public function setDocument(Zend_Search_Lucene_Document_Html $document)
+    {
+        $this->_doc = $document;
+    }
+
+    /**
+     * Get document for highlighting.
+     *
+     * @return Zend_Search_Lucene_Document_Html $document
+     */
+    public function getDocument()
+    {
+        return $this->_doc;
+    }
+
+    /**
+     * Highlight specified words
+     *
+     * @param string|array $words  Words to highlight. They could be organized using the array or string.
+     */
+    public function highlight($words)
+    {
+        $color = $this->_highlightColors[$this->_currentColorIndex];
+        $this->_currentColorIndex = ($this->_currentColorIndex + 1) % count($this->_highlightColors);
+
+        $this->_doc->highlight($words, $color);
+    }
+
+}

+ 53 - 53
library/Zend/Search/Lucene/Search/Highlighter/Interface.php

@@ -1,53 +1,53 @@
-<?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 license@zend.com so we can send you a copy immediately.
- *
- * @category   Zend
- * @package    Zend_Search_Lucene
- * @subpackage Search
- * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
- * @license    http://framework.zend.com/license/new-bsd     New BSD License
- * @version    $Id$
- */
-
-
-/**
- * @category   Zend
- * @package    Zend_Search_Lucene
- * @subpackage Search
- * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
- * @license    http://framework.zend.com/license/new-bsd     New BSD License
- */
-interface Zend_Search_Lucene_Search_Highlighter_Interface
-{
-	/**
-	 * Set document for highlighting.
-	 *
-	 * @param Zend_Search_Lucene_Document_Html $document
-     */
-	public function setDocument(Zend_Search_Lucene_Document_Html $document);
-
-    /**
-     * Get document for highlighting.
-     *
-     * @return Zend_Search_Lucene_Document_Html $document
-     */
-    public function getDocument();
-
-    /**
-     * Highlight specified words (method is invoked once per subquery)
-     *
-     * @param string|array $words  Words to highlight. They could be organized using the array or string.
-     */
-    public function highlight($words);
-}
+<?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 license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+interface Zend_Search_Lucene_Search_Highlighter_Interface
+{
+    /**
+     * Set document for highlighting.
+     *
+     * @param Zend_Search_Lucene_Document_Html $document
+     */
+    public function setDocument(Zend_Search_Lucene_Document_Html $document);
+
+    /**
+     * Get document for highlighting.
+     *
+     * @return Zend_Search_Lucene_Document_Html $document
+     */
+    public function getDocument();
+
+    /**
+     * Highlight specified words (method is invoked once per subquery)
+     *
+     * @param string|array $words  Words to highlight. They could be organized using the array or string.
+     */
+    public function highlight($words);
+}

+ 134 - 134
library/Zend/Search/Lucene/Search/Query/Preprocessing.php

@@ -1,134 +1,134 @@
-<?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 license@zend.com so we can send you a copy immediately.
- *
- * @category   Zend
- * @package    Zend_Search_Lucene
- * @subpackage Search
- * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
- * @license    http://framework.zend.com/license/new-bsd     New BSD License
- * @version    $Id$
- */
-
-
-/**
- * Zend_Search_Lucene_Search_Query
- */
-require_once 'Zend/Search/Lucene/Search/Query.php';
-
-/**
- * Zend_Search_Lucene_Search_Weight
- */
-require_once 'Zend/Search/Lucene/Search/Weight.php';
-
-
-/**
- * It's an internal abstract class intended to finalize ase a query processing after query parsing.
- * This type of query is not actually involved into query execution.
- *
- * @category   Zend
- * @package    Zend_Search_Lucene
- * @subpackage Search
- * @internal
- * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
- * @license    http://framework.zend.com/license/new-bsd     New BSD License
- */
-abstract class Zend_Search_Lucene_Search_Query_Preprocessing extends Zend_Search_Lucene_Search_Query
-{
-    /**
-     * Matched terms.
-     *
-     * Matched terms list.
-     * It's filled during rewrite operation and may be used for search result highlighting
-     *
-     * Array of Zend_Search_Lucene_Index_Term objects
-     *
-     * @var array
-     */
-    protected $_matches = null;
-
-    /**
-     * 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)
-    {
-        require_once 'Zend/Search/Lucene/Exception.php';
-        throw new Zend_Search_Lucene_Exception('This query is not intended to be executed.');
-    }
-
-    /**
-     * 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)
-    {
-        require_once 'Zend/Search/Lucene/Exception.php';
-        throw new Zend_Search_Lucene_Exception('This query is not intended to be executed.');
-    }
-
-    /**
-     * Execute query in context of index reader
-     * It also initializes necessary internal structures
-     *
-     * @param Zend_Search_Lucene_Interface $reader
-     * @param Zend_Search_Lucene_Index_DocsFilter|null $docsFilter
-     */
-    public function execute(Zend_Search_Lucene_Interface $reader, $docsFilter = null)
-    {
-        require_once 'Zend/Search/Lucene/Exception.php';
-        throw new Zend_Search_Lucene_Exception('This query is not intended to be executed.');
-    }
-
-    /**
-     * Get document ids likely matching the query
-     *
-     * It's an array with document ids as keys (performance considerations)
-     *
-     * @return array
-     */
-    public function matchedDocs()
-    {
-        require_once 'Zend/Search/Lucene/Exception.php';
-        throw new Zend_Search_Lucene_Exception('This query is not intended to be executed.');
-    }
-
-    /**
-     * Score specified document
-     *
-     * @param integer $docId
-     * @param Zend_Search_Lucene_Interface $reader
-     * @return float
-     */
-    public function score($docId, Zend_Search_Lucene_Interface $reader)
-    {
-        require_once 'Zend/Search/Lucene/Exception.php';
-        throw new Zend_Search_Lucene_Exception('This query is not intended to be executed.');
-    }
-
-    /**
-     * Return query terms
-     *
-     * @return array
-     */
-    public function getQueryTerms()
-    {
-        require_once 'Zend/Search/Lucene/Exception.php';
-        throw new Zend_Search_Lucene_Exception('Rewrite operation has to be done before retrieving query terms.');
-    }
-}
-
+<?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 license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+
+/**
+ * @see Zend_Search_Lucene_Search_Query
+ */
+require_once 'Zend/Search/Lucene/Search/Query.php';
+
+/**
+ * @see Zend_Search_Lucene_Search_Weight
+ */
+require_once 'Zend/Search/Lucene/Search/Weight.php';
+
+
+/**
+ * It's an internal abstract class intended to finalize ase a query processing after query parsing.
+ * This type of query is not actually involved into query execution.
+ *
+ * @category   Zend
+ * @package    Zend_Search_Lucene
+ * @subpackage Search
+ * @internal
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Search_Lucene_Search_Query_Preprocessing extends Zend_Search_Lucene_Search_Query
+{
+    /**
+     * Matched terms.
+     *
+     * Matched terms list.
+     * It's filled during rewrite operation and may be used for search result highlighting
+     *
+     * Array of Zend_Search_Lucene_Index_Term objects
+     *
+     * @var array
+     */
+    protected $_matches = null;
+
+    /**
+     * 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)
+    {
+        require_once 'Zend/Search/Lucene/Exception.php';
+        throw new Zend_Search_Lucene_Exception('This query is not intended to be executed.');
+    }
+
+    /**
+     * 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)
+    {
+        require_once 'Zend/Search/Lucene/Exception.php';
+        throw new Zend_Search_Lucene_Exception('This query is not intended to be executed.');
+    }
+
+    /**
+     * Execute query in context of index reader
+     * It also initializes necessary internal structures
+     *
+     * @param Zend_Search_Lucene_Interface $reader
+     * @param Zend_Search_Lucene_Index_DocsFilter|null $docsFilter
+     */
+    public function execute(Zend_Search_Lucene_Interface $reader, $docsFilter = null)
+    {
+        require_once 'Zend/Search/Lucene/Exception.php';
+        throw new Zend_Search_Lucene_Exception('This query is not intended to be executed.');
+    }
+
+    /**
+     * Get document ids likely matching the query
+     *
+     * It's an array with document ids as keys (performance considerations)
+     *
+     * @return array
+     */
+    public function matchedDocs()
+    {
+        require_once 'Zend/Search/Lucene/Exception.php';
+        throw new Zend_Search_Lucene_Exception('This query is not intended to be executed.');
+    }
+
+    /**
+     * Score specified document
+     *
+     * @param integer $docId
+     * @param Zend_Search_Lucene_Interface $reader
+     * @return float
+     */
+    public function score($docId, Zend_Search_Lucene_Interface $reader)
+    {
+        require_once 'Zend/Search/Lucene/Exception.php';
+        throw new Zend_Search_Lucene_Exception('This query is not intended to be executed.');
+    }
+
+    /**
+     * Return query terms
+     *
+     * @return array
+     */
+    public function getQueryTerms()
+    {
+        require_once 'Zend/Search/Lucene/Exception.php';
+        throw new Zend_Search_Lucene_Exception('Rewrite operation has to be done before retrieving query terms.');
+    }
+}
+

+ 287 - 287
library/Zend/Search/Lucene/Search/Query/Preprocessing/Fuzzy.php

@@ -1,287 +1,287 @@
-<?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 license@zend.com so we can send you a copy immediately.
- *
- * @category   Zend
- * @package    Zend_Search_Lucene
- * @subpackage Search
- * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
- * @license    http://framework.zend.com/license/new-bsd     New BSD License
- * @version    $Id$
- */
-
-
-/** Zend_Search_Lucene_Search_Query_Processing */
-require_once 'Zend/Search/Lucene/Search/Query/Preprocessing.php';
-
-/** Zend_Search_Lucene_Search_Query_Phrase */
-require_once 'Zend/Search/Lucene/Search/Query/Phrase.php';
-
-/** Zend_Search_Lucene_Search_Query_Insignificant */
-require_once 'Zend/Search/Lucene/Search/Query/Insignificant.php';
-
-/** Zend_Search_Lucene_Search_Query_Empty */
-require_once 'Zend/Search/Lucene/Search/Query/Empty.php';
-
-/** Zend_Search_Lucene_Search_Query_Term */
-require_once 'Zend/Search/Lucene/Search/Query/Term.php';
-
-/** Zend_Search_Lucene_Index_Term */
-require_once 'Zend/Search/Lucene/Index/Term.php';
-
-
-/**
- * It's an internal abstract class intended to finalize ase a query processing after query parsing.
- * This type of query is not actually involved into query execution.
- *
- * @category   Zend
- * @package    Zend_Search_Lucene
- * @subpackage Search
- * @internal
- * @copyright  Copyright (c) 2005-2009 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_Preprocessing_Fuzzy extends Zend_Search_Lucene_Search_Query_Preprocessing
-{
-    /**
-     * word (query parser lexeme) to find.
-     *
-     * @var string
-     */
-    private $_word;
-
-    /**
-     * Word encoding (field name is always provided using UTF-8 encoding since it may be retrieved from index).
-     *
-     * @var string
-     */
-    private $_encoding;
-
-
-    /**
-     * Field name.
-     *
-     * @var string
-     */
-    private $_field;
-
-    /**
-     * 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;
-
-    /**
-     * Class constructor.  Create a new preprocessing object for prase query.
-     *
-     * @param string $word       Non-tokenized word (query parser lexeme) to search.
-     * @param string $encoding   Word encoding.
-     * @param string $fieldName  Field name.
-     * @param float  $minimumSimilarity minimum similarity
-     */
-    public function __construct($word, $encoding, $fieldName, $minimumSimilarity)
-    {
-        $this->_word     = $word;
-        $this->_encoding = $encoding;
-        $this->_field    = $fieldName;
-        $this->_minimumSimilarity = $minimumSimilarity;
-    }
-
-    /**
-     * 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)
-    {
-        if ($this->_field === null) {
-            $query = new Zend_Search_Lucene_Search_Query_Boolean();
-
-            $hasInsignificantSubqueries = false;
-
-            if (Zend_Search_Lucene::getDefaultSearchField() === null) {
-                $searchFields = $index->getFieldNames(true);
-            } else {
-                $searchFields = array(Zend_Search_Lucene::getDefaultSearchField());
-            }
-
-            foreach ($searchFields as $fieldName) {
-                $subquery = new Zend_Search_Lucene_Search_Query_Preprocessing_Fuzzy($this->_word,
-                                                                                    $this->_encoding,
-                                                                                    $fieldName,
-                                                                                    $this->_minimumSimilarity);
-
-                $rewrittenSubquery = $subquery->rewrite($index);
-
-                if ( !($rewrittenSubquery instanceof Zend_Search_Lucene_Search_Query_Insignificant  ||
-                       $rewrittenSubquery instanceof Zend_Search_Lucene_Search_Query_Empty) ) {
-                    $query->addSubquery($rewrittenSubquery);
-                }
-
-                if ($rewrittenSubquery instanceof Zend_Search_Lucene_Search_Query_Insignificant) {
-                	$hasInsignificantSubqueries = true;
-                }
-            }
-
-            $subqueries = $query->getSubqueries();
-
-            if (count($subqueries) == 0) {
-            	$this->_matches = array();
-                if ($hasInsignificantSubqueries) {
-                    return new Zend_Search_Lucene_Search_Query_Insignificant();
-                } else {
-                    return new Zend_Search_Lucene_Search_Query_Empty();
-                }
-            }
-
-            if (count($subqueries) == 1) {
-            	$query = reset($subqueries);
-            }
-
-            $query->setBoost($this->getBoost());
-
-            $this->_matches = $query->getQueryTerms();
-            return $query;
-        }
-
-        // -------------------------------------
-        // Recognize exact term matching (it corresponds to Keyword fields stored in the index)
-        // encoding is not used since we expect binary matching
-        $term = new Zend_Search_Lucene_Index_Term($this->_word, $this->_field);
-        if ($index->hasTerm($term)) {
-            $query = new Zend_Search_Lucene_Search_Query_Fuzzy($term, $this->_minimumSimilarity);
-            $query->setBoost($this->getBoost());
-
-            // Get rewritten query. Important! It also fills terms matching container.
-            $rewrittenQuery = $query->rewrite($index);
-            $this->_matches = $query->getQueryTerms();
-
-            return $rewrittenQuery;
-        }
-
-
-        // -------------------------------------
-        // Recognize wildcard queries
-
-        /** @todo check for PCRE unicode support may be performed through Zend_Environment in some future */
-        if (@preg_match('/\pL/u', 'a') == 1) {
-        	$subPatterns = preg_split('/[*?]/u', iconv($this->_encoding, 'UTF-8', $this->_word));
-        } else {
-        	$subPatterns = preg_split('/[*?]/', $this->_word);
-        }
-        if (count($subPatterns) > 1) {
-            require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
-            throw new Zend_Search_Lucene_Search_QueryParserException('Fuzzy search doesn\'t support wildcards (except within Keyword fields).');
-        }
-
-
-        // -------------------------------------
-        // Recognize one-term multi-term and "insignificant" queries
-        $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_word, $this->_encoding);
-
-        if (count($tokens) == 0) {
-        	$this->_matches = array();
-            return new Zend_Search_Lucene_Search_Query_Insignificant();
-        }
-
-        if (count($tokens) == 1) {
-            $term  = new Zend_Search_Lucene_Index_Term($tokens[0]->getTermText(), $this->_field);
-            $query = new Zend_Search_Lucene_Search_Query_Fuzzy($term, $this->_minimumSimilarity);
-            $query->setBoost($this->getBoost());
-
-            // Get rewritten query. Important! It also fills terms matching container.
-            $rewrittenQuery = $query->rewrite($index);
-            $this->_matches = $query->getQueryTerms();
-
-            return $rewrittenQuery;
-        }
-
-        // Word is tokenized into several tokens
-        require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
-        throw new Zend_Search_Lucene_Search_QueryParserException('Fuzzy search is supported only for non-multiple word terms');
-    }
-
-    /**
-     * Query specific matches highlighting
-     *
-     * @param Zend_Search_Lucene_Search_Highlighter_Interface $highlighter  Highlighter object (also contains doc for highlighting)
-     */
-    protected function _highlightMatches(Zend_Search_Lucene_Search_Highlighter_Interface $highlighter)
-    {
-    	/** Skip fields detection. We don't need it, since we expect all fields presented in the HTML body and don't differentiate them */
-
-    	/** Skip exact term matching recognition, keyword fields highlighting is not supported */
-
-        // -------------------------------------
-        // Recognize wildcard queries
-
-        /** @todo check for PCRE unicode support may be performed through Zend_Environment in some future */
-        if (@preg_match('/\pL/u', 'a') == 1) {
-            $subPatterns = preg_split('/[*?]/u', iconv($this->_encoding, 'UTF-8', $this->_word));
-        } else {
-            $subPatterns = preg_split('/[*?]/', $this->_word);
-        }
-        if (count($subPatterns) > 1) {
-            // Do nothing
-            return;
-        }
-
-        // -------------------------------------
-        // Recognize one-term multi-term and "insignificant" queries
-        $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_word, $this->_encoding);
-        if (count($tokens) == 0) {
-            // Do nothing
-            return;
-        }
-        if (count($tokens) == 1) {
-            $term  = new Zend_Search_Lucene_Index_Term($tokens[0]->getTermText(), $this->_field);
-            $query = new Zend_Search_Lucene_Search_Query_Fuzzy($term, $this->_minimumSimilarity);
-
-            $query->_highlightMatches($highlighter);
-            return;
-        }
-
-        // Word is tokenized into several tokens
-        // But fuzzy search is supported only for non-multiple word terms
-        // Do nothing
-    }
-
-    /**
-     * Print a query
-     *
-     * @return string
-     */
-    public function __toString()
-    {
-        // It's used only for query visualisation, so we don't care about characters escaping
-        if ($this->_field !== null) {
-            $query = $this->_field . ':';
-        } else {
-            $query = '';
-        }
-
-        $query .= $this->_word;
-
-        if ($this->getBoost() != 1) {
-            $query .= '^' . round($this->getBoost(), 4);
-        }
-
-        return $query;
-    }
-}
+<?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 license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+
+/** @see Zend_Search_Lucene_Search_Query_Processing */
+require_once 'Zend/Search/Lucene/Search/Query/Preprocessing.php';
+
+/** @see Zend_Search_Lucene_Search_Query_Phrase */
+require_once 'Zend/Search/Lucene/Search/Query/Phrase.php';
+
+/** @see Zend_Search_Lucene_Search_Query_Insignificant */
+require_once 'Zend/Search/Lucene/Search/Query/Insignificant.php';
+
+/** @see Zend_Search_Lucene_Search_Query_Empty */
+require_once 'Zend/Search/Lucene/Search/Query/Empty.php';
+
+/** @see Zend_Search_Lucene_Search_Query_Term */
+require_once 'Zend/Search/Lucene/Search/Query/Term.php';
+
+/** @see Zend_Search_Lucene_Index_Term */
+require_once 'Zend/Search/Lucene/Index/Term.php';
+
+
+/**
+ * It's an internal abstract class intended to finalize ase a query processing after query parsing.
+ * This type of query is not actually involved into query execution.
+ *
+ * @category   Zend
+ * @package    Zend_Search_Lucene
+ * @subpackage Search
+ * @internal
+ * @copyright  Copyright (c) 2005-2009 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_Preprocessing_Fuzzy extends Zend_Search_Lucene_Search_Query_Preprocessing
+{
+    /**
+     * word (query parser lexeme) to find.
+     *
+     * @var string
+     */
+    private $_word;
+
+    /**
+     * Word encoding (field name is always provided using UTF-8 encoding since it may be retrieved from index).
+     *
+     * @var string
+     */
+    private $_encoding;
+
+
+    /**
+     * Field name.
+     *
+     * @var string
+     */
+    private $_field;
+
+    /**
+     * 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;
+
+    /**
+     * Class constructor.  Create a new preprocessing object for prase query.
+     *
+     * @param string $word       Non-tokenized word (query parser lexeme) to search.
+     * @param string $encoding   Word encoding.
+     * @param string $fieldName  Field name.
+     * @param float  $minimumSimilarity minimum similarity
+     */
+    public function __construct($word, $encoding, $fieldName, $minimumSimilarity)
+    {
+        $this->_word     = $word;
+        $this->_encoding = $encoding;
+        $this->_field    = $fieldName;
+        $this->_minimumSimilarity = $minimumSimilarity;
+    }
+
+    /**
+     * 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)
+    {
+        if ($this->_field === null) {
+            $query = new Zend_Search_Lucene_Search_Query_Boolean();
+
+            $hasInsignificantSubqueries = false;
+
+            if (Zend_Search_Lucene::getDefaultSearchField() === null) {
+                $searchFields = $index->getFieldNames(true);
+            } else {
+                $searchFields = array(Zend_Search_Lucene::getDefaultSearchField());
+            }
+
+            foreach ($searchFields as $fieldName) {
+                $subquery = new Zend_Search_Lucene_Search_Query_Preprocessing_Fuzzy($this->_word,
+                                                                                    $this->_encoding,
+                                                                                    $fieldName,
+                                                                                    $this->_minimumSimilarity);
+
+                $rewrittenSubquery = $subquery->rewrite($index);
+
+                if ( !($rewrittenSubquery instanceof Zend_Search_Lucene_Search_Query_Insignificant  ||
+                       $rewrittenSubquery instanceof Zend_Search_Lucene_Search_Query_Empty) ) {
+                    $query->addSubquery($rewrittenSubquery);
+                }
+
+                if ($rewrittenSubquery instanceof Zend_Search_Lucene_Search_Query_Insignificant) {
+                    $hasInsignificantSubqueries = true;
+                }
+            }
+
+            $subqueries = $query->getSubqueries();
+
+            if (count($subqueries) == 0) {
+                $this->_matches = array();
+                if ($hasInsignificantSubqueries) {
+                    return new Zend_Search_Lucene_Search_Query_Insignificant();
+                } else {
+                    return new Zend_Search_Lucene_Search_Query_Empty();
+                }
+            }
+
+            if (count($subqueries) == 1) {
+                $query = reset($subqueries);
+            }
+
+            $query->setBoost($this->getBoost());
+
+            $this->_matches = $query->getQueryTerms();
+            return $query;
+        }
+
+        // -------------------------------------
+        // Recognize exact term matching (it corresponds to Keyword fields stored in the index)
+        // encoding is not used since we expect binary matching
+        $term = new Zend_Search_Lucene_Index_Term($this->_word, $this->_field);
+        if ($index->hasTerm($term)) {
+            $query = new Zend_Search_Lucene_Search_Query_Fuzzy($term, $this->_minimumSimilarity);
+            $query->setBoost($this->getBoost());
+
+            // Get rewritten query. Important! It also fills terms matching container.
+            $rewrittenQuery = $query->rewrite($index);
+            $this->_matches = $query->getQueryTerms();
+
+            return $rewrittenQuery;
+        }
+
+
+        // -------------------------------------
+        // Recognize wildcard queries
+
+        /** @todo check for PCRE unicode support may be performed through Zend_Environment in some future */
+        if (@preg_match('/\pL/u', 'a') == 1) {
+            $subPatterns = preg_split('/[*?]/u', iconv($this->_encoding, 'UTF-8', $this->_word));
+        } else {
+            $subPatterns = preg_split('/[*?]/', $this->_word);
+        }
+        if (count($subPatterns) > 1) {
+            require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
+            throw new Zend_Search_Lucene_Search_QueryParserException('Fuzzy search doesn\'t support wildcards (except within Keyword fields).');
+        }
+
+
+        // -------------------------------------
+        // Recognize one-term multi-term and "insignificant" queries
+        $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_word, $this->_encoding);
+
+        if (count($tokens) == 0) {
+            $this->_matches = array();
+            return new Zend_Search_Lucene_Search_Query_Insignificant();
+        }
+
+        if (count($tokens) == 1) {
+            $term  = new Zend_Search_Lucene_Index_Term($tokens[0]->getTermText(), $this->_field);
+            $query = new Zend_Search_Lucene_Search_Query_Fuzzy($term, $this->_minimumSimilarity);
+            $query->setBoost($this->getBoost());
+
+            // Get rewritten query. Important! It also fills terms matching container.
+            $rewrittenQuery = $query->rewrite($index);
+            $this->_matches = $query->getQueryTerms();
+
+            return $rewrittenQuery;
+        }
+
+        // Word is tokenized into several tokens
+        require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
+        throw new Zend_Search_Lucene_Search_QueryParserException('Fuzzy search is supported only for non-multiple word terms');
+    }
+
+    /**
+     * Query specific matches highlighting
+     *
+     * @param Zend_Search_Lucene_Search_Highlighter_Interface $highlighter  Highlighter object (also contains doc for highlighting)
+     */
+    protected function _highlightMatches(Zend_Search_Lucene_Search_Highlighter_Interface $highlighter)
+    {
+        /** Skip fields detection. We don't need it, since we expect all fields presented in the HTML body and don't differentiate them */
+
+        /** Skip exact term matching recognition, keyword fields highlighting is not supported */
+
+        // -------------------------------------
+        // Recognize wildcard queries
+
+        /** @todo check for PCRE unicode support may be performed through Zend_Environment in some future */
+        if (@preg_match('/\pL/u', 'a') == 1) {
+            $subPatterns = preg_split('/[*?]/u', iconv($this->_encoding, 'UTF-8', $this->_word));
+        } else {
+            $subPatterns = preg_split('/[*?]/', $this->_word);
+        }
+        if (count($subPatterns) > 1) {
+            // Do nothing
+            return;
+        }
+
+        // -------------------------------------
+        // Recognize one-term multi-term and "insignificant" queries
+        $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_word, $this->_encoding);
+        if (count($tokens) == 0) {
+            // Do nothing
+            return;
+        }
+        if (count($tokens) == 1) {
+            $term  = new Zend_Search_Lucene_Index_Term($tokens[0]->getTermText(), $this->_field);
+            $query = new Zend_Search_Lucene_Search_Query_Fuzzy($term, $this->_minimumSimilarity);
+
+            $query->_highlightMatches($highlighter);
+            return;
+        }
+
+        // Word is tokenized into several tokens
+        // But fuzzy search is supported only for non-multiple word terms
+        // Do nothing
+    }
+
+    /**
+     * Print a query
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        // It's used only for query visualisation, so we don't care about characters escaping
+        if ($this->_field !== null) {
+            $query = $this->_field . ':';
+        } else {
+            $query = '';
+        }
+
+        $query .= $this->_word;
+
+        if ($this->getBoost() != 1) {
+            $query .= '^' . round($this->getBoost(), 4);
+        }
+
+        return $query;
+    }
+}

+ 274 - 274
library/Zend/Search/Lucene/Search/Query/Preprocessing/Phrase.php

@@ -1,274 +1,274 @@
-<?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 license@zend.com so we can send you a copy immediately.
- *
- * @category   Zend
- * @package    Zend_Search_Lucene
- * @subpackage Search
- * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
- * @license    http://framework.zend.com/license/new-bsd     New BSD License
- * @version    $Id$
- */
-
-
-/** Zend_Search_Lucene_Search_Query_Processing */
-require_once 'Zend/Search/Lucene/Search/Query/Preprocessing.php';
-
-/** Zend_Search_Lucene_Search_Query_Phrase */
-require_once 'Zend/Search/Lucene/Search/Query/Phrase.php';
-
-/** Zend_Search_Lucene_Search_Query_Insignificant */
-require_once 'Zend/Search/Lucene/Search/Query/Insignificant.php';
-
-/** Zend_Search_Lucene_Search_Query_Empty */
-require_once 'Zend/Search/Lucene/Search/Query/Empty.php';
-
-/** Zend_Search_Lucene_Search_Query_Term */
-require_once 'Zend/Search/Lucene/Search/Query/Term.php';
-
-/** Zend_Search_Lucene_Index_Term */
-require_once 'Zend/Search/Lucene/Index/Term.php';
-
-
-/**
- * It's an internal abstract class intended to finalize ase a query processing after query parsing.
- * This type of query is not actually involved into query execution.
- *
- * @category   Zend
- * @package    Zend_Search_Lucene
- * @subpackage Search
- * @internal
- * @copyright  Copyright (c) 2005-2009 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_Preprocessing_Phrase extends Zend_Search_Lucene_Search_Query_Preprocessing
-{
-    /**
-     * Phrase to find.
-     *
-     * @var string
-     */
-    private $_phrase;
-
-    /**
-     * Phrase encoding (field name is always provided using UTF-8 encoding since it may be retrieved from index).
-     *
-     * @var string
-     */
-    private $_phraseEncoding;
-
-
-    /**
-     * Field name.
-     *
-     * @var string
-     */
-    private $_field;
-
-    /**
-     * Sets the number of other words permitted between words in query phrase.
-     * If zero, then this is an exact phrase search.  For larger values this works
-     * like a WITHIN or NEAR operator.
-     *
-     * The slop is in fact an edit-distance, where the units correspond to
-     * moves of terms in the query phrase out of position.  For example, to switch
-     * the order of two words requires two moves (the first move places the words
-     * atop one another), so to permit re-orderings of phrases, the slop must be
-     * at least two.
-     * More exact matches are scored higher than sloppier matches, thus search
-     * results are sorted by exactness.
-     *
-     * The slop is zero by default, requiring exact matches.
-     *
-     * @var integer
-     */
-    private $_slop;
-
-    /**
-     * Class constructor.  Create a new preprocessing object for prase query.
-     *
-     * @param string $phrase          Phrase to search.
-     * @param string $phraseEncoding  Phrase encoding.
-     * @param string $fieldName       Field name.
-     */
-    public function __construct($phrase, $phraseEncoding, $fieldName)
-    {
-    	$this->_phrase         = $phrase;
-    	$this->_phraseEncoding = $phraseEncoding;
-    	$this->_field          = $fieldName;
-    }
-
-    /**
-     * Set slop
-     *
-     * @param integer $slop
-     */
-    public function setSlop($slop)
-    {
-        $this->_slop = $slop;
-    }
-
-
-    /**
-     * Get slop
-     *
-     * @return integer
-     */
-    public function getSlop()
-    {
-        return $this->_slop;
-    }
-
-    /**
-     * 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)
-    {
-// Allow to use wildcards within phrases
-// They are either removed by text analyzer or used as a part of keyword for keyword fields
-//
-//        if (strpos($this->_phrase, '?') !== false || strpos($this->_phrase, '*') !== false) {
-//            require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
-//            throw new Zend_Search_Lucene_Search_QueryParserException('Wildcards are only allowed in a single terms.');
-//        }
-
-    	// Split query into subqueries if field name is not specified
-    	if ($this->_field === null) {
-    		$query = new Zend_Search_Lucene_Search_Query_Boolean();
-            $query->setBoost($this->getBoost());
-
-            if (Zend_Search_Lucene::getDefaultSearchField() === null) {
-                $searchFields = $index->getFieldNames(true);
-            } else {
-                $searchFields = array(Zend_Search_Lucene::getDefaultSearchField());
-            }
-
-            foreach ($searchFields as $fieldName) {
-                $subquery = new Zend_Search_Lucene_Search_Query_Preprocessing_Phrase($this->_phrase,
-                                                                                     $this->_phraseEncoding,
-                                                                                     $fieldName);
-                $subquery->setSlop($this->getSlop());
-
-                $query->addSubquery($subquery->rewrite($index));
-            }
-
-            $this->_matches = $query->getQueryTerms();
-            return $query;
-    	}
-
-    	// Recognize exact term matching (it corresponds to Keyword fields stored in the index)
-    	// encoding is not used since we expect binary matching
-    	$term = new Zend_Search_Lucene_Index_Term($this->_phrase, $this->_field);
-    	if ($index->hasTerm($term)) {
-            $query = new Zend_Search_Lucene_Search_Query_Term($term);
-    		$query->setBoost($this->getBoost());
-
-    		$this->_matches = $query->getQueryTerms();
-    		return $query;
-    	}
-
-
-    	// tokenize phrase using current analyzer and process it as a phrase query
-        $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_phrase, $this->_phraseEncoding);
-
-        if (count($tokens) == 0) {
-        	$this->_matches = array();
-            return new Zend_Search_Lucene_Search_Query_Insignificant();
-        }
-
-        if (count($tokens) == 1) {
-            $term  = new Zend_Search_Lucene_Index_Term($tokens[0]->getTermText(), $this->_field);
-            $query = new Zend_Search_Lucene_Search_Query_Term($term);
-            $query->setBoost($this->getBoost());
-
-            $this->_matches = $query->getQueryTerms();
-            return $query;
-        }
-
-        //It's non-trivial phrase query
-        $position = -1;
-        $query = new Zend_Search_Lucene_Search_Query_Phrase();
-        foreach ($tokens as $token) {
-            $position += $token->getPositionIncrement();
-            $term = new Zend_Search_Lucene_Index_Term($token->getTermText(), $this->_field);
-            $query->addTerm($term, $position);
-            $query->setSlop($this->getSlop());
-        }
-        $this->_matches = $query->getQueryTerms();
-        return $query;
-    }
-
-    /**
-     * Query specific matches highlighting
-     *
-     * @param Zend_Search_Lucene_Search_Highlighter_Interface $highlighter  Highlighter object (also contains doc for highlighting)
-     */
-    protected function _highlightMatches(Zend_Search_Lucene_Search_Highlighter_Interface $highlighter)
-    {
-    	/** Skip fields detection. We don't need it, since we expect all fields presented in the HTML body and don't differentiate them */
-
-        /** Skip exact term matching recognition, keyword fields highlighting is not supported */
-
-        /** Skip wildcard queries recognition. Supported wildcards are removed by text analyzer */
-
-        // tokenize phrase using current analyzer and process it as a phrase query
-        $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_phrase, $this->_phraseEncoding);
-
-        if (count($tokens) == 0) {
-            // Do nothing
-            return;
-        }
-
-        if (count($tokens) == 1) {
-            $highlighter->highlight($tokens[0]->getTermText());
-            return;
-        }
-
-        //It's non-trivial phrase query
-        $words = array();
-        foreach ($tokens as $token) {
-            $words[] = $token->getTermText();
-        }
-        $highlighter->highlight($words);
-    }
-
-    /**
-     * Print a query
-     *
-     * @return string
-     */
-    public function __toString()
-    {
-        // It's used only for query visualisation, so we don't care about characters escaping
-        if ($this->_field !== null) {
-            $query = $this->_field . ':';
-        } else {
-        	$query = '';
-        }
-
-        $query .= '"' . $this->_phrase . '"';
-
-        if ($this->_slop != 0) {
-            $query .= '~' . $this->_slop;
-        }
-
-        if ($this->getBoost() != 1) {
-            $query .= '^' . round($this->getBoost(), 4);
-        }
-
-        return $query;
-    }
-}
+<?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 license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+
+/** @see Zend_Search_Lucene_Search_Query_Processing */
+require_once 'Zend/Search/Lucene/Search/Query/Preprocessing.php';
+
+/** @see Zend_Search_Lucene_Search_Query_Phrase */
+require_once 'Zend/Search/Lucene/Search/Query/Phrase.php';
+
+/** @see Zend_Search_Lucene_Search_Query_Insignificant */
+require_once 'Zend/Search/Lucene/Search/Query/Insignificant.php';
+
+/** @see Zend_Search_Lucene_Search_Query_Empty */
+require_once 'Zend/Search/Lucene/Search/Query/Empty.php';
+
+/** @see Zend_Search_Lucene_Search_Query_Term */
+require_once 'Zend/Search/Lucene/Search/Query/Term.php';
+
+/** @see Zend_Search_Lucene_Index_Term */
+require_once 'Zend/Search/Lucene/Index/Term.php';
+
+
+/**
+ * It's an internal abstract class intended to finalize ase a query processing after query parsing.
+ * This type of query is not actually involved into query execution.
+ *
+ * @category   Zend
+ * @package    Zend_Search_Lucene
+ * @subpackage Search
+ * @internal
+ * @copyright  Copyright (c) 2005-2009 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_Preprocessing_Phrase extends Zend_Search_Lucene_Search_Query_Preprocessing
+{
+    /**
+     * Phrase to find.
+     *
+     * @var string
+     */
+    private $_phrase;
+
+    /**
+     * Phrase encoding (field name is always provided using UTF-8 encoding since it may be retrieved from index).
+     *
+     * @var string
+     */
+    private $_phraseEncoding;
+
+
+    /**
+     * Field name.
+     *
+     * @var string
+     */
+    private $_field;
+
+    /**
+     * Sets the number of other words permitted between words in query phrase.
+     * If zero, then this is an exact phrase search.  For larger values this works
+     * like a WITHIN or NEAR operator.
+     *
+     * The slop is in fact an edit-distance, where the units correspond to
+     * moves of terms in the query phrase out of position.  For example, to switch
+     * the order of two words requires two moves (the first move places the words
+     * atop one another), so to permit re-orderings of phrases, the slop must be
+     * at least two.
+     * More exact matches are scored higher than sloppier matches, thus search
+     * results are sorted by exactness.
+     *
+     * The slop is zero by default, requiring exact matches.
+     *
+     * @var integer
+     */
+    private $_slop;
+
+    /**
+     * Class constructor.  Create a new preprocessing object for prase query.
+     *
+     * @param string $phrase          Phrase to search.
+     * @param string $phraseEncoding  Phrase encoding.
+     * @param string $fieldName       Field name.
+     */
+    public function __construct($phrase, $phraseEncoding, $fieldName)
+    {
+        $this->_phrase         = $phrase;
+        $this->_phraseEncoding = $phraseEncoding;
+        $this->_field          = $fieldName;
+    }
+
+    /**
+     * Set slop
+     *
+     * @param integer $slop
+     */
+    public function setSlop($slop)
+    {
+        $this->_slop = $slop;
+    }
+
+
+    /**
+     * Get slop
+     *
+     * @return integer
+     */
+    public function getSlop()
+    {
+        return $this->_slop;
+    }
+
+    /**
+     * 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)
+    {
+// Allow to use wildcards within phrases
+// They are either removed by text analyzer or used as a part of keyword for keyword fields
+//
+//        if (strpos($this->_phrase, '?') !== false || strpos($this->_phrase, '*') !== false) {
+//            require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
+//            throw new Zend_Search_Lucene_Search_QueryParserException('Wildcards are only allowed in a single terms.');
+//        }
+
+        // Split query into subqueries if field name is not specified
+        if ($this->_field === null) {
+            $query = new Zend_Search_Lucene_Search_Query_Boolean();
+            $query->setBoost($this->getBoost());
+
+            if (Zend_Search_Lucene::getDefaultSearchField() === null) {
+                $searchFields = $index->getFieldNames(true);
+            } else {
+                $searchFields = array(Zend_Search_Lucene::getDefaultSearchField());
+            }
+
+            foreach ($searchFields as $fieldName) {
+                $subquery = new Zend_Search_Lucene_Search_Query_Preprocessing_Phrase($this->_phrase,
+                                                                                     $this->_phraseEncoding,
+                                                                                     $fieldName);
+                $subquery->setSlop($this->getSlop());
+
+                $query->addSubquery($subquery->rewrite($index));
+            }
+
+            $this->_matches = $query->getQueryTerms();
+            return $query;
+        }
+
+        // Recognize exact term matching (it corresponds to Keyword fields stored in the index)
+        // encoding is not used since we expect binary matching
+        $term = new Zend_Search_Lucene_Index_Term($this->_phrase, $this->_field);
+        if ($index->hasTerm($term)) {
+            $query = new Zend_Search_Lucene_Search_Query_Term($term);
+            $query->setBoost($this->getBoost());
+
+            $this->_matches = $query->getQueryTerms();
+            return $query;
+        }
+
+
+        // tokenize phrase using current analyzer and process it as a phrase query
+        $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_phrase, $this->_phraseEncoding);
+
+        if (count($tokens) == 0) {
+            $this->_matches = array();
+            return new Zend_Search_Lucene_Search_Query_Insignificant();
+        }
+
+        if (count($tokens) == 1) {
+            $term  = new Zend_Search_Lucene_Index_Term($tokens[0]->getTermText(), $this->_field);
+            $query = new Zend_Search_Lucene_Search_Query_Term($term);
+            $query->setBoost($this->getBoost());
+
+            $this->_matches = $query->getQueryTerms();
+            return $query;
+        }
+
+        //It's non-trivial phrase query
+        $position = -1;
+        $query = new Zend_Search_Lucene_Search_Query_Phrase();
+        foreach ($tokens as $token) {
+            $position += $token->getPositionIncrement();
+            $term = new Zend_Search_Lucene_Index_Term($token->getTermText(), $this->_field);
+            $query->addTerm($term, $position);
+            $query->setSlop($this->getSlop());
+        }
+        $this->_matches = $query->getQueryTerms();
+        return $query;
+    }
+
+    /**
+     * Query specific matches highlighting
+     *
+     * @param Zend_Search_Lucene_Search_Highlighter_Interface $highlighter  Highlighter object (also contains doc for highlighting)
+     */
+    protected function _highlightMatches(Zend_Search_Lucene_Search_Highlighter_Interface $highlighter)
+    {
+        /** Skip fields detection. We don't need it, since we expect all fields presented in the HTML body and don't differentiate them */
+
+        /** Skip exact term matching recognition, keyword fields highlighting is not supported */
+
+        /** Skip wildcard queries recognition. Supported wildcards are removed by text analyzer */
+
+        // tokenize phrase using current analyzer and process it as a phrase query
+        $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_phrase, $this->_phraseEncoding);
+
+        if (count($tokens) == 0) {
+            // Do nothing
+            return;
+        }
+
+        if (count($tokens) == 1) {
+            $highlighter->highlight($tokens[0]->getTermText());
+            return;
+        }
+
+        //It's non-trivial phrase query
+        $words = array();
+        foreach ($tokens as $token) {
+            $words[] = $token->getTermText();
+        }
+        $highlighter->highlight($words);
+    }
+
+    /**
+     * Print a query
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        // It's used only for query visualisation, so we don't care about characters escaping
+        if ($this->_field !== null) {
+            $query = $this->_field . ':';
+        } else {
+            $query = '';
+        }
+
+        $query .= '"' . $this->_phrase . '"';
+
+        if ($this->_slop != 0) {
+            $query .= '~' . $this->_slop;
+        }
+
+        if ($this->getBoost() != 1) {
+            $query .= '^' . round($this->getBoost(), 4);
+        }
+
+        return $query;
+    }
+}

+ 335 - 335
library/Zend/Search/Lucene/Search/Query/Preprocessing/Term.php

@@ -1,335 +1,335 @@
-<?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 license@zend.com so we can send you a copy immediately.
- *
- * @category   Zend
- * @package    Zend_Search_Lucene
- * @subpackage Search
- * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
- * @license    http://framework.zend.com/license/new-bsd     New BSD License
- * @version    $Id$
- */
-
-
-/** Zend_Search_Lucene_Search_Query_Processing */
-require_once 'Zend/Search/Lucene/Search/Query/Preprocessing.php';
-
-/** Zend_Search_Lucene_Search_Query_Phrase */
-require_once 'Zend/Search/Lucene/Search/Query/Phrase.php';
-
-/** Zend_Search_Lucene_Search_Query_Insignificant */
-require_once 'Zend/Search/Lucene/Search/Query/Insignificant.php';
-
-/** Zend_Search_Lucene_Search_Query_Empty */
-require_once 'Zend/Search/Lucene/Search/Query/Empty.php';
-
-/** Zend_Search_Lucene_Search_Query_Term */
-require_once 'Zend/Search/Lucene/Search/Query/Term.php';
-
-/** Zend_Search_Lucene_Index_Term */
-require_once 'Zend/Search/Lucene/Index/Term.php';
-
-
-/**
- * It's an internal abstract class intended to finalize ase a query processing after query parsing.
- * This type of query is not actually involved into query execution.
- *
- * @category   Zend
- * @package    Zend_Search_Lucene
- * @subpackage Search
- * @internal
- * @copyright  Copyright (c) 2005-2009 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_Preprocessing_Term extends Zend_Search_Lucene_Search_Query_Preprocessing
-{
-    /**
-     * word (query parser lexeme) to find.
-     *
-     * @var string
-     */
-    private $_word;
-
-    /**
-     * Word encoding (field name is always provided using UTF-8 encoding since it may be retrieved from index).
-     *
-     * @var string
-     */
-    private $_encoding;
-
-
-    /**
-     * Field name.
-     *
-     * @var string
-     */
-    private $_field;
-
-    /**
-     * Class constructor.  Create a new preprocessing object for prase query.
-     *
-     * @param string $word       Non-tokenized word (query parser lexeme) to search.
-     * @param string $encoding   Word encoding.
-     * @param string $fieldName  Field name.
-     */
-    public function __construct($word, $encoding, $fieldName)
-    {
-        $this->_word     = $word;
-        $this->_encoding = $encoding;
-        $this->_field    = $fieldName;
-    }
-
-    /**
-     * 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)
-    {
-        if ($this->_field === null) {
-            $query = new Zend_Search_Lucene_Search_Query_MultiTerm();
-            $query->setBoost($this->getBoost());
-
-            $hasInsignificantSubqueries = false;
-
-            if (Zend_Search_Lucene::getDefaultSearchField() === null) {
-            	$searchFields = $index->getFieldNames(true);
-            } else {
-            	$searchFields = array(Zend_Search_Lucene::getDefaultSearchField());
-            }
-
-            foreach ($searchFields as $fieldName) {
-            	$subquery = new Zend_Search_Lucene_Search_Query_Preprocessing_Term($this->_word,
-                                                                                   $this->_encoding,
-                                                                                   $fieldName);
-                $rewrittenSubquery = $subquery->rewrite($index);
-                foreach ($rewrittenSubquery->getQueryTerms() as $term) {
-                	$query->addTerm($term);
-                }
-
-                if ($rewrittenSubquery instanceof Zend_Search_Lucene_Search_Query_Insignificant) {
-                	$hasInsignificantSubqueries = true;
-                }
-            }
-
-            if (count($query->getTerms()) == 0) {
-            	$this->_matches = array();
-            	if ($hasInsignificantSubqueries) {
-            		return new Zend_Search_Lucene_Search_Query_Insignificant();
-            	} else {
-            		return new Zend_Search_Lucene_Search_Query_Empty();
-            	}
-            }
-
-            $this->_matches = $query->getQueryTerms();
-            return $query;
-        }
-
-        // -------------------------------------
-        // Recognize exact term matching (it corresponds to Keyword fields stored in the index)
-        // encoding is not used since we expect binary matching
-        $term = new Zend_Search_Lucene_Index_Term($this->_word, $this->_field);
-        if ($index->hasTerm($term)) {
-            $query = new Zend_Search_Lucene_Search_Query_Term($term);
-            $query->setBoost($this->getBoost());
-
-            $this->_matches = $query->getQueryTerms();
-            return $query;
-        }
-
-
-        // -------------------------------------
-        // Recognize wildcard queries
-
-        /** @todo check for PCRE unicode support may be performed through Zend_Environment in some future */
-        if (@preg_match('/\pL/u', 'a') == 1) {
-        	$word = iconv($this->_encoding, 'UTF-8', $this->_word);
-        	$wildcardsPattern = '/[*?]/u';
-        	$subPatternsEncoding = 'UTF-8';
-        } else {
-        	$word = $this->_word;
-        	$wildcardsPattern = '/[*?]/';
-            $subPatternsEncoding = $this->_encoding;
-        }
-
-        $subPatterns = preg_split($wildcardsPattern, $word, -1, PREG_SPLIT_OFFSET_CAPTURE);
-
-        if (count($subPatterns) > 1) {
-        	// Wildcard query is recognized
-
-        	$pattern = '';
-
-            foreach ($subPatterns as $id => $subPattern) {
-            	// Append corresponding wildcard character to the pattern before each sub-pattern (except first)
-                if ($id != 0) {
-                	$pattern .= $word[ $subPattern[1] - 1 ];
-                }
-
-                // Check if each subputtern is a single word in terms of current analyzer
-                $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($subPattern[0], $subPatternsEncoding);
-                if (count($tokens) > 1) {
-                    require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
-                    throw new Zend_Search_Lucene_Search_QueryParserException('Wildcard search is supported only for non-multiple word terms');
-                }
-                foreach ($tokens as $token) {
-                    $pattern .= $token->getTermText();
-                }
-            }
-
-            $term  = new Zend_Search_Lucene_Index_Term($pattern, $this->_field);
-            $query = new Zend_Search_Lucene_Search_Query_Wildcard($term);
-            $query->setBoost($this->getBoost());
-
-            // Get rewritten query. Important! It also fills terms matching container.
-            $rewrittenQuery = $query->rewrite($index);
-            $this->_matches = $query->getQueryTerms();
-
-            return $rewrittenQuery;
-        }
-
-
-        // -------------------------------------
-        // Recognize one-term multi-term and "insignificant" queries
-        $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_word, $this->_encoding);
-
-        if (count($tokens) == 0) {
-        	$this->_matches = array();
-            return new Zend_Search_Lucene_Search_Query_Insignificant();
-        }
-
-        if (count($tokens) == 1) {
-            $term  = new Zend_Search_Lucene_Index_Term($tokens[0]->getTermText(), $this->_field);
-            $query = new Zend_Search_Lucene_Search_Query_Term($term);
-            $query->setBoost($this->getBoost());
-
-            $this->_matches = $query->getQueryTerms();
-            return $query;
-        }
-
-        //It's not insignificant or one term query
-        $query = new Zend_Search_Lucene_Search_Query_MultiTerm();
-
-        /**
-         * @todo Process $token->getPositionIncrement() to support stemming, synonyms and other
-         * analizer design features
-         */
-        foreach ($tokens as $token) {
-            $term = new Zend_Search_Lucene_Index_Term($token->getTermText(), $this->_field);
-            $query->addTerm($term, true); // all subterms are required
-        }
-
-        $query->setBoost($this->getBoost());
-
-        $this->_matches = $query->getQueryTerms();
-        return $query;
-    }
-
-    /**
-     * Query specific matches highlighting
-     *
-     * @param Zend_Search_Lucene_Search_Highlighter_Interface $highlighter  Highlighter object (also contains doc for highlighting)
-     */
-    protected function _highlightMatches(Zend_Search_Lucene_Search_Highlighter_Interface $highlighter)
-    {
-        /** Skip fields detection. We don't need it, since we expect all fields presented in the HTML body and don't differentiate them */
-
-        /** Skip exact term matching recognition, keyword fields highlighting is not supported */
-
-        // -------------------------------------
-        // Recognize wildcard queries
-        /** @todo check for PCRE unicode support may be performed through Zend_Environment in some future */
-        if (@preg_match('/\pL/u', 'a') == 1) {
-            $word = iconv($this->_encoding, 'UTF-8', $this->_word);
-            $wildcardsPattern = '/[*?]/u';
-            $subPatternsEncoding = 'UTF-8';
-        } else {
-            $word = $this->_word;
-            $wildcardsPattern = '/[*?]/';
-            $subPatternsEncoding = $this->_encoding;
-        }
-        $subPatterns = preg_split($wildcardsPattern, $word, -1, PREG_SPLIT_OFFSET_CAPTURE);
-        if (count($subPatterns) > 1) {
-            // Wildcard query is recognized
-
-            $pattern = '';
-
-            foreach ($subPatterns as $id => $subPattern) {
-                // Append corresponding wildcard character to the pattern before each sub-pattern (except first)
-                if ($id != 0) {
-                    $pattern .= $word[ $subPattern[1] - 1 ];
-                }
-
-                // Check if each subputtern is a single word in terms of current analyzer
-                $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($subPattern[0], $subPatternsEncoding);
-                if (count($tokens) > 1) {
-                	// Do nothing (nothing is highlighted)
-                    return;
-                }
-                foreach ($tokens as $token) {
-                    $pattern .= $token->getTermText();
-                }
-            }
-
-            $term  = new Zend_Search_Lucene_Index_Term($pattern, $this->_field);
-            $query = new Zend_Search_Lucene_Search_Query_Wildcard($term);
-
-            $query->_highlightMatches($highlighter);
-            return;
-        }
-
-        // -------------------------------------
-        // Recognize one-term multi-term and "insignificant" queries
-        $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_word, $this->_encoding);
-
-        if (count($tokens) == 0) {
-            // Do nothing
-            return;
-        }
-
-        if (count($tokens) == 1) {
-            $highlighter->highlight($tokens[0]->getTermText());
-            return;
-        }
-
-        //It's not insignificant or one term query
-        $words = array();
-        foreach ($tokens as $token) {
-            $words[] = $token->getTermText();
-        }
-        $highlighter->highlight($words);
-    }
-
-    /**
-     * Print a query
-     *
-     * @return string
-     */
-    public function __toString()
-    {
-        // It's used only for query visualisation, so we don't care about characters escaping
-        if ($this->_field !== null) {
-            $query = $this->_field . ':';
-        } else {
-            $query = '';
-        }
-
-        $query .= $this->_word;
-
-        if ($this->getBoost() != 1) {
-            $query .= '^' . round($this->getBoost(), 4);
-        }
-
-        return $query;
-    }
-}
+<?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 license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Search_Lucene
+ * @subpackage Search
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+
+/** @see Zend_Search_Lucene_Search_Query_Processing */
+require_once 'Zend/Search/Lucene/Search/Query/Preprocessing.php';
+
+/** @see Zend_Search_Lucene_Search_Query_Phrase */
+require_once 'Zend/Search/Lucene/Search/Query/Phrase.php';
+
+/** @see Zend_Search_Lucene_Search_Query_Insignificant */
+require_once 'Zend/Search/Lucene/Search/Query/Insignificant.php';
+
+/** @see Zend_Search_Lucene_Search_Query_Empty */
+require_once 'Zend/Search/Lucene/Search/Query/Empty.php';
+
+/** @see Zend_Search_Lucene_Search_Query_Term */
+require_once 'Zend/Search/Lucene/Search/Query/Term.php';
+
+/** @see Zend_Search_Lucene_Index_Term */
+require_once 'Zend/Search/Lucene/Index/Term.php';
+
+
+/**
+ * It's an internal abstract class intended to finalize ase a query processing after query parsing.
+ * This type of query is not actually involved into query execution.
+ *
+ * @category   Zend
+ * @package    Zend_Search_Lucene
+ * @subpackage Search
+ * @internal
+ * @copyright  Copyright (c) 2005-2009 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_Preprocessing_Term extends Zend_Search_Lucene_Search_Query_Preprocessing
+{
+    /**
+     * word (query parser lexeme) to find.
+     *
+     * @var string
+     */
+    private $_word;
+
+    /**
+     * Word encoding (field name is always provided using UTF-8 encoding since it may be retrieved from index).
+     *
+     * @var string
+     */
+    private $_encoding;
+
+
+    /**
+     * Field name.
+     *
+     * @var string
+     */
+    private $_field;
+
+    /**
+     * Class constructor.  Create a new preprocessing object for prase query.
+     *
+     * @param string $word       Non-tokenized word (query parser lexeme) to search.
+     * @param string $encoding   Word encoding.
+     * @param string $fieldName  Field name.
+     */
+    public function __construct($word, $encoding, $fieldName)
+    {
+        $this->_word     = $word;
+        $this->_encoding = $encoding;
+        $this->_field    = $fieldName;
+    }
+
+    /**
+     * 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)
+    {
+        if ($this->_field === null) {
+            $query = new Zend_Search_Lucene_Search_Query_MultiTerm();
+            $query->setBoost($this->getBoost());
+
+            $hasInsignificantSubqueries = false;
+
+            if (Zend_Search_Lucene::getDefaultSearchField() === null) {
+                $searchFields = $index->getFieldNames(true);
+            } else {
+                $searchFields = array(Zend_Search_Lucene::getDefaultSearchField());
+            }
+
+            foreach ($searchFields as $fieldName) {
+                $subquery = new Zend_Search_Lucene_Search_Query_Preprocessing_Term($this->_word,
+                                                                                   $this->_encoding,
+                                                                                   $fieldName);
+                $rewrittenSubquery = $subquery->rewrite($index);
+                foreach ($rewrittenSubquery->getQueryTerms() as $term) {
+                    $query->addTerm($term);
+                }
+
+                if ($rewrittenSubquery instanceof Zend_Search_Lucene_Search_Query_Insignificant) {
+                    $hasInsignificantSubqueries = true;
+                }
+            }
+
+            if (count($query->getTerms()) == 0) {
+                $this->_matches = array();
+                if ($hasInsignificantSubqueries) {
+                    return new Zend_Search_Lucene_Search_Query_Insignificant();
+                } else {
+                    return new Zend_Search_Lucene_Search_Query_Empty();
+                }
+            }
+
+            $this->_matches = $query->getQueryTerms();
+            return $query;
+        }
+
+        // -------------------------------------
+        // Recognize exact term matching (it corresponds to Keyword fields stored in the index)
+        // encoding is not used since we expect binary matching
+        $term = new Zend_Search_Lucene_Index_Term($this->_word, $this->_field);
+        if ($index->hasTerm($term)) {
+            $query = new Zend_Search_Lucene_Search_Query_Term($term);
+            $query->setBoost($this->getBoost());
+
+            $this->_matches = $query->getQueryTerms();
+            return $query;
+        }
+
+
+        // -------------------------------------
+        // Recognize wildcard queries
+
+        /** @todo check for PCRE unicode support may be performed through Zend_Environment in some future */
+        if (@preg_match('/\pL/u', 'a') == 1) {
+            $word = iconv($this->_encoding, 'UTF-8', $this->_word);
+            $wildcardsPattern = '/[*?]/u';
+            $subPatternsEncoding = 'UTF-8';
+        } else {
+            $word = $this->_word;
+            $wildcardsPattern = '/[*?]/';
+            $subPatternsEncoding = $this->_encoding;
+        }
+
+        $subPatterns = preg_split($wildcardsPattern, $word, -1, PREG_SPLIT_OFFSET_CAPTURE);
+
+        if (count($subPatterns) > 1) {
+            // Wildcard query is recognized
+
+            $pattern = '';
+
+            foreach ($subPatterns as $id => $subPattern) {
+                // Append corresponding wildcard character to the pattern before each sub-pattern (except first)
+                if ($id != 0) {
+                    $pattern .= $word[ $subPattern[1] - 1 ];
+                }
+
+                // Check if each subputtern is a single word in terms of current analyzer
+                $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($subPattern[0], $subPatternsEncoding);
+                if (count($tokens) > 1) {
+                    require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
+                    throw new Zend_Search_Lucene_Search_QueryParserException('Wildcard search is supported only for non-multiple word terms');
+                }
+                foreach ($tokens as $token) {
+                    $pattern .= $token->getTermText();
+                }
+            }
+
+            $term  = new Zend_Search_Lucene_Index_Term($pattern, $this->_field);
+            $query = new Zend_Search_Lucene_Search_Query_Wildcard($term);
+            $query->setBoost($this->getBoost());
+
+            // Get rewritten query. Important! It also fills terms matching container.
+            $rewrittenQuery = $query->rewrite($index);
+            $this->_matches = $query->getQueryTerms();
+
+            return $rewrittenQuery;
+        }
+
+
+        // -------------------------------------
+        // Recognize one-term multi-term and "insignificant" queries
+        $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_word, $this->_encoding);
+
+        if (count($tokens) == 0) {
+            $this->_matches = array();
+            return new Zend_Search_Lucene_Search_Query_Insignificant();
+        }
+
+        if (count($tokens) == 1) {
+            $term  = new Zend_Search_Lucene_Index_Term($tokens[0]->getTermText(), $this->_field);
+            $query = new Zend_Search_Lucene_Search_Query_Term($term);
+            $query->setBoost($this->getBoost());
+
+            $this->_matches = $query->getQueryTerms();
+            return $query;
+        }
+
+        //It's not insignificant or one term query
+        $query = new Zend_Search_Lucene_Search_Query_MultiTerm();
+
+        /**
+         * @todo Process $token->getPositionIncrement() to support stemming, synonyms and other
+         * analizer design features
+         */
+        foreach ($tokens as $token) {
+            $term = new Zend_Search_Lucene_Index_Term($token->getTermText(), $this->_field);
+            $query->addTerm($term, true); // all subterms are required
+        }
+
+        $query->setBoost($this->getBoost());
+
+        $this->_matches = $query->getQueryTerms();
+        return $query;
+    }
+
+    /**
+     * Query specific matches highlighting
+     *
+     * @param Zend_Search_Lucene_Search_Highlighter_Interface $highlighter  Highlighter object (also contains doc for highlighting)
+     */
+    protected function _highlightMatches(Zend_Search_Lucene_Search_Highlighter_Interface $highlighter)
+    {
+        /** Skip fields detection. We don't need it, since we expect all fields presented in the HTML body and don't differentiate them */
+
+        /** Skip exact term matching recognition, keyword fields highlighting is not supported */
+
+        // -------------------------------------
+        // Recognize wildcard queries
+        /** @todo check for PCRE unicode support may be performed through Zend_Environment in some future */
+        if (@preg_match('/\pL/u', 'a') == 1) {
+            $word = iconv($this->_encoding, 'UTF-8', $this->_word);
+            $wildcardsPattern = '/[*?]/u';
+            $subPatternsEncoding = 'UTF-8';
+        } else {
+            $word = $this->_word;
+            $wildcardsPattern = '/[*?]/';
+            $subPatternsEncoding = $this->_encoding;
+        }
+        $subPatterns = preg_split($wildcardsPattern, $word, -1, PREG_SPLIT_OFFSET_CAPTURE);
+        if (count($subPatterns) > 1) {
+            // Wildcard query is recognized
+
+            $pattern = '';
+
+            foreach ($subPatterns as $id => $subPattern) {
+                // Append corresponding wildcard character to the pattern before each sub-pattern (except first)
+                if ($id != 0) {
+                    $pattern .= $word[ $subPattern[1] - 1 ];
+                }
+
+                // Check if each subputtern is a single word in terms of current analyzer
+                $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($subPattern[0], $subPatternsEncoding);
+                if (count($tokens) > 1) {
+                    // Do nothing (nothing is highlighted)
+                    return;
+                }
+                foreach ($tokens as $token) {
+                    $pattern .= $token->getTermText();
+                }
+            }
+
+            $term  = new Zend_Search_Lucene_Index_Term($pattern, $this->_field);
+            $query = new Zend_Search_Lucene_Search_Query_Wildcard($term);
+
+            $query->_highlightMatches($highlighter);
+            return;
+        }
+
+        // -------------------------------------
+        // Recognize one-term multi-term and "insignificant" queries
+        $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_word, $this->_encoding);
+
+        if (count($tokens) == 0) {
+            // Do nothing
+            return;
+        }
+
+        if (count($tokens) == 1) {
+            $highlighter->highlight($tokens[0]->getTermText());
+            return;
+        }
+
+        //It's not insignificant or one term query
+        $words = array();
+        foreach ($tokens as $token) {
+            $words[] = $token->getTermText();
+        }
+        $highlighter->highlight($words);
+    }
+
+    /**
+     * Print a query
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        // It's used only for query visualisation, so we don't care about characters escaping
+        if ($this->_field !== null) {
+            $query = $this->_field . ':';
+        } else {
+            $query = '';
+        }
+
+        $query .= $this->_word;
+
+        if ($this->getBoost() != 1) {
+            $query .= '^' . round($this->getBoost(), 4);
+        }
+
+        return $query;
+    }
+}

+ 176 - 176
library/Zend/Search/Lucene/TermStreamsPriorityQueue.php

@@ -1,176 +1,176 @@
-<?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 license@zend.com so we can send you a copy immediately.
- *
- * @category   Zend
- * @package    Zend_Search_Lucene
- * @subpackage Index
- * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
- * @license    http://framework.zend.com/license/new-bsd     New BSD License
- * @version    $Id$
- */
-
-/** Zend_Search_Lucene_Index_TermsStream_Interface */
-require_once 'Zend/Search/Lucene/Index/TermsStream/Interface.php';
-
-/** Zend_Search_Lucene_Index_TermsPriorityQueue */
-require_once 'Zend/Search/Lucene/Index/TermsPriorityQueue.php';
-
-
-/**
- * @category   Zend
- * @package    Zend_Search_Lucene
- * @subpackage Index
- * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
- * @license    http://framework.zend.com/license/new-bsd     New BSD License
- */
-class Zend_Search_Lucene_TermStreamsPriorityQueue implements Zend_Search_Lucene_Index_TermsStream_Interface
-{
-	/**
-	 * Array of term streams (Zend_Search_Lucene_Index_TermsStream_Interface objects)
-	 *
-	 * @var array
-	 */
-	protected $_termStreams;
-
-	/**
-     * Terms stream queue
-     *
-     * @var Zend_Search_Lucene_Index_TermsPriorityQueue
-     */
-    protected $_termsStreamQueue = null;
-
-    /**
-     * Last Term in a terms stream
-     *
-     * @var Zend_Search_Lucene_Index_Term
-     */
-    protected $_lastTerm = null;
-
-
-    /**
-     * Object constructor
-     *
-     * @param array $termStreams  array of term streams (Zend_Search_Lucene_Index_TermsStream_Interface objects)
-     */
-	public function __construct(array $termStreams)
-	{
-		$this->_termStreams = $termStreams;
-
-		$this->resetTermsStream();
-	}
-
-	/**
-     * Reset terms stream.
-     */
-    public function resetTermsStream()
-    {
-        $this->_termsStreamQueue = new Zend_Search_Lucene_Index_TermsPriorityQueue();
-
-        foreach ($this->_termStreams as $termStream) {
-            $termStream->resetTermsStream();
-
-            // Skip "empty" containers
-            if ($termStream->currentTerm() !== null) {
-                $this->_termsStreamQueue->put($termStream);
-            }
-        }
-
-        $this->nextTerm();
-    }
-
-    /**
-     * Skip terms stream up to specified term preffix.
-     *
-     * Prefix contains fully specified field info and portion of searched term
-     *
-     * @param Zend_Search_Lucene_Index_Term $prefix
-     */
-    public function skipTo(Zend_Search_Lucene_Index_Term $prefix)
-    {
-        $termStreams = array();
-
-        while (($termStream = $this->_termsStreamQueue->pop()) !== null) {
-            $termStreams[] = $termStream;
-        }
-
-        foreach ($termStreams as $termStream) {
-            $termStream->skipTo($prefix);
-
-            if ($termStream->currentTerm() !== null) {
-                $this->_termsStreamQueue->put($termStream);
-            }
-        }
-
-        $this->nextTerm();
-    }
-
-    /**
-     * Scans term streams and returns next term
-     *
-     * @return Zend_Search_Lucene_Index_Term|null
-     */
-    public function nextTerm()
-    {
-        while (($termStream = $this->_termsStreamQueue->pop()) !== null) {
-            if ($this->_termsStreamQueue->top() === null ||
-                $this->_termsStreamQueue->top()->currentTerm()->key() !=
-                            $termStream->currentTerm()->key()) {
-                // We got new term
-                $this->_lastTerm = $termStream->currentTerm();
-
-                if ($termStream->nextTerm() !== null) {
-                    // Put segment back into the priority queue
-                    $this->_termsStreamQueue->put($termStream);
-                }
-
-                return $this->_lastTerm;
-            }
-
-            if ($termStream->nextTerm() !== null) {
-                // Put segment back into the priority queue
-                $this->_termsStreamQueue->put($termStream);
-            }
-        }
-
-        // End of stream
-        $this->_lastTerm = null;
-
-        return null;
-    }
-
-    /**
-     * Returns term in current position
-     *
-     * @return Zend_Search_Lucene_Index_Term|null
-     */
-    public function currentTerm()
-    {
-        return $this->_lastTerm;
-    }
-
-    /**
-     * Close terms stream
-     *
-     * Should be used for resources clean up if stream is not read up to the end
-     */
-    public function closeTermsStream()
-    {
-        while (($termStream = $this->_termsStreamQueue->pop()) !== null) {
-            $termStream->closeTermsStream();
-        }
-
-        $this->_termsStreamQueue = null;
-        $this->_lastTerm         = null;
-    }
-}
+<?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 license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Search_Lucene
+ * @subpackage Index
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/** @see Zend_Search_Lucene_Index_TermsStream_Interface */
+require_once 'Zend/Search/Lucene/Index/TermsStream/Interface.php';
+
+/** @see Zend_Search_Lucene_Index_TermsPriorityQueue */
+require_once 'Zend/Search/Lucene/Index/TermsPriorityQueue.php';
+
+
+/**
+ * @category   Zend
+ * @package    Zend_Search_Lucene
+ * @subpackage Index
+ * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Search_Lucene_TermStreamsPriorityQueue implements Zend_Search_Lucene_Index_TermsStream_Interface
+{
+    /**
+     * Array of term streams (Zend_Search_Lucene_Index_TermsStream_Interface objects)
+     *
+     * @var array
+     */
+    protected $_termStreams;
+
+    /**
+     * Terms stream queue
+     *
+     * @var Zend_Search_Lucene_Index_TermsPriorityQueue
+     */
+    protected $_termsStreamQueue = null;
+
+    /**
+     * Last Term in a terms stream
+     *
+     * @var Zend_Search_Lucene_Index_Term
+     */
+    protected $_lastTerm = null;
+
+
+    /**
+     * Object constructor
+     *
+     * @param array $termStreams  array of term streams (Zend_Search_Lucene_Index_TermsStream_Interface objects)
+     */
+    public function __construct(array $termStreams)
+    {
+        $this->_termStreams = $termStreams;
+
+        $this->resetTermsStream();
+    }
+
+    /**
+     * Reset terms stream.
+     */
+    public function resetTermsStream()
+    {
+        $this->_termsStreamQueue = new Zend_Search_Lucene_Index_TermsPriorityQueue();
+
+        foreach ($this->_termStreams as $termStream) {
+            $termStream->resetTermsStream();
+
+            // Skip "empty" containers
+            if ($termStream->currentTerm() !== null) {
+                $this->_termsStreamQueue->put($termStream);
+            }
+        }
+
+        $this->nextTerm();
+    }
+
+    /**
+     * Skip terms stream up to specified term preffix.
+     *
+     * Prefix contains fully specified field info and portion of searched term
+     *
+     * @param Zend_Search_Lucene_Index_Term $prefix
+     */
+    public function skipTo(Zend_Search_Lucene_Index_Term $prefix)
+    {
+        $termStreams = array();
+
+        while (($termStream = $this->_termsStreamQueue->pop()) !== null) {
+            $termStreams[] = $termStream;
+        }
+
+        foreach ($termStreams as $termStream) {
+            $termStream->skipTo($prefix);
+
+            if ($termStream->currentTerm() !== null) {
+                $this->_termsStreamQueue->put($termStream);
+            }
+        }
+
+        $this->nextTerm();
+    }
+
+    /**
+     * Scans term streams and returns next term
+     *
+     * @return Zend_Search_Lucene_Index_Term|null
+     */
+    public function nextTerm()
+    {
+        while (($termStream = $this->_termsStreamQueue->pop()) !== null) {
+            if ($this->_termsStreamQueue->top() === null ||
+                $this->_termsStreamQueue->top()->currentTerm()->key() !=
+                            $termStream->currentTerm()->key()) {
+                // We got new term
+                $this->_lastTerm = $termStream->currentTerm();
+
+                if ($termStream->nextTerm() !== null) {
+                    // Put segment back into the priority queue
+                    $this->_termsStreamQueue->put($termStream);
+                }
+
+                return $this->_lastTerm;
+            }
+
+            if ($termStream->nextTerm() !== null) {
+                // Put segment back into the priority queue
+                $this->_termsStreamQueue->put($termStream);
+            }
+        }
+
+        // End of stream
+        $this->_lastTerm = null;
+
+        return null;
+    }
+
+    /**
+     * Returns term in current position
+     *
+     * @return Zend_Search_Lucene_Index_Term|null
+     */
+    public function currentTerm()
+    {
+        return $this->_lastTerm;
+    }
+
+    /**
+     * Close terms stream
+     *
+     * Should be used for resources clean up if stream is not read up to the end
+     */
+    public function closeTermsStream()
+    {
+        while (($termStream = $this->_termsStreamQueue->pop()) !== null) {
+            $termStream->closeTermsStream();
+        }
+
+        $this->_termsStreamQueue = null;
+        $this->_lastTerm         = null;
+    }
+}