全文检索Lucene(二)---索引库维护

维护索引库
1,创建索引库
2,删除索引库
3,更新索引库
4,索引库的优化

索引设置的一些建议:

  1. 尽量减少不必要的存储
  2. 不需要检索的内容不要建立索引
  3. 非文本格式需要提前转化
    4)需要整体存放的内容不要分词

数据与Document、Field的转换
我们在应用程序中使用对象表示数据。在数据库中使用的是表记录,所以存在来回转换的问题。同样,要索引库中使用的是Document,也存在来回转换的问题。
对于一个要进行搜索的实体对象,我们会写一个对应的工具类,其中有两个方法:

Document Object2Document(Object object); // 对象Document
Object Document2Object(Document doc); // Document对象

在转换时,对象中的属性对应Document中的Field。由于Lucene只处理文本,所有所有的属性值在存储前都要先转成字符串。使用构造方法:Field(String name, String value, Store store, Index index)。
Store 指定当前字段的数据要不要存到索引库中
Index 指定当前字段的数据是否可以被搜索(是否更新词汇表)
Store与Index都是枚举类型。Store:指定是否把当前属性值的原始内容存储到索引库中。如果存储(YES),在搜索出相应数据时这个属性就有原始的值;如果不存储(NO),得到的数据的这个属性的值为null。Index:指定是否建立索引(词汇表)。建立索引才能被搜索到。不可以不存储也不建立索引(没有意义)。

NumericUtils与DateTools
如果属性的类型不是字符串,则要先进转换:如果是数字类型,使用NumericUtils。如果是日期类型,则使用DataTools。

代码示例:
Article.java

package com.my.bean;

public class Article {
	
	private Integer id;
	private String title;
	private String content;
	
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	
	

}

QueryPageResult.java

package com.my.bean;

import java.util.List;

public class QueryPageResult {
	private int count;
	private List list;

	public QueryPageResult(int count, List list) {
		this.count = count;
		this.list = list;
	}

	public int getCount() {
		return count;
	}

	public void setCount(int count) {
		this.count = count;
	}

	public List getList() {
		return list;
	}

	public void setList(List list) {
		this.list = list;
	}

}

ArticleDocumentUtils.java

package com.my.utils;

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 com.my.bean.Article;

public class ArticleDocumentUtils {
	
	public static Document article2Document(Article article) {
		Document doc = new Document();

		String idStr = article.getId().toString(); // 要把Integer的id值转为String型
		doc.add(new Field("id", idStr, Store.YES, Index.NOT_ANALYZED));
		doc.add(new Field("title", article.getTitle(), Store.YES, Index.ANALYZED));
		doc.add(new Field("content", article.getContent(), Store.YES, Index.ANALYZED));

		return doc;
	}
	
	public static Article document2Article(Document doc) {
		Article article = new Article();

		Integer id = Integer.parseInt(doc.get("id")); // 要把String型的值转为Integer型
		article.setId(id);
		article.setTitle(doc.get("title"));
		article.setContent(doc.get("content"));

		return article;
	}

}

Configuration.java

package com.my.utils;

import java.io.File;
import java.io.IOException;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;

public class Configuration {
	
	private static Directory directory;
	private static Analyzer analyzer;

