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的)。
相关推荐
Solr 的默认 requestHandler(org.apache.solr.handler.component.SearchHandler)已经包含了 Facet 组件(org.apache.solr.handler.component.FacetComponent)。如果自定义 requestHandler 或者对默认的 ...
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
搜索引擎如ElasticSearch和Solr,以及分布式和微服务相关技术(服务发现/注册、网关、服务调用、熔断/降级、配置中心、认证鉴权、分布式事务、任务调度、链路追踪和监控、日志分析和监控)也是必备技能。 最后,...
二、Solr的特性 1. 分布式搜索:Solr 6.2.0支持集群部署,可以将索引分片到多个节点,实现水平扩展,提升系统处理能力。 2. 实时搜索:Solr具有实时索引和查询的能力,一旦数据被更新,索引会立即反映出来,提供实时...
源码分析是深入理解一个软件系统工作原理的重要途径,对于Solr这样的复杂系统尤其如此。这里我们将围绕"solr-9.0.0-src.tgz"这个源码包,详细探讨其主要组成部分、核心功能以及开发过程中的关键知识点。 1. **Solr...
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它是一种开放源码的、基于 Lucene 的搜索服务器,可以高效的完成全文检索的功能。在本套课程中,我们将全面的讲解Solr,从Solr基础到Solr高级,再到项目实战,基本上涵盖了Solr中所有的知识点。 主讲内容 章节一...
Solr,全称为Apache Solr,是一款开源的全文搜索引擎,被广泛应用于企业级搜索解决方案中。它基于Java,能够高效地处理大量数据的检索、排序和过滤等任务。在这个“配置好的solr启动环境”中,我们有一个预先配置好...
总之,通过分析"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”的文件,这个文件是Solr的一个重要组件——DataImportHandler...
Solr集群安装与配置(一)第11讲 Solr集群安装与配置(二)第12讲 SolrCloud基本概念第13讲 Solrj操作SolrCloud第14讲 solr索引主从同步第15讲 solr之Facet第16讲 solr之FacetPivot第17讲 solr之Group第18讲 solr之...
Solr项目源码及solr资源包是一个针对搜索引擎平台Apache Solr的学习与实践资源集合,主要结合了Spring Data Solr框架进行操作。这个项目旨在帮助开发者更好地理解和运用Solr进行数据索引和检索。让我们详细地探讨...
这个源码包包含了 Solr 的所有源代码,对于理解 Solr 的工作原理、进行二次开发或者定制化配置有着非常重要的意义。接下来,我们将深入探讨 Solr 6.6.0 中的一些关键知识点。 一、Solr 架构与组件 Solr 的核心架构...
中文分词器ik-analyzer-solr5-5.x.jar,已经打包好,直接用就可以 2积分不黑心
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.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.4的所有源代码,对于开发者来说,这是一个深入了解Solr工作原理、定制功能以及进行二次开发的重要资源。 1. **Solr简介** Apache Solr是一个基于Lucene的全文检索服务,提供了一个高效...
Solr 数据导入调度器(solr-dataimport-scheduler.jar)是一个专门为Apache Solr 7.x版本设计的组件,用于实现数据的定期索引更新。在理解这个知识点之前,我们需要先了解Solr的基本概念以及数据导入处理...
3. **Solr配置**:源码中可能包含针对不同环境(如开发、测试、生产)的Solr配置,通过solr.yml管理。这些配置可能涉及Solr服务器地址、端口、数据目录等。 4. **错误处理**:在与Solr通信过程中,可能会遇到网络...