感谢支持
我们一直在努力

Solr 的edismax与dismax比较与分析

edismax支持boost函数与score相乘作为,而dismax只能使用bf作用效果是相加,所以在处理多个维度排序时,score其实也应该是其中一个维度 ,用相加的方式处理调整麻烦。

而dismax的实现代码逻辑比较简单,看起来比较易理解,edismax是它的加强版,其实是改变了不少。。比如在以下:

先看看dismax的解析主要实现思路:

首先取出搜索字段名qf

将最终解析成一个BooleanQuery

先解析主mainQuery:

  • 用户主要是搜索串的解析
  • altQuery解析处理,看是否使用用户定义的后备搜索串
  • PhraseQuery解析组装

再解析bq查询,主要是额外加分的查询,不会影响搜索结果数,只会影响排序

再则是bf解析,函数搜索最后会以加的方式作用于文档评分

看主要代码更清晰:

  1. @Override 
  2. public Query parse() throws ParseException { 
  3.   SolrParams solrParams = SolrParams.wrapDefaults(localParams, params); 
  4.  
  5.   queryFields = SolrPluginUtils.parseFieldBoosts(solrParams.getParams(DisMaxParams.QF)); 
  6.   if (0 == queryFields.size()) { 
  7.     queryFields.put(req.getSchema().getDefaultSearchFieldName(), 1.0f); 
  8.   } 
  9.    
  10.   /* the main query we will execute.  we disable the coord because 
  11.    * this query is an artificial construct 
  12.    */ 
  13.   BooleanQuery query = new BooleanQuery(true); 
  14.  
  15.   boolean notBlank = addMainQuery(query, solrParams); 
  16.   if (!notBlank) 
  17.     return null
  18.   addBoostQuery(query, solrParams); 
  19.   addBoostFunctions(query, solrParams); 
  20.  
  21.   return query; 

edismax的主要实现思路跟dismax差不多,以下是一些主要差别之处:

edismax解析含有+,OR,NOT,-语法时,就会忽略掉使用MM。

以下是主要代码实现:

统计搜索串中+,OR ,NOT,-语法元个数

  1. // defer escaping and only do if lucene parsing fails, or we need phrases  
  2.      // parsing fails.  Need to sloppy phrase queries anyway though.  
  3.      List<Clause> clauses = null
  4.      int numPluses = 0
  5.      int numMinuses = 0
  6.      int numOR = 0
  7.      int numNOT = 0
  8.  
  9.      clauses = splitIntoClauses(userQuery, false); 
  10.      for (Clause clause : clauses) { 
  11.        if (clause.must == ‘+’) numPluses++; 
  12.        if (clause.must == ‘-‘) numMinuses++; 
  13.        if (clause.isBareWord()) { 
  14.          String s = clause.val; 
  15.          if (“OR”.equals(s)) { 
  16.            numOR++; 
  17.          } else if (“NOT”.equals(s)) { 
  18.            numNOT++; 
  19.          } else if (lowercaseOperators && “or”.equals(s)) { 
  20.            numOR++; 
  21.          } 
  22.        } 
  23.      } 

/////当搜索串里包含有+,OR ,NOT,-这四种时候,mm就会失效

  1. boolean doMinMatched = (numOR + numNOT + numPluses + numMinuses) == 0
  2. (parsedUserQuery != null && doMinMatched) { 
  3.   String minShouldMatch = solrParams.get(DisMaxParams.MM, “100%”); 
  4.   if (parsedUserQuery instanceof BooleanQuery) { 
  5.     SolrPluginUtils.setMinShouldMatch((BooleanQuery)parsedUserQuery, minShouldMatch); 
  6.   } 

短语查询,先找出普通的查询,原来就是短语查询的、或者属于“OR”,“AND”,“NOT”,’TO‘类型的都不要。由于edismax支持解析符合lucene语法的搜索串,所以不像dismax那样,只需要简单的将搜索串去掉\“,然后加个“”括起来就行       

        // find non-field clauses

        List<Clause>normalClauses =new ArrayList<Clause>(clauses.size());

        for (Clauseclause :clauses) {

          if (clause.field !=null ||clause.isPhrase)continue;

          // check for keywords “AND,OR,TO”

          if (clause.isBareWord()) {

            String s =clause.val.toString();

            // avoid putting explict operators in the phrase query

            if (“OR”.equals(s) ||”AND”.equals(s) ||”NOT”.equals(s) || “TO”.equals(s))continue;

          }

          normalClauses.add(clause);

        }

 

        // full phrase…

        addShingledPhraseQueries(query, normalClauses, phraseFields, 0,

                                tiebreaker,pslop);

        // shingles…

        addShingledPhraseQueries(query, normalClauses, phraseFields2, 2, 

                                tiebreaker,pslop);

        addShingledPhraseQueries(query, normalClauses, phraseFields3, 3,

                                tiebreaker,pslop);

 

////下面是dismax获取短语查询的作法:

  1. protected Query getPhraseQuery(String userQuery, SolrPluginUtils.DisjunctionMaxQueryParser pp) throws ParseException { 
  2.   String userPhraseQuery = userQuery.replace(“\””“”); 
  3.   return pp.parse(“\”” + userPhraseQuery + “\””); 

下面是edismax的作法:

  1. private void addShingledPhraseQueries(final BooleanQuery mainQuery,   
  2.                                       final List<Clause> clauses, 
  3.                                       final Map<String,Float> fields, 
  4.                                       int shingleSize, 
  5.                                       final float tiebreaker, 
  6.                                       final int slop)   
  7.   throws ParseException {     
  8.   if (null == fields || fields.isEmpty() ||   
  9.       null == clauses || clauses.size() <= shingleSize )   
  10.     return
  11.   if (0 == shingleSize) shingleSize = clauses.size(); 
  12.   final int goat = shingleSize-1// :TODO: better name for var?  
  13.   StringBuilder userPhraseQuery = new StringBuilder(); 
  14.     for (int i=0; i < clauses.size() – goat; i++) { 
  15.       userPhraseQuery.append(‘”‘); 
  16.       for (int j=0; j <= goat; j++) { 
  17.         userPhraseQuery.append(clauses.get(i + j).val); 
  18.         userPhraseQuery.append(‘ ‘); 
  19.       } 
  20.       userPhraseQuery.append(‘”‘); 
  21.       userPhraseQuery.append(‘ ‘); 
  22.     } 
  23.     ExtendedSolrQueryParser pp = 
  24.       new ExtendedSolrQueryParser(this, IMPOSSIBLE_FIELD_NAME); 
  25.     pp.addAlias(IMPOSSIBLE_FIELD_NAME, tiebreaker, fields); 
  26.     pp.setPhraseSlop(slop); 
  27.     pp.setRemoveStopFilter(true);  // remove stop filter and keep stopwords  
  28.     pp.makeDismax = true;   
  29.     pp.minClauseSize = 2;   
  30.     Query phrase = pp.parse(userPhraseQuery.toString()); 
  31.     if (phrase != null) { 
  32.       mainQuery.add(phrase, BooleanClause.Occur.SHOULD); 
  33.     } 

edismax技术另一个重要的boost查询,

boost查询也是不会影响搜索结果数,但是影响排序,主要作用是将最后得分以相乘的方式作用于score,函数的解析跟bf差不多。

  1. //  
  2.    // create a boosted query (scores multiplied by boosts)  
  3.    //  
  4.    Query topQuery = query; 
  5.    multBoosts = solrParams.getParams(“boost”); 
  6.    if (multBoosts!=null && multBoosts.length>0) { 
  7.  
  8.      List<ValueSource> boosts = new ArrayList<ValueSource>(); 
  9.      for (String boostStr : multBoosts) { 
  10.        if (boostStr==null || boostStr.length()==0continue
  11.        Query boost = subQuery(boostStr, FunctionQParserPlugin.NAME).getQuery(); 
  12.        ValueSource vs; 
  13.        if (boost instanceof FunctionQuery) { 
  14.          vs = ((FunctionQuery)boost).getValueSource(); 
  15.        } else { 
  16.          vs = new QueryValueSource(boost, 1.0f); 
  17.        } 
  18.        boosts.add(vs); 
  19.      } 
  20.  
  21.      if (boosts.size()>1) { 
  22.        ValueSource prod = new ProductFloatFunction(boosts.toArray(new ValueSource[boosts.size()])); 
  23.        topQuery = new BoostedQuery(query, prod); 
  24.      } else if (boosts.size() == 1) { 
  25.        topQuery = new BoostedQuery(query, boosts.get(0)); 
  26.      } 
  27.    } 

可以看到最后不是一个BooleanQuery,而是一个BoostedQuery。

它就是简单处理子查询的分值再与函数查询的分值相乘返回 :主要的score方法如下:

  1. public float score() throws IOException { 
  2.   float score = qWeight * scorer.score() * vals.floatVal(scorer.docID()); 
  3.   return score>Float.NEGATIVE_INFINITY ? score : -Float.MAX_VALUE; 

 

 

赞(0) 打赏
转载请注明出处:服务器评测 » Solr 的edismax与dismax比较与分析
分享到: 更多 (0)

听说打赏我的人,都进福布斯排行榜啦!

支付宝扫一扫打赏

微信扫一扫打赏