天天看點

Lucene系列:(5)LuceneUtils之索引庫優化

1、什麼是索引庫

     索引庫是Lucene的重要的存儲結構,它包括二部份:原始記錄表,詞彙表

     原始記錄表:存放的是原始記錄資訊,Lucene為存入的内容配置設定一個唯一的編号

     詞彙表:存放的是經過分詞器拆分出來的詞彙和該詞彙在原始記錄表中的編号

2、為什麼要将索引庫進行優化

     在預設情況下,向索引庫中增加一個Document對象時,索引庫自動會添加一個擴充名叫*.cfs的二進制壓縮檔案,如果向索引庫中存Document對象過多,那麼*.cfs也會不斷增加,同時索引庫的容量也會不斷增加,影響索引庫的大小。

3、索引庫優化方案

3.1、合并cfs檔案

合并後的cfs檔案是二進制壓縮字元,能解決是的檔案大小和數量的問題 

indexWriter.addDocument(document);
indexWriter.optimize();
indexWriter.close();      

3.2、設定合并因子

自動合并cfs檔案,預設10個cfs檔案合并成一個cfs檔案

indexWriter.addDocument(document);
indexWriter.setMergeFactor(3);
indexWriter.close();      

3.3、使用RAMDirectory

    類似于記憶體索引庫,能解決是的讀取索引庫檔案的速度問題,

它以空間換時間,提高速度快,但不能持久儲存,是以啟動時加載硬碟中的索引庫到記憶體中的索引庫,退出時将記憶體中的索引庫儲存到硬碟中的索引庫,且内容不能重複。

Directory fsDirectory = FSDirectory.open(new File("D:/rk/indexDB"));
Directory ramDirectory = new RAMDirectory(fsDirectory);
IndexWriter fsIndexWriter = new IndexWriter(fsDirectory,LuceneUtil.getAnalyzer(),true,LuceneUtil.getMaxFieldLength());
IndexWriter ramIndexWriter = new IndexWriter(ramDirectory,LuceneUtil.getAnalyzer(),LuceneUtil.getMaxFieldLength());
ramIndexWriter.addDocument(document);
ramIndexWriter.close();
fsIndexWriter.addIndexesNoOptimize(ramDirectory);
fsIndexWriter.close();      

ArticleDao.java

package com.rk.lucene.a_optimize;

import java.io.File;
import java.util.List;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.RAMDirectory;
import org.junit.Test;

import com.rk.lucene.entity.Article;
import com.rk.lucene.utils.LuceneUtils;

/**
 * 對索引庫進行優化
 */
public class ArticleDao {
	/**
	 * 增加document對象到索引庫中
	 */
	@Test
	public void testAdd01() throws Exception{
		Article article = new Article(1, "你好", "世界,你好!Hello World");
		Document document = LuceneUtils.javabean2document(article);
		IndexWriter indexWriter = new IndexWriter(LuceneUtils.getDirectory(), LuceneUtils.getAnalyzer(), LuceneUtils.getMaxFieldLength());
		indexWriter.addDocument(document);
		indexWriter.close();
	}
	
	/**
	 * 合并cfs檔案,合并後的cfs檔案是二進制壓縮字元,能解決是的檔案大小和數量的問題
	 */
	@Test
	public void testAdd02() throws Exception{
		Article article = new Article(2, "你好", "世界,你好!Hello World");
		Document document = LuceneUtils.javabean2document(article);
		IndexWriter indexWriter = new IndexWriter(LuceneUtils.getDirectory(), LuceneUtils.getAnalyzer(), LuceneUtils.getMaxFieldLength());
		indexWriter.addDocument(document);
		//合并cfs文本
		indexWriter.optimize();
		indexWriter.close();
	}
	
