lucene初探(三):lucene详解及流程介绍

简介

Lucene是一套用于全文检索和搜寻的开源程式库,由Apache软件基金会支持和提供。Lucene提供了一个简单却强大的应用程式接口,能够做全文索引和搜寻。在Java开发环境里Lucene是一个成熟的免 
费开源工具。就其本身而言,Lucene是目前以及最近几年最受欢迎的免费Java资讯检索程式库。人们经常提到资讯检索程式库,虽然与搜寻引擎有关,但不应该将资讯检索程式库与搜索引擎相混淆。

特点及优势

作为一个开放源代码项目,Lucene从问世之后,引发了开放源代码社群的巨大反响,程序员们不仅使用它构建具体的全文检索应用,而且将之集成到各种系统软件中去,以及构建Web应用,甚至某些商业软件也采用了Lucene作为其内部全文检索子系统的核心。Lucene是一个高性能、可伸缩的信息搜索(IR)库。它可以为你的应用程序添加索引和搜索能力。

突出的优点

Lucene作为一个全文检索引擎,其具有如下突出的优点:
1)索引文件格式独立于应用平台。Lucene定义了一套以8位字节为基础的索引文件格式,使得兼容系统或者不同平台的应用能够共享建立的索引文件。
2)在传统全文检索引擎的倒排索引的基础上,实现了分块索引,能够针对新的文件建立小文件索引,提升索引速度。然后通过与原有索引的合并,达到优化的目的。
3)优秀的面向对象的系统架构,使得对于Lucene扩展的学习难度降低,方便扩充新功能。
4)设计了独立于语言和文件格式的文本分析接口,索引器通过接受Token流完成索引文件的创立,用户扩展新的语言和文件格式,只需要实现文本分析的接口。
5)已经默认实现了一套强大的查询引擎,用户无需自己编写代码即使系统可获得强大的查询能力,Lucene的查询实现中默认实现了布尔操作、模糊查询(Fuzzy Search)、分组查询等等。

索引的重要性

通常意义的索引定义:将文献中具有检索意义的事项(可以是人名、地名、词语、概念、或其他事项)按照一定方式有序编排起来,以供检索的工具书。
那么lucene建立索引的初衷也是一样的,优化过SQL的开发人员都知道,什么是全表扫描,什么是索引扫描,利用索引可以产生事半功倍的效果,极大的提高检索性能。假如你需要搜索大量的文件,你希望找到那些包含某个单词或词组的文件。你将如何去写一个程序实现这个功能?一个做法就是按顺序扫描每一个文件,搜索是否包含给定的单词或词组。但是这样的做法有很多缺陷的,其中最明显的就是在大量的文件存在的情况下,速度是令人无法接受的。这种情况下,索引产生了。为了搜索大量的文本,你首先要对这些文本以特定的结构存储,这种存储结构可以让你迅速的搜索,消除慢的顺序扫描的过程。这种存储结构就叫索引,将文本转换成特定结构存储的过程,就叫建立索引。

Lucene的索引是一种专门设计的数据结构,类似于字典的目录,某个词对应到某一页,查找的时候直接定位到那一页,速度就非常快,不用一页一页的翻去查找,通常作为一组索引文件存储在文件系统上。

如何利用索引搜索

在索引中搜索关键词,找到包含关键词的文档的过程就是搜索。搜索质量通常使用准确度和召回率来描述。所谓召回率是指一次搜索结果集合中符合用户要求的数目与和用户查询相关的总数之比,而准确率是指一次搜索结果集合中符合用户要求的数目与该次搜索结果总数之比。我们也需要考虑其他有关搜索的因素,比如速度和快速搜索大量文本的能力,单个和多项查询、短语查询、通配符、结果的排名和排序的支持也很重要。

扫描二维码关注公众号,回复: 752956 查看本文章

我们先来一个创建索引的例子

这个例子需要引入一个apache commons io包,方便我们读取文件

maven配置

写道
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.4</version>
</dependency>
写道
package com.iris.scm.lucene.test2;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.apache.lucene.analysis.Analyzer;
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.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.index.LogByteSizeMergePolicy;
import org.apache.lucene.index.LogMergePolicy;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.wltea.analyzer.lucene.IKAnalyzer;

public class IndexBuilder {

/**
* @param args
*/
public static void main(String[] args) throws Exception {
createIndex("D:\\lucene\\textfiles", "D:\\lucene\\LuceneTest2");
}

/**
* 将指定目录的文本全部记录到索引中.
*
* <pre>
* 创建索引的步骤:
* 1)创建存放索引的目录Directory
* 2)创建索引器配置管理类IndexWriterConfig
* 3)使用索引目录和配置管理类创建索引器
* 4)使用索引器将Document写到索引文件中
* </pre>
*
* @param filePath
* @param indexPath
* @throws Exception
*/
public static void createIndex(String filePath, String indexPath) throws Exception {

// 指定索引目录
File indexDir = new File(indexPath);
// 创建索引目录
Directory directory = FSDirectory.open(indexDir);
// 创建一个中文分词器
Analyzer analyzer = new IKAnalyzer();

// 创建索引配置器
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_36, analyzer);
// 创建批量创建索引导入策略,类似数据库分批事务
LogMergePolicy mergePolicy = new LogByteSizeMergePolicy();
// 设置segment添加文档(Document)时的合并频率
// 值较小,建立索引的速度就较慢
// 值较大,建立索引的速度就较快,>10适合批量建立索引
mergePolicy.setMergeFactor(50);
// 设置segment最大合并文档(Document)数
// 值较小有利于追加索引的速度
// 值较大,适合批量建立索引和更快的搜索
mergePolicy.setMaxMergeDocs(5000);
// 启用复合式索引文件格式,合并多个segment
mergePolicy.setUseCompoundFile(true);
indexWriterConfig.setMergePolicy(mergePolicy);

// 设置索引的打开模式
indexWriterConfig.setOpenMode(OpenMode.CREATE_OR_APPEND);

// 创建索引器
IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);

