热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

solr搜索过程解析

Search步骤a.SolrParams参数准备q–查询语句sort–排序字段&排序方式rows–返回多少条start–起始点fl–返回字段,需要返回scorer字段的话

Search步骤
a.SolrParams参数准备
q – 查询语句
sort – 排序字段&排序方式
rows – 返回多少条 start – 起始点
fl – 返回字段,需要返回scorer字段的话这个KEY对应的VALUE里包含”score”就可以了
qt – 查询类型,根据这个字段找Handler
fq – FilterQuery

b.根据coreName到coreContains拿到SolrCore,再到SolrParams里去qt的VALUE到SolrCore找对应的SolrRequestHandler
如果没找到需要调用core.close表示归还,在从coreContains那里拿core时将它的被引用做了++操作了的

c.根据SolrParams&SolrCore构造一个SolrQueryRequest,其包含如下属性SolrCore, SolrParams, Map context
实例化一个SolrQueryResponse,有了这两个对象后就可以调用solrcore.execute执行查询操作了

d.solrcore.execute – 实际上执行的是handler.handleRequest(request, response)
[1].SolrRequestHandler可以配置三个不同重要性的默认参数,defaults,appends,invariants上面提到过,handler
在处理请求的第一步就是用这三个SolrParams包装用户提交的SolrParams。其包装逻辑是params为用户提交的SolrParams
defaults作为params的默认取值SolrParams -> params
appends作为params里getParams添加参数的SolrParams –> params
params作为invariants的默认取值SolrParams –> params
[2].先前提到对于SearchHandler有SearchComponent,这里只需要注意下QueryComponent就可以了,添加其他的组件
一 是可能没有用二是会带来内存开销,比如StatsComponent组件加入的话查询会返回符合结果的DocSet,其本身也没什么用在 SearchHandler处理查询请求逻辑里面其会先初始化一个ResponseBuilder,这个对象用来存放各个SearchComponent 处理的其他或者结构需要的数据,资源协调者;其再迭代SearchComponent调用prepare函数;其再迭代SearchComponent调 用process函数
整个core里SearchComponent只有一个实例,处理查询请求的数据当然不能放在这些SearchComponent里,只能放在资源协调者ResponseBuilder上…
[3].组件QueryComponent的prepare(ResponseBuilder)逻辑
---------
request.params.get[fl]拿到希望返回的域,判断里面是否包含scorer –> 设置到ResponseBuilder上
request.params.get[defType]拿到QueryParserPlugin名字,如果为null则使用LuceneQParserPlugin.NAME
request.params.get[q]拿到查询字符串 –>设置到ResponseBuilder上
QParser.getParser(queryStr, queryParserType, request)解析得到Qparser,其解析过程大致是
---------
a.实例化一个Map用来存放从queryStr以正则表达式{!(.*)}抽取的group(1)里符合正则
x|x=’(.*)’|x=”(.*)”|x=/$(.*)/$|x=(.*) |x=(.*)$ - x表示符合JAVA变量命名规则的字符串
对于第一种往MAP里PUT(“type”, x),对于第四种则PUT(x, request.params.get(group(1)));其他的put(x, group(1))
queryStr里正则{!(.*)}剩下的PUT(“v”, 剩下的str),MAP –> MapSolrParams作为localParams
b.从localParams找”typd”.value作为指定QueryParserPlugin的值,如果没有还是使用默认的LuceneQParserPlugin.NAME
如果queryStr里包含正则{!(.*)}则queryStr=localParams.get(“v”)
c.根据指定的QueryParserPlugin的名称到core里获取QueryParserPlugin,调用其createParser函数构造Qparser
其传入的参数有{queryStr, localParams, requst.params, request}
d.以LuceneQParserPlugin为例子查看其构造Qparser - LuceneQParser的过程,Qparser的作用主要是构造一个Query
在实例化时如果localParams.get(“tags”)不为null则会取request.context.get(“tags”)其值是一个
Map,如果取的值是null则new以个PUT到request.context里,再来解析localParams.get(“tags”)
得到的字符串,有”,”切割得到的子字符串PUT到request.context.get(“tags”)得到的Map中,VALUE=
List.add(LuceneQParserPlugin),以解析{!tag=”wdx,monkey”}为例子,context.get(“tags”)里有两个映射
“wdx” -> List – {LuceneQParserPlugin} //LuceneQParserPlugin持有request, queryStr, params, localParams
“monkey” -> List – {LuceneQParserPlugin}
---------
调用Qparser.parse获取Query的逻辑是
a.到localParam¶ms里找”df” – 默认字段,找VALUE时localParam优先,找不到再到params里面,如果没找到则
到core.indexSchema那里拿
b.到localParam¶ms里找”q.op” – BooleanQuery默认关系,拿的逻辑同上
c.实例化一个SolrQueryParser解析queryStr -> Query输出
得到Query –>设置到ResponseBuilder上
---------
抽取localParam¶ms里的”sort”,”rows”,”start” –> 设置到ResponseBuilder上,其中sort可以指定
schema里指定的域名|score|docid
同时把Qparser设置到ResponseBuilder上
---------
处理”fq” – FilterQuery
request.params.get(“fq”)得到过滤查询String[],迭代这个数组调用QParser.getParser(str, null, request)
获取Qparser解析str得到过滤查询Query,QParser.getParser的逻辑上面介绍过,这样得到一个过滤查询的链表,将这个
链表设置到ResponseBuilder上