	/**
	 * 設定合并因子,自動合并cfs檔案
	 */
	@Test
	public void testAdd03() throws Exception{
		Article article = new Article(3, "你好", "世界,你好!Hello World");
		Document document = LuceneUtils.javabean2document(article);
		IndexWriter indexWriter = new IndexWriter(LuceneUtils.getDirectory(), LuceneUtils.getAnalyzer(), LuceneUtils.getMaxFieldLength());
		indexWriter.addDocument(document);
		//設定合并因子,即滿足3個cfs文本一合并
		indexWriter.setMergeFactor(3);
		indexWriter.close();
	}
	
	
	/**
	 * 使用RAMDirectory,類似于記憶體索引庫,能解決是的讀取索引庫檔案的速度問題
	 */
	@Test
	public void testAdd04() throws Exception{
		Article article = new Article(4, "你好", "世界,你好!Hello World");
		Document document = LuceneUtils.javabean2document(article);
		
		//硬碟索引庫
		Directory fsDirectory = FSDirectory.open(new File("D:/rk/indexDB"));
		
		//記憶體索引庫,因為硬碟索引庫的内容要同步到記憶體索引庫中
		Directory ramDirectory = new RAMDirectory(fsDirectory);
		
		//指向硬碟索引庫的字元流,true表示如果記憶體索引庫中和硬碟索引庫中的相同的document對象時,先删除硬碟索引庫中的document對象,
		//再将記憶體索引庫的document對象寫入硬碟索引庫中
		//反之是false,預設為false,這個boolean值寫在硬碟字元流的構造器
		IndexWriter fsIndexWriter = new IndexWriter(fsDirectory, LuceneUtils.getAnalyzer(), true, LuceneUtils.getMaxFieldLength()); 
		
		//指向記憶體索引庫的字元流
		IndexWriter ramIndexWriter = new IndexWriter(ramDirectory, LuceneUtils.getAnalyzer(), LuceneUtils.getMaxFieldLength());
		
		//将document對象寫入記憶體索引庫
		ramIndexWriter.addDocument(document);
		ramIndexWriter.close();
		
		//将記憶體索引庫的所有document對象同步到硬碟索引庫中
		fsIndexWriter.addIndexesNoOptimize(ramDirectory);
		fsIndexWriter.close();
	}
	
	
	@Test
	public void Search() throws Exception{
		String keyword = "世界";
		List<Article> list = LuceneUtils.search("content", keyword, 10, Article.class);
		LuceneUtils.printList(list);
	}
}      

LuceneUtils.java

package com.rk.lucene.utils;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Index;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.IndexWriter.MaxFieldLength;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;

import com.rk.lucene.entity.Page;

public class LuceneUtils {
	private static Directory directory;
	private static Version version;
	private static Analyzer analyzer;
	private static MaxFieldLength maxFieldLength;
	private static final String LUCENE_DIRECTORY= "D:/rk/indexDB";
	
