全文检索Lucene(三)—-查询,分词器,排序,过滤,高亮详解编程语言

Lucene查询:
查询出所有
关键词查询
范围查询
通配符查询
模糊查询
短语查询
布尔查询

QueryParser与MultiFieldQueryParser的区别
QueryParser:只在一个字段中查询
MultiFieldQueryParser:可以在多个字段查询

布尔查询:
public void add(Query query, Occur occur)

Occur 用于表示布尔查询子句关系的类,包括:
Occur.MUST,Occur.MUST_NOT,Occur.SHOULD。

1, MUST和MUST:取得连个查询子句的交集。
2, MUST和MUST_NOT:包含MUST并且查询结果中不包含MUST_NOT的检索结果。
3, SHOULD与SHOULD,表示“或”关系,最终检索结果为所有检索子句的并集。

一般不单独使用,因为单独就不应使用BooleanQuery了。

使用时注意:
1, 单独使用MUST_NOT:无意义,检索无结果。(也不报错)
2, MUST_NOT和MUST_NOT:无意义,检索无结果。(也不报错)

3, 单独使用SHOULD:结果相当于MUST。
4, SHOULD和MUST_NOT: 此时SHOULD相当于MUST,结果同MUST和MUST_NOT。
5, MUST和SHOULD:此时SHOULD无意义,结果为MUST子句的检索结果。

代码示例(utils和bean类可以去上一篇文章中去找):

package com.my.lucene; 
 
import java.util.ArrayList; 
import java.util.List; 
 
import org.apache.lucene.document.Document; 
import org.apache.lucene.index.Term; 
import org.apache.lucene.queryParser.MultiFieldQueryParser; 
import org.apache.lucene.queryParser.QueryParser; 
import org.apache.lucene.search.BooleanClause.Occur; 
import org.apache.lucene.search.BooleanQuery; 
import org.apache.lucene.search.FuzzyQuery; 
import org.apache.lucene.search.IndexSearcher; 
import org.apache.lucene.search.MatchAllDocsQuery; 
import org.apache.lucene.search.NumericRangeQuery; 
import org.apache.lucene.search.PhraseQuery; 
import org.apache.lucene.search.Query; 
import org.apache.lucene.search.ScoreDoc; 
import org.apache.lucene.search.TermQuery; 
import org.apache.lucene.search.TopDocs; 
import org.apache.lucene.search.WildcardQuery; 
import org.apache.lucene.util.Version; 
import org.junit.Test; 
 
import com.my.bean.Article; 
import com.my.utils.ArticleDocumentUtils; 
import com.my.utils.Configuration; 
 
public class QueryTest { 
 
    @Test 
    public void searchByString() throws Exception { 
        // 搜索条件 
        String queryString = "content:lucene"; 
        // String queryString = "全文 OR 用户"; 
        // String queryString = "全文  AND 用户"; 
 
        //把查询字符串转为Query对象 
        QueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_30,new String[] { "title", "content" },Configuration.getAnalyzer()); // 在title与content中查询 
        Query query = queryParser.parse(queryString); 
 
        //查询,得到中间结果 
        IndexSearcher indexSearcher = new IndexSearcher(Configuration.getDirectory()); 
        TopDocs topDocs = indexSearcher.search(query, 100); // 按指定条件条询,只返回前n条结束 
        ScoreDoc[] scoreDocs = topDocs.scoreDocs; // 前n条结果的信息 
 
        //处理结果 
        List<Article> list = new ArrayList<Article>(); 
        for (int i = 0; i < scoreDocs.length; i++) { 
            Document doc = indexSearcher.doc(scoreDocs[i].doc); 
            list.add(ArticleDocumentUtils.document2Article(doc)); 
        } 
        indexSearcher.close(); 
 
