`
suichangkele
  • 浏览: 200309 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

solr的facet源码解读(二)——facet.field

阅读更多

facet.field比facet.query要复杂的多,参数也更多,看看代码吧,方法是:SimpleFacets.getFacetFieldCounts()

public NamedList<Object> getFacetFieldCounts() throws IOException, SyntaxError {
	NamedList<Object> res = new SimpleOrderedMap<>();
	String[] facetFs = params.getParams(FacetParams.FACET_FIELD);
	if (null == facetFs) {
		return res;
	}
 	int maxThreads = req.getParams().getInt(FacetParams.FACET_THREADS, 0);//有的情况下可以使用多个线程处理facet。参数是facet.threads,如果大于0,使用一个多线程的线程池。不过多个任务的处理是对于多个facet field来说的,如果只有一个域的facet,则这个参数设置了也没用。
	Executor executor = maxThreads == 0 ? directExecutor : facetExecutor;
	//如果小于0,表示使用一个没有限制的线程池(当然他的实现还是使用了一个类似CachedThreadPool的线程池,即最大值没有限制的,但是可以使用下面的semaphore来做一个限制,实现同样的功能)
	final Semaphore semaphore = new Semaphore((maxThreads <= 0) ? Integer.MAX_VALUE : maxThreads);
	List<Future<NamedList>> futures = new ArrayList<>(facetFs.length);//多个域的facet的结果。
	try {
		for (String f : facetFs) {//循环所有的facet field
			parseParams(FacetParams.FACET_FIELD, f);//解析这个域的facet的参数
			final String termList = localParams == null ? null : localParams.get(CommonParams.TERMS);//没有看这个情况,下文中全部假设termList=null
			final String workerKey = key;//在没有localParam的时候,具体的field
			final String workerFacetValue = facetValue;//facet的对象,也就是field的名字
			final DocSet workerBase = this.docs;//之前的查询的过程中由q和fq获得的所有的doc的id
			Callable<NamedList> callable = new Callable<NamedList>() {//这个就是要提交到线程池中的任务,用于处理某一个facet.field
				@Override
				public NamedList call() throws Exception {
					try {
						NamedList<Object> result = new SimpleOrderedMap<>();
						if (termList != null) {//不考虑这种情况。工作中没用到
							List<String> terms = StrUtils.splitSmart(termList, ",", true);
							result.add(workerKey, getListedTermCounts(workerFacetValue, workerBase, terms));
						} else {
							result.add(workerKey, getTermCounts(workerFacetValue, workerBase));//具体的方法就是这个,
						}
						return result;
					} catch (SolrException se) {
						throw se;
					} catch (Exception e) {
						throw new SolrException(ErrorCode.SERVER_ERROR,
								"Exception during facet.field: " + workerFacetValue, e);
					} finally {
						semaphore.release();//释放资源
					}
				}
			};
			RunnableFuture<NamedList> runnableFuture = new FutureTask<>(callable);
			semaphore.acquire();// may block and/or interrupt
			executor.execute(runnableFuture);// releases semaphore when done
			futures.add(runnableFuture);
		} // facetFs loop
		// Loop over futures to get the values. The order is the same as facetFs but shouldn't matter.
		for (Future<NamedList> future : futures) {
			res.addAll(future.get());
		}
		assert semaphore.availablePermits() >= maxThreads;
	} catch (InterruptedException e) {
		throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,"Error while processing facet fields: InterruptedException", e);
	} catch (ExecutionException ee) {
		Throwable e = ee.getCause();// unwrap
		if (e instanceof RuntimeException) {
			throw (RuntimeException) e;
		}
		throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,"Error while processing facet fields: " + e.toString(), e);
	}
	return res;
}

上面可以看到,对于多个facet.field是可以使用一个线程池的,将多个facet.field提交给不同的cpu并行处理,可以提高速度,控制使用多线程的参数是facet.threads,但是如果只有一个facet.field的话,就没有用了,因为他只有一个任务。

下面最关键的就是getTermCounts方法了

/**
 * 在base的范围内进行facet <br/>
 * Term counts for use in field faceting that resepects the appropriate mincount
 * @see FacetParams#FACET_MINCOUNT
 */
public NamedList<Integer> getTermCounts(String field, DocSet base) throws IOException {
	Integer mincount = params.getFieldInt(field, FacetParams.FACET_MINCOUNT);//每个term匹配的doc的最少的数量
	return getTermCounts(field, mincount, base);
}

 

/**
 * @param filed:facet的域
 * @param mincount :符合条件的term匹配最少的doc的值
 * @param base 由q和fq确定的集合
 */
private NamedList<Integer> getTermCounts(String field, Integer mincount, DocSet base) throws IOException {
	
	int offset = params.getFieldInt(field, FacetParams.FACET_OFFSET, 0);//偏移量
	int limit = params.getFieldInt(field, FacetParams.FACET_LIMIT, 100);//要多少个
	if (limit == 0)
		return new NamedList<>();
	
	if (mincount == null) {
		// 判断要不要收集没有doc匹配的term的值
		Boolean zeros = params.getFieldBool(field, FacetParams.FACET_ZEROS);
		mincount = (zeros != null && !zeros) ? 1 : 0;
	}
	// 是不是要收集null的值,有的doc在这个域中没有值,用null表示,如果是true,则要返回一个null的term命中的doc的数量。
	boolean missing = params.getFieldBool(field, FacetParams.FACET_MISSING, false);
	// default to sorting if there is a limit.    facet结果的排序,如果没有指定sort,如果limit>0,则使用count排序,即命中的doc的数量,否则使用facet到的值得字面值排序
	String sort = params.getFieldParam(field, FacetParams.FACET_SORT,limit > 0 ? FacetParams.FACET_SORT_COUNT : FacetParams.FACET_SORT_INDEX);
	String prefix = params.getFieldParam(field, FacetParams.FACET_PREFIX);//必须匹配的前缀的值

	NamedList<Integer> counts;
	SchemaField sf = searcher.getSchema().getField(field);
	FieldType ft = sf.getType();

	// 确定faceting method
	final String methodStr = params.getFieldParam(field, FacetParams.FACET_METHOD);
	FacetMethod method = null;
	if (FacetParams.FACET_METHOD_enum.equals(methodStr)) {
		method = FacetMethod.ENUM;//忽略这个情况,没有遇到过
	} else if (FacetParams.FACET_METHOD_fcs.equals(methodStr)) {
		method = FacetMethod.FCS;
	} else if (FacetParams.FACET_METHOD_fc.equals(methodStr)) {
		method = FacetMethod.FC;
	}
	if (method == FacetMethod.ENUM && TrieField.getMainValuePrefix(ft) != null) {
		method = sf.multiValued() ? FacetMethod.FC : FacetMethod.FCS;
	}
	if (method == null && ft instanceof BoolField) {
		// Always use filters for booleans... we know the number of values is very small.
		method = FacetMethod.ENUM;
	}
	//是不是多值域或者分词的。
	final boolean multiToken = sf.multiValued() || ft.multiValuedFieldCache();
	if (method == null && ft.getNumericType() != null && !sf.multiValued()) {//如果没有指定方法,且是单值域的数字类型,优先使用FCS.
		// the per-segment approach is optimal for numeric field types since there are no global ords to merge and no need to create an expensive top-level reader
		method = FacetMethod.FCS;//fcs(只能facet单值域的域)
	}
	if (ft.getNumericType() != null && sf.hasDocValues()) {//如果是数字类型的且有docVaue,推荐使用FCS
		// only fcs is able to leverage the numeric field caches
		method = FacetMethod.FCS;//
	}
	if (method == null) {//如果上面的都没有进入,默认使用FC
		method = FacetMethod.FC;
	}
	if (method == FacetMethod.FCS && multiToken) {//FCS不能处理多值域的,所以要切换为FC
		method = FacetMethod.FC;
	}
	if (method == FacetMethod.ENUM && sf.hasDocValues()) {
		method = FacetMethod.FC;
	}
	if (params.getFieldBool(field, GroupParams.GROUP_FACET, false)) {//没有用到这个功能,忽略
	        counts = getGroupedCounts(searcher, base, field, multiToken, offset, limit, mincount, missing, sort,prefix);
	} else {
		assert method != null;
		switch (method) {
		case ENUM:
			assert TrieField.getMainValuePrefix(ft) == null;
			counts = getFacetTermEnumCounts(searcher, base, field, offset, limit, mincount, missing, sort, prefix);
			break;
		case FCS://只能处理单值域且不分词的。
			assert !multiToken;
			if (ft.getNumericType() != null/* && !sf.multiValued()*/) {//这个是我自己注释的。因为如果使用FCS且getNumericType !=null 的话,一定不会是multiValued的,所以第二个条件是没用的。
				if (prefix != null && !prefix.isEmpty()) {
					throw new SolrException(ErrorCode.BAD_REQUEST, FacetParams.FACET_PREFIX + " is not supported on numeric types");
				}
				// 这个会尽可能的不使用读取词典表,除非是要返回的结果不够了且使用了minCount=0的参数
				counts = NumericFacets.getCounts(searcher, base, field, offset, limit, mincount, missing, sort);
			} else {//单值域的facet
				PerSegmentSingleValuedFaceting ps = new PerSegmentSingleValuedFaceting(searcher, base, field, offset, limit, mincount, missing, sort, prefix);
				Executor executor = threads == 0 ? directExecutor : facetExecutor;
				ps.setNumThreads(threads);
				counts = ps.getFacetCounts(executor);
			}
			break;
		case FC:
			if (sf.hasDocValues()) {//如果有docValue,
				counts = DocValuesFacets.getCounts(searcher, base, field, offset, limit, mincount, missing, sort, prefix);
			} else if (multiToken || TrieField.getMainValuePrefix(ft) != null) {//如果没有docValue且是多值域的
				UnInvertedField uif = UnInvertedField.getUnInvertedField(field, searcher);
				counts = uif.getCounts(searcher, base, offset, limit, mincount, missing, sort, prefix);
			} else {
				counts = getFieldCacheCounts(searcher, base, field, offset, limit, mincount, missing, sort, prefix);
			}
			break;
		default:
			throw new AssertionError();
		}
	}
	return counts;
}

 从上面的代码来看,如果没有指定类型,如果是单值域的数字,则优先使用FCS,否则使用FC.如果指定了使用FCS,但是如果域是多值域的也会使用FC,所以我们只需要看一下FCS的数字的情况和FC的情况即可。在接下来的几篇博客中,会仔细的看下FCS的数字和非数字的两种情况以及FCS的含有docValue的情况(不含有docValue的情况不看了,工作中都是含有docValue的)。

  

分享到:
评论

相关推荐

    solrj的facet查询总结

    Solr 的默认 requestHandler(org.apache.solr.handler.component.SearchHandler)已经包含了 Facet 组件(org.apache.solr.handler.component.FacetComponent)。如果自定义 requestHandler 或者对默认的 ...

    solr facet 笔记

    Solr 提供了多种 Facet 组件,包括 `facet.field` 用于基于字段的 Faceting,`facet.query` 用于自定义查询的 Faceting,以及 `facet.date` 用于日期的 Faceting。这些组件可以通过配置在查询请求中指定,以满足不同...

    solr-mongo-importer-1.1.0.jar

    solr-mongo-importer-1.1.0.jar solr-mongo-importer-1.1.0.jar solr-mongo-importer-1.1.0.jar

    Java后端学习路线梳理(csdn)————程序.pdf

    搜索引擎如ElasticSearch和Solr,以及分布式和微服务相关技术(服务发现/注册、网关、服务调用、熔断/降级、配置中心、认证鉴权、分布式事务、任务调度、链路追踪和监控、日志分析和监控)也是必备技能。 最后,...

    solr-6.2.0源码

    二、Solr的特性 1. 分布式搜索:Solr 6.2.0支持集群部署,可以将索引分片到多个节点,实现水平扩展,提升系统处理能力。 2. 实时搜索:Solr具有实时索引和查询的能力,一旦数据被更新,索引会立即反映出来,提供实时...

    solr(solr-9.0.0-src.tgz)源码

    源码分析是深入理解一个软件系统工作原理的重要途径,对于Solr这样的复杂系统尤其如此。这里我们将围绕"solr-9.0.0-src.tgz"这个源码包,详细探讨其主要组成部分、核心功能以及开发过程中的关键知识点。 1. **Solr...

    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包...

    java进阶Solr从基础到实战

    Solr它是一种开放源码的、基于 Lucene 的搜索服务器,可以高效的完成全文检索的功能。在本套课程中,我们将全面的讲解Solr,从Solr基础到Solr高级,再到项目实战,基本上涵盖了Solr中所有的知识点。 主讲内容 章节一...

    配置好的solr启动环境

    Solr,全称为Apache Solr,是一款开源的全文搜索引擎,被广泛应用于企业级搜索解决方案中。它基于Java,能够高效地处理大量数据的检索、排序和过滤等任务。在这个“配置好的solr启动环境”中,我们有一个预先配置好...

    apache-solr-4.0.0-ALPHA-src.gz官方包

    总之,通过分析"apache-solr-4.0.0-ALPHA-src.gz"源代码,开发者不仅可以深入理解Solr的工作机制,还可以获取到进行二次开发所需的底层知识。这将对提升开发者的技能水平和解决实际问题的能力大有裨益。

    apache-solr-dataimporthandler-extras-1.4.0.jar.zip

    在给定的压缩包“apache-solr-dataimporthandler-extras-1.4.0.jar.zip”中,主要包含了一个名为“apache-solr-dataimporthandler-extras-1.4.0.jar”的文件,这个文件是Solr的一个重要组件——DataImportHandler...

    快速上手数据挖掘之solr搜索引擎高级教程(Solr集群、KI分词)第15讲 solr之Facet 共7页.pptx

    Solr集群安装与配置(一)第11讲 Solr集群安装与配置(二)第12讲 SolrCloud基本概念第13讲 Solrj操作SolrCloud第14讲 solr索引主从同步第15讲 solr之Facet第16讲 solr之FacetPivot第17讲 solr之Group第18讲 solr之...

    Solr项目源码及solr资源包

    Solr项目源码及solr资源包是一个针对搜索引擎平台Apache Solr的学习与实践资源集合,主要结合了Spring Data Solr框架进行操作。这个项目旨在帮助开发者更好地理解和运用Solr进行数据索引和检索。让我们详细地探讨...

    solr6.6.0源码

    这个源码包包含了 Solr 的所有源代码,对于理解 Solr 的工作原理、进行二次开发或者定制化配置有着非常重要的意义。接下来,我们将深入探讨 Solr 6.6.0 中的一些关键知识点。 一、Solr 架构与组件 Solr 的核心架构...

    ik-analyzer-solr5-5.x.jar

    中文分词器ik-analyzer-solr5-5.x.jar,已经打包好,直接用就可以 2积分不黑心

    Solr in action.mobi

    PART 1 MEET SOLR. .................................................................1 1 ■ Introduction to Solr 3 2 ■ Getting to know Solr 26 3 ■ Key Solr concepts 48 4 ■ Configuring Solr 82 5 ■ ...

    solr-5.2.1.part1.rar 编译第1部分,共2部分

    把solr.war(solr-4.2.0\example\solr-webapp\solr.war)里的东西全复制到WebRoot下 2. 创建solr/home, 把solr-4.2.0\example\solr所有文件复制到你创建的solr/home目录下 3. 创建JNDI让程序找到solr/home(当然你也...

    solr 4.10源码

    这个源码包包含了Solr 4.10.4的所有源代码,对于开发者来说,这是一个深入了解Solr工作原理、定制功能以及进行二次开发的重要资源。 1. **Solr简介** Apache Solr是一个基于Lucene的全文检索服务,提供了一个高效...

    solr-dataimport-scheduler.jar 可使用于solr7.x版本

    Solr 数据导入调度器(solr-dataimport-scheduler.jar)是一个专门为Apache Solr 7.x版本设计的组件,用于实现数据的定期索引更新。在理解这个知识点之前,我们需要先了解Solr的基本概念以及数据导入处理...

    bhl_rails_solr-源码.rar

    3. **Solr配置**:源码中可能包含针对不同环境(如开发、测试、生产)的Solr配置,通过solr.yml管理。这些配置可能涉及Solr服务器地址、端口、数据目录等。 4. **错误处理**:在与Solr通信过程中,可能会遇到网络...

Global site tag (gtag.js) - Google Analytics