项目中都会有搜索的功能,有些搜索非常简单,就是按照姓名查询或者按着性别查询。这样的查询我们会用到模糊查询,也就是like。如果是两个也就是用like 和or关键字。
Like关键字是非常影响效率的,这点我们可以从一个生活中的例子来看就能够知道。我们使用like关键字就好像是我们在查一本没有目录的字典,我们要在从字典的第一页开始,一个一个的找,知道找出所有符合条件的结果。所以说like关键字是非常影响效率的。
那么我们在生活中比较正规的方法查字典的方法是,我们会通过目录(这个目录可能是拼音或者偏旁)查询,进而一部定位结果。
所以,lucene给我们提供了这样一种比较符合于我们生活中的方式来查阅信息的一种实现。也就是说lucene的出现就是方便我们快速检索信息。他主要完成的工作就是首先对我们的整个内容建立一个索引(这个索引就相当于我们生活中的那个字典的目录),之后我们通过目录可以直接定位并且查出我们所需要的结果。这就是lucene出现的意义所在。
实际上,我们所看到的谷歌、百度、还有一个搜索的工具如谷歌桌面搜索、everything所实现的原理都是这样的,我们在用这些工具的时候他们也是通过这种思路来实现搜索功能的。
Lucene架构:
他的架构图还是比较简单的,我们从图中就能看出,lucene的架构就是两方面内容:
收集数据--->建立索引--->存储索引
用户查询--->搜索索引---->从数据库中查询---->呈现搜索结果---->用户
Lucene中有这样一些核心类,通过他们我们可以完成lucene架构图中展示的建立索引和搜索功能:
另外在我们建立索引的时候会涉及到一个是否要建立索引的问题和一个是否要存储的问题,一下的红色字体部分为这两个知识点的解释:
Index.ANALYZED:进行分词和索引,适用于标题、内容等
Index.NOT_ANALYZED:进行索引,但是不进行分词,如果身份证号,姓名,ID等,适用于精确搜索
Index.ANALYZED_NOT_NORMS:进行分词但是不存储norms信息,这个norms中包括了创建索引的时间和权值等信息
Index.NOT_ANALYZED_NOT_NORMS:即不进行分词也不存储norms信息
Index.NO:不进行索引
YES:将会存储域值,原始字符串的值会保存在索引,以此可以进行相应的恢复操作,对于主键,标题可以是这种方式存储
NO:不会存储域值,通常与Index.ANAYLIZED合起来使用,索引一些如文章正文等不需要恢复的文档
NOT_ANALYZED_NOT_NORMS | YES | 标识符(主键、文件名),电话号码,身份证号,姓名,日期 |
ANAYLZED | YES | 文档标题和摘要 |
ANAYLZED | NO | 文档正文 |
NO | YES | 文档类型,数据库主键(不进行索引) |
NOT_ANALYZED | NO | 隐藏关键字 |
另外,强大的lucene全文搜索工具中还有一个非常重要的概念就是分词,这个概念可以说是搜索中非常关键的部分。分词器做好处理之后得到的一个流,这个流中存储了分词的各种信息,可以通过TokenStream有效的获取到分词单元信息。这个过程如图所示:
Lucene中带有的分词器有SimpleAnalyzer、StopAnalyzer、WhitespaceAnalyzer、StandardAnalyzer(这个就部分别给大家做事例说明他们的区别了,大家可以自己研究一下)。另外lucene支持我们自定义分词器。而且lucene中自带分词器一般是支持英文的,那么我们中文分词器比较常见的有两种,一个是庖丁解牛分词器【没有更新了】,一个是mmseg4j【使用的是搜狗词库】分词器。
和lucene相关的工具还有tika,solr,luke(下面介绍):
Tika是apache的一个项目,主要是用于打开各种不同的文档,以便lucene做索引;solr是一个全文搜索的服务器,可以与tomcat集成。还有就是luke,这个工具主要是查看lucene建立的索引的。
最后做一个简单的建立索引和简单查询的实例:
用一个简单的实例来建立索引:
1、创建Directory
2、创建IndexWriter
3、循环遍历文件,创建document
4、添加document(文档相当于表中的每一条记录,域相当于表中每一个字段)
5、 关闭IndexWriter
publicvoid index(){
IndexWriter writer=null;
try {
//Directory dir = new RAMDirectory();
Directory dir=FSDirectory.open(new File("d:/java/lucence/index01"));
IndexWriterConfig iwc=new IndexWriterConfig(Version.LUCENE_36, new StandardAnalyzer(Version.LUCENE_36));
writer=new IndexWriter(dir, iwc);
Document doc=null;
File file=new File("d:/java/lucence/example");
for(File f:file.listFiles()){
doc=new Document();
doc.add(new Field("content", new FileReader(f)));
doc.add(new Field("filename", f.getName(),Field.Store.YES,Field.Index.NOT_ANALYZED));
doc.add(new Field("path",f.getAbsolutePath(),Field.Store.YES,Field.Index.NOT_ANALYZED));
writer.addDocument(doc);
}
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (LockObtainFailedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(writer!=null){
try {
writer.close();
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
建立索引为:
这些索引如何来看呢?
用lukeall-3.5.0.jar 这个工具(注意要和lucene版本一致)
搜索的简单实现:
1、 创建IndexReader
2、 创建IndexSearcher
3、 创建Term和TermQuery
4、 根据TermQuery获取TopDocs
5、 根据TopDocs获取ScoreDoc
6、 根据ScoreDoc获取相应文档
publicvoid searcher(){
try {
Directory dir=FSDirectory.open(new File("d:/java/lucence/index01"));
IndexReader reader=IndexReader.open(dir);
IndexSearcher searcher=new IndexSearcher(reader);
QueryParser parser=new QueryParser(Version.LUCENE_36, "content", new StandardAnalyzer(Version.LUCENE_36));
Query query=parser.parse("java");
TopDocs tds=searcher.search(query, 10);
ScoreDoc[] sds=tds.scoreDocs;
for(ScoreDoc sd:sds){
Document d=searcher.doc(sd.doc);
System.out.println(d.get("filename")+"~~~"+d.get("path"));
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
}
}
备注:lucene工具支持很多查询:通配符、数字、日期、范围、模糊等:
最后要说的是,这篇博客仅仅简单介绍了一下lucene这个工具,关于深层次的使用问题,我想还是要靠大家自己完成。当我们知道lucene这个工具的时候,面对我们的问题也就有很多了,像我们如何完成实时搜索,如何完成像百度推广似的排名靠前,如何完成高亮显示,如何更好的保证搜索质量和效率等,我想这个问题都是比较不太容易完美实现的问题,所以这些问题还有待大家共同来思考和解决·····