lucene是apache软件基金会的一个开源全文检索收索引擎。源代码由java开发,是Java开发环境里的一个成熟的免费开源工具。lucene的核心代码仅有1m左右切不依赖于其他jar包。尽管lucene是有java开发的,随着lucene的流行,现已经能在开源网站找到其他语言的移植版本(包括c/c++,c#,Ruby,Perl,Python,以及PHP版本)
lucene的实现其实相当的简单,易用,你只需要实现几个接口就行。
提供一个在线lucene文档学习库
易百教程-lucene教程
全文数据库是全文检索系统的主要构成部分。所谓全文数据库是将一个完整的信息源的全部内容转化为计算机可以识别、处理的信息单元而形成的数据集合。全文数据库不仅存储了信息,而且还有对全文数据进行词、字、段落等更深层次的编辑、加工的功能,而且所有全文数据库无一不是海量信息数据库。
而使用lucene,就是我们将所有需要通过检索出来的文件保存到全文数据库。让我们快速的能通过检索字段来在大量数据中查找我们需要的文章。查考各大浏览器的搜索方式,和各大门户网站的搜索效果。
lucene只是一个软件类库,或者说是工具箱,而不是完整的收索程序。lucene专注于文本搜索和搜索功能。使用lucene能够使让你的应用程序在不用了解复杂的索引和收索的实现的情况下,通过调用它简单易用的api,就能按照固定的规则来进行事务处理,这时你的整个程序将围绕lucene这个核心来运行。
我们使用lucene这个工具类,主要是要理解索引这个概念。lucene官方文档一半篇幅在将索引这个内容。使用lucene的运行流程就是创建索引,更新索引,删除索引,检索索引。主要是流的输入输出(在后面提供的工具类可以查看代码)。(lucene在window上的运行可能会出现问题,而Linux运行较好,主要是由于window系统设置文件在打开状态无法删除导致。)
org.apache.lucene
lucene-core
6.4.1
org.apache.lucene
lucene-queryparser
6.4.1
org.apache.lucene
lucene-analyzers-common
6.4.1
com.janeluo
ikanalyzer
2012_u6
org.apache.lucene
lucene-highlighter
6.4.1
其中 indexDer 为lucene的全文检索库位置。这个地址可以根据自己情况选择。
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.zhiliao.common.utils.PathUtil;
import com.zhiliao.component.lucene.util.DocumentUtil;
import com.zhiliao.component.lucene.util.IKAnalyzer5x;
import com.zhiliao.component.lucene.util.IndexObject;
import com.zhiliao.component.lucene.util.QueryUtil;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.*;
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.search.highlight.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
public class LuceneManager {
private final static Logger log = LoggerFactory.getLogger(LuceneManager.class);
private Directory directory = null;
private Analyzer analyzer = null;
private static String indexDer = null;
public void setIndexDer(String indexDer) {
this.indexDer = indexDer;
}
public Analyzer getAnalyzer() {
if(analyzer == null){
analyzer = new IKAnalyzer5x(true);
}
return analyzer;
}
public Directory getDirectory() throws IOException {
if(directory == null){
File indexRepositoryFile = new File(this.indexDer);
Path path = indexRepositoryFile.toPath();
directory = FSDirectory.open(path);
}
return directory;
}
/*创建索引*/
public void create(IndexObject indexObject) {
IndexWriter indexWriter = null;
try {
IndexWriterConfig config = new IndexWriterConfig(this.getAnalyzer());
indexWriter = new IndexWriter(this.getDirectory(), config);
indexWriter.addDocument(DocumentUtil.IndexObject2Document(indexObject));
indexWriter.commit();
} catch (Exception e) {
e.printStackTrace();
if(indexWriter!=null){
try {
indexWriter.rollback();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}finally {
if(indexWriter!=null){
try {
indexWriter.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
/* 删除索引 */
public void delete(IndexObject indexObject) {
IndexWriter indexWriter = null;
try {
IndexWriterConfig config = new IndexWriterConfig(this.getAnalyzer());
indexWriter = new IndexWriter(this.getDirectory(), config);
Long result =indexWriter.deleteDocuments(new Term("id", indexObject.getId()));
log.info("deleted:{}",result);
} catch (Exception e) {
e.printStackTrace();
if(indexWriter!=null){
try {
indexWriter.rollback();
} catch (IOException e1) {
e1.printStackTrace();
}
}
} finally {
if(indexWriter!=null){
try {
indexWriter.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
/* 删除索引 */
public void deleteAll() {
IndexWriter indexWriter = null;
try {
IndexWriterConfig config = new IndexWriterConfig(this.getAnalyzer());
indexWriter = new IndexWriter(this.getDirectory(), config);
Long result =indexWriter.deleteAll();
/*清空回收站*/
indexWriter.forceMergeDeletes();
log.info("deleted:{}",result);
} catch (Exception e) {
e.printStackTrace();
if(indexWriter!=null){
try {
indexWriter.rollback();
} catch (IOException e1) {
e1.printStackTrace();
}
}
} finally {
if(indexWriter!=null){
try {
indexWriter.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
/* 更新单挑索引 */
public void update(IndexObject indexObject) {
IndexWriter indexWriter = null;
try {
IndexWriterConfig config = new IndexWriterConfig(this.getAnalyzer());
indexWriter = new IndexWriter(this.getDirectory(), config);
indexWriter.updateDocument(new Term("id", indexObject.getId()),DocumentUtil.IndexObject2Document(indexObject));
} catch (Exception e) {
e.printStackTrace();
if(indexWriter!=null){
try {
indexWriter.rollback();
} catch (IOException e1) {
e1.printStackTrace();
}
}
} finally {
if(indexWriter!=null){
try {
indexWriter.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
/* 查询索引 */
public PageInfo page(Integer pageNumber, Integer pageSize,String keyword){
IndexReader indexReader = null;
PageInfo pageQuery = null;
List searchResults = Lists.newArrayList();
try {
indexReader = DirectoryReader.open(this.getDirectory());
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
Query query = QueryUtil.query(keyword,this.getAnalyzer(),"title","keywords","description");
ScoreDoc lastScoreDoc = this.getLastScoreDoc(pageNumber, pageSize, query, indexSearcher);
/*将上一页的最后一个document传递给searchAfter方法以得到下一页的结果 */
TopDocs topDocs = indexSearcher.searchAfter(lastScoreDoc,query, pageSize);
Highlighter highlighter = this.addStringHighlighter(query);
log.info("搜索词语:{}",keyword);
log.info("总共的查询结果:{}", topDocs.totalHits);
for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
int docID = scoreDoc.doc;
float score = scoreDoc.score;
Document document = indexSearcher.doc(docID);
IndexObject indexObject =DocumentUtil.document2IndexObject(this.getAnalyzer(), highlighter, document,score);
searchResults.add(indexObject);
log.info("相关度得分:" + score);
}
pageQuery = new PageInfo<>();
// int page=0;
// if(topDocs.totalHits%pageSize!=0){
// page=(topDocs.totalHits%pageSize)+1;
// }else {
// page=topDocs.totalHits%pageSize;
// }
// pageQuery.setPages(page);
pageQuery.setPageNum(pageNumber);
pageQuery.setPageSize(pageSize);
pageQuery.setTotal(topDocs.totalHits);
Collections.sort(searchResults);
pageQuery.setList(searchResults);
} catch (Exception e) {
deleteAll();
e.printStackTrace();
}finally {
if(indexReader!=null){
try {
indexReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return pageQuery;
}
/* 根据页码和分页大小获取上一次的最后一个ScoreDoc */
private ScoreDoc getLastScoreDoc(Integer pageNumber,Integer pageSize,Query query,IndexSearcher searcher) throws IOException {
if(pageNumber==1)return null;
int total = pageSize*(pageNumber-1);
TopDocs topDocs = searcher.search(query,total);
return topDocs.scoreDocs[total -1];
}
/* 设置字符串高亮 */
private Highlighter addStringHighlighter(Query query){
QueryScorer scorer=new QueryScorer(query);
Fragmenter fragmenter=new SimpleSpanFragmenter(scorer);
SimpleHTMLFormatter simpleHTMLFormatter=new SimpleHTMLFormatter("","");
Highlighter highlighter=new Highlighter(simpleHTMLFormatter, scorer);
highlighter.setTextFragmenter(fragmenter);
return highlighter;
}
}
使用了项目中一个cms建站系统的测试案例。搜索后提供相关度评分。检索内容红色显示。
import com.github.pagehelper.PageInfo;
import com.zhiliao.common.utils.DateUtil;
import com.zhiliao.common.utils.PathUtil;
import com.zhiliao.component.lucene.LuceneManager;
import com.zhiliao.component.lucene.util.IndexObject;
import java.io.File;
import java.util.List;
public class LuceneTest {
static LuceneManager luceneManager=new LuceneManager();
static {
String indexDer=PathUtil.getRootClassPath()+ File.separator+"lucene";
System.out.println(indexDer);
luceneManager.setIndexDer(indexDer); //设置全文检索数据库地址
}
public static void main(String[] args) {
deleteAll(); //删除所有索引
create(); //创建索引
update(); //更新索引
serach(); //检索索引
}
public static void create() {
IndexObject object2 = new IndexObject();
object2.setTitle("2说白了只是为了自己方便使用");
object2.setId("1");
object2.setKeywords("第二个关键字");
object2.setUrl("www.baidu.com");
object2.setPostDate(DateUtil.now());
object2.setDescription(
"说白了只是为了自己方便使用,并没显示,美观整洁;"
+ "多种格式文件导出支持,可将当前 Markdown 文件另存为 LibreOffice ODT Document、Latex、PDF、reStructured Text、Media Wiki markup、epub 以及 plain txt 等格式文件输出;");
luceneManager.create(object2);
IndexObject object3 = new IndexObject();
object3.setTitle("3说白了只是为了自己方便使用");
object3.setId("31");
object3.setKeywords("第三个关键字");
object3.setUrl("www.baidu.com");
object3.setPostDate(DateUtil.now());
object3.setDescription(
"说白了只是为了自己方便使用,并没什么新奇台支持;完美支持 LaTex 数学公式、脚注、尾注等,支持使用本地 MathJax 调用,不需要在线访问 MathJax CDN;"
+ "用户可配置的 Markdown 语法高亮显示,美观整洁;"
+ "多种格式文件导出支持,可将当前 Markdown/ LibreOffice ODT Document、Latex、PDF、reStructured Text、Media Wiki markup、epub 以及 plain txt 等格式文件输出;");
luceneManager.create(object3);
IndexObject object4 = new IndexObject();
object4.setTitle("4说白了只是为了自己方便使用");
object4.setId("41");
object4.setKeywords("第四个关键字");
object4.setUrl("www.baidu.com");
object4.setPostDate(DateUtil.now());
object4.setDescription(
"说白了只是为了自己方便使用,并没什么新奇的东西。我使用 pandoc 来转化 markdown,但是我不想在修改文件时总是在编辑器、文字终端和浏览器间换来换去,因此我写了一个简单的编辑器,它在后台调用 pandoc 将当前编辑的 markdown 内容转化为 HTML,而后将 HTML 在 smark 中的浏览器中显示出来,就是这么回事。Smark 依赖于 pandoc、Qt 4.8 和 MathJax,在此向上述软件包开发者们致敬。请注意继承于 pandoc 的发布协议,Smark 同样遵循 GPL,如有任何疑问请联系 [email protected],我将尽快做出回复。"
+ "主要特性:Wi持 LaTex 数学公式、脚注、尾注等,支持使用本地 MathJax 调用,不需要在线访问 MathJax CDN;"
+ "用户可配置的 Markdown 语法高亮显示,美观整洁;"
+ "多种格式文件导出支持,可将当前 Markdown 文件另存为 HTML、 Miscrosoft Word、OpenOffice / LibreOffice ODT Document、Latex、PDF、reStructured Text、Media Wiki markup、epub 以及 plain txt 等格式文件输出;");
luceneManager.create(object4);
}
public static void delete() {
IndexObject object4 = new IndexObject();
object4.setId("41");
luceneManager.delete(object4);
}
public static void deleteAll() {
luceneManager.deleteAll();
}
public static void update() {
IndexObject object4 = new IndexObject();
object4.setTitle("4说白了只是为了自己方便使用");
object4.setId("31");
object4.setKeywords("第四个关键字");
object4.setUrl("www");
object4.setPostDate(DateUtil.now());
object4.setDescription(
"说白了只是为了自己方便使用,并没什么新奇的东西。我使用 pandoc 来转化 markdown,但是我不想在修改文件时总是在编辑器、文字终端和浏览器间换来换去,因此我写了一个简单的编辑器,它在后台调用 pandoc 将当前编辑的 markdown 内容转化为 HTML,而后将 HTML 在 smark 中的浏览器中显示出来,就是这么回事。Smark 依赖于 pandoc、Qt 4.8 和 MathJax,在此向上述软件包开发者们致敬。请注意继承于 pandoc 的发布协议,Smark 同样遵循 GPL,如有任何疑问请联系 [email protected],我将尽快做出回复。"
+ "主要特性:Wi持 LaTex 数学公式、脚注、尾注等,支持使用本地 MathJax 调用,不需要在线访问 MathJax CDN;"
+ "用户可配置的 Markdown 语法高亮显示,美观整洁;"
+ "多种格式文件导出支持,可将当前 Markdown 文件另存为 HTML、 Miscrosoft Word、OpenOffice / LibreOffice ODT Document、Latex、PDF、reStructured Text、Media Wiki markup、epub 以及 plain txt 等格式文件输出;");
luceneManager.update(object4);
}
public static void serach(){
// "C:/Users/Administrator/Desktop/Java-Workspace/LuceneDemo/data/index"
System.out.println(PathUtil.getRootClassPath()+ File.separator+"lucene");
PageInfo p = luceneManager.page(1,10,"白了只是为了自己方便使用");
List list = p.getList();
for(IndexObject obj:list){
System.out.println(obj.getId());
System.out.println(obj.getTitle());
System.out.println(obj.getKeywords());
System.out.println(obj.getDescription());
System.out.println(obj.getUrl());
System.out.println(obj.getPostDate());
}
}
}