`
mozhenghua
  • 浏览: 317960 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Solr PostFilter优化查询性能

    博客分类:
  • solr
阅读更多

背景

       实际业务场景中,有时会需要两阶段过滤,最终的搜索结果是在前一个搜索结果上进一步搜索而得到的(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在特定场景下可以大大提高查询效率,不妨试试吧!

分享到:
评论

相关推荐

    solr索引和检索性能测试报告

    solr在做检索的时候时常需要得知他的性能参数,此处使用8G内存,双核处理器测试的结果

    基于Solr的海量日志信息查询性能优化的研究

    随着传统互联网和移动互联网的持续发展,网络带给我们的...目前一些搜索公司在公共互联网领域提供了很好的解决方案,但是企业或者政府机关内部相关信息往往需要应用独立的搜索系统,Solr Cloud则是很好的一个平台选择。

    solr的优化实例1

    solr的优化实例1 用户开发人员参考,tomcat6 solr3.5架构使用

    solr7.0性能测试报告

    该资源为本人亲自整理的报告,多线程代码未给出,不是太难我相信你能搞定。

    基于Solr的多表join查询加速方法

    NULL 博文链接:https://mozhenghua.iteye.com/blog/2275318

    最新springboot solr查询

    Solr是一个开源搜索平台,用于构建搜索应用程序。Solr可以和Hadoop一起使用。由于Hadoop处理大量数据,Solr帮助我们从这么大的...总之,Solr是一个可扩展的,可部署,搜索/存储引擎,优化搜索大量以文本为中心的数据。

    Solr权威指南-上卷

    Solr的多种性能优化技巧,如索引的性能优化、缓存的性能 优化、查询的性能优化、JVM和Web容器的优化,以及操作系统级别的优化。 拓展知识中首先讲解了Solr的一些比较生僻的知识点,如伪域、多语种索引支持、安全认证...

    solr查询语法

    solr查询语法,solr常用查询语法汇总。

    Solr权威指南-下卷

    Solr的多种性能优化技巧,如索引的性能优化、缓存的性能 优化、查询的性能优化、JVM和Web容器的优化,以及操作系统级别的优化。 拓展知识中首先讲解了Solr的一些比较生僻的知识点,如伪域、多语种索引支持、安全认证...

    已编译版本solr-8.11.2.tgz

    并对其进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置,可扩展并对查询性能进行了优化,提供了一个完善的功能管理页面,是一款非常优秀的全文搜索引擎。 3,solr工作方式 文档通过http利用xml加...

    Solr索引测试报告

    Solr 索引 测试报告 性能

    java solr solrj 带账号密码增量查询添加索引

    主要讲解了 solr客户端如何调用带账号密码的solr服务器调用,实现添加索引和查询索引,以及分组查询

    针对Solr的SQL查询引擎

    Solr-SQL为Solr Cloud提供了SQL接口,开发人员可以通过JDBC协议在Solr Cloud上运行。同时,solr-sql是用于solr的Apache Calcite(见 http://calcite.apache.org)适配器。solr-sql 是用 Scala 编写的,它可以生成像 ...

    solr.war包solr.war包solr.war包solr.war包solr.war包

    solr.warsolr.war包solr.war包solr.war包solr.war包solr.war包solr.war包solr.war包solr.war包solr.war包solr.war包solr.war包solr.war包solr.war包solr.war包solr.war包solr.war包solr.war包solr.war包solr.war包...

    solr创建索引并查询

    solr创建索引并查询,希望能够帮助有需要的人。。。

    solr.md Solr是一个高性能,采用Java开发

    solr的使用

    solr安装教程,安装包

    本人最近学习了传智播客的一个电商项目...同时对其进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展并对查询性能进行了优化,并且提供了一个完善的功能管理界面,是一款非常优秀的全文搜索引擎

    java进阶Solr从基础到实战

    2. Solr查询 3. Facet查询 4. Group查询 5. 高亮查询 6. Suggest查询 7. SolrJ 章节三:Solr高级(上) 1. Solr Cloud 2. 函数查询 3. 地理位置查询 4. JSON Facet 章节四:Solr高级(下) 1. 深度分页 2. Solr Join...

    apache solr1.3.0开发包源码及文档

    Apache Solr 1.3.0发布,Apache Solr是一个性能强大的,基于 Lucene 的全文搜索的 开源企业级搜索服务器,拥有XML/HTTP,JSON APIs,hit highlighting, faceted search, caching, replication,web管理界面等很多功能...

    Solr优化案例分析

    本文来自于csdn,本文主要从Solr系统层面和索引字段优化两个方面进行优化以及展开一下的案例分析。随着umc接入主机的数量越来越多,每天产生的syslog日志数量也在剧增,之前一天产生syslog数量才不到1W,随着整个...

Global site tag (gtag.js) - Google Analytics