Lucene BooleanQuery中的Occur.MUST与Occur.Should

本文详细解释了Lucene中BooleanQuery的MUST、SHOULD、MUST_NOT操作符的使用方式及它们在构建复杂查询时的作用。重点讨论了如何避免误用SHOULD导致的查询结果偏差,并提供了正确构造与或非关系的代码示例。

1.  多个MUST的组合不必多说,就是交集

2.  MUST和SHOULD的组合。是在MUST搜出来的doc里面,根据SHOULD的query进行打分。也就是说,SHOULD的query不会影响最终的HITS,只会提供打分依据。

3.  SHOULD的组合。如果最终的BooleanQuery只有SHOULD,那么搜出来的doc至少要满足一个SHOULD的query,也就是说是逻辑OR。

那么在下面这段代码中,问题就出现了:

代码的本意是在baseQuery的基础上和geoQuery做一个交集

public Map<String, Query> buildGeoQuery(Query baseQuery) {
    Map<String, Query> queryMap = new HashMap<String, Query>();
    for(String key : localHashMap.keySet()) {
        List<String> hashValues = localHashMap.get(key);
        BooleanQuery bq = new BooleanQuery();
        bq.add(baseQuery, Occur.MUST);
        if(hashValues.size() == 1) {
            TermQuery hashQuery =  
new TermQuery(new Term(Constants.FIELD_SEARCH_HASH, hashValues.get(0)));
            bq.add(hashQuery, Occur.MUST);
        }
        else if(hashValues.size() > 1) {
            for(String value : hashValues) {
                TermQuery hashQuery = new TermQuery(new Term(Constants.FIELD_SEARCH_HASH, value));
                bq.add(hashQuery, Occur.SHOULD);
            }
        }
        queryMap.put(key, bq);
    }
    return queryMap;
}

在第三个用红色标注的语句中,本意是对多个geohash query做逻辑OR的操作(使用了should),但是由于最开始的basequery是以MUST关键字加入的,那么这些个geohash query只做为打分依据,而不是必须出现的,这样就会导致有一些额外的doc被搜出来。

正确的做法应该是用一个独立的GeoQuery来把geohash termquery组合起来,最后将geoQuery和baseQuery用Occur.MUST组合

 

 

 

===============================================================================

 

lucene3.0中BooleanQuery 实现与或的复合搜索 .
BooleanClause用于表示布尔查询子句关系的类,包 括:BooleanClause.Occur.MUST,BooleanClause.Occur.MUST_NOT,BooleanClause.Occur.SHOULD。 必须包含,不能包含,可以包含三种.有以下6种组合: 
 
1.MUST和MUST:取得连个查询子句的交集。 
2.MUST和MUST_NOT:表示查询结果中不能包含MUST_NOT所对应得查询子句的检索结果。 
3.SHOULD与MUST_NOT:连用时,功能同MUST和MUST_NOT。
4.SHOULD与MUST连用时,结果为MUST子句的检索结果,但是SHOULD可影响排序。
5.SHOULD与SHOULD:表示“或”关系,最终检索结果为所有检索子句的并集。
6.MUST_NOT和MUST_NOT:无意义,检索无结果。

 

 

====================================================================================

 

在输入要搜索的关键字时,Lucene是这样处理的:

+a +b:搜索同时包含a又包含b的结果集

a  b:搜索包含a或包含b的结果集

+a -b:搜索包含a不包含b的结果集

也就是如下结论:

  a & b =>  +a +b

  a || b =>  a  b

  a !b  =>  +a -b

 

那在代码中该如何来构造这种与或非的关系呢?

一般用BooleanQuery来构造。

//构造BooleanQuery
      QueryParser parser = new QueryParser("content", analyzer);
      BooleanQuery bquery = new BooleanQuery();
      TokenStream ts = analyzer.TokenStream(null, new StringReader(querystring));
      Lucene.Net.Analysis.Token token;
      while ((token = ts.Next()) != null)
      {
        Query query = parser.Parse(token.TermText());
        bquery.Add(query, BooleanClause.Occur.MUST);
      }
      //构造完成
            IndexSearcher searcher = new IndexSearcher("IndexDirectory");
      
      //Query query = parser.Parse(querystring);
      //输出我们要查看的表达式
      Console.WriteLine(bquery.ToString());
      Hits hits = searcher.Search(bquery);
      for (int i = 0; i < hits.Length(); i++)
      {
        Document doc = hits.Doc(i);
        Console.WriteLine(doc.Get("title"));
      }
    }

其中,bquery.Add(query, BooleanClause.Occur.MUST);MUST构造出“与”的关系

构造“或”关系:bquery.Add(query, BooleanClause.Occur.SHOULD);

构造“非”关系:bquery.Add(query, BooleanClause.Occur.MUST_NOT);

 

原文链接:http://www.cnblogs.com/weipeng/archive/2012/04/18/2455079.html

