Lucene4:利用Filter实现两组关键词组合查询

时间:2022-09-16 11:17:51

1. 需求

根据客户名称,查询客户网络上面的负面信息。如客户名称为”盐城 盐城市“,并自定义负面关键词“贪污 受贿 被曝 曝光 小三 裸照”等,则是要求将包含这些负面关键词且包含客户名称的信息查询出来。
 
注意事项:客户名称可定义多个(10个以内),负面关键词可以定义多个(300个以内)。

分析: 

如果只用一个参数q去定义查询,很可能查询字符串长度会溢出,查询q应该形如:

((盐城 OR 盐城市) AND 贪污) OR ((盐城 OR 盐城市) AND 受贿)...... 

所以本文采用Filter的形式来解决这一问题。
2. 解决步骤

步骤一:将所需要查询的客户名称、负面关键词加入到中文分词器词典文件中;(本文略)
         只有中文能够将这些关键词正确分词,其它各项操作才能顺利进行。
步骤二:用爬虫工具将网络上的信息抓取下来后,在创建Lucene索引的时候, 将包含负面关键词的文档自定义分值(包含负面关键词越多,分值越高);(本步骤略,可以参考:Lucene4.1:运用中文分词器创建索引,给指定文本增加boost值
         这样可以保证查询时,包含负面关键词越多的文档,查询时排在越前面。
步骤三:按客户名称查询(如:盐城 盐城市);
         正常查询输入。
步骤四:查询时加入过滤器(Filter),过滤器的输入就是负面关键词列表;
         加入过滤器后,实现的查询结果类似于:((盐城 OR 盐城市) AND 贪污) OR ((盐城 OR 盐城市) AND 受贿)......
步骤五:自定义高亮结果。
         因为客户名称需要高亮,负面关键词也需要高亮,所以需要自定义高亮显示。

3. 参考源代码

package com.clzhang.sample.lucene;

import java.io.*;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.queryparser.classic.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.util.Version;
import org.apache.lucene.search.QueryWrapperFilter;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.SimpleHTMLFormatter;
import org.apache.lucene.search.highlight.SimpleSpanFragmenter;
import org.apache.lucene.search.highlight.TokenSources;

//import org.wltea.analyzer.lucene.IKAnalyzer;
import com.chenlb.mmseg4j.Dictionary;
import com.chenlb.mmseg4j.analysis.SimpleAnalyzer;

import org.junit.Test;

/**
 * 环境:Lucene 4.1版本/IKAnalyzer 2012 FF版本/mmseg4j 1.9版本
 * 
 * 根据客户名称,查询客户网络上面的负面信息。如客户名称为”盐城 盐城市“,并自定义负面关键词“贪污 受贿 被曝 曝光 小三 裸照”等,则是要求将包含这些负面关键词且包含客户名称的信息查询出来。
 * 注意事项:客户名称可定义多个(10个以内),负面关键词可以定义多个(300个以内)。
 * 
 * 分析: 如果只用一个参数q去定义查询,很可能查询字符串长度会溢出,查询q应该形如:
 * ((盐城 OR 盐城市) AND 贪污) OR ((盐城 OR 盐城市) AND 受贿)...... 
 * 所以本文采用Filter的形式来解决这一问题。
 * 
 * 解决: 
 * 步骤一:将所需要查询的客户名称、负面关键词加入到中文分词器词典文件中;(本文略)
 *     只有中文能够将这些关键词正确分词,其它各项操作才能顺利进行。
 * 步骤二:用爬虫工具将网络上的信息抓取下来后,在创建Lucene索引的时候, 将包含负面关键词的文档自定义分值(包含负面关键词越多,分值越高);(本文略,可以参考:Lucene学习笔记二(运用中文分词器创建索引,给指定文本增加boost值))
 *     这样可以保证查询时,包含负面关键词越多的文档,查询时排在越前面。
 * 步骤三:按客户名称查询(如:盐城 盐城市);
 *     正常查询输入。
 * 步骤四:查询时加入过滤器(Filter),过滤器的输入就是负面关键词列表;
 *     加入过滤器后,实现的查询结果类似于:((盐城 OR 盐城市) AND 贪污) OR ((盐城 OR 盐城市) AND 受贿)......
 * 步骤五:自定义高亮结果。
 *     因为客户名称需要高亮,负面关键词也需要高亮,所以需要自定义高亮显示。
 * @author Administrator
 *
 */
public class ComplexDemo {
    // mmseg4j字典路径
    private static final String MMSEG4J_DICT_PATH = "C:\\solr\\mm4jdic";
    private static Dictionary dictionary = Dictionary.getInstance(MMSEG4J_DICT_PATH);
    
    // Lucene索引存放路径 
    private static final String LUCENE_INDEX_DIR = "C:\\solr\\news\\data\\index";

    @Test
    public void doUserQuery() throws Exception {
        // 实例化IKAnalyzer分词器
//        Analyzer analyzer = new IKAnalyzer();
        
        // 实例化mmseg4j分词器
        Analyzer analyzer = new SimpleAnalyzer(dictionary);

        // 实例化搜索器
        final String FIELD_NAME = "webTitle";
        Directory directory = FSDirectory.open(new File(LUCENE_INDEX_DIR));
        DirectoryReader reader = DirectoryReader.open(directory);
        IndexSearcher searcher = new IndexSearcher(reader);
        QueryParser qp = new QueryParser(Version.LUCENE_41, FIELD_NAME, analyzer);

        // 步骤三:按客户名称查询
        String keyword = "盐城 盐城市";
        Query query = qp.parse(keyword);
        
        // 步骤四:查询时加入过滤器(Filter)......
        String negativeWord = "贪污 受贿 被曝 曝光 小三 裸照";
        Query kwQuery = qp.parse(negativeWord);
        QueryWrapperFilter qwFilter = new QueryWrapperFilter(kwQuery);
        
        // 搜索相似度最高的5条记录
        TopDocs hits = searcher.search(query, qwFilter, 5);
        System.out.println("命中:" + hits.totalHits);

        // 步骤五:自定义高亮代码......
        QueryScorer scorer = new QueryScorer(qp.parse(keyword + " " + negativeWord), FIELD_NAME);
        SimpleHTMLFormatter simpleHtmlFormatter = new SimpleHTMLFormatter("<EM>", "</EM>"); 
        Highlighter highlighter = new Highlighter(simpleHtmlFormatter, scorer);
        highlighter.setTextFragmenter(
                       new SimpleSpanFragmenter(scorer));

        // 输出结果
        for (ScoreDoc scoreDoc : hits.scoreDocs) {
            Document doc = searcher.doc(scoreDoc.doc);
            String title = doc.get(FIELD_NAME);
            
            // 高亮代码输出
            TokenStream stream = TokenSources.getAnyTokenStream(
                    searcher.getIndexReader(), scoreDoc.doc, FIELD_NAME, doc, analyzer);
            String fragment = highlighter.getBestFragment(stream, title);
            System.out.println(fragment);
        }
        reader.close();
        directory.close();
    }
}

查询输出:

命中:13
[<EM>曝光</EM>台]实名举报江苏<EM>盐城市</EM>阜宁县吴滩镇派出所办案无能
<EM>盐城</EM>质监局长冯建东贪@污糜烂<EM>被曝</EM>光:包二奶、玩小姐&nbsp;还找黑客删帖遭<EM>曝光</EM>
<EM>盐城</EM>质监局长冯建东“包二奶、招小姐、淫下属”丑行<EM>曝光</EM>
江苏<EM>盐城市</EM>中级人民法院是什么鸟人?居然造出一个<EM>贪污</EM>大犯赵作海
[爆 猛 料]<EM>盐城市</EM>亭湖农委下属单位领导<EM>贪污</EM>特种苗木补助款