final move of files
[web.mtrack] / Zend / Search / Lucene / MultiSearcher.php
1 <?php\r
2 /**\r
3  * Zend Framework\r
4  *\r
5  * LICENSE\r
6  *\r
7  * This source file is subject to the new BSD license that is bundled\r
8  * with this package in the file LICENSE.txt.\r
9  * It is also available through the world-wide-web at this URL:\r
10  * http://framework.zend.com/license/new-bsd\r
11  * If you did not receive a copy of the license and are unable to\r
12  * obtain it through the world-wide-web, please send an email\r
13  * to license@zend.com so we can send you a copy immediately.\r
14  *\r
15  * @category   Zend\r
16  * @package    Zend_Search_Lucene\r
17  * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)\r
18  * @license    http://framework.zend.com/license/new-bsd     New BSD License\r
19  * @version    $Id: MultiSearcher.php 16971 2009-07-22 18:05:45Z mikaelkael $\r
20  */\r
21 \r
22 /** Zend_Search_Lucene_TermStreamsPriorityQueue */\r
23 require_once 'Zend/Search/Lucene/TermStreamsPriorityQueue.php';\r
24 \r
25 /** Zend_Search_Lucene_Interface */\r
26 require_once 'Zend/Search/Lucene/Interface.php';\r
27 \r
28 /**\r
29  * Multisearcher allows to search through several independent indexes.\r
30  *\r
31  * @category   Zend\r
32  * @package    Zend_Search_Lucene\r
33  * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)\r
34  * @license    http://framework.zend.com/license/new-bsd     New BSD License\r
35  */\r
36 class Zend_Search_Lucene_Interface_MultiSearcher implements Zend_Search_Lucene_Interface\r
37 {\r
38         /**\r
39          * List of indices for searching.\r
40          * Array of Zend_Search_Lucene_Interface objects\r
41          *\r
42          * @var array\r
43          */\r
44         protected $_indices;\r
45 \r
46         /**\r
47          * Object constructor.\r
48          *\r
49          * @param array $indices   Arrays of indices for search\r
50          * @throws Zend_Search_Lucene_Exception\r
51          */\r
52         public function __construct($indices = array())\r
53         {\r
54                 $this->_indices = $indices;\r
55 \r
56                 foreach ($this->_indices as $index) {\r
57                         if (!$index instanceof Zend_Search_Lucene_Interface) {\r
58                 require_once 'Zend/Search/Lucene/Exception.php';\r
59                 throw new Zend_Search_Lucene_Exception('sub-index objects have to implement Zend_Search_Lucene_Interface.');\r
60                         }\r
61                 }\r
62         }\r
63 \r
64     /**\r
65      * Add index for searching.\r
66      *\r
67      * @param Zend_Search_Lucene_Interface $index\r
68      */\r
69     public function addIndex(Zend_Search_Lucene_Interface $index)\r
70     {\r
71         $this->_indices[] = $index;\r
72     }\r
73 \r
74 \r
75     /**\r
76      * Get current generation number\r
77      *\r
78      * Returns generation number\r
79      * 0 means pre-2.1 index format\r
80      * -1 means there are no segments files.\r
81      *\r
82      * @param Zend_Search_Lucene_Storage_Directory $directory\r
83      * @return integer\r
84      * @throws Zend_Search_Lucene_Exception\r
85      */\r
86     public static function getActualGeneration(Zend_Search_Lucene_Storage_Directory $directory)\r
87     {\r
88         require_once 'Zend/Search/Lucene/Exception.php';\r
89         throw new Zend_Search_Lucene_Exception("Generation number can't be retrieved for multi-searcher");\r
90     }\r
91 \r
92     /**\r
93      * Get segments file name\r
94      *\r
95      * @param integer $generation\r
96      * @return string\r
97      */\r
98     public static function getSegmentFileName($generation)\r
99     {\r
100         return Zend_Search_Lucene::getSegmentFileName($generation);\r
101     }\r
102 \r
103     /**\r
104      * Get index format version\r
105      *\r
106      * @return integer\r
107      * @throws Zend_Search_Lucene_Exception\r
108      */\r
109     public function getFormatVersion()\r
110     {\r
111         require_once 'Zend/Search/Lucene/Exception.php';\r
112         throw new Zend_Search_Lucene_Exception("Format version can't be retrieved for multi-searcher");\r
113     }\r
114 \r
115     /**\r
116      * Set index format version.\r
117      * Index is converted to this format at the nearest upfdate time\r
118      *\r
119      * @param int $formatVersion\r
120      */\r
121     public function setFormatVersion($formatVersion)\r
122     {\r
123         foreach ($this->_indices as $index) {\r
124                 $index->setFormatVersion($formatVersion);\r
125         }\r
126     }\r
127 \r
128     /**\r
129      * Returns the Zend_Search_Lucene_Storage_Directory instance for this index.\r
130      *\r
131      * @return Zend_Search_Lucene_Storage_Directory\r
132      */\r
133     public function getDirectory()\r
134     {\r
135         require_once 'Zend/Search/Lucene/Exception.php';\r
136         throw new Zend_Search_Lucene_Exception("Index directory can't be retrieved for multi-searcher");\r
137     }\r
138 \r
139     /**\r
140      * Returns the total number of documents in this index (including deleted documents).\r
141      *\r
142      * @return integer\r
143      */\r
144     public function count()\r
145     {\r
146         $count = 0;\r
147 \r
148         foreach ($this->_indices as $index) {\r
149                 $count += $this->_indices->count();\r
150         }\r
151 \r
152         return $count;\r
153     }\r
154 \r
155     /**\r
156      * Returns one greater than the largest possible document number.\r
157      * This may be used to, e.g., determine how big to allocate a structure which will have\r
158      * an element for every document number in an index.\r
159      *\r
160      * @return integer\r
161      */\r
162     public function maxDoc()\r
163     {\r
164         return $this->count();\r
165     }\r
166 \r
167     /**\r
168      * Returns the total number of non-deleted documents in this index.\r
169      *\r
170      * @return integer\r
171      */\r
172     public function numDocs()\r
173     {\r
174         $docs = 0;\r
175 \r
176         foreach ($this->_indices as $index) {\r
177             $docs += $this->_indices->numDocs();\r
178         }\r
179 \r
180         return $docs;\r
181     }\r
182 \r
183     /**\r
184      * Checks, that document is deleted\r
185      *\r
186      * @param integer $id\r
187      * @return boolean\r
188      * @throws Zend_Search_Lucene_Exception    Exception is thrown if $id is out of the range\r
189      */\r
190     public function isDeleted($id)\r
191     {\r
192         foreach ($this->_indices as $index) {\r
193                 $indexCount = $index->count();\r
194 \r
195                 if ($indexCount > $id) {\r
196                 return $index->isDeleted($id);\r
197             }\r
198 \r
199             $id -= $indexCount;\r
200         }\r
201 \r
202         require_once 'Zend/Search/Lucene/Exception.php';\r
203         throw new Zend_Search_Lucene_Exception('Document id is out of the range.');\r
204     }\r
205 \r
206     /**\r
207      * Set default search field.\r
208      *\r
209      * Null means, that search is performed through all fields by default\r
210      *\r
211      * Default value is null\r
212      *\r
213      * @param string $fieldName\r
214      */\r
215     public static function setDefaultSearchField($fieldName)\r
216     {\r
217         foreach ($this->_indices as $index) {\r
218                 $index->setDefaultSearchField($fieldName);\r
219         }\r
220     }\r
221 \r
222 \r
223     /**\r
224      * Get default search field.\r
225      *\r
226      * Null means, that search is performed through all fields by default\r
227      *\r
228      * @return string\r
229      * @throws Zend_Search_Lucene_Exception\r
230      */\r
231     public static function getDefaultSearchField()\r
232     {\r
233         if (count($this->_indices) == 0) {\r
234             require_once 'Zend/Search/Lucene/Exception.php';\r
235             throw new Zend_Search_Lucene_Exception('Indices list is empty');\r
236         }\r
237 \r
238         $defaultSearchField = reset($this->_indices)->getDefaultSearchField();\r
239 \r
240         foreach ($this->_indices as $index) {\r
241                 if ($index->getDefaultSearchField() !== $defaultSearchField) {\r
242                 require_once 'Zend/Search/Lucene/Exception.php';\r
243                 throw new Zend_Search_Lucene_Exception('Indices have different default search field.');\r
244                 }\r
245         }\r
246 \r
247         return $defaultSearchField;\r
248     }\r
249 \r
250     /**\r
251      * Set result set limit.\r
252      *\r
253      * 0 (default) means no limit\r
254      *\r
255      * @param integer $limit\r
256      */\r
257     public static function setResultSetLimit($limit)\r
258     {\r
259         foreach ($this->_indices as $index) {\r
260             $index->setResultSetLimit($limit);\r
261         }\r
262     }\r
263 \r
264     /**\r
265      * Set result set limit.\r
266      *\r
267      * 0 means no limit\r
268      *\r
269      * @return integer\r
270      * @throws Zend_Search_Lucene_Exception\r
271      */\r
272     public static function getResultSetLimit()\r
273     {\r
274         if (count($this->_indices) == 0) {\r
275             require_once 'Zend/Search/Lucene/Exception.php';\r
276             throw new Zend_Search_Lucene_Exception('Indices list is empty');\r
277         }\r
278 \r
279         $defaultResultSetLimit = reset($this->_indices)->getResultSetLimit();\r
280 \r
281         foreach ($this->_indices as $index) {\r
282             if ($index->getResultSetLimit() !== $defaultResultSetLimit) {\r
283                 require_once 'Zend/Search/Lucene/Exception.php';\r
284                 throw new Zend_Search_Lucene_Exception('Indices have different default search field.');\r
285             }\r
286         }\r
287 \r
288         return $defaultResultSetLimit;\r
289     }\r
290 \r
291     /**\r
292      * Retrieve index maxBufferedDocs option\r
293      *\r
294      * maxBufferedDocs is a minimal number of documents required before\r
295      * the buffered in-memory documents are written into a new Segment\r
296      *\r
297      * Default value is 10\r
298      *\r
299      * @return integer\r
300      * @throws Zend_Search_Lucene_Exception\r
301      */\r
302     public function getMaxBufferedDocs()\r
303     {\r
304         if (count($this->_indices) == 0) {\r
305             require_once 'Zend/Search/Lucene/Exception.php';\r
306             throw new Zend_Search_Lucene_Exception('Indices list is empty');\r
307         }\r
308 \r
309         $maxBufferedDocs = reset($this->_indices)->getMaxBufferedDocs();\r
310 \r
311         foreach ($this->_indices as $index) {\r
312             if ($index->getMaxBufferedDocs() !== $maxBufferedDocs) {\r
313                 require_once 'Zend/Search/Lucene/Exception.php';\r
314                 throw new Zend_Search_Lucene_Exception('Indices have different default search field.');\r
315             }\r
316         }\r
317 \r
318         return $maxBufferedDocs;\r
319     }\r
320 \r
321     /**\r
322      * Set index maxBufferedDocs option\r
323      *\r
324      * maxBufferedDocs is a minimal number of documents required before\r
325      * the buffered in-memory documents are written into a new Segment\r
326      *\r
327      * Default value is 10\r
328      *\r
329      * @param integer $maxBufferedDocs\r
330      */\r
331     public function setMaxBufferedDocs($maxBufferedDocs)\r
332     {\r
333         foreach ($this->_indices as $index) {\r
334             $index->setMaxBufferedDocs($maxBufferedDocs);\r
335         }\r
336     }\r
337 \r
338     /**\r
339      * Retrieve index maxMergeDocs option\r
340      *\r
341      * maxMergeDocs is a largest number of documents ever merged by addDocument().\r
342      * Small values (e.g., less than 10,000) are best for interactive indexing,\r
343      * as this limits the length of pauses while indexing to a few seconds.\r
344      * Larger values are best for batched indexing and speedier searches.\r
345      *\r
346      * Default value is PHP_INT_MAX\r
347      *\r
348      * @return integer\r
349      * @throws Zend_Search_Lucene_Exception\r
350      */\r
351     public function getMaxMergeDocs()\r
352     {\r
353         if (count($this->_indices) == 0) {\r
354             require_once 'Zend/Search/Lucene/Exception.php';\r
355             throw new Zend_Search_Lucene_Exception('Indices list is empty');\r
356         }\r
357 \r
358         $maxMergeDocs = reset($this->_indices)->getMaxMergeDocs();\r
359 \r
360         foreach ($this->_indices as $index) {\r
361             if ($index->getMaxMergeDocs() !== $maxMergeDocs) {\r
362                 require_once 'Zend/Search/Lucene/Exception.php';\r
363                 throw new Zend_Search_Lucene_Exception('Indices have different default search field.');\r
364             }\r
365         }\r
366 \r
367         return $maxMergeDocs;\r
368     }\r
369 \r
370     /**\r
371      * Set index maxMergeDocs option\r
372      *\r
373      * maxMergeDocs is a largest number of documents ever merged by addDocument().\r
374      * Small values (e.g., less than 10,000) are best for interactive indexing,\r
375      * as this limits the length of pauses while indexing to a few seconds.\r
376      * Larger values are best for batched indexing and speedier searches.\r
377      *\r
378      * Default value is PHP_INT_MAX\r
379      *\r
380      * @param integer $maxMergeDocs\r
381      */\r
382     public function setMaxMergeDocs($maxMergeDocs)\r
383     {\r
384         foreach ($this->_indices as $index) {\r
385             $index->setMaxMergeDocs($maxMergeDocs);\r
386         }\r
387     }\r
388 \r
389     /**\r
390      * Retrieve index mergeFactor option\r
391      *\r
392      * mergeFactor determines how often segment indices are merged by addDocument().\r
393      * With smaller values, less RAM is used while indexing,\r
394      * and searches on unoptimized indices are faster,\r
395      * but indexing speed is slower.\r
396      * With larger values, more RAM is used during indexing,\r
397      * and while searches on unoptimized indices are slower,\r
398      * indexing is faster.\r
399      * Thus larger values (> 10) are best for batch index creation,\r
400      * and smaller values (< 10) for indices that are interactively maintained.\r
401      *\r
402      * Default value is 10\r
403      *\r
404      * @return integer\r
405      * @throws Zend_Search_Lucene_Exception\r
406      */\r
407     public function getMergeFactor()\r
408     {\r
409         if (count($this->_indices) == 0) {\r
410             require_once 'Zend/Search/Lucene/Exception.php';\r
411             throw new Zend_Search_Lucene_Exception('Indices list is empty');\r
412         }\r
413 \r
414         $mergeFactor = reset($this->_indices)->getMergeFactor();\r
415 \r
416         foreach ($this->_indices as $index) {\r
417             if ($index->getMergeFactor() !== $mergeFactor) {\r
418                 require_once 'Zend/Search/Lucene/Exception.php';\r
419                 throw new Zend_Search_Lucene_Exception('Indices have different default search field.');\r
420             }\r
421         }\r
422 \r
423         return $mergeFactor;\r
424     }\r
425 \r
426     /**\r
427      * Set index mergeFactor option\r
428      *\r
429      * mergeFactor determines how often segment indices are merged by addDocument().\r
430      * With smaller values, less RAM is used while indexing,\r
431      * and searches on unoptimized indices are faster,\r
432      * but indexing speed is slower.\r
433      * With larger values, more RAM is used during indexing,\r
434      * and while searches on unoptimized indices are slower,\r
435      * indexing is faster.\r
436      * Thus larger values (> 10) are best for batch index creation,\r
437      * and smaller values (< 10) for indices that are interactively maintained.\r
438      *\r
439      * Default value is 10\r
440      *\r
441      * @param integer $maxMergeDocs\r
442      */\r
443     public function setMergeFactor($mergeFactor)\r
444     {\r
445         foreach ($this->_indices as $index) {\r
446             $index->setMaxMergeDocs($maxMergeDocs);\r
447         }\r
448     }\r
449 \r
450     /**\r
451      * Performs a query against the index and returns an array\r
452      * of Zend_Search_Lucene_Search_QueryHit objects.\r
453      * Input is a string or Zend_Search_Lucene_Search_Query.\r
454      *\r
455      * @param mixed $query\r
456      * @return array Zend_Search_Lucene_Search_QueryHit\r
457      * @throws Zend_Search_Lucene_Exception\r
458      */\r
459     public function find($query)\r
460     {\r
461         $hitsList = array();\r
462 \r
463         $indexShift = 0;\r
464         foreach ($this->_indices as $index) {\r
465                 $hits = $index->find($query);\r
466 \r
467                 if ($indexShift != 0) {\r
468                 foreach ($hits as $hit) {\r
469                     $hit->id += $indexShift;\r
470                 }\r
471                 }\r
472 \r
473                 $indexShift += $index->count();\r
474                 $hitsList[] = $hits;\r
475         }\r
476 \r
477         /** @todo Implement advanced sorting */\r
478 \r
479         return call_user_func_array('array_merge', $hitsList);\r
480     }\r
481 \r
482     /**\r
483      * Returns a list of all unique field names that exist in this index.\r
484      *\r
485      * @param boolean $indexed\r
486      * @return array\r
487      */\r
488     public function getFieldNames($indexed = false)\r
489     {\r
490         $fieldNamesList = array();\r
491 \r
492         foreach ($this->_indices as $index) {\r
493                 $fieldNamesList[] = $index->getFieldNames($indexed);\r
494         }\r
495 \r
496         return array_unique(call_user_func_array('array_merge', $fieldNamesList));\r
497     }\r
498 \r
499     /**\r
500      * Returns a Zend_Search_Lucene_Document object for the document\r
501      * number $id in this index.\r
502      *\r
503      * @param integer|Zend_Search_Lucene_Search_QueryHit $id\r
504      * @return Zend_Search_Lucene_Document\r
505      * @throws Zend_Search_Lucene_Exception    Exception is thrown if $id is out of the range\r
506      */\r
507     public function getDocument($id)\r
508     {\r
509         if ($id instanceof Zend_Search_Lucene_Search_QueryHit) {\r
510             /* @var $id Zend_Search_Lucene_Search_QueryHit */\r
511             $id = $id->id;\r
512         }\r
513 \r
514         foreach ($this->_indices as $index) {\r
515             $indexCount = $index->count();\r
516 \r
517             if ($indexCount > $id) {\r
518                 return $index->getDocument($id);\r
519             }\r
520 \r
521             $id -= $indexCount;\r
522         }\r
523 \r
524         require_once 'Zend/Search/Lucene/Exception.php';\r
525         throw new Zend_Search_Lucene_Exception('Document id is out of the range.');\r
526     }\r
527 \r
528     /**\r
529      * Returns true if index contain documents with specified term.\r
530      *\r
531      * Is used for query optimization.\r
532      *\r
533      * @param Zend_Search_Lucene_Index_Term $term\r
534      * @return boolean\r
535      */\r
536     public function hasTerm(Zend_Search_Lucene_Index_Term $term)\r
537     {\r
538         foreach ($this->_indices as $index) {\r
539                 if ($index->hasTerm($term)) {\r
540                         return true;\r
541                 }\r
542         }\r
543 \r
544         return false;\r
545     }\r
546 \r
547     /**\r
548      * Returns IDs of all the documents containing term.\r
549      *\r
550      * @param Zend_Search_Lucene_Index_Term $term\r
551      * @param Zend_Search_Lucene_Index_DocsFilter|null $docsFilter\r
552      * @return array\r
553      * @throws Zend_Search_Lucene_Exception\r
554      */\r
555     public function termDocs(Zend_Search_Lucene_Index_Term $term, $docsFilter = null)\r
556     {\r
557         if ($docsFilter != null) {\r
558             require_once 'Zend/Search/Lucene/Exception.php';\r
559             throw new Zend_Search_Lucene_Exception('Document filters could not used with multi-searcher');\r
560         }\r
561 \r
562         $docsList = array();\r
563 \r
564         $indexShift = 0;\r
565         foreach ($this->_indices as $index) {\r
566             $docs = $index->termDocs($term);\r
567 \r
568             if ($indexShift != 0) {\r
569                 foreach ($docs as $id => $docId) {\r
570                     $docs[$id] += $indexShift;\r
571                 }\r
572             }\r
573 \r
574             $indexShift += $index->count();\r
575             $docsList[] = $docs;\r
576         }\r
577 \r
578         return call_user_func_array('array_merge', $docsList);\r
579     }\r
580 \r
581     /**\r
582      * Returns documents filter for all documents containing term.\r
583      *\r
584      * It performs the same operation as termDocs, but return result as\r
585      * Zend_Search_Lucene_Index_DocsFilter object\r
586      *\r
587      * @param Zend_Search_Lucene_Index_Term $term\r
588      * @param Zend_Search_Lucene_Index_DocsFilter|null $docsFilter\r
589      * @return Zend_Search_Lucene_Index_DocsFilter\r
590      * @throws Zend_Search_Lucene_Exception\r
591      */\r
592     public function termDocsFilter(Zend_Search_Lucene_Index_Term $term, $docsFilter = null)\r
593     {\r
594         require_once 'Zend/Search/Lucene/Exception.php';\r
595         throw new Zend_Search_Lucene_Exception('Document filters could not used with multi-searcher');\r
596     }\r
597 \r
598     /**\r
599      * Returns an array of all term freqs.\r
600      * Return array structure: array( docId => freq, ...)\r
601      *\r
602      * @param Zend_Search_Lucene_Index_Term $term\r
603      * @param Zend_Search_Lucene_Index_DocsFilter|null $docsFilter\r
604      * @return integer\r
605      * @throws Zend_Search_Lucene_Exception\r
606      */\r
607     public function termFreqs(Zend_Search_Lucene_Index_Term $term, $docsFilter = null)\r
608     {\r
609         if ($docsFilter != null) {\r
610             require_once 'Zend/Search/Lucene/Exception.php';\r
611             throw new Zend_Search_Lucene_Exception('Document filters could not used with multi-searcher');\r
612         }\r
613 \r
614         $freqsList = array();\r
615 \r
616         $indexShift = 0;\r
617         foreach ($this->_indices as $index) {\r
618             $freqs = $index->termFreqs($term);\r
619 \r
620             if ($indexShift != 0) {\r
621                 $freqsShifted = array();\r
622 \r
623                 foreach ($freqs as $docId => $freq) {\r
624                         $freqsShifted[$docId + $indexShift] = $freq;\r
625                 }\r
626                 $freqs = $freqsShifted;\r
627             }\r
628 \r
629             $indexShift += $index->count();\r
630             $freqsList[] = $freqs;\r
631         }\r
632 \r
633         return call_user_func_array('array_merge', $freqsList);\r
634     }\r
635 \r
636     /**\r
637      * Returns an array of all term positions in the documents.\r
638      * Return array structure: array( docId => array( pos1, pos2, ...), ...)\r
639      *\r
640      * @param Zend_Search_Lucene_Index_Term $term\r
641      * @param Zend_Search_Lucene_Index_DocsFilter|null $docsFilter\r
642      * @return array\r
643      * @throws Zend_Search_Lucene_Exception\r
644      */\r
645     public function termPositions(Zend_Search_Lucene_Index_Term $term, $docsFilter = null)\r
646     {\r
647         if ($docsFilter != null) {\r
648             require_once 'Zend/Search/Lucene/Exception.php';\r
649             throw new Zend_Search_Lucene_Exception('Document filters could not used with multi-searcher');\r
650         }\r
651 \r
652         $termPositionsList = array();\r
653 \r
654         $indexShift = 0;\r
655         foreach ($this->_indices as $index) {\r
656             $termPositions = $index->termPositions($term);\r
657 \r
658             if ($indexShift != 0) {\r
659                 $termPositionsShifted = array();\r
660 \r
661                 foreach ($termPositions as $docId => $positions) {\r
662                     $termPositions[$docId + $indexShift] = $positions;\r
663                 }\r
664                 $termPositions = $termPositionsShifted;\r
665             }\r
666 \r
667             $indexShift += $index->count();\r
668             $termPositionsList[] = $termPositions;\r
669         }\r
670 \r
671         return call_user_func_array('array_merge', $termPositions);\r
672     }\r
673 \r
674     /**\r
675      * Returns the number of documents in this index containing the $term.\r
676      *\r
677      * @param Zend_Search_Lucene_Index_Term $term\r
678      * @return integer\r
679      */\r
680     public function docFreq(Zend_Search_Lucene_Index_Term $term)\r
681     {\r
682         $docFreq = 0;\r
683 \r
684         foreach ($this->_indices as $index) {\r
685                 $docFreq += $index->docFreq($term);\r
686         }\r
687 \r
688         return $docFreq;\r
689     }\r
690 \r
691     /**\r
692      * Retrive similarity used by index reader\r
693      *\r
694      * @return Zend_Search_Lucene_Search_Similarity\r
695      * @throws Zend_Search_Lucene_Exception\r
696      */\r
697     public function getSimilarity()\r
698     {\r
699         if (count($this->_indices) == 0) {\r
700             require_once 'Zend/Search/Lucene/Exception.php';\r
701             throw new Zend_Search_Lucene_Exception('Indices list is empty');\r
702         }\r
703 \r
704         $similarity = reset($this->_indices)->getSimilarity();\r
705 \r
706         foreach ($this->_indices as $index) {\r
707             if ($index->getSimilarity() !== $similarity) {\r
708                 require_once 'Zend/Search/Lucene/Exception.php';\r
709                 throw new Zend_Search_Lucene_Exception('Indices have different similarity.');\r
710             }\r
711         }\r
712 \r
713         return $similarity;\r
714     }\r
715 \r
716     /**\r
717      * Returns a normalization factor for "field, document" pair.\r
718      *\r
719      * @param integer $id\r
720      * @param string $fieldName\r
721      * @return float\r
722      */\r
723     public function norm($id, $fieldName)\r
724     {\r
725         foreach ($this->_indices as $index) {\r
726             $indexCount = $index->count();\r
727 \r
728             if ($indexCount > $id) {\r
729                 return $index->norm($id, $fieldName);\r
730             }\r
731 \r
732             $id -= $indexCount;\r
733         }\r
734 \r
735         return null;\r
736     }\r
737 \r
738     /**\r
739      * Returns true if any documents have been deleted from this index.\r
740      *\r
741      * @return boolean\r
742      */\r
743     public function hasDeletions()\r
744     {\r
745         foreach ($this->_indices as $index) {\r
746                 if ($index->hasDeletions()) {\r
747                         return true;\r
748                 }\r
749         }\r
750 \r
751         return false;\r
752     }\r
753 \r
754     /**\r
755      * Deletes a document from the index.\r
756      * $id is an internal document id\r
757      *\r
758      * @param integer|Zend_Search_Lucene_Search_QueryHit $id\r
759      * @throws Zend_Search_Lucene_Exception\r
760      */\r
761     public function delete($id)\r
762     {\r
763         foreach ($this->_indices as $index) {\r
764             $indexCount = $index->count();\r
765 \r
766             if ($indexCount > $id) {\r
767                 $index->delete($id);\r
768                 return;\r
769             }\r
770 \r
771             $id -= $indexCount;\r
772         }\r
773 \r
774         require_once 'Zend/Search/Lucene/Exception.php';\r
775         throw new Zend_Search_Lucene_Exception('Document id is out of the range.');\r
776     }\r
777 \r
778 \r
779     /**\r
780      * Callback used to choose target index for new documents\r
781      *\r
782      * Function/method signature:\r
783      *    Zend_Search_Lucene_Interface  callbackFunction(Zend_Search_Lucene_Document $document, array $indices);\r
784      *\r
785      * null means "default documents distributing algorithm"\r
786      *\r
787      * @var callback\r
788      */\r
789     protected $_documentDistributorCallBack = null;\r
790 \r
791     /**\r
792      * Set callback for choosing target index.\r
793      *\r
794      * @param callback $callback\r
795      */\r
796     public function setDocumentDistributorCallback($callback)\r
797     {\r
798         if ($callback !== null  &&  !is_callable($callback))\r
799         $this->_documentDistributorCallBack = $callback;\r
800     }\r
801 \r
802     /**\r
803      * Get callback for choosing target index.\r
804      *\r
805      * @return callback\r
806      */\r
807     public function getDocumentDistributorCallback()\r
808     {\r
809         return $this->_documentDistributorCallBack;\r
810     }\r
811 \r
812     /**\r
813      * Adds a document to this index.\r
814      *\r
815      * @param Zend_Search_Lucene_Document $document\r
816      * @throws Zend_Search_Lucene_Exception\r
817      */\r
818     public function addDocument(Zend_Search_Lucene_Document $document)\r
819     {\r
820         if ($this->_documentDistributorCallBack !== null) {\r
821                 $index = call_user_func($this->_documentDistributorCallBack, $document, $this->_indices);\r
822         } else {\r
823                 $index = $this->_indices[ array_rand($this->_indices) ];\r
824         }\r
825 \r
826         $index->addDocument($document);\r
827     }\r
828 \r
829     /**\r
830      * Commit changes resulting from delete() or undeleteAll() operations.\r
831      */\r
832     public function commit()\r
833     {\r
834         foreach ($this->_indices as $index) {\r
835                 $index->commit();\r
836         }\r
837     }\r
838 \r
839     /**\r
840      * Optimize index.\r
841      *\r
842      * Merges all segments into one\r
843      */\r
844     public function optimize()\r
845     {\r
846         foreach ($this->_indices as $index) {\r
847                 $index->_optimise();\r
848         }\r
849     }\r
850 \r
851     /**\r
852      * Returns an array of all terms in this index.\r
853      *\r
854      * @return array\r
855      */\r
856     public function terms()\r
857     {\r
858         $termsList = array();\r
859 \r
860         foreach ($this->_indices as $index) {\r
861                 $termsList[] = $index->terms();\r
862         }\r
863 \r
864         return array_unique(call_user_func_array('array_merge', $termsList));\r
865     }\r
866 \r
867 \r
868     /**\r
869      * Terms stream priority queue object\r
870      *\r
871      * @var Zend_Search_Lucene_TermStreamsPriorityQueue\r
872      */\r
873     private $_termsStream = null;\r
874 \r
875     /**\r
876      * Reset terms stream.\r
877      */\r
878     public function resetTermsStream()\r
879     {\r
880         if ($this->_termsStream === null) {\r
881             $this->_termsStream = new Zend_Search_Lucene_TermStreamsPriorityQueue($this->_indices);\r
882         } else {\r
883             $this->_termsStream->resetTermsStream();\r
884         }\r
885     }\r
886 \r
887     /**\r
888      * Skip terms stream up to specified term preffix.\r
889      *\r
890      * Prefix contains fully specified field info and portion of searched term\r
891      *\r
892      * @param Zend_Search_Lucene_Index_Term $prefix\r
893      */\r
894     public function skipTo(Zend_Search_Lucene_Index_Term $prefix)\r
895     {\r
896         $this->_termsStream->skipTo($prefix);\r
897     }\r
898 \r
899     /**\r
900      * Scans terms dictionary and returns next term\r
901      *\r
902      * @return Zend_Search_Lucene_Index_Term|null\r
903      */\r
904     public function nextTerm()\r
905     {\r
906         return $this->_termsStream->nextTerm();\r
907     }\r
908 \r
909     /**\r
910      * Returns term in current position\r
911      *\r
912      * @return Zend_Search_Lucene_Index_Term|null\r
913      */\r
914     public function currentTerm()\r
915     {\r
916         return $this->_termsStream->currentTerm();\r
917     }\r
918 \r
919     /**\r
920      * Close terms stream\r
921      *\r
922      * Should be used for resources clean up if stream is not read up to the end\r
923      */\r
924     public function closeTermsStream()\r
925     {\r
926         $this->_termsStream->closeTermsStream();\r
927         $this->_termsStream = null;\r
928     }\r
929 \r
930 \r
931     /**\r
932      * Undeletes all documents currently marked as deleted in this index.\r
933      */\r
934     public function undeleteAll()\r
935     {\r
936         foreach ($this->_indices as $index) {\r
937             $index->undeleteAll();\r
938         }\r
939     }\r
940 \r
941 \r
942     /**\r
943      * Add reference to the index object\r
944      *\r
945      * @internal\r
946      */\r
947     public function addReference()\r
948     {\r
949         // Do nothing, since it's never referenced by indices\r
950     }\r
951 \r
952     /**\r
953      * Remove reference from the index object\r
954      *\r
955      * When reference count becomes zero, index is closed and resources are cleaned up\r
956      *\r
957      * @internal\r
958      */\r
959     public function removeReference()\r
960     {\r
961         // Do nothing, since it's never referenced by indices\r
962     }\r
963 }\r