Lucene4:利用Filter实现两组关键词组合查询
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>光:包二奶、玩小姐 还找黑客删帖遭<EM>曝光</EM>
<EM>盐城</EM>质监局长冯建东“包二奶、招小姐、淫下属”丑行<EM>曝光</EM>
江苏<EM>盐城市</EM>中级人民法院是什么鸟人?居然造出一个<EM>贪污</EM>大犯赵作海
[爆 猛 料]<EM>盐城市</EM>亭湖农委下属单位领导<EM>贪污</EM>特种苗木补助款