        //显示结果 
        System.out.println("总结果数量为:" + list.size()); 
        for (Article article : list) { 
            System.out.println("--------> id = " + article.getId()); 
            System.out.println("title  = " + article.getTitle()); 
            System.out.println("content= " + article.getContent()); 
        } 
    } 
 
    @Test 
    public void searchByQuery() throws Exception { 
 
        Query query = new TermQuery(new Term("title", "lucene")); 
        // Query query = new TermQuery(new Term("content", "lucene")); 
        System.out.println("对应的查询字符串为:" + query.toString()); 
 
        //查询,得到中间结果 
        IndexSearcher indexSearcher = new IndexSearcher(Configuration.getDirectory()); 
        TopDocs topDocs = indexSearcher.search(query, 100); // 按指定条件条询,只返回前n条结束 
        ScoreDoc[] scoreDocs = topDocs.scoreDocs; // 前n条结果的信息 
 
        //处理结果 
        List<Article> list = new ArrayList<Article>(); 
        for (int i = 0; i < scoreDocs.length; i++) { 
            Document doc = indexSearcher.doc(scoreDocs[i].doc); 
            list.add(ArticleDocumentUtils.document2Article(doc)); 
        } 
        indexSearcher.close(); 
 
        // 显示结果 
        System.out.println("总结果数量为:" + list.size()); 
        for (Article article : list) { 
            System.out.println("--------> id = " + article.getId()); 
            System.out.println("title  = " + article.getTitle()); 
            System.out.println("content= " + article.getContent()); 
        } 
    } 
 
    // 查询出所有文档 
    @Test 
    public void testMatchAllDocsQuery() { 
        // 对应的查询字符串为:*:* 
        Query query = new MatchAllDocsQuery(); 
        searchByQuery(query); 
    } 
 
    // 关键词查询 
    @Test 
    public void testTermQuery() { 
        // 对应的查询字符串为:title:lucene 
        Query query = new TermQuery(new Term("title", "lucene")); 
        searchByQuery(query); 
    } 
 
    // 范围查询 
    @Test 
    public void testRangeQuery() { 
        // 对应的查询字符串为:id:[5 TO 15] 
        // Query query = NumericRangeQuery.newIntRange("id", 5, 15, true, true); 
        // 对应的查询字符串为:id:{5 TO 15} 
        // Query query = NumericRangeQuery.newIntRange("id", 5, 15, false, 
        // false); 
        // 对应的查询字符串为:id:[5 TO 15} 
        Query query = NumericRangeQuery.newIntRange("id", 5, 15, true, false); 
        searchByQuery(query); 
    } 
 
    // 通配符查询 
    // ? 代表1个字符 
    // * 代表0个或多个字符 
    @Test 
    public void testWildcardQuery() { 
        // 对应的查询字符串为:title:lucen? 
        // Query query = new WildcardQuery(new Term("title", "lucen?")); 
        // 对应的查询字符串为:title:luce* 
        Query query = new WildcardQuery(new Term("title", "luce*")); 
 
        searchByQuery(query); 
    } 
 
    // 模糊查询 
    @Test 
    public void testFuzzyQuery() { 
        // 对应的查询字符串为:title:lucena~0.5 
        // Query query = new FuzzyQuery(new Term("title", "lucena")); 
 
        // 第2个参数minimumSimilarity(相似度)的取值范围是 0<= minimumSimilarity < 1 
        // minimumSimilarity(相似度)是指超过多少字母相同就可以查出来。 
        // 如指定0.7,表示相同的字符超过70%就可以查出来(如果刚好有70%相同则查不出来) 
        Query query = new FuzzyQuery(new Term("title", "luceneutii"), 0.7F); 
        searchByQuery(query); 
    } 
 
    // 短语查询 
    @Test 
    public void testPhraseQuery() { 
        // 对应的查询字符串为:title:"lucene ? ? 框架" 
        PhraseQuery phraseQuery = new PhraseQuery(); 
        // phraseQuery.add(new Term("title", "lucene"), 0); // 指定词的位置,第1个从0开始 
        // phraseQuery.add(new Term("title", "框架"), 3); 
 
        // 对应的查询字符串为:title:"lucene 框架"~5 
        phraseQuery.add(new Term("title", "lucene")); 
        phraseQuery.add(new Term("title", "框架")); 
        phraseQuery.setSlop(5); // 词之间的间隔最多不超过5个 
 
        searchByQuery(phraseQuery); 
    } 
 
    // 布尔查询 
    @Test 
    public void testBooleanQuery() { 
        BooleanQuery booleanQuery = new BooleanQuery(); 
        // booleanQuery.add(query, Occur.MUST); // 必须满足 
        // booleanQuery.add(query, Occur.MUST_NOT); // 非 
        // booleanQuery.add(query, Occur.SHOULD); // 多个SHOULD一起用是OR的关系 
 
        Query query1 = new TermQuery(new Term("title", "lucene")); 
        Query query2 = NumericRangeQuery.newIntRange("id", 5, 15, false, true); 
 
        // // 对应的查询字符串为:+title:lucene +id:{5 TO 15] 
        // // 对应的查询字符串为:title:lucene AND id:{5 TO 15] 
        // booleanQuery.add(query1, Occur.MUST); 
        // booleanQuery.add(query2, Occur.MUST); 
 
        // // 对应的查询字符串为:+title:lucene -id:{5 TO 15] 
        // // 对应的查询字符串为:title:lucene NOT id:{5 TO 15] 
        // booleanQuery.add(query1, Occur.MUST); 
        // booleanQuery.add(query2, Occur.MUST_NOT); 
 
        // 对应的查询字符串为:title:lucene id:{5 TO 15] 
        // 对应的查询字符串为:title:lucene OR id:{5 TO 15] 
        booleanQuery.add(query1, Occur.SHOULD); 
        booleanQuery.add(query2, Occur.SHOULD); 
 
        // 以下三种组合不要使用 
        // MUST 与 SHOULD,只有MUST的结果(这时的SHOULD相当于没有指定) 
        // MUST_NOT 与 MUST_NOT,没有结果,也不会报错 
        // MUST_NOT 与 SHOULD,这时SHOULD相当于MUST,他的条件必须满足,即相当于MUST与MUST_NOT的效果 
 
        // 可以使用括号 
        // 对应的查询字符串为:+(title:lucene OR content:lucene) +id:{5 TO 15] 
 
        searchByQuery(booleanQuery); 
    } 
 
    //查询并显示结果 
    private void searchByQuery(Query query) { 
        try { 
            System.out.println(" //对应的查询字符串为:" + query.toString()); 
 
            //查询,得到中间结果 
            IndexSearcher indexSearcher = new IndexSearcher(Configuration.getDirectory()); 
            TopDocs topDocs = indexSearcher.search(query, 100); // 按指定条件条询,只返回前n条结束 
            ScoreDoc[] scoreDocs = topDocs.scoreDocs; // 前n条结果的信息 
 
            //处理结果 
            List<Article> list = new ArrayList<Article>(); 
            for (int i = 0; i < scoreDocs.length; i++) { 
                Document doc = indexSearcher.doc(scoreDocs[i].doc); 
                list.add(ArticleDocumentUtils.document2Article(doc)); 
            } 
            indexSearcher.close(); 
 
            //显示结果 
            System.out.println("总结果数量为:" + list.size()); 
            for (Article article : list) { 
                System.out.println("--------> id = " + article.getId()); 
                System.out.println("title  = " + article.getTitle()); 
                System.out.println("content= " + article.getContent()); 
            } 
        } catch (Exception e) { 
            throw new RuntimeException(e); 
        } 
    } 
 
} 