List<Map<String, String>> list = readerFiles(filePath);
for (Map<String, String> map : list) {

// Document是Lucene的文档结构,需要索引的对象都要转换为Document
Document document = new Document();
// 文件名,可查询,不分词,存储到索引库记录中
document.add(new Field("file_name", map.get("file_name"), Store.YES, Index.NOT_ANALYZED));
// 文件路径,可查询,不分词,存储到索引库记录中
document.add(new Field("file_path", map.get("file_path"), Store.YES, Index.NOT_ANALYZED));
// 文本内容,这里因为文件较小,直接存储到索引记录库,如果大文件不建议存储
document.add(new Field("content", map.get("content"), Store.YES, Index.ANALYZED));

// 把文档添加到索引库
indexWriter.addDocument(document);
}

indexWriter.close();
}

/**
* 读取指定目录下的文件文本内容.
*
* @param dir
* @return
* @throws Exception
*/
public static List<Map<String, String>> readerFiles(String dir) throws Exception {

File dirFile = new File(dir);
File[] files = dirFile.listFiles();
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
for (File file : files) {
// 目录,退出
if (file.isDirectory()) {
continue;
}
Map<String, String> map = new HashMap<String, String>();
// 读取文件内容
String content = FileUtils.readFileToString(file);
map.put("file_name", file.getName());
map.put("file_path", file.getPath());
map.put("content", content);
list.add(map);
}
return list;

}
}

 

 再来一个检索索引的例子

写道
package com.iris.scm.lucene.test2;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexReader;
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.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.wltea.analyzer.lucene.IKAnalyzer;

public class IndexQuery {

public static void main(String[] args) throws Exception {

List<Map<String, String>> list1 = query("D:\\lucene\\LuceneTest2", "重金属");
System.out.println("检索式关键词:重金属");
printFileInfo(list1);

List<Map<String, String>> list2 = query("D:\\lucene\\LuceneTest2", "商业化生物");
System.out.println("检索式关键词:商业化生物");
printFileInfo(list2);
}

public static void printFileInfo(List<Map<String, String>> list) {

for (Map<String, String> map : list) {
System.out.println("文件名:" + map.get("file_name"));
System.out.println("文件路径:" + map.get("file_path"));
System.out.println("文件内容:" + map.get("content"));
System.out.println();
System.out.println();
}
}

/**
* 利用创建的索引检索.
*
* <pre>
* 利用索引检索的步骤:
* 1)使用QueryParser将查询的关键词解析成Lucene的查询对象Query。创建QueryParser的时候我们需要用到分词器,这个分词器要和前面创建索引的时候使用的分词器一致。
* 2)使用FSDirectory打开索引所在的目录。
* 3)使用IndexReader读取索引目录和使用IndexSearcher进行搜索。
* 4)返回搜索结果对象TopDocs。TopDocs包含搜索到结果总数和结果的集合ScoreDocs数组
* 5)遍历结果的集合ScoreDocs数组,根据每一个ScoreDoc的文档编号获取Document
* </pre>
*
* @param idxDir
* @param keyword
* @return
* @throws Exception
*/
public static List<Map<String, String>> query(String idxDir, String keyword) throws Exception {

String[] fields = { "file_name", "content" };
// 创建一个分词器,和创建索引时用的分词器要一致
Analyzer analyzer = new IKAnalyzer();
// 创建查询解析器
QueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_36, fields, analyzer);
// 将查询关键词解析成Lucene的Query对象
Query query = queryParser.parse(keyword);

// 打开索引目录
File indexDir = new File(idxDir);
Directory directory = FSDirectory.open(indexDir);

// 获取访问索引的接口,进行搜索
IndexReader indexReader = IndexReader.open(directory);
IndexSearcher indexSearcher = new IndexSearcher(indexReader);

// TopDocs 搜索返回的结果
TopDocs topDocs = indexSearcher.search(query, 100);// 只返回前100条记录

int totalCount = topDocs.totalHits; // 搜索结果总数量
System.out.println("搜索到的结果总数量为:" + totalCount);
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
ScoreDoc[] scoreDocs = topDocs.scoreDocs; // 搜索的结果集合
for (ScoreDoc scoreDoc : scoreDocs) {
// 文档编号
int docID = scoreDoc.doc;
// 根据文档编号获取文档
Document doc = indexSearcher.doc(docID);
Map<String, String> map = new HashMap<String, String>();
map.put("file_name", doc.get("file_name"));
map.put("file_path", doc.get("file_path"));
map.put("content", doc.get("content"));
list.add(map);
}
indexReader.close();
indexSearcher.close();
return list;
}
}

猜你喜欢

转载自beyondqinghua.iteye.com/blog/1837108