	static {
		// 读取配置文件,并初始化配置(这里只模拟一下)
		try {
			directory = FSDirectory.open(new File("./indexDir"));
			analyzer = new StandardAnalyzer(Version.LUCENE_30);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	public static Directory getDirectory() {
		return directory;
	}

	public static Analyzer getAnalyzer() {
		return analyzer;
	}

}

LuceneUtils.java

package com.my.utils;

import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriter.MaxFieldLength;

public class LuceneUtils {
	
	private static IndexWriter indexWriter;

	static {
		// 在加载类时初始化一次
		try {
			indexWriter = new IndexWriter(Configuration.getDirectory(), Configuration.getAnalyzer(), MaxFieldLength.LIMITED);
			System.out.println("-- 已经初始化IndexWriter --");
		} catch (Exception e) {
			throw new RuntimeException(e);
		}

		// 在程序退出前关闭
		Runtime.getRuntime().addShutdownHook(new Thread() {
			@Override
			public void run() { // 在JVM退出前会执行这个run()方法
				try {
					indexWriter.close();
					System.out.println("-- IndexWriter已关闭 --");
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			}
		});
	}

	public static IndexWriter getIndexWriter() {
		return indexWriter;
	}


}

ArticleIndexDao.java

package com.my.indexdao;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
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.util.Version;

import com.my.bean.Article;
import com.my.bean.QueryPageResult;
import com.my.utils.ArticleDocumentUtils;
import com.my.utils.Configuration;
import com.my.utils.LuceneUtils;

public class ArticleIndexDao {

	/**
	 * 创建索引(保存到索引库)
	 * 
	 * @param article
	 */
	public void save(Article article) {
		// 1,把Article转成Document
		Document doc = ArticleDocumentUtils.article2Document(article);

		// 2,添加索引库中
		try {
			LuceneUtils.getIndexWriter().addDocument(doc); // 保存
			LuceneUtils.getIndexWriter().commit(); // 提交更改
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 删除索引
	 * 
	 * Term:是指某字段中的某个关键词(在目录中出现的关键词)
	 * 
	 * @param id
	 */
	public void delete(Integer id) {
		try {
			Term term = new Term("id", id.toString());

			LuceneUtils.getIndexWriter().deleteDocuments(term); // 删除所有包含指定term的数据
			LuceneUtils.getIndexWriter().commit(); // 提交更改
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 更新索引
	 * 
	 * @param article
	 */
	public void update(Article article) {
		try {
			Term term = new Term("id", article.getId().toString());
			Document doc = ArticleDocumentUtils.article2Document(article);

			LuceneUtils.getIndexWriter().updateDocument(term, doc); // 更新
			LuceneUtils.getIndexWriter().commit(); // 提交更改

			// // 更新就是先删除再创建
			// indexWriter.deleteDocuments(term);
			// indexWriter.addDocument(doc);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	
}

IndexDaoTest.java

package com.my.indexdao;

import java.util.List;

import org.junit.Test;

import com.my.bean.Article;
import com.my.bean.QueryPageResult;

public class IndexDaoTest {
	
	private ArticleIndexDao articleIndexDao = new ArticleIndexDao();

	@Test
	public void testSave_1() {
		// 模拟一条刚保存到数据库中的数据
		Article article = new Article();
		article.setId(1);
		article.setTitle("Lucene是全文检索的框架");
		article.setContent("如果信息检索系统在用户发出了检索请求后再去互联网上找答案,根本无法在有限的时间内返回结果。");

		// 建立索引 ?
		articleIndexDao.save(article);
	}

	@Test
	public void testSave_25() {
		for (int i = 1; i <= 25; i++) {
			// 模拟一条刚保存到数据库中的数据
			Article article = new Article();
			article.setId(i);
			article.setTitle("Lucene是全文检索的框架");
			article.setContent("如果信息检索系统在用户发出了检索请求后再去互联网上找答案,根本无法在有限的时间内返回结果。");

			// 建立索引 ?
			articleIndexDao.save(article);
		}
	}

	@Test
	public void testDelete() {
		articleIndexDao.delete(1);
	}

	@Test
	public void testUpdate() {
		// 模拟一条游离状态的数据
		Article article = new Article();
		article.setId(1);
		article.setTitle("Lucene是全文检索的框架");
		article.setContent("这是更新后的结果");

		articleIndexDao.update(article);
	}



}

索引库优化:
1,合并索引库文件
核心API

IndexWriter.optimize()
indexWriter.setMergeFactor(int)

代码示例
OptimizeTest.java

package com.my.lucene;

import org.apache.lucene.document.Document;
import org.junit.Test;

import com.my.bean.Article;
import com.my.utils.ArticleDocumentUtils;
import com.my.utils.LuceneUtils;

public class OptimizeTest {

	// 合并索引库中的多个小文件为一个大文件
	@Test
	public void testOptimize() throws Exception {
		LuceneUtils.getIndexWriter().optimize();
	}

	// 自动合并:在小文件的数量到达多少个后就自动的合并成一个大文件
	@Test
	public void testAutoOptimize() throws Exception {
		// 设置当小文件的数量到达多少个之后就自动的合并,This must never be less than 2. The default value is 10.
		LuceneUtils.getIndexWriter().setMergeFactor(3); // 设置后是在下一次的增删改操作后才会生效

		// 添加一条索引数据
		Article article = new Article();
		article.setId(1);
		article.setTitle("Lucene是全文检索的框架");
		article.setContent("如果信息检索系统在用户发出了检索请求后再去互联网上找答案,根本无法在有限的时间内返回结果。");
		Document doc = ArticleDocumentUtils.article2Document(article);
		LuceneUtils.getIndexWriter().addDocument(doc);
	}

	
}

2,使用RAMDirectory
Lucene的API接口设计的比较通用,输入输出结构都很像数据库的表==>记录==>字段,所以很多传统的应用的文件、数据库等都可以比较方便的映射到Lucene的存储结构/接口中。总体上看:可以先把Lucene当成一个支持全文索引的数据库系统。
Lucene的索引存储位置使用的是一个接口(抽象类),也就可以实现各种各样的实际存储方式(实现类、子类),比如存到文件系统中,存在内存中、存在数据库中等等。Lucene提供了两个子类:FSDirectory与RAMDirectory。
1, FSDirectory:在文件系统中,是真实的文件夹与文件。
2, RAMDirectory:在内存中,是模拟的文件夹与文件。与FSDirectory相比:1因为没有IO操作,所以速度快。2,因为在内存中,所以在程序退出后索引库数据就不存在了。

代码示例:
DirectoryTest.java

package com.my.lucene;

import java.io.File;

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

import com.my.bean.Article;
import com.my.utils.ArticleDocumentUtils;
import com.my.utils.Configuration;

public class DirectoryTest {

	@Test
	public void testRAMDirectory() throws Exception {
		// 一、程序启动时加载到RAMDirectory中(在构造对象时传递一个参数就可以了)
		Directory fsDir = FSDirectory.open(new File("./indexDir/")); // 文件系统中的真实的目录与文件:速度慢,能长久保存
		Directory ramDir = new RAMDirectory(fsDir); // 内存中虚拟出来的目录与文件:速度快,程序退出就没有了

		// ========== 在程序运行的过程,会对RAMDirectory进行操作 ==========
		IndexWriter ramIndexWriter = new IndexWriter(ramDir, Configuration.getAnalyzer(), MaxFieldLength.LIMITED);

		// 向ramDir中添加一条索引数据
		Article article = new Article();
		article.setId(1);
		article.setTitle("Lucene是全文检索的框架");
		article.setContent("如果信息检索系统在用户发出了检索请求后再去互联网上找答案,根本无法在有限的时间内返回结果。");
		Document doc = ArticleDocumentUtils.article2Document(article);
		ramIndexWriter.addDocument(doc); // 添加
		ramIndexWriter.close();
		// ========== 程序要退出了,对RAMDirectory的操作完毕 ==========

		// 二、退出前把RAMDirectory中数据保存到FSDirectory中
		// 如果有索引库,就追加,如果没有,就创建
		// IndexWriter fsIndexWriter = new IndexWriter(fsDir, Configuration.getAnalyzer(), MaxFieldLength.LIMITED);
		// 第3个参数create: true to create the index or overwrite the existing one; false to append to the existing index,如果在存在,就报错
		IndexWriter fsIndexWriter = new IndexWriter(fsDir, Configuration.getAnalyzer(), true, MaxFieldLength.LIMITED);
		fsIndexWriter.addIndexesNoOptimize(ramDir); // 把指定索引库中的数据添加到当前的索引库中
		// fsIndexWriter.optimize();
		fsIndexWriter.close();
	}
	
}

Coding Diary

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值