热门标签 | 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返回了...


推荐阅读
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 本文介绍了如何使用Express App提供静态文件,同时提到了一些不需要使用的文件,如package.json和/.ssh/known_hosts,并解释了为什么app.get('*')无法捕获所有请求以及为什么app.use(express.static(__dirname))可能会提供不需要的文件。 ... [详细]
  • VueCLI多页分目录打包的步骤记录
    本文介绍了使用VueCLI进行多页分目录打包的步骤,包括页面目录结构、安装依赖、获取Vue CLI需要的多页对象等内容。同时还提供了自定义不同模块页面标题的方法。 ... [详细]
  • 如何使用Python从工程图图像中提取底部的方法?
    本文介绍了使用Python从工程图图像中提取底部的方法。首先将输入图片转换为灰度图像,并进行高斯模糊和阈值处理。然后通过填充潜在的轮廓以及使用轮廓逼近和矩形核进行过滤,去除非矩形轮廓。最后通过查找轮廓并使用轮廓近似、宽高比和轮廓区域进行过滤,隔离所需的底部轮廓,并使用Numpy切片提取底部模板部分。 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • javascript  – 概述在Firefox上无法正常工作
    我试图提出一些自定义大纲,以达到一些Web可访问性建议.但我不能用Firefox制作.这就是它在Chrome上的外观:而那个图标实际上是一个锚点.在Firefox上,它只概述了整个 ... [详细]
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
  • 本文讨论了如何使用Web.Config进行自定义配置节的配置转换。作者提到,他将msbuild设置为详细模式,但转换却忽略了带有替换转换的自定义部分的存在。 ... [详细]
  • 本文介绍了在CentOS 6.4系统中更新源地址的方法,包括备份现有源文件、下载163源、修改文件名、更新列表和系统,并提供了相应的命令。 ... [详细]
  • 本文讨论了在shiro java配置中加入Shiro listener后启动失败的问题。作者引入了一系列jar包,并在web.xml中配置了相关内容,但启动后却无法正常运行。文章提供了具体引入的jar包和web.xml的配置内容,并指出可能的错误原因。该问题可能与jar包版本不兼容、web.xml配置错误等有关。 ... [详细]
  • 本文介绍了如何在Jquery中通过元素的样式值获取元素,并将其赋值给一个变量。提供了5种解决方案供参考。 ... [详细]
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社区 版权所有