[4].组件QueryComponent的process(ResponseBuilder)逻辑
a.每个request都只持有一个IndexSearcher的引用,通过调用core.getSearcher获取,QueryComponent
需要先获取IndexSearcher,core.getSearcher里的逻辑是
----getSearcher的逻辑
getSearcher – (forceNew, returnSearcher, waitSearcher-Futures)
关注solr全局三个点调用getSearcher函数 : solrCore初始化时(false, false, null),QueryComponent处理查询
请求时(false, true, null),UpdateHandler在处理commit请求时(true, false, new Future[1])
---------
1.solrCore初始化时
根据solrconfig配置的IndexReaderFactory&DirectoryFactory获取索引的IndexReader,再使用这个reader
封装一个SolrIndexReader,再使用这个SolrIndexReader封装一个RefCounted(searcher的引用计数器,当搜索
组件获取一个组件后引用++,用完后调用close引用--,当引用数为0时将这个引用从core管理的一个当前被使用的
searcher的链表移除,同时调用searcher.close回收资源),将这个引用添加到core管理的一个当前被使用的searcher
的链表里如果firstSearcherListeners不为空则回调这些监听器,这个回调是交给core的一个newSingleThreadExecutor去
做的,再往这个线程池里添加一个任务:将这个RefCounted设置为core当前最新的searcher的引用计数器
最后返回null,因为returnSearcher=false
在solrCore初始化时这样做的主要目的是在初始化时就加载好IndexSearcher,搜索请求来了之后能立即返回,而不必等待加载IndexSearcher
---------
2.QueryComponent处理查询请求时
由于core当前最新的searcher的引用计数器不为null且这个获取IndexSearcher的请求不是强制要求获取最新的,且
returnSearcher=true故直接返回core当前最新的searcher的引用计数器,且这个引用计数器做++
这里面还有段当前searcher的引用计数器为null的逻辑,但是没有发现有什么情况会导致这种情况发生故不累述了
---------
3.UpdateHandler在处理commit请求时
首先到core管理的一个当前被使用的searcher的链表里获取目前最新的searcher;同时会加载索引目录下的
index.properties文件(如果存在的话),拿到KEY=’index’的值,其指明目前索引的存放地方;如果获取的目录和当前
最新的searcher使用的目录一致且solrConfig.reopenReaders为true则获取通过searher.reader.reopen获取
最新的reader -> 封装成searcher,否则直接IndexReader.open获取reader。
获取到searcher后的一段逻辑[RefCount封装,添加到searchers链表]和core初始化时是一样的,接下来的逻辑是
如果solrConfig.useColdSearcher为TRUE其当前searcher的引用为null-导致来自QueryComponent的请求阻塞
[现在还没发现什么情况会导致searcher的引用为null]
立即将这个新的searcher的引用设置为core当前最新的searcher的引用计数器,这样来自QueryComponent的请求
拿到这个引用后返回,当时这时这个新建的searcher是没有经过其前一个searcher的cache热身的,同时这样会导致这个
新建的searcher不会进行热身活动
如果solrConfig.useColdSearcher为FALSE则会往线程池里添加一个热身的任务
如果newSearcherListeners不为空则回调这些监听器,也是给线程池的任务
最后如果先前没有做将新的searcher的引用设置为core当前最新的searcher的引用计数器的行为的话,则往线程池添加
一个任务 – 将新的searcher的引用设置为core当前最新的searcher的引用计数器
最后返回null,因为returnSearcher=false
---------
----solr的索引视图
用IndexReader构建SolrIndexSearcher时会先用solr的视图包装这个IndexReader –> SolrIndexReader
solr 的索引视图在lucene上添加了一层快速处理多个索引目录的视图,在用IndexReader构建SolrIndexReader时会获取这个 reader下的所有叶子Reader-SegmentReader,并建立索引,基于这样索引传入一个DOCID后能快速的定位 SegementReader,即其会持有其下的所有SegementReader&其start-offset,同时search时如果是 TermQuery则直接用叶子reader来做
----SolrIndexSearcher.cache
a.SolrIndexSearcher有四个缓存 – 对于cache来说有三个角色CacheConfig,SolrCache,CacheRegenerator
其分工是CacheConfig来自于solrconfig文件配置,在一个searcher初始化时会迭代fieldValueCache,filterCache,
queryResultCache,documentCache的配置,如果有配置则通过CacheConfig @class创建SolrCache实例
CacheRegenerator是负责新searcher创建后用老的searcher热身行为的,会遍历老的searcher的各个cache的key-value
具体做什么由各个CacheRegenerator说了算,因为这时候由于update的commit操作会有新的doc进入索引,所有老的value都不能用了,唯一能做的就是知道哪些key比较热门
对于filterCache来说其会使用新的searcher去加载这些热门的key的值放到cache里
对于queryResultCache来说其会使用新的searcher去加载这些热门query的值放到cache里,当时查询参数flag会加上
NO_CHECK_QCACHE
-------
SolrCache : filterCache queryResultCache documentCache
queryResultCache – key=QueryResultKey value=DocList,主要是存放一个查询请求返回的DocList数据,QueryResultKey计算hash的影响因素有 : query filters sort
documentCache – key=int value=Document,主要是存放docid->document的映射,在加载document时都会走这个缓存,如果不存在则用reader去加载,然后PUT到cache里
filterCache :key=Query value=DocSet,主要存放query->docSet的映射,满足这个query的所有docid的映射,其作用和
queryResultCache比区别在于其存放的可能只是中间结果,queryResultCache存放的是最终结果
值得注意的是使用这些cache是很耗内存空间的,为了限制内存空间的使用,cache的实现也是使用LRU机制,通过实现LinkedHashMap的removeEldestEntry函数里判断目前的大小是否大于了限制来实现的
-------
b.对于纯粹的来自QueryComponent组件的查询请求是只需要返回DocList就可以了,可能还需要分数,这里分析为了简便只考虑需要DocList和分数的情况
根据offset+len计算当期期望的最大的docId,再和maxDoc比较,不能比maxDoc大
如果queryResultCache存在且这个查询没有过滤查询则到用这个查询封装一个QueryResultKey到queryResultCache取值
如果存在且[查询不需要分数或者cache里有分数]这样就立即拿到了docList返回,查询结束
如果cache里没有找到或者需要分数而cache里没分数则需要走查询了,对于查询的结果如果可以cache则put到queryResultCache
---------------
拿到DocList了后就可以给SolrQueryResponse返回了...