分词器

分词器的作用
在创建索引时会用到分词器,在使用字符串搜索时也会用到分词器,这两个地方要使用同一个分词器,否则可能会搜索不出结果。
Analyzer(分词器)的作用是把一段文本中的词按规则取出所包含的所有词。对应的是Analyzer类,这是一个抽象类,切分词的具体规则是由子类实现的,所以对于不同的语言(规则),要用不同的分词器

英文)分词器的一般工作流程:
1, 切分关键词
2, 去除停用词
3,对于英文单词,把所有字母转为小写(搜索时不区分大小写)

停用词:
有些词在文本中出现的频率非常高,但是对文本所携带的信息基本不产生影响,例如英文的“a、an、the、of”,或中文的“的、了、着”,以及各种标点符号等,这样的词称为停用词(stop word)。文本经过分词之后,停用词通常被过滤掉,不会被进行索引。在检索的时候,用户的查询中如果含有停用词,检索系统也会将其过滤掉(因为用户输入的查询字符串也要进行分词处理)。排除停用词可以加快建立索引的速度,减小索引库文件的大小。

自带标准的分词器其核心类为StandardAnalyzer
另外还有单字分词 ChineseAnalyzer
二分法分词 CJKAnalyzer
还有第三方的分词器如IKAnalyzer