package com.boe.cim.teacher.luence; import java.io.StringReader; import java.nio.file.Paths; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.Term; import org.apache.lucene.queryparser.classic.QueryParser; import org.apache.lucene.search.BooleanQuery; 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.WildcardQuery; import org.apache.lucene.search.highlight.Fragmenter; 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.BooleanClause; import org.apache.lucene.store.Directory; import org.apache.lucene.store.FSDirectory; import org.apache.lucene.document.LongPoint; import com.boe.cim.teacher.pojo.RequirementInfo; public class LuceneSearchRequirement { /** * @param indexreDir * 索引文件路径path * @param queryField * 被索引字段 Field * @param queryMsg * 索引值 * @return * @throws Exception */ public List<RequirementInfo> search(String indexreDir, String queryField, String queryMsg) throws Exception { // 得到读取索引文件的路径 Directory dir = FSDirectory.open(Paths.get(indexreDir)); // 通过dir得到的路径下的所有的文件 // 建立索引查询器 IndexReader reader = DirectoryReader.open(dir); IndexSearcher searcher = new IndexSearcher(reader); // 中文分词器 SmartChineseAnalyzer analyzer = new SmartChineseAnalyzer(); // ======== 新增:日期范围查询处理 ======== Query dateQuery = null; if ("requirementtime".equals(queryField)) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); long minTime = sdf.parse(queryMsg).getTime(); dateQuery = LongPoint.newRangeQuery("time", minTime, Long.MAX_VALUE); } // 建立查询解析器 /** * 第一个参数是要查询的字段; 第二个参数是分析器Analyzer */ // QueryParser parser = new QueryParser(queryField, analyzer); // 根据传进来的par查找 // Query query = parser.parse(queryMsg); // Query query = new TermQuery(new Term("teacher",queryMsg)); // Query query = new WildcardQuery(new Term(queryField,"*"+queryMsg+"*")); Query query; //这四种类型需要特别匹配,不需要分词器进行搜索 if(queryField.equals("requirement") || queryField.equals("department") || queryField.equals("liaisonman") || queryField.equals("requirementtype") ) { query = new WildcardQuery(new Term(queryField,"*"+queryMsg+"*")); }else { QueryParser parser = new QueryParser(queryField, analyzer); query = parser.parse(queryMsg); } // ======== 新增:组合日期查询 ======== if (dateQuery != null) { BooleanQuery.Builder builder = new BooleanQuery.Builder(); builder.add(query, BooleanClause.Occur.MUST); builder.add(dateQuery, BooleanClause.Occur.MUST); query = builder.build(); } // 计算索引开始时间 long start = System.currentTimeMillis(); // 开始查询 /** * 第一个参数是通过传过来的参数来查找得到的query; 第二个参数是要出查询的行数 */ TopDocs topDocs = searcher.search(query, 104); // 索引结束时间 long end = System.currentTimeMillis(); System.out.println("匹配:["+queryField+"]," + queryMsg + ",总共花费了" + (end - start) + "毫秒,共查到" + topDocs.totalHits + "条记录。"); // 高亮显示start // 算分 QueryScorer scorer = new QueryScorer(query); // 显示得分高的片段 Fragmenter fragmenter = new SimpleSpanFragmenter(scorer); // 设置标签内部关键字的颜色 // 第一个参数:标签的前半部分;第二个参数:标签的后半部分。 SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("<b><font color='red'>", "</font></b>"); // 第一个参数是对查到的结果进行实例化;第二个是片段得分(显示得分高的片段,即摘要) Highlighter highlighter = new Highlighter(simpleHTMLFormatter, scorer); // 设置片段 highlighter.setTextFragmenter(fragmenter); // 高亮显示end // 遍历topDocs /** * ScoreDoc:是代表一个结果的相关度得分文档编号等信息的对象。 scoreDocs:代表文件的数组 * * @throws Exception */ List<RequirementInfo> listinfo = new ArrayList<>(); for (ScoreDoc scoreDoc : topDocs.scoreDocs) { RequirementInfo requirementinfo = new RequirementInfo(); // 获取文档 Document document = searcher.doc(scoreDoc.doc); // 输出全路径 String id = document.get("id"); String queryType = document.get(queryField); requirementinfo.setId(Integer.parseInt(id)); if (id != null) { // 把全部得分高的摘要给显示出来 // 第一个参数是对哪个参数进行设置;第二个是以流的方式读入 TokenStream tokenStream = analyzer.tokenStream(queryField, new StringReader(queryType)); // 获取最高的片段 String highlighterString; if(queryField.equals("requirement") || queryField.equals("department") || queryField.equals("liaisonman") || queryField.equals("requirementtype") ) { highlighterString = queryType.replaceAll(queryMsg, "<b><font color='red'>"+queryMsg+"</font></b>"); }else { highlighterString = highlighter.getBestFragment(tokenStream, queryType); } // String highlighterString = highlighter.getBestFragment(new SmartChineseAnalyzer(), queryField, queryType); //设置高亮字段 switch (queryField) { // 根据搜索条件进行赋值 case "requirement": //需求名称 requirementinfo.setRequirement(highlighterString); break; case "department": //需求组织 requirementinfo.setDepartment(highlighterString); break; case "liaisonman": //需求联系人 requirementinfo.setLiaisonman(highlighterString); break; case "requirementtype": //需求类型 requirementinfo.setRequirementtype(Integer.parseInt(highlighterString.replaceAll("<[^>]+>", ""))); break; case "requirementcontents": //需求内容 requirementinfo.setRequirementcontents(highlighterString); break; case "requirementbackground": //需求背景 requirementinfo.setRequirementbackground(highlighterString); break; } listinfo.add(requirementinfo); } } reader.close(); return listinfo; } } 修改后报错The import org.apache.lucene.document.LongPoint cannot be resolved
09-13
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值