之前一直用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 {
Map 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;
}
2、addBoostQuery:
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;
Map ff = 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同样如此。