你好,游客 登录 注册 搜索
背景:
阅读新闻

Solr实现Low Level查询解析(QParser)

[日期:2012-05-05] 来源:Linux社区  作者:shirdrn [字体: ]

首先,和前端设计定义统一的接口:

  1. 北京OR北平OR首都OR京城OR京都AND首都博物馆OR首博  <=>  +((title:北京 content:北京) (title:北平 content:北平) (title:首都 content:首都) (title:京城 content:京城) (title:京都 content:京都)) +((title:首都博物馆 content:首都博物馆) (title:首博 content:首博))  

我们通过在扩展的QParser中进行解析,代码如下所示:

  1. package org.shirdrn.solr.search;  
  2.   
  3. import java.util.HashMap;  
  4. import java.util.Iterator;  
  5. import java.util.Map;  
  6. import java.util.Map.Entry;  
  7.   
  8. import org.apache.lucene.index.Term;  
  9. import org.apache.lucene.queryParser.ParseException;  
  10. import org.apache.lucene.search.BooleanClause;  
  11. import org.apache.lucene.search.BooleanQuery;  
  12. import org.apache.lucene.search.DisjunctionMaxQuery;  
  13. import org.apache.lucene.search.PhraseQuery;  
  14. import org.apache.lucene.search.Query;  
  15. import org.apache.lucene.search.TermQuery;  
  16. import org.apache.solr.common.params.CommonParams;  
  17. import org.apache.solr.common.params.DefaultSolrParams;  
  18. import org.apache.solr.common.params.DisMaxParams;  
  19. import org.apache.solr.common.params.SolrParams;  
  20. import org.apache.solr.request.SolrQueryRequest;  
  21. import org.apache.solr.search.DisMaxQParser;  
  22. import org.apache.solr.util.SolrPluginUtils;  
  23. import org.slf4j.Logger;  
  24. import org.slf4j.LoggerFactory;  
  25.   
  26. /** 
  27.  * Customized solr QParser of the plugin 
  28.  *  
  29.  * @author shirdrn 2011/11/03 
  30.  */  
  31. public class SimpleQParser extends DisMaxQParser {  
  32.     private final Logger LOG = LoggerFactory.getLogger(SimpleQParser.class);  
  33.     // using low level Term query? For internal search usage.   
  34.     private boolean useLowLevelTermQuery = false;  
  35.     private float tiebreaker = 0f;  
  36.     private static Float mainBoost = 1.0f;  
  37.     private static Float frontBoost = 1.0f;  
  38.     private static Float rearBoost = 1.0f;  
  39.     private String userQuery = "";  
  40.   
  41.     public SimpleQParser(String qstr, SolrParams localParams,  
  42.             SolrParams params, SolrQueryRequest req) {  
  43.         super(qstr, localParams, params, req);  
  44.     }  
  45.       
  46.     @Override  
  47.       public Query parse() throws ParseException {  
  48.         SolrParams solrParams = localParams == null ? params : new DefaultSolrParams(localParams, params);  
  49.         queryFields = SolrPluginUtils.parseFieldBoosts(solrParams.getParams(DisMaxParams.QF));  
  50.         if (0 == queryFields.size()) {  
  51.           queryFields.put(req.getSchema().getDefaultSearchFieldName(), 1.0f);  
  52.         }  
  53.           
  54.         /* the main query we will execute.  we disable the coord because 
  55.          * this query is an artificial construct 
  56.          */  
  57.         BooleanQuery query = new BooleanQuery(true);  
  58.         addMainQuery(query, solrParams);  
  59.      // rewrite q parameter for highlighting   
  60.         if(useLowLevelTermQuery) {  
  61.             query = new BooleanQuery(true);  
  62.             rewriteAndOrQuery(userQuery, query, solrParams);  
  63.         }  
  64.         addBoostQuery(query, solrParams);  
  65.         addBoostFunctions(query, solrParams);  
  66.         return query;  
  67.       }  
  68.   
  69.     protected void addMainQuery(BooleanQuery query, SolrParams solrParams)throws ParseException {  
  70.         tiebreaker = solrParams.getFloat(DisMaxParams.TIE, 0.0f);  
  71.         // get the comma separated list of fields used for payload   
  72.   
  73.         /* 
  74.          * a parser for dealing with user input, which will convert things to 
  75.          * DisjunctionMaxQueries 
  76.          */  
  77.         SolrPluginUtils.DisjunctionMaxQueryParser up = getParser(queryFields,DisMaxParams.QS, solrParams, tiebreaker);  
  78.   
  79.         /* * * Main User Query * * */  
  80.         parsedUserQuery = null;  
  81.         userQuery = getString();  
  82.         altUserQuery = null;  
  83.         if (userQuery == null || userQuery.trim().length() < 1) {  
  84.           // If no query is specified, we may have an alternate   
  85.           altUserQuery = getAlternateUserQuery(solrParams);  
  86.           query.add(altUserQuery, BooleanClause.Occur.MUST);  
  87.         } else {  
  88.           // There is a valid query string   
  89.           userQuery = SolrPluginUtils.partialEscape(SolrPluginUtils.stripUnbalancedQuotes(userQuery)).toString();  
  90.           userQuery = SolrPluginUtils.stripIllegalOperators(userQuery).toString();  
  91.   
  92.           // use low level Term for constructing TermQuery or BooleanQuery.   
  93.           // warning: for internal AND, OR query, in order to integrate with Solr for obtaining highlight   
  94.           String luceneQueryText = userQuery;  
  95.           String q = solrParams.get(CommonParams.Q);  
  96.             if(q!=null && (q.indexOf("AND")!=-1 || q.indexOf("OR")!=-1)) {  
  97.               addBasicAndOrQuery(luceneQueryText, query, solrParams);  
  98.               luceneQueryText = query.toString();  
  99.               useLowLevelTermQuery = true;  
  100.           }  
  101.             
  102.           LOG.debug("userQuery=" + luceneQueryText);  
  103.             parsedUserQuery = getUserQuery(luceneQueryText, up, solrParams);  
  104.               
  105.             BooleanQuery rewritedQuery = rewriteQueries(parsedUserQuery);  
  106.             query.add(rewritedQuery, BooleanClause.Occur.MUST);  
  107.         }  
  108.     }  
  109.       
  110.     protected void rewriteAndOrQuery(String userQuery, BooleanQuery query, SolrParams solrParams)throws ParseException {  
  111.         addBasicAndOrQuery(userQuery, query, solrParams);  
  112.     }  
  113.       
  114.     /** 
  115.      * Parse mixing MUST and SHOULD query defined by us,  
  116.      * e.g. 首都OR北京OR北平AND首博OR首都博物馆 
  117.      * @param userQuery 
  118.      * @param query 
  119.      * @param solrParams 
  120.      * @throws ParseException 
  121.      */  
  122.     protected void addBasicAndOrQuery(String userQuery, BooleanQuery query, SolrParams solrParams)throws ParseException {  
  123.             userQuery = SolrPluginUtils.partialEscape(SolrPluginUtils.stripUnbalancedQuotes(userQuery)).toString();  
  124.             userQuery = SolrPluginUtils.stripIllegalOperators(userQuery).toString();  
  125.             LOG.debug("userQuery=" + userQuery);  
  126.             BooleanQuery parsedUserQuery = new BooleanQuery(true);  
  127.             String[] a = userQuery.split("\\s*AND\\s*");  
  128.             String q = "";  
  129.             if(a.length==0) {  
  130.                 createTermQuery(parsedUserQuery, userQuery);  
  131.             } if(a.length>=3) {  
  132.                 if(userQuery.indexOf("OR")==-1) { // e.g. 首都AND北京AND北平   
  133.                     BooleanQuery andBooleanQuery = parseAndQuery(a);  
  134.                     parsedUserQuery.add(andBooleanQuery, BooleanClause.Occur.MUST);  
  135.                 }  
  136.             } else{  
  137.                 if(a.length>0) {  
  138.                     q = a[0].trim();  
  139.                     if(q.indexOf("OR")!=-1 || q.length()>0) {  
  140.                         parsedUserQuery.add(parseOrQuery(q, frontBoost), BooleanClause.Occur.MUST);  
  141.                     }  
  142.                 }  
  143.                 if(a.length==2) {  
  144.                     q = a[1].trim();  
  145.                     if(q.indexOf("OR")!=-1 || q.length()>0) {  
  146.                         parsedUserQuery.add(parseOrQuery(q, rearBoost), BooleanClause.Occur.MUST);  
  147.                     }  
  148.                 }  
  149.             }  
  150.             parsedUserQuery.setBoost(mainBoost);  
  151.             BooleanQuery rewritedQuery = rewriteQueries(parsedUserQuery);  
  152.             query.add(rewritedQuery, BooleanClause.Occur.MUST);  
  153.     }  
  154.       
  155.     /** 
  156.      * Parse SHOULD query, e.g. 北京OR北平OR首都 
  157.      * @param ors 
  158.      * @param boost 
  159.      * @return 
  160.      */  
  161.     private BooleanQuery parseOrQuery(String ors, Float boost) {  
  162.         BooleanQuery bq = new BooleanQuery(true);  
  163.         for(String or : ors.split("\\s*OR\\s*")) {  
  164.             if(!or.isEmpty()) {  
  165.                 createTermQuery(bq, or.trim());  
  166.             }  
  167.         }  
  168.         bq.setBoost(boost);  
  169.         return bq;  
  170.     }  
  171.   
  172.     /** 
  173.      * Create TermQuery for some term text, query fields. 
  174.      * @param bq 
  175.      * @param qsr 
  176.      */  
  177.     private void createTermQuery(BooleanQuery bq, String qsr) {  
  178.         for(String field : queryFields.keySet()) {  
  179.             TermQuery tq = new TermQuery(new Term(field, qsr));  
  180.             if(queryFields.get(field)!=null) {  
  181.                 tq.setBoost(queryFields.get(field));  
  182.             }  
  183.             bq.add(tq, BooleanClause.Occur.SHOULD);  
  184.         }  
  185.     }  
  186.       
  187.     /** 
  188.      * Parse MUST query, e.g. 首都AND北京AND北平 
  189.      * @param ands 
  190.      * @return 
  191.      */  
  192.     private BooleanQuery parseAndQuery(String[] ands) {  
  193.         BooleanQuery andBooleanQuery = new BooleanQuery(true);  
  194.         for(String and : ands) {  
  195.             if(!and.isEmpty()) {  
  196.                 BooleanQuery bq = new BooleanQuery(true);  
  197.                 createTermQuery(bq, and);  
  198.                 andBooleanQuery.add(bq, BooleanClause.Occur.MUST);  
  199.             }  
  200.         }  
  201.         return andBooleanQuery;  
  202.     }  
  203.       
  204.     /** 
  205.      * Rewrite a query, especially a {@link BooleanQuery}, whose 
  206.      * subclauses maybe include {@link BooleanQuery}s, {@link DisjunctionMaxQuery}s, 
  207.      * {@link TermQuery}s, {@link PhraseQuery}s, {@link PayloadQuery}s, etc. 
  208.      * @param input 
  209.      * @return 
  210.      */  
  211.     private BooleanQuery rewriteQueries(Query input) {   
  212.         BooleanQuery output = new BooleanQuery(true);  
  213.         if(input instanceof BooleanQuery) {  
  214.             BooleanQuery bq = (BooleanQuery) input;  
  215.             for(BooleanClause clause : bq.clauses()) {  
  216.                 if(clause.getQuery() instanceof DisjunctionMaxQuery) {  
  217.                     BooleanClause.Occur occur = clause.getOccur();  
  218.                     output.add(rewriteDisjunctionMaxQueries((DisjunctionMaxQuery) clause.getQuery()), occur); // BooleanClause.Occur.SHOULD   
  219.                 } else {  
  220.                     output.add(clause.getQuery(), clause.getOccur());  
  221.                 }  
  222.             }  
  223.         } else if(input instanceof DisjunctionMaxQuery) {  
  224.             output.add(rewriteDisjunctionMaxQueries((DisjunctionMaxQuery) input), BooleanClause.Occur.SHOULD); // BooleanClause.Occur.SHOULD   
  225.         }  
  226.         output.setBoost(input.getBoost()); // boost main clause   
  227.         return output;  
  228.     }  
  229.       
  230.     /** 
  231.      * Rewrite the {@link DisjunctionMaxQuery}, because of default parsing 
  232.      * query string to {@link PhraseQuery}s which are not what we want. 
  233.      * @param input 
  234.      * @return 
  235.      */  
  236.     private BooleanQuery rewriteDisjunctionMaxQueries(DisjunctionMaxQuery input) {   
  237.         // input e.g. (content:"吉林 长白山 内蒙古 九寨沟" | title:"吉林 长白山 内蒙古 九寨沟"^1.5)~1.0   
  238.         Map<String, BooleanQuery> m = new HashMap<String, BooleanQuery>();  
  239.         Iterator<Query> iter = input.iterator();  
  240.         while (iter.hasNext()) {  
  241.             Query query = iter.next();  
  242.             if(query instanceof PhraseQuery) {  
  243.                 PhraseQuery pq = (PhraseQuery) query; // e.g. content:"吉林 长白山 内蒙古 九寨沟"   
  244.                 for(Term term : pq.getTerms()) {  
  245.                     BooleanQuery fieldsQuery = m.get(term.text());  
  246.                     if(fieldsQuery==null) {  
  247.                         fieldsQuery = new BooleanQuery(true);  
  248.                         m.put(term.text(), fieldsQuery);  
  249.                     }  
  250.                     fieldsQuery.setBoost(pq.getBoost());  
  251.                     fieldsQuery.add(new TermQuery(term), BooleanClause.Occur.SHOULD);  
  252.                 }                 
  253.             } else if(query instanceof TermQuery) {  
  254.                 TermQuery termQuery = (TermQuery) query;  
  255.                 BooleanQuery fieldsQuery = m.get(termQuery.getTerm().text());  
  256.                 if(fieldsQuery==null) {  
  257.                     fieldsQuery = new BooleanQuery(true);  
  258.                     m.put(termQuery.getTerm().text(), fieldsQuery);  
  259.                 }  
  260.                 fieldsQuery.setBoost(termQuery.getBoost());  
  261.                 fieldsQuery.add(termQuery, BooleanClause.Occur.SHOULD);  
  262.             }  
  263.         }  
  264.           
  265.         Iterator<Entry<String, BooleanQuery>> it = m.entrySet().iterator();  
  266.         BooleanQuery mustBooleanQuery = new BooleanQuery(true);  
  267.         while(it.hasNext()) {  
  268.             Entry<String, BooleanQuery> entry = it.next();  
  269.             BooleanQuery shouldBooleanQuery = new BooleanQuery(true);  
  270.             createTermQuery(shouldBooleanQuery, entry.getKey());  
  271.             mustBooleanQuery.add(shouldBooleanQuery, BooleanClause.Occur.MUST);  
  272.         }  
  273.         return mustBooleanQuery;  
  274.     }  
  275.   
  276. }  
linux
相关资讯       Solr 
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数

       

评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款