Lucene不是一个完整的全文索引应用,而是是一个用Java写的全文索引引擎工具包,它可以方便的嵌入到各种应用中实现针对应用的全文索引/检索功能。
Lucene的作者:Lucene的贡献者Doug Cutting是一位资深全文索引/检索专家,曾经是V-Twin搜索引擎(Apple的Copland操作系统的成就之一)的主要开发者,后在 Excite担任高级系统架构设计师,目前从事于一些INTERNET底层架构的研究。他贡献出的Lucene的目标是为各种中小型应用程序加入全文检索功能。
Lucene的发展历程:早先发布在作者自己的
www.lucene.com,后来发布在SourceForge,2001年年底成为APACHE基金会jakarta的一个子项目:
http://jakarta.apache.org/lucene/
已经有很多Java项目都使用了Lucene作为其后台的全文索引引擎
一、开始
首先在Apache下载Lucene 2.3.0包,其中包含了核心jar和LuceneAPI文档,解压后,将 lucene-core-2.3.0.jar放在classpath中。
首先在Apache下载Lucene 2.3.0包,其中包含了核心jar和LuceneAPI文档,解压后,将 lucene-core-2.3.0.jar放在classpath中。
二、创建索引
创建索引时需要指定存放索引的目录(将来检索时需要对这个目录中的索引进行检索),和文件的目录(如果是对文件进行索引的话)代码如下:
public void crateIndex() throws Exception {
File indexDir = new File("D://luceneIndex");
// 存储索引文件夹
File dataDir = new File("D://test");
// 需要检索文件夹
Analyzer luceneAnalyzer = new PaodingAnalyzer();
// PaodingAnalyzer这个类是庖丁解牛中文分词分析器类继承了Lucene的Analyzer接口,对于检索中文分词有很大帮助
File[] dataFiles = dataDir.listFiles();
boolean fileIsExist = false;
if (indexDir.listFiles().length == 0)
fileIsExist = true;
IndexWriter indexWriter = new IndexWriter(indexDir, luceneAnalyzer,
fileIsExist);
// 第三个参数是一个布尔型的变量,如果为 true 的话就代表创建一个新的索引,为 false 的话就代表在原来索引的基础上进行操作。
long startTime = new Date().getTime();
this.doIndex(dataFiles, indexWriter);
indexWriter.optimize();// 优化索引
indexWriter.close();// 关闭索引
long endTime = new Date().getTime();
System.out.println("It takes " + (endTime - startTime)
+ " milliseconds to create index for the files in directory "
+ dataDir.getPath());
}
private void doIndex(File[] dataFiles, IndexWriter indexWriter)
throws Exception {
for (int i = 0; i < dataFiles.length; i++) {
if (dataFiles[i].isFile()
&& dataFiles[i].getName().endsWith(".html")) {// 索引所有html格式文件
System.out.println("Indexing file "
+ dataFiles[i].getCanonicalPath());
Reader txtReader = new FileReader(dataFiles[i]);
Document document = new Document();
// Field.Store.YES 存储Field.Store.NO 不存储
// Field.Index.TOKENIZED 分词 Field.Index.UN_TOKENIZED 不分词
document.add(new Field("path", dataFiles[i].getCanonicalPath(),
Field.Store.YES, Field.Index.UN_TOKENIZED));
document.add(new Field("filename", dataFiles[i].getName(),
Field.Store.YES, Field.Index.TOKENIZED));
// 另外一个构造函数,接受一个Reader对象
document.add(new Field("contents", txtReader));
indexWriter.addDocument(document);
} else if (dataFiles[i].isFile()
&& dataFiles[i].getName().endsWith(".doc")) {// 索引所有word文件
FileInputStream in = new FileInputStream(dataFiles[i]);// 获得文件流
WordExtractor extractor = new WordExtractor(in);// 使用POI对word文件进行解析
String str = extractor.getText();// 返回String
Document document = new Document();// 生成Document对象,其中有3个Field,分别是path,filename,contents
document.add(new Field("path", dataFiles[i].getCanonicalPath(),
Field.Store.YES, Field.Index.UN_TOKENIZED));
document.add(new Field("filename", dataFiles[i].getName(),
Field.Store.YES, Field.Index.TOKENIZED));
// 另外一个构造函数,接受一个Reader对象
document.add(new Field("contents", str, Field.Store.YES,
Field.Index.TOKENIZED,
Field.TermVector.WITH_POSITIONS_OFFSETS));
indexWriter.addDocument(document);
} else {
if (dataFiles[i].isDirectory()) {
doIndex(dataFiles[i].listFiles(), indexWriter);// 使用递归,继续索引文件夹
}
}
}
}
从上面代码中可以看到对文件(或者说是数据)创建索引是一件很容易的事,首先确定需要索引的文件夹(或者数据库中的数据注:Lucene只接受数据,他不会区分数据的来源,也就是说不管是什么你只要把它转为String格式的数据,Lucene就能创建索引),然后指定创建后索引存放的地方,我们自己对数据处理后创建一个
Document对象这里面你可以自己定义放几
个
Field,并定义
Field是否进行分词什么的,这样索引就创建好了.
注:使用庖丁解牛中文分词,需要将"庖丁"中的词典(dic文件夹)放到classpath 中再把 paoding-analyzer.properties文件也放到classpath中 properties文件内容如下:
paoding.imports
=
ifexists:classpath:paoding-analysis-default.properties;
ifexists:classpath:paoding-analysis-user.properties;
ifexists:classpath:paoding-knives-user.properties
ifexists:classpath:paoding-analysis-user.properties;
ifexists:classpath:paoding-knives-user.properties
paoding.dic.home
=
classpath:dic
三、检索
对于创建数据的索引我们已经了解了,下面介绍一下,检索数据, 检索数据的时候我们不用关心原始的数据或者文件,我们只关心lucene生成的索引,
但是要使用当初生成索引时的同一个分析器进行分析索引.
public void searchIndex() throws Exception {
String contents = "项目";// 内容的关键字
String filename = "测试";// 文件名的关键字
File indexDir = new File("D:\\luceneIndex");//存放索引的文件夹
FSDirectory directory = FSDirectory.getDirectory(indexDir);
Searcher searcher = new IndexSearcher(directory);
QueryParser parserContents = new QueryParser("contents", luceneAnalyzer);
QueryParser parserFilename = new QueryParser("filename", luceneAnalyzer);
// 使用同一个分析器luceneAnalyzer分别生成两个QueryParser对象
Query query1 = parserContents.parse(contents);
Query query2 = parserFilename.parse(filename);
BooleanQuery query = new BooleanQuery();
query.add(query1, BooleanClause.Occur.MUST);
query.add(query2, BooleanClause.Occur.MUST);
SimpleHTMLFormatter formatter =new SimpleHTMLFormatter("<span class=\"highlight\">","</span>");
Highlighter highlighter = new Highlighter(formatter,newQueryScorer(query));
highlighter.setTextFragmenter(newSimpleFragmenter(60));
// Lucene自带的高亮功能,在Lucene发布的bin中的lucene-2.3.0\contrib\highlighter文件夹下
// lucene-highlighter-2.3.0.jar 需要导入
Hits hits = searcher.search(query);
for(int i=0;i<hits.length();i++){
TokenStream tokenStream = luceneAnalyzer.tokenStream("contents", new StringReader(hits.doc.get("contents")));
this.pageContext.getOut().println("<font style='font-size:13px'><a href='"+ hits.doc.get("path") +"'><b>"+ELFuncUtil.setStyle(hits.doc.get("filename"), filename)+"</b></a></font><br>");
String str = highlighter.getBestFragment(tokenStream,hits.doc.get("contents") + "...");
this.pageContext.getOut().println("<font style='font-size:12px'>"+str+"</font>");
this.pageContext.getOut().println("<br><hr><br>");
}
}
<
style
>
.highlight
{
background
:
yellow
;
color
:
#CC0033
;
}
</ style >
</ style >
这样外界的访问直接通过Lucene去检索索引,不去触及真正的文件,效率大大提高.页面再加上一点修饰一个使用Lucene构建的搜索引擎就完成了.
版权声明:原创作品,允许转载,转载时请务必以超链接形式标明文章
原始出处 、作者信息和本声明。否则将追究法律责任。
http://tonyaction.blog.51cto.com/227462/62451
作者:
tony_action