中文分词器使用IKAnalyzer,主页:http://www.oschina.net/p/ikanalyzer
实现了以词典为基础的正反向全切分,以及正反向最大匹配切分两种方法。IKAnalyzer是第三方实现的分词器,继承自Lucene的Analyzer类,针对中文文本进行处理。具体的使用方式参见其文档。

代码示例:

package com.my.lucene; 
 
import java.io.StringReader; 
 
import org.apache.lucene.analysis.Analyzer; 
import org.apache.lucene.analysis.TokenStream; 
import org.apache.lucene.analysis.cjk.CJKAnalyzer; 
import org.apache.lucene.analysis.cn.ChineseAnalyzer; 
import org.apache.lucene.analysis.standard.StandardAnalyzer; 
import org.apache.lucene.analysis.tokenattributes.TermAttribute; 
import org.apache.lucene.util.Version; 
import org.junit.Test; 
import org.wltea.analyzer.lucene.IKAnalyzer; 
 
public class AnalyzerTest { 
 
    @Test 
    public void test() throws Exception{ 
        // 英文分词 
        String text = "An IndexWriter creates and maintains an index."; 
        Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30); 
        testAnalyzer(analyzer, text); 
 
        // 中文分词 
        String text2 = "传智播客的Lucene是全文检索的框架"; 
        testAnalyzer(new ChineseAnalyzer(), text2); // 单字分词 
        testAnalyzer(new CJKAnalyzer(Version.LUCENE_30), text2); // 二分法分词 
        testAnalyzer(new IKAnalyzer(), text2); // 词库分词 
        testAnalyzer(new IKAnalyzer(), text); // 词库分词 
    } 
 
    /** 
     * 使用指定的分词器对指定的文本进行分词,并打印出分出的词 
     *  
     * @param analyzer 
     * @param text 
     * @throws Exception 
     */ 
    private void testAnalyzer(Analyzer analyzer, String text) throws Exception { 
        System.out.println("当前使用的分词器:" + analyzer.getClass()); 
        TokenStream tokenStream = analyzer.tokenStream("content", new StringReader(text)); 
        tokenStream.addAttribute(TermAttribute.class); 
        while (tokenStream.incrementToken()) { 
            TermAttribute termAttribute = tokenStream.getAttribute(TermAttribute.class); 
            System.out.println(termAttribute.term()); 
        } 
        System.out.println(); 
    } 
 
} 
 

分词器配置文件
IKAnalyzer.cfg.xml

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">   
<properties>   
    <comment>IK Analyzer 扩展配置</comment> 
    <!--用户可以在这里配置自己的扩展字典-->  
    <entry key="ext_dict">/mydict.dic</entry>  
 
     <!--用户可以在这里配置自己的扩展停止词字典--> 
    <entry key="ext_stopwords">/ext_stopword.dic</entry>  
 
</properties>

mydict.dic

全文检索

ext_stopword.dic

就 
并 
很 
或 
把 
是 
的 
着 
给 
而 
被 
让 
在 
还 
比 
等 
当 
与 
于 
但

注意:查询必须使用与建立索引库相同的分词器。


排序

通过改变文档Boost值来改变排序结果。Boost是指索引建立过程中,给整篇文档或者文档的某一特定属性设定的权值因子,在检索时,优先返回分数高的。通过Document对象的setBoost()方法和Field对象的setBoost()方法,可以分别为Document和Field指定Boost参数。不同在于前者对文档中每一个域都修改了参数,而后者只针对指定域进行修改。默认情值为1F,一般不做修改。

