Lucene支持創建多個索引目錄,同時存儲多個索引。我們可能擔心的問題是,在索引的過程中,分散地存儲到多個索引目錄中,是否在搜索時能夠得到全局的相關度計算得分,其實Lucene的ParallelMultiSearcher和MultiSearcher支持全局得分的計算,也就是說,雖然索引分布在多個索引目錄中,在搜索的時候還會將全部的索引數據聚合在一起進行查詢匹配和得分計算。
索引目錄處理
下面我們通過將索引隨機地分布到以a~z的26個目錄中,並實現一個索引和搜索的程序,來驗證一下Lucene得分的計算。
首先,實現一個用來構建索引目錄以及處理搜索的工具類,代碼如下所示:
- package org.shirdrn.lucene;
-
- import java.io.File;
- import java.io.IOException;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.Random;
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
-
- import org.apache.lucene.index.CorruptIndexException;
- import org.apache.lucene.index.IndexWriter;
- import org.apache.lucene.index.IndexWriterConfig;
- import org.apache.lucene.index.IndexWriter.MaxFieldLength;
- import org.apache.lucene.search.DefaultSimilarity;
- import org.apache.lucene.search.IndexSearcher;
- import org.apache.lucene.search.Searchable;
- import org.apache.lucene.search.Similarity;
- import org.apache.lucene.store.FSDirectory;
- import org.apache.lucene.store.LockObtainFailedException;
- import org.shirdrn.lucene.MultipleIndexing.IndexWriterObj;
-
- /**
- * Indexing accross multiple Lucene indexes.
- *
- * @author shirdrn
- * @date 2011-12-12
- */
- public class IndexHelper {
-
- private static WriterHelper writerHelper = null;
- private static SearcherHelper searcherHelper = null;
-
- public static WriterHelper newWriterHelper(String root, IndexWriterConfig indexConfig) {
- return WriterHelper.newInstance(root, indexConfig);
- }
-
- public static SearcherHelper newSearcherHelper(String root, IndexWriterConfig indexConfig) {
- return SearcherHelper.newInstance(root, indexConfig);
- }
-
- protected static class WriterHelper {
- private String alphabet = "abcdefghijklmnopqrstuvwxyz";
- private Lock locker = new ReentrantLock();
- private String indexRootDir = null;
- private IndexWriterConfig indexConfig;
- private Map<Character, IndexWriterObj> indexWriters = new HashMap<Character, IndexWriterObj>();
- private static Random random = new Random();
- private WriterHelper() {
-
- }
- private synchronized static WriterHelper newInstance(String root, IndexWriterConfig indexConfig) {
- if(writerHelper==null) {
- writerHelper = new WriterHelper();
- writerHelper.indexRootDir = root;
- writerHelper.indexConfig = indexConfig;
- }
- return writerHelper;
- }
- public IndexWriterObj selectIndexWriter() {
- int pos = random.nextInt(alphabet.length());
- char ch = alphabet.charAt(pos);
- String dir = new String(new char[] {ch});
- locker.lock();
- try {
- File path = new File(indexRootDir, dir);
- if(!path.exists()) {
- path.mkdir();
- }
- if(!indexWriters.containsKey(ch)) {
- IndexWriter indexWriter = new IndexWriter(FSDirectory.open(path), indexConfig.getAnalyzer(), MaxFieldLength.UNLIMITED);
- indexWriters.put(ch, new IndexWriterObj(indexWriter, dir));
- }
- } catch (CorruptIndexException e) {
- e.printStackTrace();
- } catch (LockObtainFailedException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- locker.unlock();
- }
- return indexWriters.get(ch);
- }
- @SuppressWarnings("deprecation")
- public void closeAll(boolean autoOptimize) {
- Iterator<Map.Entry<Character, IndexWriterObj>> iter = indexWriters.entrySet().iterator();
- while(iter.hasNext()) {
- Map.Entry<Character, IndexWriterObj> entry = iter.next();
- try {
- if(autoOptimize) {
- entry.getValue().indexWriter.optimize();
- }
- entry.getValue().indexWriter.close();
- } catch (CorruptIndexException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
-
- protected static class SearcherHelper {
- private List<IndexSearcher> searchers = new ArrayList<IndexSearcher>();
- private Similarity similarity = new DefaultSimilarity();
- private SearcherHelper() {
-
- }
- private synchronized static SearcherHelper newInstance(String root, IndexWriterConfig indexConfig) {
- if(searcherHelper==null) {
- searcherHelper = new SearcherHelper();
- if(indexConfig.getSimilarity()!=null) {
- searcherHelper.similarity = indexConfig.getSimilarity();
- }
- File indexRoot = new File(root);
- File[] files = indexRoot.listFiles();
- for(File f : files) {
- IndexSearcher searcher = null;
- try {
- searcher = new IndexSearcher(FSDirectory.open(f));
- } catch (CorruptIndexException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- if(searcher!=null) {
- searcher.setSimilarity(searcherHelper.similarity);
- searcherHelper.searchers.add(searcher);
- }
- }
- }
- return searcherHelper;
- }
- public void closeAll() {
- Iterator<IndexSearcher> iter = searchers.iterator();
- while(iter.hasNext()) {
- try {
- iter.next().close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- public Searchable[] getSearchers() {
- Searchable[] a = new Searchable[searchers.size()];
- return searchers.toArray(a);
- }
- }
- }
由於在索引的時候,同時打開了多個Directory實例,而每個Directory對應一個IndexWriter,我們通過記錄a~z這26個字母為每個IndexWriter的名字,將IndexWriter和目錄名稱包裹在IndexWriterObj類的對象中,便於通過日志看到實際數據的分布。在進行Lucene Document構建的時候,將這個索引目錄的名字(a~z字符中之一)做成一個Field。在索引的時候,值需要調用IndexHelper.WriterHelper的selectIndexWriter()方法,即可以自動選擇對應的IndexWriter實例去進行索引。
在搜索的時候,通過IndexHelper.SearcherHelper工具來獲取多個Searchable實例的數組,調用getSearchers()即可以獲取到,提供給MultiSearcher構建搜索。