3.6 Lucene基本检索+关键词高亮+分页

3.2节我们已经运行了一个Lucene实现检索的小程序,这一节我们将以这个小程序为例,讲一下Lucene检索的基本步骤,同时介绍关键词高亮显示和分页返回结果这两个有用的技巧。

一、Lucene检索的基本步骤

 1 import java.nio.file.Paths;
 2 import java.io.*;
 3 
 4 import org.apache.lucene.analysis.standard.StandardAnalyzer;
 5 import org.apache.lucene.document.Document;
 6 import org.apache.lucene.index.DirectoryReader;
 7 import org.apache.lucene.queryparser.classic.QueryParser;
 8 import org.apache.lucene.search.IndexSearcher;
 9 import org.apache.lucene.search.Query;
10 import org.apache.lucene.search.ScoreDoc;
11 import org.apache.lucene.search.TopDocs;
12 import org.apache.lucene.store.Directory;
13 import org.apache.lucene.store.FSDirectory;
14 import org.apache.lucene.util.Version;
15 
16 /**
17  * @author csl
18  * @description: 
19  * 依赖jar:Lucene-core,lucene-analyzers-common,lucene-queryparser
20  * 作用:使用索引搜索文件
21  */
22 public class Searcher {
23     public static Version luceneVersion = Version.LATEST;
24     /**
25      * 查询内容
26      */
27     public static String indexSearch(String keywords){
28         String res = "";
29         DirectoryReader reader = null;
30         try{
31 //            1、创建Directory
32              Directory directory = FSDirectory.open(Paths.get("index"));//在硬盘上生成Directory
33 //            2、创建IndexReader
34              reader = DirectoryReader.open(directory);
35 //            3、根据IndexReader创建IndexSearcher
36              IndexSearcher searcher =  new IndexSearcher(reader);
37 //            4、创建搜索的query
38 //            创建parse用来确定搜索的内容,第二个参数表示搜索的域
39              QueryParser parser = new QueryParser("content",new StandardAnalyzer());//content表示搜索的域或者说字段
40              Query query = parser.parse(keywords);//被搜索的内容
41 //            5、根据Searcher返回TopDocs
42              TopDocs tds = searcher.search(query, 20);//查询20条记录
43 //            6、根据TopDocs获取ScoreDoc
44              ScoreDoc[] sds = tds.scoreDocs;
45 //            7、根据Searcher和ScoreDoc获取搜索到的document对象
46              int cou=0;
47              for(ScoreDoc sd:sds){
48                  cou++;
49                  Document d = searcher.doc(sd.doc);
50 //                    8、根据document对象获取查询的字段值
51                  /**  查询结果中content为空,是因为索引中没有存储content的内容,需要根据索引path和name从原文件中获取content**/
52                  res+=cou+". "+d.get("path")+" "+d.get("name")+" "+d.get("content")+"\n";
53              }
54 
55             
56         }catch(Exception e){
57             e.printStackTrace();
58         }finally{
59             //9、关闭reader
60             try {
61                 reader.close();
62             } catch (IOException e) {
63                 e.printStackTrace();
64             }
65         }
66         return res;
67     }
68     public static void main(String[] args) throws IOException
69     {
70         System.out.println(indexSearch("你好")); //搜索的内容可以修改
71     }
72 }
Searcher

搜索的过程总的来说就是将词典及倒排表信息从索引中读出来,根据用户输入的查询语句合并倒排表,得到结果文档集并对文档进行打分的过程。

总结起来检索有以下以下五个步骤:

1. 打开IndexReader指向索引文件夹。

1 Directory directory = FSDirectory.open(Paths.get("index"));
2 IndexReader reader = DirectoryReader.open(directory);
IndexReader

这一步骤将磁盘上的索引信息读入内存。

2. 创建IndexSearcher准备进行搜索。

1 IndexSearcher searcher =  new IndexSearcher(reader);
IndexSearcher

IndexSearcher提供了两个非常重要的函数:

  • void setSimilarity(Similarity similarity),用户可以实现自己的Similarity对象,从而影响搜索过程的打分。
  • 一系列search函数,是搜索过程的关键,主要负责打分的计算和倒排表的合并。

