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;
	}

}
时间: 2024-12-23 10:13:27

Lucene系列:(5)LuceneUtils之索引库优化的相关文章

lucene_indexWriter说明、索引库优化

IndexWriter Hibernate的SessionFactory 在Hibernate中,一般保持一个数据库就只有一个SessionFactory.因为在SessionFactory中维护二级缓存,而SessionFactory又是线程安全的.所以SessionFactory是共享的. lucene的IndexWriter 如果同时在一个索引库中同时建立两个IndexWriter,例如: IndexWriter indexWriter = new IndexWriter(LuceneUt

全文检索之lucene的优化篇--创建索引库

在上一篇HelloWorld的基础上,建立一个directory的包,添加一个DirectoryTest的测试类,用来根据指定的索引目录创建目录存放指引. DirectoryTest类中的代码如下,基本上就是在HelloWorld的基础上改改就可以了. 里面一共三个方法,testDirectory(),测试创建索引库;testDirectoryFSAndRAM(),结合方法1的两种创建方式,优化;testDirectoryOptimize(),在方法2个基础上,研究索引的优化创建,减少创建的索引

Lucene建立索引库

问题?Lucene如何建立索引库,lucene所需要的jar包是那些  , lucene如何使用索引库,lucene的核心原理 一.Lucene是什么? 全文检索只是一个概念,而具体实现有很多框架,lucene是其中的一种方式.本文将以lucene3.0进行开发 官兵与Luncne的jar包可以去官网下载:点击打开链接,不过好像Lucene已经更新到6.1了. 二.建立索引库 1.互联网搜索全文搜索引擎结构图: 2.Lucene的结构图: 说明: (1)在数据库中,数据库中的数据文件存储在磁盘上

lucene内存索引库、分词器

内存索引库 特点 在内存中开辟一块空间,专门为索引库存放.这样有以下几个特征: 1)    因为索引库在内存中,所以访问速度更快. 2)    在程序退出时,索引库中的文件也相应的消失了. 3)    如果索引库比较大,必须得保证足够多的内存空间. 编码 在cn.hqu.directory 下新建:DirectoryTest /** * 1.能不能设置很多个索引库 *    可以设置很多个索引库 * 2.索引库能不能合并起来 *    如果是内存索引库 *      Directory ramD

lucene索引库的增删改查操作

1. 索引库的操作 保持数据库与索引库的同步 说明:在一个系统中,如果索引功能存在,那么数据库和索引库应该是同时存在的.这个时候需要保证索引库的数据和数据库中的数据保持一致性.可以在对数据库进行增.删.改操作的同时对索引库也进行相应的操作.这样就可以保证数据库与索引库的一致性. 工具类DocumentUtils 在对索引库进行操作时,增.删.改过程要把一个JavaBean封装成Document,而查询的过程是要把一个Document转化成JavaBean.在进行维护的工作中,要反复进行这样的操作

2.Lucene3.6.2包介绍,第一个Lucene案例介绍,查看索引信息的工具lukeall介绍,Luke查看的索引库内容,索引查找过程

 1  Lucen目录介绍 2  lucene-core-3.6.2.jar是lucene开发核心jar包 contrib  目录存放,包含一些扩展jar包 3  案例 建立第一个Lucene项目:lucene3_day1 (1)需要先将数据转换成为Document对象,每一个数据信息转换成为Field(String name, String value, Field.Store store, Field.Indexindex) (2)指定索引库位置Directorydirectory =

第一个lucene程序,把一个信息写入到索引库中、根据关键词把对象从索引库中提取出来、lucene读写过程分析

新建一个Java Project :LunceneTest 准备lucene的jar包,要加入的jar包至少有: 1)lucene-core-3.1.0.jar     (核心包) 2) lucene-analyzers-3.1.0.jar    (分词器) 3) lucene-highlighter-3.1.0.jar    (高亮器) 4) lucene-memory-3.1.0.jar       (高亮器) 新建实体类:Article, 属性:id,title,content; gett

lucene索引文件大小优化小结

随着业务快速发展,基于lucene的索引文件zip压缩后也接近了GB量级,而保持索引文件大小为一个可以接受的范围非常有必要,不仅可以提高索引传输.读取速度,还能提高索引cache效率(lucene打开索引文件的时候往往会进行缓存,比如MMapDirectory通过内存映射方式进行缓存). 如何降低我们的索引文件大小呢?本文进行了一些尝试,下文将一一介绍. 1 数值数据类型索引优化 1.1 数值类型索引问题 lucene本质上是一个全文检索引擎而非传统的数据库系统,它基于倒排索引,非常适合处理文本

使用Lucene的java api 写入和读取索引库

import org.apache.commons.io.FileUtils;import org.apache.lucene.analysis.standard.StandardAnalyzer;import org.apache.lucene.document.Document;import org.apache.lucene.document.Field;import org.apache.lucene.document.NumericDocValuesField;import org.a