solr 的edismax插件扩展方式

前言

solr 通过插件的方式实现对edismax的支持。在熟悉了solr插件的执行流程后我,我们也可以对solr插件功能进行定制。实现更加强大的功能。

类图

  • ExtendedDismaxQParserPlugin

图片

  1. NamedListInitializedPlugin 作用
    NameList作为solr存储map的方式,可以看成是一个key value容器。
    NameListInitializedPlugin是可以通过NameList初始化的插件。只有一个init方法。接收NameList参数。
  2. SolrInfoMBean 作用
    提供Solr后台基础信息的Bean接口。
    3:QparserPlugin
    所有插件的父类,定义了默认方法,保存声明了所有solr已经实现的plugin。
  • ExtendedDismaxQparser

图片

ExtendDismaxQparser
描述edismax支持的配置和方法,集合了edismax所需功能的各种操作。并创建 ExtendedSolrDismaxQueryParser,为其提供职责更为简明的操作环境。是edismax语法支持类。

  • ExtendSolrQueryParser

图片

  1. QueryBuilder
    lucene 提供用于创建查询器的工厂类

可以被当做自定义解析器的子类,使得查询解析器更容易地集成到分析链中。生成查询可以定制化。
所有默认lucene提供的query在这里创建

  1. SolrQueryParserBase
    Solr继承自QueryBuilder的类。作为Solr标准查询解析器的父类。Solr对lucene的扩展,加入了 MagicFieldName RawQuery等支持。初始化时读入 schema配置。可以修改这个类来扩展schema标签功能。

  2. QueryParser
    默认的query解析器

  3. SolrQueryParser
    Solr’s 的默认查询解析器schema驱动的经典lucene查询解析方式。

  4. ExtendedSolrQueryParser
    作为ExtendedSolrQParser的内部类存在。实施最终的查询解析。

请求过程

SolrDispatchFilter(doFilter,execute)

->SolrCore.execute

->RequestHandlerBase.handleRequest

->SearchHandler.handleRequestBody
//有可能执行多个Component
//query,facet,group等等,这里每个查询特性对应每个SearchComponent
->QueryComponent.process

->SolrIndexSearcher(search,getDocListC)

描述
edismax请求到 SearchHandler 分析调用的SearchComponent链,其中QueryComponent 通过defType选择 插件ExtendedDismaxQParserPlugin 创建queryParser初始化插件paraer

QueryComponent prepare调用
QParser rqparser = QParser.getParser(rankQueryString, defType, req);
先解析出参数语法包含的解析器信息

QueryComponent 源码部分 关键方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public static QParser getParser(String qstr, String defaultParser, SolrQueryRequest req) throws SyntaxError {
...
//存在字符 {!需要分析参数需要什么样的查询解析器选定信息
if (qstr != null && qstr.startsWith(QueryParsing.LOCALPARAM_START)) {
localParams = new ModifiableSolrParams();
localParamsEnd = QueryParsing.parseLocalParams(qstr, 0, localParams, globalParams);

String val = localParams.get(QueryParsing.V);
if (val != null) {
// val was directly specified in localParams via v=<something> or v=$arg
valFollowedParams = false;
} else {
// use the remainder of the string as the value
valFollowedParams = true;
val = qstr.substring(localParamsEnd);
localParams.set(QueryParsing.V, val);
}
}
...

//localParams 语法解析出来的localParams是否需要特殊的解析器来解析查询。
if (localParams == null) {
parserName = defaultParser;
} else {
//显示的defType与解析出来的信息一起判断优先级来选定解析器名称
parserName = localParams.get(QueryParsing.TYPE,defaultParser);
qstr = localParams.get("v");
}

parserName = parserName==null ? QParserPlugin.DEFAULT_QTYPE : parserName;
//确定后通过名称获取解析插件
QParserPlugin qplug = req.getCore().getQueryPlugin(parserName);
QParser parser = qplug.createParser(qstr, localParams, req.getParams(), req);
...
return parser;
}

对edisMax查询操作,QueryComponet返回了 ExtendDismaxQparser 作为查询解析器作为后续的查询支持 。
默认解析器 lucene

扩展功能

扩展步骤:
1:新建 XXPlugin 继承 QParserPlugin
实现方法:

1
2
3
4
5
6
//定义插件名称,用于配置指定
public static String NAME = "XXXqueryPlus";
@Override
public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
return new XXXQParser(qstr, localParams, params, req);
}

2:创建XXXQParser继承QParser, XXXSolrQueryParser继承SolrQueryParser
可以采用组合方式,也可以采用内部类的方式。在XXXQParser里重写 parser()方法

1
2
3
4
5
6
7
8
@Override
public Query parse() throws SyntaxError {
//返回具体的 查询Query
//可以结合XXXSolrQueryParser方法返回.
//this.parser() --> XXSolrQueryParser.parser()。
//提交给 SolrQuerybase执行,只要根据需要重写 SolrQueryBase里的
// protected Query getFieldQuery(String field, String queryText, //boolean quoted, boolean raw)方法即可。
}

3:solrConfig里配置

1
2
3
4
5
6

<requestHandler name="standard" class="solr.SearchHandler" default="true">
<lst name="defaults">
<str name="defType">XXXqueryPlus</str>
</lst>
</requestHandler>