3. 创建QueryParser解析查询语句生成查询对象。

1 QueryParser parser = new QueryParser("content",new StandardAnalyzer());//content表示搜索的域或者说字段
2 Query query = parser.parse(keywords);//被搜索的内容
QueryParser

解析分为两个过程:

  • 创建Analyer用来对查询语句进行词法分析和语言处理。
  • QueryParser调用parser进行语法分析,形成查询语法树,放到Query中。

4. IndexSearcher调用search对查询语法树Query进行搜索,得到结果集Topdocs。

1 //            5、根据Searcher返回TopDocs
2              TopDocs tds = searcher.search(query, 20);//查询20条记录
3 //            6、根据TopDocs获取ScoreDoc
4              ScoreDoc[] sds = tds.scoreDocs;
Search

该方法收集文档集合并计算打分。

5. 返回查询结果给用户。

1 int cou=0;
2              for(ScoreDoc sd:sds){
3                  cou++;
4                  Document d = searcher.doc(sd.doc);
5 //                    8、根据document对象获取查询的字段值
6                  /**  查询结果中content为空,是因为索引中没有存储content的内容,需要根据索引path和name从原文件中获取content**/
7                  res+=cou+". "+d.get("path")+" "+d.get("name")+" "+d.get("content")+"\n";
8              }
Document

在返回查询结果给用户时,为了提高用户体验,我们可以给关键词标注高亮和分页返回结果。

5.1 给关键词标注高亮。

 1     public  static String displayHtmlHighlight(Query query, String fieldName, String fieldContent) throws IOException, InvalidTokenOffsetsException
 2     {
 3          MyIkAnalyzer analyzer=new MyIkAnalyzer();
 4          //设置高亮标签,可以自定义
 5          SimpleHTMLFormatter formatter = new SimpleHTMLFormatter("<font color='#ff0000'>", "</font>");  
 6          /**创建QueryScorer*/
 7          //评分
 8          QueryScorer scorer=new QueryScorer(query);  
 9          /**创建Fragmenter*/  
10          Fragmenter fragmenter = new SimpleSpanFragmenter(scorer);  
11          //高亮分析器
12          Highlighter highlight=new Highlighter(formatter,scorer);  
13          highlight.setTextFragmenter(fragmenter);  
14          //fieldname是域名,如"title",fieldContent是d.get("title");
15          String str=highlight.getBestFragment(analyzer, fieldName, fieldContent);
16          if (str==null) return fieldContent;
17          return str;
18      }
displayHtmlHighlight

该函数有三个参数:

  • Query query是第4步产生的查询对象。
  • String fieldName是要标注内容的域名,比如“title”
  • String fieldContent是要标注的具体内容,比如某一个“title”的具体内容。

该函数实现了两个基本功能:

  • 如果要标注内容fieldContent为空,返回空串。
  • 不为空时,对fieldContent进行自定义的html标签标注。
1 SimpleHTMLFormatter formatter = new SimpleHTMLFormatter("<font color='#ff0000'>", "</font>");  
formatter

这里可以进行个性化定制。关于HighLighter的具体用法大家可以参考我的另一篇博客【lucene系列学习二】Lucene实现高亮显示关键词

关于关键词高亮的具体原理可以参考搜索结果的处理和显示《第六篇》

5.2 分页展示结果。

这里介绍一种简单的分页方法:

1 int start=(pageIndex-1)*pageSize;
2 int end=pageIndex*pageSize;
3  Document d=null;
4  int cnt=0;
5  for(int i=start;i<end&&i<sds.length;i++)
6 {
7         d = searcher.doc(sds[i].doc);
8     //输出d
9 }     
分页

其中pageIndex和pageSize可以是前端传的参数。

以上五个步骤就可以基本实现Lucene的检索、关键词高亮和分页返回结果了,是不是很简单呢?

下节我们会介绍Lucene的高级检索方式~~

posted @ 2017-05-09 22:24  shulin15  阅读(956)  评论(0编辑  收藏  举报