使用Sort对象定制排序。Sort支持的排序功能以文档当中的域为单位,通过这种方法,可以实现一个或者多个不同域的多形式的值排序。时间类型的属性采用STRING常量。

按相关度排序
1,相关度得分是在查询时根据查询条件实进计算出来的
2,如果索引库据不变,查询条件不变,查出的文档得分也不变

按指定的字段排序
利用Field.Index.NOT_ANALYZED.

代码示例:

package com.my.lucene; 
 
import java.util.ArrayList; 
import java.util.List; 
 
import org.apache.lucene.document.Document; 
import org.apache.lucene.queryParser.MultiFieldQueryParser; 
import org.apache.lucene.queryParser.QueryParser; 
import org.apache.lucene.search.IndexSearcher; 
import org.apache.lucene.search.Query; 
import org.apache.lucene.search.ScoreDoc; 
import org.apache.lucene.search.Sort; 
import org.apache.lucene.search.SortField; 
import org.apache.lucene.search.TopDocs; 
import org.apache.lucene.util.Version; 
import org.junit.Test; 
 
import com.my.bean.Article; 
import com.my.utils.ArticleDocumentUtils; 
import com.my.utils.Configuration; 
import com.my.utils.LuceneUtils; 
 
public class SortTest { 
 
    @Test 
    public void createIndex() throws Exception { 
        // 模拟一条刚保存到数据库中的数据 
        Article article = new Article(); 
        article.setId(31); 
        article.setTitle("Lucene是全文检索的框架"); 
        article.setContent("如果信息检索系统在用户发出了检索请求后再去互联网上找答案,根本无法在有限的时间内返回用户。"); 
        // 建立索引 
        Document doc = ArticleDocumentUtils.article2Document(article); 
        doc.setBoost(0.5F); // 使用boost影响相关度得分,默认值为1F 
        LuceneUtils.getIndexWriter().addDocument(doc); 
    } 
 
    @Test 
    public void search1() throws Exception { 
        // 搜索条件 
        String queryString = "用户"; 
        // 1,把查询字符串转为Query对象 
        QueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_30, new String[] { "title", "content" }, Configuration.getAnalyzer()); // 在title与content中查询 
        Query query = queryParser.parse(queryString); 
 
        // 2,查询,得到中间结果 
        IndexSearcher indexSearcher = new IndexSearcher(Configuration.getDirectory()); 
        TopDocs topDocs = indexSearcher.search(query, 100); // 按指定条件条询,只返回前n条结束 
        ScoreDoc[] scoreDocs = topDocs.scoreDocs; // 前n条结果的信息 
 
        // 3,处理结果 
        List<Article> list = new ArrayList<Article>(); 
        for (int i = 0; i < scoreDocs.length; i++) { 
            System.out.println(scoreDocs[i].score); // 相关度得分 
 
            // 根据编号取出真正的Document数据 
            Document doc = indexSearcher.doc(scoreDocs[i].doc); 
            // 把Document转成Article 
            Article article = ArticleDocumentUtils.document2Article(doc); 
            list.add(article); 
        } 
 
        indexSearcher.close(); 
 
