首页 > 代码库 > [Java Web]Java的全文搜索类库 Lucene

[Java Web]Java的全文搜索类库 Lucene

Lucene是apache软件基金会4 jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,即它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎(英文与德文两种西方语言)。Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎。


Lucene的实现机制是倒排表。例如现在有2个文档需要建立索引,内容分别为"Lucene Learning"和"Lucene Hadoop",建立后的索引实际是这样的:技术分享

索引的优势在于:虽然建立索引会比较耗时,但是在一次建立后便可以永远支持高效的查询。


Lucene的核心类有:

Directory:索引对象,常用有RAMDirectory和FSDirectory两种,前者代表一个内存中的索引文件,后者代表一个本地的索引文件。

Filed:字段,由name和value组成,常用的字段分为三种:

  1. StoredFiled:仅用来存储数据的字段,该字段的数据不会建立索引。
  2. StringFiled、LongFiled等:将数据整体建立为索引的字段。
  3. TextFiled:将数据分词并建立索引的字段。

Document:索引中存储的文档结构,一个文档由多个字段组成。

Term:词元,相当于索引中的键,每个Term都指向一个由文档组成的链表。

IndexReader:索引的读取流,可以从索引中读取出需要的文档。

IndexWriter:索引的输出流,将文档写入索引。

IndexSearcher:索引的搜索功能。

Query:搜索条件。

Analyzer:分词器,可以将字段中的数据分解成多个词元。


Lucene的流程:

技术分享

写入:将数据源转换为文档,然后使用IndexWriter的addDocument方法将文档添加到索引。

查询:使用IndexReader获取一个IndexSearcher对象,并创建一个Query对象指定查询类型,然后使用IndexSearcher的search方法得到文档。


代码实例:

添加索引:

public static void init() throws IOException {
	//索引文件
	Directory dir = FSDirectory.open(new File("E:/index"));
	//索引配置(指定Lucene版本和分词器)
	IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_4_10_0, new StandardAnalyzer());
	//得到输出流
	IndexWriter writer = new IndexWriter(dir, config);
	//创建文档
	Document doc = new Document();
	//id会以一个整体被作为词元被索引 并且会被存储在文档中
	doc.add(new StringField("id", "123", Field.Store.YES));
	//content会被分词器分割成数个词元被索引 但不会存储在文档中
	doc.add(new TextField("content", "Lucene是apache软件基金会4 jakarta" +
			"项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,即它不是一个完整的全文检索引擎," +
			"而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎," +
			"部分文本分析引擎(英文与德文两种西方语言)。Lucene的目的是为软件" +
			"开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的" +
			"功能,或者是以此为基础建立起完整的全文检索引擎。", Field.Store.NO));
	//address会被分词器分割成数个词元被索引 并且会存储在文档中
	doc.add(new TextField("address", "北京市海淀区翠微路凯德大厦", Field.Store.YES));
	//将文档添加到索引
	writer.addDocument(doc);
	//打印当前已经被索引的文档个数
	System.out.println("被索引的文档个数:" + writer.numDocs());
	writer.close();
}

遍历索引中所有的文档:

public static void ergodic() throws IOException {
	//索引文件
	Directory dir = FSDirectory.open(new File("E:/index"));
	//得到输入流
	IndexReader reader = DirectoryReader.open(dir);
	IndexSearcher searcher = new IndexSearcher(reader);
	Document doc = null;
	for (int i = 0; i < reader.maxDoc(); i++) {
		doc = searcher.doc(i);
		System.out.println("Doc [" + i + "] : id: "
				+ doc.get("id") + ", desc: " + doc.get("content"));
	}

}


搜索文档:

public static void search() throws IOException, ParseException {
	//索引文件
	Directory dir = FSDirectory.open(new File("E:/index.txt"));
	//输入流
	IndexReader reader = DirectoryReader.open(dir);
	IndexSearcher searcher = new IndexSearcher(reader);
	// 搜索条件
	QueryParser parser = new QueryParser("desc",
			new StandardAnalyzer());
	Query query = parser.parse("索引文件");
	// 搜索结果
	TopDocs hits = searcher.search(query, 2);
	System.out.println("查询出来的总数是:" + hits.totalHits);
	// 获取搜索结果
	Document doc = null;
	for (ScoreDoc scoreDoc : hits.scoreDocs) {
		doc = searcher.doc(scoreDoc.doc);
		System.out.println("Doc [" + scoreDoc.doc + "] : id: "
				+ doc.get("id") + ", desc: " + doc.get("content"));
	}
}

一些细节:

  1. 对于一个索引文件,同时只能有一个输出流对它进行操作.
  2. 为了避免频繁的I/O操作,可以先将索引建立在RAMDirectory中,然后一次性写入本地文件。
  3. 中文分词库比较好用的有Ansj和IK,可以自行百度了解。
  4. Lucene不支持去重,DuplicateFilter会先将索引进行去重后在进行条件查询,这样会造成数据缺失。
  5. 索引的更新可以先根据词元删除原本的文档后再添加新的文档,在批量操作时效率很高。


[Java Web]Java的全文搜索类库 Lucene