fq(Filter Query)参数
fq 参数定义了一个查询,可以用来限制可以返回的文档的超集,而不影响 score。这对于加快复杂查询非常有用,因为指定的查询 fq 是独立于主查询而被缓存的。当以后的查询使用相同的过滤器时,会有一个缓存命中,过滤器结果从缓存中快速返回。
使用该 fq 参数时,请记住以下几点:
- 该 fq 参数可以在查询中多次指定
- filter 查询可能涉及复杂的 Boolean 查询
- 每个过滤器查询的文档集都是独立缓存的
- 还可以在 fq 内部使用 filter(condition) 语法来单独缓存子句, 以及在其他情况下,实现缓存的筛选器查询的联合
- 与所有参数一样:URL 中的特殊字符需要正确转义并编码为十六进制值
cache 参数
Solr 默认缓存所有查询的结果并过滤查询。
要禁用结果缓存,请设置 cache=false 参数。
可以使用 成本(cost)选项来控制计算非缓存筛选器查询的顺序。这使您可以在昂贵成本的非缓存过滤器之前订购更便宜成本的非缓存过滤器。
对于成本非常高的过滤器,如果 cache=false 并且 cost>=100 和查询实现了 PostFilter 接口,则将从该查询请求收集器,并在匹配主查询和所有其他过滤器查询后用于过滤文档。
可以有多个后置过滤器;他们也按成本cost排序。
SolrIndexSearcher
SolrIndexSearcher类中getProcessedFilter方法定义了PostFilter的使用逻辑
public ProcessedFilter getProcessedFilter(DocSet setFilter, List<Query> queries) throws IOException {
ProcessedFilter pf = new ProcessedFilter();
if (queries==null || queries.size()==0) {
if (setFilter != null)
pf.filter = setFilter.getTopFilter();
return pf;
}
........省略部分代码..........
for (Query q : queries) {
if (q instanceof ExtendedQuery) {
ExtendedQuery eq = (ExtendedQuery)q;
if (!eq.getCache()) {
if (eq.getCost() >= 100 && eq instanceof PostFilter) {
if (postFilters == null) postFilters = new ArrayList<Query>(sets.length-end);
postFilters.add(q);
} else {
if (notCached == null) notCached = new ArrayList<Query>(sets.length-end);
notCached.add(q);
}
continue;
}
}
........省略部分代码..........
}
........省略部分代码..........
if (postFilters != null) {
Collections.sort(postFilters, sortByCost);
for (int i=postFilters.size()-1; i>=0; i--) {
DelegatingCollector prev = pf.postFilter;
pf.postFilter = ((PostFilter)postFilters.get(i)).getFilterCollector(this);
if (prev != null) pf.postFilter.setDelegate(prev);
}
}
return pf;
}
分析代码可知要使用自定义PostFilter,必须cache=false 并且 cost>=100
编写自定义PostFilter
1) schema.xml定义
id 和 spopulation 启用docValues,在过滤器中可通过docid取 id 和 spopulation 值
<field name="id" type="string" indexed="true" stored="true" multiValued="false" docValues="true" />
<field name="sname" type="string" indexed="true" stored="true" multiValued="false" />
<field name="slocation" type="string" indexed="true" stored="true" multiValued="false" />
<field name="sdes" type="text_ik" indexed="true" stored="true" multiValued="false" />
<field name="sarea" type="string" indexed="true" stored="true" multiValued="false" />
<field name="spopulation" type="long" indexed="true" stored="true" multiValued="false" docValues="true" />
2)过滤逻辑
假设过滤查询结果中 spopulation>某个值 and id 不能空的
3) 编写过滤器
public class MyFilterQParserPlugin extends QParserPlugin {
private final Logger log = LoggerFactory.getLogger(MyFilterQParserPlugin.class);
public MyFilterQParserPlugin() {
}
@Override
public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
log.info("MyFilterQParserPlugin createParser start !!!");
// 是否启用过滤逻辑
boolean enable = localParams.getBool("enable");
// 过滤条件最小值
String min = localParams.get("min");
if (StringUtils.isBlank(min)) {
throw new IllegalArgumentException(
"field:" + enable + " has not been define in localParam");
}
log.info("MyFilterQParserPlugin qstr=" + qstr);
log.info("MyFilterQParserPlugin enable=" + String.valueOf(enable));
log.info("MyFilterQParserPlugin min=" + min);
final MyFilterQuery q = new MyFilterQuery(enable, min);
return new QParser(qstr, localParams, params, req) {
@Override
public Query parse() throws SyntaxError {
return q;
}
};
}
@Override
public void init(NamedList args) {
}
}
public class MyFilterQuery extends ExtendedQueryBase implements PostFilter {
private final Logger log = LoggerFactory.getLogger(MyFilterQuery.class);
private final String min;
private final Boolean enable;
public MyFilterQuery(Boolean enable,String min) {
this.enable = enable;
this.min = min;
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
@Override
public boolean equals(Object obj) {
return this == obj;
}
@Override
public void setCache(boolean cache) {
}
@Override
public boolean getCache() {
return false;
}
@Override
public int getCost() {
return Math.max(super.getCost(), 100);
}
@Override
public DelegatingCollector getFilterCollector(IndexSearcher searcher) {
try {
return new DelegatingCollector() {
AtomicReader reader;
@Override
public void collect(int doc) throws IOException {
//获取id的docValues
SortedDocValues idDocValue = this.reader.getSortedDocValues("id");
BytesRef byteRef = new BytesRef();
//根据docid取值
idDocValue.get(doc, byteRef);
String id = byteRef.utf8ToString();
Long spopulation=0L;
//获取spopulation的docValues
NumericDocValues spopulationDocValue = this.reader.getNumericDocValues("spopulation");
//根据docid取值
spopulation=spopulationDocValue.get(doc);
/**
* 业务处理,id不为空,并且 spopulation 大于最小值
*/
if(enable) {
if (StringUtils.isNotEmpty(id) && spopulation >= Long.valueOf(min)) {
// 保留记录
super.collect(doc);
}
}
else{
super.collect(doc);
}
}
@Override
public void setNextReader(AtomicReaderContext context) throws IOException {
super.setNextReader(context);
this.reader = context.reader();
}
};
} catch (Exception var3) {
this.log.error("Exception{}", var3);
return null;
}
}
}
自定义过滤器有2个自定义参数:
- enable 是否启用过滤逻辑
- min 过滤的最小值
过滤器使用方式:
fq={!myFilter min=1000000000 enable=true}查询字符串
4) solrconfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<config>
<queryParser name="myFilter" class="cn.chy.plugin.postfilter.MyFilterQParserPlugin" />
....省略部分配置.....
</config>
5)jar包
solr默认加载core目录中lib目录中的jar包,将jar包放到lib目录即可
6)启动加载solrcore配置
7) 查询
8) enable:false
不启用过滤
9) enable:true
启用过滤