        // 显示结果 
        System.out.println("总结果数量为:" + list.size()); 
        for (Article article : list) { 
            System.out.println("--------> id = " + article.getId()); 
            System.out.println("title  = " + article.getTitle()); 
            System.out.println("content= " + article.getContent()); 
        } 
    } 
 
    @Test 
    public void search2() throws Exception { 
        // 搜索条件 
        String queryString = "用户"; 
 
        // 进行搜索,得到结果 ? 
        // 1,把查询字符串转为Query对象 
        QueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_30, new String[] { "title", "content" }, Configuration.getAnalyzer()); // 在title与content中查询 
        Query query = queryParser.parse(queryString); 
 
        // 2,查询,得到中间结果 
        IndexSearcher indexSearcher = new IndexSearcher(Configuration.getDirectory()); 
        // TopDocs topDocs = indexSearcher.search(query, 100); // 按指定条件条询,只返回前n条结束 
        // ==================================================================== 
        // indexSearcher.search(query, n); 
        // indexSearcher.search(query, filter, n); 
        // indexSearcher.search(query, filter, n, sort); 
        // 指定排序的字段 
        Sort sort = new Sort(new SortField("id", SortField.INT)); // 按id升序排列 
        // Sort sort = new Sort(new SortField("id", SortField.INT, true)); // 按id降序排列 
        TopDocs topDocs = indexSearcher.search(query, null, 100, sort); 
        // ==================================================================== 
        ScoreDoc[] scoreDocs = topDocs.scoreDocs; // 前n条结果的信息 
 
        // 3,处理结果 
        List<Article> list = new ArrayList<Article>(); 
        for (int i = 0; i < scoreDocs.length; i++) { 
            System.out.println(scoreDocs[i].score); // 相关度得分 
 
            // 根据编号取出真正的Document数据 
            Document doc = indexSearcher.doc(scoreDocs[i].doc); 
            // 把Document转成Article 
            Article article = ArticleDocumentUtils.document2Article(doc); 
            list.add(article); 
        } 
 
        indexSearcher.close(); 
 
        // 显示结果 
        System.out.println("总结果数量为:" + list.size()); 
        for (Article article : list) { 
            System.out.println("--------> id = " + article.getId()); 
            System.out.println("title  = " + article.getTitle()); 
            System.out.println("content= " + article.getContent()); 
        } 
    } 
 
 
} 

过滤
使用Filter 可以对搜索结果进行过滤以获得更小范围的结果。使用Filter对性能的影响很大(有可能会使查询慢上百倍)。

代码示例:

package com.my.lucene; 
 
import java.util.ArrayList; 
import java.util.List; 
 
import org.apache.lucene.document.Document; 
import org.apache.lucene.queryParser.MultiFieldQueryParser; 
import org.apache.lucene.queryParser.QueryParser; 
import org.apache.lucene.search.Filter; 
import org.apache.lucene.search.IndexSearcher; 
import org.apache.lucene.search.NumericRangeFilter; 
import org.apache.lucene.search.Query; 
import org.apache.lucene.search.ScoreDoc; 
import org.apache.lucene.search.TopDocs; 
import org.apache.lucene.util.Version; 
import org.junit.Test; 
 
import com.my.bean.Article; 
import com.my.utils.ArticleDocumentUtils; 
import com.my.utils.Configuration; 
 
public class FilterTest { 
 
    @Test 
    public void search() throws Exception { 
        // 搜索条件 
        String queryString = "用户"; 
 
        // 1,把查询字符串转为Query对象 
        QueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_30, new String[] { "title", "content" }, Configuration.getAnalyzer()); // 在title与content中查询 
        Query query = queryParser.parse(queryString); 
 
        // 2,查询,得到中间结果 
        IndexSearcher indexSearcher = new IndexSearcher(Configuration.getDirectory()); 
        // TopDocs topDocs = indexSearcher.search(query, 100); // 按指定条件条询,只返回前n条结束 
        // indexSearcher.search(query, n); 
        // indexSearcher.search(query, filter, n); 
        // indexSearcher.search(query, filter, n, sort); 
        // 指定过滤的条件 
        Filter filter = NumericRangeFilter.newIntRange("id", 1, 15, true, true); 
        TopDocs topDocs = indexSearcher.search(query, filter, 100); 
        ScoreDoc[] scoreDocs = topDocs.scoreDocs; // 前n条结果的信息 
 
        // 3,处理结果 
        List<Article> list = new ArrayList<Article>(); 
        for (int i = 0; i < scoreDocs.length; i++) { 
            System.out.println(scoreDocs[i].score); // 相关度得分 
 
            // 根据编号取出真正的Document数据 
            Document doc = indexSearcher.doc(scoreDocs[i].doc); 
            // 把Document转成Article 
            Article article = ArticleDocumentUtils.document2Article(doc); 
            list.add(article); 
        } 
 
