Solr PostFilter优化查询性能

标签: solr postfilter 优化 | 发表时间:2017-02-07 14:20 | 作者:mozhenghua
出处:http://www.iteye.com

背景

       实际业务场景中,有时会需要两阶段过滤,最终的搜索结果是在前一个搜索结果上进一步搜索而得到的(search-within-search)的特性。

       假设,最终搜索结果集是由(A AND B)两个条件对应的命中结果集求交而得到的。如果A条件对应的文档集合非常小(大概不超过300个),而B条件对应的文档集合非常大。在这样的场景下在solr中使用二阶段过滤的方式来查询就再合适不过了。

 

详细实现

   第一阶段,通过A求得命中结果集合,然后第二节点在第一阶段基础上再进行过滤。

对于第一阶段没有什么好说了了,只要在solr中设置普通参数q就能实现,关键是第二阶段过滤。

 

   在已经得到的命中结果集合上继续进行搜索缩小结果集合的方法其实就是早期Lucene版本中的Filter,但是不知道为什么,在高版本的Lucene中已经把Filter从lucene中去掉了,完全由collector链取代了。(可能是觉得Filter和Collector的功能重合了)

    首先要使用org.apache.solr.search.PostFilter, 接口说明如下:

/** The PostFilter interface provides a mechanism to further filter documents
 * after they have already gone through the main query and other filters.
 * This is appropriate for filters with a very high cost.
 * <p>
 * The filtering mechanism used is a {@link DelegatingCollector}
 * that allows the filter to not call the delegate for certain documents,
 * thus effectively filtering them out.  This also avoids the normal
 * filter advancing mechanism which asks for the first acceptable document on
 * or after the target (which is undesirable for expensive filters).
 * This collector interface also enables better performance when an external system
 * must be consulted, since document ids may be buffered and batched into
 * a single request to the external system.
 * <p>
 * Implementations of this interface must also be a Query.
 * If an implementation can only support the collector method of
 * filtering through getFilterCollector, then ExtendedQuery.getCached()
 * should always return false, and ExtendedQuery.getCost() should
 * return no less than 100.
 */

 很重要的一点,在子类中需要设置cache为false,cost不能小于100,对应的代码为SolrIndexSearcher中的+getProcessedFilter()方法中的一小段:

if (q instanceof ExtendedQuery) {
        ExtendedQuery eq = (ExtendedQuery)q;
        if (!eq.getCache()) {
          if (eq.getCost() >= 100 && eq instanceof PostFilter) {
            if (postFilters == null) postFilters = new ArrayList<>(sets.length-end);
            postFilters.add(q);
          } else {
            if (notCached == null) notCached = new ArrayList<>(sets.length-end);
            notCached.add(q);
          }
          continue;
        }
} 

 当Query对象满足eq.getCache()为false,cost>=100,且PostFilter对象之后会把query对象放到postFilters list中以备后用。

 

 

另外,加之lucene高版本中,加入了docValue这一特性,使得在第二阶段中通过docid求对应field内容变得可行了,以前没有docvalue的时候,只能讲field的值通过fieldCache的方式缓存到内存中,现在使用docValue大大降低了内存的开销。

 

构建PostFilterQuery:

 

public  class PostFilterQuery extends ExtendedQueryBase implements PostFilter {
		private final boolean exclude;
		private final Set<String> items;
		private final String field;

		public PostFilterQuery(boolean exclude, Set<String> items, String field) {
			super();
			this.exclude = exclude;
			this.items = items;
			this.field = field;
		}
		@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;
		}

		public int getCost() {
			return Math.max(super.getCost(), 100);
		}

		@Override
		public DelegatingCollector getFilterCollector(IndexSearcher searcher) {
			return new DelegatingCollector() {
				private SortedDocValues docValue;
				@Override
				public void collect(int doc) throws IOException {
					int order = this.docValue.getOrd(doc);
					if (order == -1) {
						if (exclude) {
							super.collect(doc);
						}
						return;
					}
					BytesRef ref = this.docValue.lookupOrd(order);
					if (items.contains(ref.utf8ToString())) {
						if (!exclude) {
							super.collect(doc);
						}
					} else {
						if (exclude) {
							super.collect(doc);
						}
					}
				}

				@Override
				protected void doSetNextReader(LeafReaderContext context) throws IOException {
					super.doSetNextReader(context);
					this.docValue = DocValues.getSorted(context.reader(), field);
				}
			};
		}

	}

 该类中构造函数参数传入了三个值的意义:

 

  1. boolean exclude:使用排除过滤还是包含过滤
  2. Set<String> items:需要过滤的item集合
  3. String field:通过Document文档上的那个field来过滤。

为了让这个Query类在查询的时候生效,需要写一个queryParserPlugin:

public class PostFilterQParserPlugin extends QParserPlugin {

	@Override
	@SuppressWarnings("all")
	public void init(NamedList args) {
	}

	@Override
	public QParser createParser(String qstr, SolrParams localParams, SolrParams params,
			SolrQueryRequest req) {
		boolean exclude = localParams.getBool("exclude");
		String field = localParams.get(CommonParams.FIELD);
		if (field == null) {
			throw new IllegalArgumentException(
					"field:" + field + " has not been define in localParam");
		}
		Set<String> items = Sets.newHashSet(StringUtils.split(qstr, ','));
		final PostFilterQuery q = new PostFilterQuery(exclude, items, field);
		return new QParser(qstr, localParams, params, req) {
			@Override
			public Query parse() throws SyntaxError {
				return q;			}
		};
	}}

 将这个plugin配置solr-config.xml中:

<queryParser name="postfilter" class="com.dfire.tis.solrextend.queryparse.PostFilterQParserPlugin" />    

 

    接下来就是在Solr客户端查询过程中使用了,以下是一个例子:

        SolrQuery query = new SolrQuery();
		
	query.setQuery("customerregister_id:193d43b1734245f5d3bf35092dbb3a40");
	query.addFilterQuery("{!postfilter f=menu_id exclude=true}000008424a4234f0014a5746c2cd1065,000008424a4234f0014a5746c2cd1065");
	SimpleQueryResult<Object> result = client.query("search4totalpay",
				"00000241", query, Object.class);
	System.out.println("getNumberFound:" + result.getNumberFound());

 

总结

  使用postfilter在特定场景下可以大大提高查询效率,不妨试试吧!



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [solr postfilter 优化] 推荐:

Solr PostFilter优化查询性能

- - 开源软件 - ITeye博客
       实际业务场景中,有时会需要两阶段过滤,最终的搜索结果是在前一个搜索结果上进一步搜索而得到的(search-within-search)的特性.        假设,最终搜索结果集是由(A AND B)两个条件对应的命中结果集求交而得到的. 如果A条件对应的文档集合非常小(大概不超过300个),而B条件对应的文档集合非常大.

如何大幅优化solr的查询性能

- - 鲁塔弗的博客
提升软件性能,通常喜欢去调整各种启动参数,这没有多大意义,小伎俩. 性能优化要从架构和策略入手,才有可能得到较大的收益. Solr的查询是基于Field的,以Field为基本单元,例如一个文章站要索引. 查询参数: q=title:big && content:six. Solr会顺序执行两次 field查询 ,这个开销非常大.

Solr SpellCheck 应用

- - 开源软件 - ITeye博客
通过对各类型的SpellCheck组件学习,完成项目拼写检查功能. 本文使用基于拼写词典的实现方式,solr版本为5.3.0. SpellCheck 简述. 拼写检查是对用户错误输入,响应正确的检查建议. 比如输入:周杰轮,响应:你是不是想找 周杰伦. Solr的拼写检查大致可分为两类,基于词典与基于Solr索引.

Solr DocValues详解

- - 企业架构 - ITeye博客
什么是docValues. docValues是一种记录doc字段值的一种形式,在例如在结果排序和统计Facet查询时,需要通过docid取字段值的场景下是非常高效的. 为什么要使用docValues. 这种形式比老版本中利用fieldCache来实现正排查找更加高效,更加节省内存. 倒排索引将字段内存切分成一个term列表,每个term都对应着一个docid列表,这样一种结构使得查询能够非常快速,因为term对应的docid是现成就有的.

solr的使用

- - Web前端 - ITeye博客
solr的原理不和大家一一讲述,主要讲solr在使用过程中的注意事项.  首先是安装solr,安装步骤省略. (不要说我懒,安装步骤导出都是. 成功之后 需要在solr里面建立一个针对你的业务的服务,我想建立一个叫做discuz的服务. 然后你在你的solr目录 :solr-5.5.3/server/solr/  下看见了discuz   ,这是你刚刚创建的,针对某一业务的整个搜索配置都是在这个目录下配置的.

Solr调优参考

- - 淘宝网通用产品团队博客
共整理三部分,第一部分Solr常规处理,第二部分针对性性处理,前者比较通用,后者有局限性. 务必根据具体应用特性,具体调节参数,对比性能. 具体应用需要全面去把控,各个因素一起起作用. 第一部分. E文连接 http://wiki.apache.org/solr/SolrPerformanceFactors.

Solr之缓存篇

- - 淘宝网综合业务平台团队博客
Solr在Lucene之上开发了很多Cache功能,从目前提供的Cache类型有:. 而每种Cache针对具体的查询请求进行对应的Cache. 本文将从几个方面来阐述上述几种Cache在Solr的运用,具体如下:. (1)Cache的生命周期. (2)Cache的使用场景. (3)Cache的配置介绍.

Solr主从备份

- - 研发管理 - ITeye博客
SOLR复制模式,是一种在分布式环境下用于同步主从服务器的一种实现方式,因之前提到的基于rsync的SOLR不同方式部署成本过高,被SOLR1.4版本所替换,取而代之的就是基于HTTP协议的索引文件传输机制,该方式部署简单,只需配置一个文件即可. 以下讲解具体操作步骤: . 步骤分主服务器和从服务器,允许有多个从服务器,即从服务器的配置一样.

solr相似匹配

- - CSDN博客推荐文章
相似匹配   在我们使用网页搜索时,会注意到每一个结果都包含一个 “相似页面” 链接,单击该链接,就会发布另一个搜索请求,查找出与起初结果类似的文档. Solr 使用 MoreLikeThisComponent(MLT)和 MoreLikeThisHandler 实现了一样的功能. 如上所述,MLT 是与标准 SolrRequestHandler 集成在一起的;MoreLikeThisHandler 与 MLT 结合在一起,并添加了一些其他选项,但它要求发布一个单一的请求.

Solr与Mysql集成指南

- sun - 草根网:互联网界的读者文摘
在《企业级搜索引擎Solr使用入门指南》及《企业级搜索引擎Solr交流》中对Solr的使用做了简单介绍. 在数据库驱动的应用中,当时采....