Lucene是Apache开源的全文检索框架, 是单纯的搜索工具, 简单易用. 现在已经出到5.2.1的版本, 只需在项目中导入必需的几个jar包就能使用. 使用的过程可以概括为,
1) 建立索引
2) 搜索查找, 获取搜索结果
这里我们一起先来学习几个会用到的核心类:
Directory
该类在Lucene中用于描述索引存放的位置信息. 比如:
Directory dir = FSDirectory.open(Paths.get("c:\\lucene\\index"));
其中" c:\\lucene\\index" 是存放索引的文件夹位置.
Analyzer
Analyzer是Lucene的分词器, 可以说是分词解析技术也可以说是搜索引擎的核心技术之一. 一句话被断句分词分析, .使搜索结果更智能更精准. 中文词库分词, 可以使用IKAnalyzer等中文分词工具包.
Analyzer这个类的作用要结合IndexWriterConfig和IndexWriter这两个类去认识:
IndexWriterConfig, 从类名可知, 是一个保存参数配置的类, 用于生成IndexWriter. 比如:
IndexWriterConfig iwc = new IndexWriterConfig(luceneAnalyzer);
iwc.setOpenMode(OpenMode.CREATE);
IndexWriter indexWriter = new IndexWriter(dir,iwc);
当然还有更多的参数设置, 可以参考这篇文章哦. IndexWriterConfig配置参数说明
上面的三行代码也是创建一个IndexWriter的过程. (dir这个参数就是第一个提及的类Directory.)
IndexWriter 是建立索引的核心类. 如果你也知道Android的SharePreference, SharePreference里面有一个Editor类. IndexWriter 就是类似Editor这样的类, 可以针对索引进行添加(创建新的索引并写入索引文档中), 删除(从索引文档删除索引)和更新(更新索引文档中的索引) 操作. 顺便提一下, Lucene的索引, 会生成对应的索引文档, 所以最好建立文件夹专门存放这些文档.
Document
Document类顾名思义是"文件"类, 其实它是用来存放Field集合. 可以理解为存放文件, 一般都是可转换的文本信息, 比如doc, txt等等. 当把用于被查找的文件信息加入Document后, 再通过
indexWriter.addDocument(document);
这样就添加了一个索引. 来到这里, 也许你会想到, 以后要查询直接先来索引这里就行了.
IndexReader
对应于IndexWriter, 就有IndexReader. 创建方法 :
IndexReader reader = DirectoryReader.open(FSDirectory.open(Paths.get(index)));
它只读取索引文档. 然后交给检索工具IndexSearcher 去完成查找. 根据传进Query检索条件进行检索查找后, 会得到一个ScoreDoc类型的结果集, 然后读取它的Document信息, 就能获取检索结果的具体信息, 比如关键字包含在哪些内容中, 已经这些内容文档的存放路径等等. 这样就算是完成整个检索过程了.
OK, 下面我们来简单的例子体验下, 有兴趣可以自己去看文档详细了解哦.
注: 本例子的代码来自网络, 是很简单易懂的例子, 所以就不再另外写了, 这里只是体验学习, 我们直接学习别人的. 额, 原作者不知道是谁了, 在这里感谢一下.
经过上面的关键类的介绍, 相信看下面例子的代码会容易懂很多了, So 直接上代码.
1) 建立索引文档.
随便在本地建立一个文件夹, 比如C盘根目录创建index文件夹. 路径就是 C:\index . 而要检索的内容文档放在 C盘根目录的source文件夹中, 路径就是C:\source .
public class CreateIndex {
public static void main(String[] args) throws Exception {
/* 指明要索引文件夹的位置,这里是C盘的source文件夹下 */
File fileDir = new File("C:\\source");
/* 这里放索引文件的位置 */
//File indexDir = new File("c:\\index");
String indexPath = "c:\\index";
// Directory dir = FSDirectory.open(indexDir); //v3.6.0
Directory dir = FSDirectory.open(Paths.get(indexPath));
//Analyzer luceneAnalyzer = new StandardAnalyzer(Version.LUCENE_3_6_0);
Analyzer luceneAnalyzer = new StandardAnalyzer();
IndexWriterConfig iwc = new IndexWriterConfig(luceneAnalyzer);
iwc.setOpenMode(OpenMode.CREATE);
IndexWriter indexWriter = new IndexWriter(dir,iwc);
File[] textFiles = fileDir.listFiles();
long startTime = new Date().getTime();
//增加document到索引去
for (int i = 0; i < textFiles.length; i++) {
if (textFiles[i].isFile()
) {
System.out.println("File " + textFiles[i].getCanonicalPath()
+ "正在被索引....");
String temp = FileReaderAll(textFiles[i].getCanonicalPath(),
"GBK");
System.out.println(temp);
Document document = new Document();
Field FieldPath = new StringField("path", textFiles[i].getPath(), Field.Store.YES);
Field FieldBody = new TextField("body", temp, Field.Store.YES);
document.add(FieldPath);
document.add(FieldBody);
indexWriter.addDocument(document);
}
}
indexWriter.close();
//测试一下索引的时间
long endTime = new Date().getTime();
System.out
.println("这占用了"
+ (endTime - startTime)
+ " 毫秒来把文档增加到索引里面去!"
+ fileDir.getPath());
}
public static String FileReaderAll(String FileName, String charset)
throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(
new FileInputStream(FileName), charset));
String line = new String();
String temp = new String();
while ((line = reader.readLine()) != null) {
temp += line;
}
reader.close();
return temp;
}
}
public class ExecuteQuery {
public static void main(String[] args) throws IOException, ParseException {
String index="c:\\index";//搜索的索引路径
// IndexReader reader=IndexReader.open(FSDirectory.open(Paths.get(index)); //v3.6.0的写法
IndexReader reader = DirectoryReader.open(FSDirectory.open(Paths.get(index)));
IndexSearcher searcher=new IndexSearcher(reader);//检索工具
ScoreDoc[] hits=null;
String queryString="好"; //搜索的索引名称
Query query=null;
Analyzer analyzer= new StandardAnalyzer();
try {
//QueryParser qp=new QueryParser(Version.LUCENE_3_6_0,"body",analyzer);//用于解析用户输入的工具 v3.6.0
QueryParser qp=new QueryParser("body",analyzer);//用于解析用户输入的工具
query=qp.parse(queryString);
} catch (ParseException e) {
// TODO: handle exception
}
if (searcher!=null) {
TopDocs results=searcher.search(query, 10);//只取排名前十的搜索结果
hits=results.scoreDocs;
Document document=null;
for (int i = 0; i < hits.length; i++) {
document=searcher.doc(hits[i].doc);
String body=document.get("body");
String path=document.get("path");
String modifiedtime=document.get("modifiField");
System.out.println("BODY---"+body+" ");
System.out.println("PATH--"+path);
}
if (hits.length>0) {
System.out.println("输入关键字为:"+queryString+","+"找到"+hits.length+"条结果!");
}
// searcher.close();
reader.close();
}
}
}
然后修改一下内容文档:
搜索"努力"
中文搜索ok~
当然这是很简单的例子. 本文只是体验学习, 让我们在以后实现全文检索时多一个学习研究的方向. 另外, 好的检索工具, 分词器是非常关键的. 同时英文和中文的词库又不一样. 所以在实现真正的检索时就要考虑这些影响搜索结果的因素.
例子源码
参考文章
Lucene学习总结之一:全文检索的基本原理
Lucene in 5 minutes