推荐阅读
  • 技术日志:使用 Ruby 爬虫抓取拉勾网职位数据并生成词云分析报告
    技术日志:使用 Ruby 爬虫抓取拉勾网职位数据并生成词云分析报告 ... [详细]
  • 本文介绍了在 Java 编程中遇到的一个常见错误:对象无法转换为 long 类型,并提供了详细的解决方案。 ... [详细]
  • 如何在Java中使用DButils类
    这期内容当中小编将会给大家带来有关如何在Java中使用DButils类,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。D ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • 在JavaWeb开发中,文件上传是一个常见的需求。无论是通过表单还是其他方式上传文件,都必须使用POST请求。前端部分通常采用HTML表单来实现文件选择和提交功能。后端则利用Apache Commons FileUpload库来处理上传的文件,该库提供了强大的文件解析和存储能力,能够高效地处理各种文件类型。此外,为了提高系统的安全性和稳定性,还需要对上传文件的大小、格式等进行严格的校验和限制。 ... [详细]
  • 您的数据库配置是否安全?DBSAT工具助您一臂之力!
    本文探讨了Oracle提供的免费工具DBSAT,该工具能够有效协助用户检测和优化数据库配置的安全性。通过全面的分析和报告,DBSAT帮助用户识别潜在的安全漏洞,并提供针对性的改进建议,确保数据库系统的稳定性和安全性。 ... [详细]
  • Flowable 流程图路径与节点展示:已执行节点高亮红色标记,增强可视化效果
    在Flowable流程图中,通常仅显示当前节点,而路径则需自行获取。特别是在多次驳回的情况下,节点可能会出现混乱。本文重点探讨了如何准确地展示流程图效果,包括已结束的流程和正在执行的流程。具体实现方法包括生成带有高亮红色标记的图片,以增强可视化效果,确保用户能够清晰地了解每个节点的状态。 ... [详细]
  • 为了确保iOS应用能够安全地访问网站数据,本文介绍了如何在Nginx服务器上轻松配置CertBot以实现SSL证书的自动化管理。通过这一过程,可以确保应用始终使用HTTPS协议,从而提升数据传输的安全性和可靠性。文章详细阐述了配置步骤和常见问题的解决方法,帮助读者快速上手并成功部署SSL证书。 ... [详细]
  • HBase Java API 进阶:过滤器详解与应用实例
    本文详细探讨了HBase 1.2.6版本中Java API的高级应用,重点介绍了过滤器的使用方法和实际案例。首先,文章对几种常见的HBase过滤器进行了概述,包括列前缀过滤器(ColumnPrefixFilter)和时间戳过滤器(TimestampsFilter)。此外,还详细讲解了分页过滤器(PageFilter)的实现原理及其在大数据查询中的应用场景。通过具体的代码示例,读者可以更好地理解和掌握这些过滤器的使用技巧,从而提高数据处理的效率和灵活性。 ... [详细]
  • com.sun.javadoc.PackageDoc.exceptions()方法的使用及代码示例 ... [详细]
  • MySQL 5.7 学习指南:SQLyog 中的主键、列属性和数据类型
    本文介绍了 MySQL 5.7 中主键(Primary Key)和自增(Auto-Increment)的概念,以及如何在 SQLyog 中设置这些属性。同时,还探讨了数据类型的分类和选择,以及列属性的设置方法。 ... [详细]
  • com.hazelcast.config.MapConfig.isStatisticsEnabled()方法的使用及代码示例 ... [详细]
  • 在尝试对 QQmlPropertyMap 类进行测试驱动开发时,发现其派生类中无法正常调用槽函数或 Q_INVOKABLE 方法。这可能是由于 QQmlPropertyMap 的内部实现机制导致的,需要进一步研究以找到解决方案。 ... [详细]
  • 本文详细介绍了在MySQL中如何高效利用EXPLAIN命令进行查询优化。通过实例解析和步骤说明,文章旨在帮助读者深入理解EXPLAIN命令的工作原理及其在性能调优中的应用,内容通俗易懂且结构清晰,适合各水平的数据库管理员和技术人员参考学习。 ... [详细]
  • Node.js 配置文件管理方法详解与最佳实践
    本文详细介绍了 Node.js 中配置文件管理的方法与最佳实践,涵盖常见的配置文件格式及其优缺点,并提供了多种实用技巧和示例代码,帮助开发者高效地管理和维护项目配置,具有较高的参考价值。 ... [详细]
author-avatar
梦蕾AngeL
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有