solr 的edismax與dismax比較與分析

edismax支持boost函數與score相乘做爲,而dismax只能使用bf做用效果是相加,因此在處理多個維度排序時,score其實也應該是其中一個維度 ,用相加的方式處理調整麻煩。java

而dismax的實現代碼邏輯比較簡單,看起來比較易理解,edismax是它的增強版,實際上是改變了很多。。好比在如下:api


先看看dismax的解析主要實現思路:app

首先取出搜索字段名qfide

將最終解析成一個BooleanQuery函數

先解析主mainQuery:ui

  1.  用戶主要是搜索串的解析
  2. altQuery解析處理,看是否使用用戶定義的後備搜索串
  3. PhraseQuery解析組裝
再解析bq查詢,主要是額外加分的查詢,不會影響搜索結果數,只會影響排序

再則是bf解析,函數搜索最後會以加的方式做用於文檔評分

看主要代碼更清晰:this


[java]  view plain copy
  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;  
  22. }  



edismax的主要實現思路跟dismax差很少,如下是一些主要差異之處:spa

edismax解析含有+,OR,NOT,-語法時,就會忽略掉使用MM。.net

如下是主要代碼實現:orm

統計搜索串中+,OR ,NOT,-語法元個數

[java]  view plain copy
  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就會失效

[java]  view plain copy
  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.   }  
  7. }  






短語查詢,先找出普通的查詢,原來就是短語查詢的、或者屬於「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(querynormalClausesphraseFields0

                                 tiebreaker,pslop);

        // shingles...

        addShingledPhraseQueries(querynormalClausesphraseFields22,  

                                 tiebreaker,pslop);

        addShingledPhraseQueries(querynormalClausesphraseFields33,

                                 tiebreaker,pslop);


////下面是dismax獲取短語查詢的做法:


[java]  view plain copy
  1. protected Query getPhraseQuery(String userQuery, SolrPluginUtils.DisjunctionMaxQueryParser pp) throws ParseException {  
  2.   String userPhraseQuery = userQuery.replace("\"""");  
  3.   return pp.parse("\"" + userPhraseQuery + "\"");  
  4. }  

下面是edismax的做法:

[java]  view plain copy
  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.     }  
  34. }  



edismax技術另外一個重要的boost查詢,


boost查詢也是不會影響搜索結果數,可是影響排序,主要做用是將最後得分以相乘的方式做用於score,函數的解析跟bf差很少。

[java]  view plain copy
  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方法以下:


[java]  view plain copy
  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;  
  4. }  




轉貼請聲明來源:http://blog.csdn.net/duck_genuine/article/details/8060026

相關文章
相關標籤/搜索