        indexSearcher.close(); 
 
        // 显示结果 
        System.out.println("总结果数量为:" + list.size()); 
        for (Article article : list) { 
            System.out.println("--------> id = " + article.getId()); 
            System.out.println("title  = " + article.getTitle()); 
            System.out.println("content= " + article.getContent()); 
        } 
    } 
 
} 

高亮

高亮的核心类为
org.apache.lucene.search.highlight.Formatter
org.apache.lucene.search.highlight.Highlighter
org.apache.lucene.search.highlight.Scorer

代码示例:

package com.my.lucene; 
 
import java.util.ArrayList; 
import java.util.List; 
 
import org.apache.lucene.document.Document; 
import org.apache.lucene.queryParser.MultiFieldQueryParser; 
import org.apache.lucene.queryParser.QueryParser; 
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.Formatter; 
import org.apache.lucene.search.highlight.Highlighter; 
import org.apache.lucene.search.highlight.QueryScorer; 
import org.apache.lucene.search.highlight.Scorer; 
import org.apache.lucene.search.highlight.SimpleFragmenter; 
import org.apache.lucene.search.highlight.SimpleHTMLFormatter; 
import org.apache.lucene.util.Version; 
import org.junit.Test; 
 
import com.my.bean.Article; 
import com.my.utils.ArticleDocumentUtils; 
import com.my.utils.Configuration; 
 
public class HighlighterTest { 
 
    @Test 
    public void search() throws Exception { 
        // 搜索条件 
        String queryString = "用户"; 
 
        // 1,把查询字符串转为Query对象 
        QueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_30, new String[] { "title", "content" }, Configuration.getAnalyzer()); // 在title与content中查询 
        Query query = queryParser.parse(queryString); 
 
        // 2,查询,得到中间结果 
        IndexSearcher indexSearcher = new IndexSearcher(Configuration.getDirectory()); 
        TopDocs topDocs = indexSearcher.search(query, 100); // 按指定条件条询,只返回前n条结束 
        ScoreDoc[] scoreDocs = topDocs.scoreDocs; // 前n条结果的信息 
 
        // 创建并配置高亮器(前缀、后缀、摘要大小) 
        Formatter formatter = new SimpleHTMLFormatter("<font color='red'>", "</font>"); // 指定前缀与后缀,默认使用<b>与</b> 
        Scorer scorer = new QueryScorer(query); 
        Highlighter highlighter = new Highlighter(formatter, scorer); 
        highlighter.setTextFragmenter(new SimpleFragmenter(20)); // 指定摘要大小,默认是100个字符 
 
        // 3,处理结果 
        List<Article> list = new ArrayList<Article>(); 
        for (int i = 0; i < scoreDocs.length; i++) { 
            // 根据编号取出真正的Document数据 
            Document doc = indexSearcher.doc(scoreDocs[i].doc); 
 
            // 进行高亮操作,一次只能高亮一个字段的值,返回高亮后的文本(一段摘要),如果当前高亮的属性值中没有出现要搜索的关键字,则返回null 
            String text = highlighter.getBestFragment(Configuration.getAnalyzer(), "content", doc.get("content")); 
            if (text != null) { 
                doc.getField("content").setValue(text); // 使用高亮后的文本替换原始内容 
            } 
 
            // 把Document转成Article 
            Article article = ArticleDocumentUtils.document2Article(doc); 
            list.add(article); 
        } 
 
        indexSearcher.close(); 
 
        // 显示结果 
        System.out.println("总结果数量为:" + list.size()); 
        for (Article article : list) { 
            System.out.println("--------> id = " + article.getId()); 
            System.out.println("title  = " + article.getTitle()); 
            System.out.println("content= " + article.getContent()); 
        } 
    } 
 
} 

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/12105.html

(0)
上一篇 2021年7月19日 14:24
下一篇 2021年7月19日 14:24

相关推荐

发表回复

登录后才能评论