	static{
		try {
			directory = FSDirectory.open(new File(LUCENE_DIRECTORY));
			version = Version.LUCENE_30;
			analyzer = new StandardAnalyzer(version);
			maxFieldLength = MaxFieldLength.LIMITED;
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
	
	//不讓外部new目前幫助類的對象
	private LuceneUtils(){}
	
	public static <T> void pagination(Page<T> page,String field,String keyword,Class<T> clazz) throws Exception{
		QueryParser queryParser = new QueryParser(getVersion(), field, getAnalyzer()); 
		Query query = queryParser.parse(keyword);
		
		IndexSearcher indexSearcher = new IndexSearcher(getDirectory());
		TopDocs topDocs = indexSearcher.search(query, 200);
		int totalHits = topDocs.totalHits;
		int curPage = page.getCurPage();
		int pageSize = page.getPageSize();
		int quotient = totalHits / pageSize;
		int remainder = totalHits % pageSize;
		int totalPages = remainder==0 ? quotient : quotient+1;
		int startIndex = (curPage-1) * pageSize;
		int stopIndex = Math.min(startIndex + pageSize, totalHits);
		List<T> list = page.getItems();
		if(list == null){
			list = new ArrayList<T>();
			page.setItems(list);
		}
		list.clear();
		for(int i=startIndex;i<stopIndex;i++){
			ScoreDoc scoreDoc = topDocs.scoreDocs[i];
			int docIndex = scoreDoc.doc;
			Document document = indexSearcher.doc(docIndex);
			T t = document2javabean(document, clazz);
			list.add(t);
		}
		
		page.setTotalPages(totalPages);
		page.setTotalItems(totalHits);
		indexSearcher.close();
	}
	
	public static <T> void add(T t) throws Exception{
		Document document = javabean2document(t);
		IndexWriter indexWriter = new IndexWriter(getDirectory(), getAnalyzer(), getMaxFieldLength());
		indexWriter.addDocument(document);
		indexWriter.close();
	}
	
	public static <T> void addAll(List<T> list) throws Exception{
		IndexWriter indexWriter = new IndexWriter(getDirectory(), getAnalyzer(), getMaxFieldLength());
		for(T t : list){
			Document doc = javabean2document(t);
			indexWriter.addDocument(doc);
		}
		indexWriter.close();
	}
	
	public static <T> void update(String field,String value,T t) throws Exception{
		Document document = javabean2document(t);
		IndexWriter indexWriter = new IndexWriter(getDirectory(), getAnalyzer(), getMaxFieldLength());
		indexWriter.updateDocument(new Term(field,value), document);
		indexWriter.close();
	}
	
	public static <T> void delete(String field,String value) throws Exception{
		IndexWriter indexWriter = new IndexWriter(getDirectory(), getAnalyzer(), getMaxFieldLength());
		indexWriter.deleteDocuments(new Term(field,value));
		indexWriter.close();
	}
	
	/**
	 * 删除所有記錄
	 */
	public static void deleteAll() throws Exception {
		IndexWriter indexWriter = new IndexWriter(getDirectory(), getAnalyzer(), getMaxFieldLength());
		indexWriter.deleteAll();
		indexWriter.close();
	}
	
	/**
	 * 根據關鍵字進行搜尋
	 */
	public static <T> List<T> search(String field,String keyword,int topN,Class<T> clazz) throws Exception{
		List<T> list = new ArrayList<T>();
		
		QueryParser queryParser = new QueryParser(getVersion(), field, getAnalyzer());
		Query query = queryParser.parse(keyword);
		
		IndexSearcher indexSearcher = new IndexSearcher(getDirectory());
		TopDocs topDocs = indexSearcher.search(query, topN);
		
		for(int i=0;i<topDocs.scoreDocs.length;i++){
			ScoreDoc scoreDoc = topDocs.scoreDocs[i];
			int docIndex = scoreDoc.doc;
			System.out.println("文檔索引号" + docIndex + ",文檔得分:" + scoreDoc.score);
			Document document = indexSearcher.doc(docIndex);
			T entity = (T) document2javabean(document, clazz);
			list.add(entity);
		}
		indexSearcher.close();
		return list;
	}
	
	/**
	 * 列印List
	 */
	public static <T> void printList(List<T> list){
		if(list != null && list.size()>0){
			for(T t : list){
				System.out.println(t);
			}
		}
	}
	
	//将JavaBean轉成Document對象
	public static Document javabean2document(Object obj) throws Exception{
		//建立Document對象
		Document document = new Document();
		//擷取obj引用的對象位元組碼
		Class clazz = obj.getClass();
		//通過對象位元組碼擷取私有的屬性
		java.lang.reflect.Field[] reflectFields = clazz.getDeclaredFields();
		//疊代
		for(java.lang.reflect.Field reflectField : reflectFields){
			//反射
			reflectField.setAccessible(true);
			//擷取字段名
			String name = reflectField.getName();
			//擷取字段值
			String value = reflectField.get(obj).toString();
			//加入到Document對象中去,這時javabean的屬性與document對象的屬性相同
			document.add(new Field(name, value, Store.YES, Index.ANALYZED));
		}
		//傳回document對象
		return document;
	}
	
	//将Document對象轉換成JavaBean對象
	public static <T> T document2javabean(Document document,Class<T> clazz) throws Exception{
		T obj = clazz.newInstance();
		java.lang.reflect.Field[] reflectFields = clazz.getDeclaredFields();
		for(java.lang.reflect.Field reflectField : reflectFields){
			reflectField.setAccessible(true);
			String name = reflectField.getName();
			String value = document.get(name);
			BeanUtils.setProperty(obj, name, value);
		}
		return obj;
	}
	
	public static Directory getDirectory() {
		return directory;
	}

	public static void setDirectory(Directory directory) {
		LuceneUtils.directory = directory;
	}

	public static Version getVersion() {
		return version;
	}

	public static void setVersion(Version version) {
		LuceneUtils.version = version;
	}

	public static Analyzer getAnalyzer() {
		return analyzer;
	}

	public static void setAnalyzer(Analyzer analyzer) {
		LuceneUtils.analyzer = analyzer;
	}

	public static MaxFieldLength getMaxFieldLength() {
		return maxFieldLength;
	}

	public static void setMaxFieldLength(MaxFieldLength maxFieldLength) {
		LuceneUtils.maxFieldLength = maxFieldLength;
	}

}