之前一直用dismax,但是一直没有看看他到底是怎么实现的,今天终于看了一遍源码,记个笔记,方便以后查阅。
当我们使用defType=dismax的时候就会使用到DisMaxQParser这个类,他是一个QParser,用于根据用户的输入解析为一个Query类的实例。我们看看他的解析方法:
@Override public Query parse() throws SyntaxError { parsed = true; SolrParams solrParams = SolrParams.wrapDefaults(localParams, params);// 将localParams域params合并。优先使用localParams queryFields = parseQueryFields(req.getSchema(), solrParams);//获得所有的qf,即要匹配的域已经各个域的boost,queryFields是个map,key是String,value是float。 /* * the main query we will execute. we disable the coord because this query is an artificial construct */ BooleanQuery query = new BooleanQuery(true);//最后形成的BooleanQuery, boolean notBlank = addMainQuery(query, solrParams);//向最后的BooleanQuery中添加mainQuery,mainQuery包含两部分,一个是匹配的qf,一个是匹配的pf,等会再看 if (!notBlank) return null; addBoostQuery(query, solrParams);//向最后的booleanQuery中添加boostQuery,用于提高某些文档的得分 addBoostFunctions(query, solrParams);//向最后的booleaQuery中添加boostFunction的query,用于提高某些文档的得分 return query; }下面我们一个一个方法的来看:
1、addMainQuery:
protected boolean addMainQuery(BooleanQuery query, SolrParams solrParams) throws SyntaxError { Map2、addBoostQuery:phraseFields = SolrPluginUtils.parseFieldBoosts(solrParams.getParams(DisMaxParams.PF));//获得pf的域以及每个域的boost float tiebreaker = solrParams.getFloat(DisMaxParams.TIE, 0.0f);//获得参数中的tie,也就是disjunctionMaxQuery中的tie,用于计算最后的得分。默认是0.0f,也就是只关心最大的得分,不关心其他的得分。 /* * a parser for dealing with user input, which will convert things to DisjunctionMaxQueries */ SolrPluginUtils.DisjunctionMaxQueryParser up = getParser(queryFields, DisMaxParams.QS, solrParams, tiebreaker);//这个是解析queryFields的queryParser,也即是产生由参数中的查询字符串和qf以及qs(query slope)形成的query的queryParser。 /* for parsing sloppy phrases using DisjunctionMaxQueries */ SolrPluginUtils.DisjunctionMaxQueryParser pp = getParser(phraseFields, DisMaxParams.PS, solrParams, tiebreaker);//这个和上面的up差不多,只不过他是产生由查询字符串,phraseFields,以及ps(phrase fileds)形成的query的queryParser。 /* * * Main User Query * * */ parsedUserQuery = null; String userQuery = getString();//前台输入的字符串。 altUserQuery = null; if (userQuery == null || userQuery.trim().length() < 1) {// 优先使用q,如果没有则使用q.alt。之后的逻辑和使用userQuery的逻辑一样。 // If no query is specified, we may have an alternate altUserQuery = getAlternateUserQuery(solrParams); if (altUserQuery == null) return false; query.add(altUserQuery, BooleanClause.Occur.MUST); } else { // There is a valid query string userQuery = SolrPluginUtils.partialEscape(SolrPluginUtils.stripUnbalancedQuotes(userQuery)).toString();//删除不对称的引号(逻辑是如果是奇数个引号,则将引号全部删了),然后过滤特殊字符(比如*、:、!、@、(、{,【,[),只保留" + -。 从这里可以看出,使用*:*是什么也搜不到的,也就是他不支持luceneQParser的普通的功能,仅仅支持+、-两个特殊的字符。 userQuery = SolrPluginUtils.stripIllegalOperators(userQuery).toString();//删掉过多的+、- parsedUserQuery = getUserQuery(userQuery, up, solrParams);//用up这个queryParser解析成query,并设置mm这个属性(也就是如果在查询字符串中有用空格分开的字符串的话,对于optional的booleanClause必须要满足的个数),因为可能parsdUserQuery是一个booleanquery query.add(parsedUserQuery, BooleanClause.Occur.MUST);//将解析的parsedUserQuery添加到最后的query中,设置为must,即必须出现。 Query phrase = getPhraseQuery(userQuery, pp);//这个和上面的getUserQuery是一样的,只不过没有设置mm的步骤。 if (null != phrase) { query.add(phrase, BooleanClause.Occur.SHOULD);//如果有phrase,则添加到最后的booleanQuery中,设置为optional的,即仅仅用于将那些匹配的document提高得分。 } } return true; }
protected void addBoostQuery(BooleanQuery query, SolrParams solrParams) throws SyntaxError { boostParams = solrParams.getParams(DisMaxParams.BQ);//从请求参数中获得bq boostQueries = null; if (boostParams != null && boostParams.length > 0) { boostQueries = new ArrayList<>();//最后形成的boost query。 for (String qs : boostParams) { if (qs.trim().length() == 0) continue; Query q = subQuery(qs, null).getQuery();//这个是使用默认的queryParser对qs进行解析,解析为一个query,默认的queryParser即QParserPlugin中的LuceneQParserPlugin。 boostQueries.add(q);// } } //下面是将上面形成的boost query添加到最终的booleanquery中去,用于提高命中的document的得分。 if (null != boostQueries) { if (1 == boostQueries.size() && 1 == boostParams.length) {//如果生成的boostquery仅仅有一个子query Query f = boostQueries.get(0); if (1.0f == f.getBoost() && f instanceof BooleanQuery) { /* * if the default boost was used, and we've got a BooleanQuery extract the subqueries out and use them * directly */ for (Object c : ((BooleanQuery) f).clauses()) { query.add((BooleanClause) c); } } else { query.add(f, BooleanClause.Occur.SHOULD); } } else { for (Query f : boostQueries) { query.add(f, BooleanClause.Occur.SHOULD); } } } }3、addFunctionQuery
protected void addBoostFunctions(BooleanQuery query, SolrParams solrParams) throws SyntaxError { String[] boostFuncs = solrParams.getParams(DisMaxParams.BF);//获得bf参数,即函数查询的参数, if (null != boostFuncs && 0 != boostFuncs.length) { for (String boostFunc : boostFuncs) { if (null == boostFunc || "".equals(boostFunc)) continue; Mapff = SolrPluginUtils.parseFieldBoosts(boostFunc); for (String f : ff.keySet()) { Query fq = subQuery(f, FunctionQParserPlugin.NAME).getQuery();//指定了解析器 Float b = ff.get(f); if (null != b) { fq.setBoost(b); } query.add(fq, BooleanClause.Occur.SHOULD);//将解析的query作为一个optional的添加到最终的query中 } } } }
就这样就看完了dismax,qf,qs是限定要获取的数据的,体现在他在最终的booleanQuery中是must,pf 、ps是用来增加命中的得分的,一般都是将ps设置的比qs大得多,bq也是用来增加命中的document的得分的,bf同样如此。