solr.in.action-ch07(7.1-7.3)

7.1.1请求处理器
请求处理器是所有到solr请求的入口,它们的工作就是接收一个请求,进行一些函数,然后返回一个响应到客户端.solr包含大量的请求处理器来涵盖一切.运行一个搜索(搜索处理器),从一台服务器复制solr索引到另一台服务器(复制处理器),发送一份新文档来更新solr索引(更新请求处理器).大多数请求处理器都是从一个叫RequestHandlerBase的java类继承,虽然这不是必需的.大多数用户通过solr来获取内置的请求处理器.图7.1显示了solor的大多数内置请求处理器的层次结构.
7.1.2搜索组件
通过发送分离的请求到多个可用的请求处理器,可以调用很多搜索功能.理想情况下,你只发送单个请求到solr,获取所有期望的信息.这就是搜索组件存的原因了.搜索组件是发生在一个搜索处理器生命周期内可配置的处理步骤.正如在第4章讨论过的,搜索组件是配置在solrconfig.xml.
<searchComponent name="query" class="solr.QueryComponent" /> 
<searchComponent name="facet" class="solr.FacetComponent" /> 
<searchComponent name="mlt" class="solr.MoreLikeThisComponent" /> 
<searchComponent name="highlight" class="solr.HighlightComponent" /> 
<searchComponent name="stats" class="solr.StatsComponent" /> 
<searchComponent name="debug" class="solr.DebugComponent" /> 
<requestHandler name="/select"class="solr.SearchHandler">
  <arr name="components"> 
    <str>query</str>
    <str>facet</str>
    <str>mlt</str>
    <str>highlight</str>
    <str>stats</str>
    <str>debug</str>
  </arr>
</requestHandler>


虽然来自于列表的一切就是默认行为——这意味着,无论如何,它都会发生,即使不考虑solrconfig.xml——检查此列表来理解如何配置和启用新的搜索组件,以及来理解默认情况下,通过此搜索处理器如何搜索进行工作是有用的.
如果你看看第一个<searchComponent />标签,你会看到定义了两个属性,一个是name,一个是class.名称就是query,类就是solr.QueryComponent.这个搜索组件仅需要定义一次,之后就可以被做任意多个请求处理器使用.再看看/select请求处理器,你会看到一个name为component的arr(数组)元素.components部分的每一条目必须和一个搜索组件的名字相应.或者默认是启用的(如上面清单的所有组件),或者是定义在solrconfig.xml的其它地方.
为请求处理器和搜索组件设置默认值:代码中的默认值和在solr URL传递过来的查询字符串变量.
大多数搜索组件允许在XML配置属性.可以重写("query", "facet", "mlt","highlight", "stats", or "debug")这些默认搜索组件.下面是为"query"组件设置默认配置:
<searchComponent name="query" class="solr.QueryComponent">
  <lst name="invariants">
    <str name="rows">25</str>
    <str name="df">content_field</str>
  </lst>
  <lst name="defaults">
    <str name="q">*:*</str>
    <str name="indent">true</str>
    <str name="echoParams">explicit</str>
  </lst>
</searchComponent>
即使在solrconfig.xml文件没有明确定义,每一个默认搜索组件默认是存在的.像前面的清单明确定定义他们就会替换默认配置.有可能通过增加first-components或last-components来插入组件,这样在运行搜索处理器列表的开始或结束能追加附加的组件.在搜索处理中所有的搜索组件,query组件是最重要的.因为它负责初始化运行查询和在响应中提供结果可用(供其它搜索组件在后面使用).query组件使用一个查询解析器来解析这个进入的从请求到搜索处理器的查询
7.1.3查询解析器
查询解析器是用于解释一个搜索语法成一个Lucene查询,查找一组期望的文档.正如搜索组件是特定于单个请求处理器(SearchHandler),查询解析器也是特定使用在单个搜索组件中.下图(15111401)显示了这种关系:搜索处理器使用搜索组件执行,而搜索组件使用查询解析器.你可以从图看到很多solr查询解析器.每一个都实现作为一个QParserPlugin类,如果需要自定义查询解析功能,而这些可用的查询解析器都不支持,那你也可以写自己的QParserPlugin类.solr最常用的查询解析是Lucene查询解析器(LuceneQParserPlugin)和eDisMax查询解析器(ExtendedDismaxQParserPlugin),将会在7.4与7.5节详细介绍.图中的其它查询解析器将会在7.6节介绍.在我们深入到每一个查询解析的机制之前,理解与查询解析器一起工作的机制是有用的.


7.2与查询解析器工作
7.2.1指定一个查询解析器
执行一个搜索时,QueryCompnent处理用户查询(q参数),通过将它的值传到查询分析器.正如上一节提过,LuceneQParserPlugin是solr的默认查询分析器.此默认是容易重写的,也很容易在同一个查询组合多个查询解析器.每个查询解析器能够进行它自己种类的查询,并可以接受自己的查询语法.所以某些查询解析器是更适合于不同的场景(将会在7.4-7.6节介绍),这一节介绍,与查询解析器一起工作,包括如何改变默认查询解析器,如何组合查询解析器和如何为查询解析器指定设置.
在搜索请求上,使用defType参数可以修改使用QueryComponent的默认查询解析器.
/select?defType=edismax&q=…
/select?defType=term&q=…
另外修改默认查询解析器类型,你也可以在你的查询内部使用solr一种特定语法来修改查询解析器.
/select?q={!edismax}hello world
/select?q={!term}hello
/select?q={!edismax}hello world OR {!lucene}title:"my title"
第三个例子,你注意到两种不同的查询解析器(eDisMax和Lucene)在同一个查询被调用.这是相对于使用defType参数,指定查询解析器的一个优点.solr用于定义查询解析器内联调用一个功能的{!...}这种语法称为本地参数,这会在接下来的章节更加详细地讨论.
7.2.2本地参数
本地参数提供本地化请求参数到提定上下文的能力.通常情况下,你会在solr的URL上传递请求参数.但是有时你想可能只想在查询的某些部分应用某些参数.在一个查询的上下文内,本地参数仅允许你传递请求参数到你考虑的指定查询解析器,而不是全局设置所有请求参数.在前一节你已看到在你的内联查询是有可能修改你的查询分析器的.不仅你能修改查询分析器,而且你能利用本地参数修改任何请求参数.
本地参数语法:本地参数是一组代表请求参数的键/值对,它们只在当前的上下文计算,语法如下:
{!param1=value1 param2=value2 … paramN=valueN}
一组本地参数以{!开始,以}结束,并包含以空格的键/值对列表,键和值是通过等号分隔的.例如
/select?q=hello world&defType=edismax&qf=title^10 text&q.op=AND
利用本地参数,功能上等同于如下查询.
/select?q={!defType=edismax qf="title^10 text" q.op=AND}hello world
这两个查询的真实不同点,第一个例子,所有的请求参数是全局的,所以在请求内,它们会在检查它们的任何地方应用.在本地参数例子,defType, qf, 和 q.op参数只定义在特定q参数的作用范围上下文内.如果你需要在一个查询内,使用不同设置不止一次重用一个查询解析器,那么本地参数就是此工作机制.我们在本地参数内部指定defType参数,但是也有可能使用type参数重写defType参数.你想一下,defType代表默认类型,这意味着它为所有查询指定默认查询解析器.你可以在一组本地参数内,在一个特定上下文,使用type参数重写默认类型:
/select?q={!type=edismax qf="title^10 text" q.op=AND}hello world
type参数仅用在本地参数上下文内,如果你想在顶级定义一种类型作为默认来服务,则使用defType参数.相反,defType参数既能在请求级别和在一组本地参数内使用.因为有些本地参数值可能包含特殊字符(空格,引号等),你可能需要使用引号(单引或双引号)来括起本地参数值或特殊的转义字符.在最后的那个查询,你可以看到qf参数是使用双引号包起来,因为它的值包含一个空格.要了解更多solr有关转义字符,请看7.4.1节尾.
可能你注意到7.2.1节和此节有一点的不一致,使用本地参数语法,一个查询解析器是如何被指定的.在此节,使用{!type=edismax …}指定,前面使用了更简化的 {!edismax …}表示.这两种都能起作用.因为仅指定一个值时,type就是默认参数key.正如是有可能,不用指定一个字段而使用默认字段来运行一个关键字搜索.所以也有可能使用本地参数传递一个值,而应用默认本地参数type这个key.因为语法更短,通常你看到查询解析器使用{!queryParserName}语法来定义,其它本地参数必须使用{!key1=value1 key2=value2 …}全语法.
本地参数声明后面的值是传递进入查询解析器的值.下面本地参数声明,传入查询解析器的值就是hello world.
/select?q={!edismax qf="title^10 text"}hello world
然而,替换特定本地参数声明后的值,有时更容易在本地参数块内定义该值.一个特定本地参数v的key就是为了这个需求而保留.例如,前面查询可选定义为
/select?q={!edismax qf="title^10 text" v="hello world"
值得指出的是,在本地参数块,通过移动查询值成v参数.你必须牢记转义字符.你也不必担心这一点,例如,你想要搜索引号短语"hello world",如下两种查询是相当的.
/select?q={!edismax qf="title^10 text"}"hello world"
/select?q={!edismax qf="title^10 text" v="\"hello world\""}
因为在本地参数块明确定义关联的复杂性,并为了通过请求语法提升重用参数,往往使用参数引用是有用的.
参数引用
参数引用提供替换任意变量进入你的查询的能力.此功能类似于Sql参数化查询.因为它让你从查询语法分开来定义你的查询输入.引用参数语法如下所示:
/select?q={!edismax v=$userQuery}&userQuery="hello world"
此例,userQuery参数作为用户自定义的查询字符变量传入去,solr原本不理解此变量,通过指定传递给eDisMax查询解析器的值应引用$userQuery参数,该值可以从请求的其它地方获取.这似乎看起来,只在开始时是有用的,但由于默认参数可以在solrconfig.xml中为每个请求处理器或请求组件,使用默认,追加,常量来定义(第4章介绍过的).你可以为你的应用程序,拿出自己的一组替换成一个预定义配置的请求参数.
使用参数引用减少重复:如果你需要使用不同的方法重用查询部分,并且不想在请求上复制,那么参数引用是有用的.
现在你知道了如何更改查询解析器与在全局和本地级别维护传入到它们的值.下一步,在我转向学习solr的每一个查询分析器是如何工作之前,我们先看看用户查询和过滤器是如何工作的.
7.3查询与过滤器
在讨论每个solr可用的查询分析器如何工作之前,对用户查询和过滤是如何工作,有一个好的理解是有用的.它们有什么不同,他们如何相互作用,以及他们最终如何影响性能和你的搜索请求的质量.solr的一个搜索由两个主要操作组成.查找匹配请求参数的文档和排序文档,以便只有顶级匹配需要被返回.默认情况下,文档是基于相关性排序的.这意味着在最终找到的那些匹配文档之后,额外的计算是必要的.以计算每份文档的相关性得分.使用转化的索引查找匹配的文档处理和计算每一份文档的相关性得分的默认算法已在第3章介绍过了
7.3.1fq与q参数
为有效地处理找到匹配文档和计算文档的得分的操作,solr使用了两个参数:fq和q,fq代表过滤查询,q参数代表查询.第一眼看这两个参数可能看起来难以区分,因为相同的查询语法传入这两个参数的其中一个参数 将会返回相同数目的文档.正因为如此,许多使用单一q参数来搜索整个请求.理解这两个参数的不同可以让你运行更有效的搜索.
相关性影响
那么q和fq参数有什么不同呢?fq服务于单个目的:限制一套匹配的文档到你的结果.相反,q参数服务于两个目的:限制一套匹配的文档到你的结果;使用词语来计算相关性得分提供相关性算法;
因此,可以认为q参数作为一个特殊的过滤器,它告诉solr什么词语应该在相关性计算考虑,因为这种区别,solr用户倾向于把用户输入词语(如keywords:"apache solr")在q参数和机器生成过滤器(如category:"technology")在fq参数.
缓存与执行速度
从主查询分离出来的过滤查询服务于两个目的.首先因为查询过滤器经常在搜索器(通常不包含任意的关键字)间是可重用的,在过滤缓存中,缓存他们的结果是可能的.第二,因为相关性计分操作必段对每个文档的每一个词语查询(q)进行计算,通过分离你的查询的某些部分进入一个过滤查询(fq),在fq参数的这些部分不会引起不必要的附加相关性计算来执行.对于查询某些部分仅作为过滤器,这样能在相关性计分期间节省工作量.
指定多个查询和过滤器
需要注意查询和过滤器的最后一个特点就是你可能添加多个fq你想要的参数到你的solr请求,但只有一个q参数.因此,一个solr查询q=keywords:solr&fq=category:technology&fq=year:2013将和q=keywords:solr&fq=category:technology AND year:2013以同样的顺序返回同样的文档.除了fq参数(每个fq参数都会独立缓存)的一些缓存影响(这里的意思应该是不考虑缓存的影响),使用多个fq参数在功能上等同于组合成单个fq参数的版本.虽然在后面章节的很多查询解析器会使用q和fq参数一起工作,但是你应该牢记相关性以及当选择那个参数适合你的给定的使用用例的缓存影响,
执行顺序
因为查询和过滤器都查找文档和在他们上面设定操作,出现一个常见问题就是,查询和过滤器按什么顺序执行.一些发布的资源说明过滤器先执行,有些说明查询先执行,还有其他说的是查询和过滤器并行执行.那么,那一个是真的?这是一个复杂的问题,答案依赖于使用案例.从技术来说,操作的顺序如下:
1.每个fq参数在过滤缓存中查找,如果它存在,就返回一个缓存文档集(封装的一个OpenBitSet),这个文档集的每一份文档在索引都带有一个位(0或1),表明是否文档是否被过滤器包含.
2.如果fa参数在过滤缓存中没有找到,并且缓存是启用的,过滤器对索引执行以获得一个新的docset,它可以在后面被缓存.
3.所有的DocSets相交(并在一起)得到单一的DocSets.
4.q参数被传入去(伴随着过滤DocSets)来执行作为一个查询.当执行查询,Lucene扮演查询与组合的过滤器间的跨越,推进查询与过滤器的结果对象到他们的下一个代表内部ID(一个整数).当查询结果与过滤器结果对象都包含相同的ID,该ID会被收集,包含为每份文档生成相关得分的一个过程.
5.如果该查询包含任何其它后置过滤器(下一节讨论),他们会作为收集处理的部分(查询与过滤器相交后),然后仅在已匹配组合的查询和组合的过滤器的文档上
如图15111402
基于此种解析,当缓存启用时,过滤器确实在主查询之前执行.查询与过滤器在收集处理期间(跨越式那步)是同时计算的,然后特定种类的过滤器称为后置过滤器,都能在查询和过滤器找到他们匹配的文档之后插入收集处理.
图中编号的每个阶段是与大纲步骤相应的.1在过滤缓存中,查找过滤器;2为丢失的过滤器查询索引;3组件每一个过滤器;4.使用跨越处理,组合查询与(组合过的)过滤器;5应用耗资源人后置过滤器;当然,最后一步是返回最终的DocSet,如果需要用于排序或检索,计算仅基于查询(不是过滤器)的相关性,然后,在solr响应里,返回前面的文档.
这听起来相当的复杂,实际上也是.solr隐藏这种复杂性,做得很好.但是为了优化耗资源的过滤器,理解这个过程还是有用的.solr提供了精细的控制,使你可以指定那些过滤器将被缓存,以什么顺序评估你的过滤器,包括评估它们之前,之后或同时与主查询.下一节将演示如何通过调节缓存开/关来控制你的过滤器的执行顺序,指定过滤器的顺序,并决定过滤器是否在查询和其它过滤器执行后运行.
7.3.2处理耗资源的过滤器
通过可缓存与和为查询只指定的部分作为一个过滤器通过绕过相关性处理,过滤器使你节省大量的处理时间.然而并非所有过滤器创建相当的,如果你尝试过滤结果以围绕特定的经纬度的一个地理半径(在15章的15.2节介绍),这种过滤器很可能会很耗资源来计算,因为涉及所有的数学.此处,如果你为数以百万计的位置生成不同的过滤器,这种半径过滤器的数量可能很难来缓存.或者有的情况,你的应用程序可能会生成很多唯一的过滤器(例如,在一个唯一的ID上过滤),这种过滤缓存将会变成过载.无论常用的过滤器被回收或者搜索器的预热时间可能变得过大.对于像这样的情况,solr允许你控制任意过滤器是否应该被缓存以及过滤器的计算顺序.
关闭过滤器的缓存
某些情况下,你可能有大量的唯一不值得缓存的过滤器.因为你有你能缓存的过滤器的固定上限.如果用得最多的过滤器一直保留缓存,solr实例的性能会是最好的.通过减少重要的过滤器来防止缓存过载,你可以使用以下语法明确为任意过滤器关闭缓存:
fq={!cache=false}id:123&
fq={!frange l=90 u=100 cache=false}
scale(query({!v="content:(solr OR lucene)"}),0,100
在前面的过滤器中,第一个是特定的,可以为每一份叭一的文档产生不同的过滤器,所以缓存过滤器将不合理填补过滤缓存.第二个例子,此过滤器也是特定的:试图查找与查询匹配content:(solr OR lucene)最相关的前10%的文档.它这样完成的,通过获得查询的相关性得分,在1到100之间缩放所有文档的分数,然后过滤掉那些不落在90到100之间的文档.由于这种过滤器包含变量输入(输入到查询功能),关闭过滤可能是一个好的候选.你可以增加尽可能多的过滤器到你的请求,每个FQ参数支持 开启缓存功能或独立地关闭,通过指定cache=true或者(更实际)留下缓存本地参数处于关闭,因为缓存默认为true
改变过滤器的执行顺序
如果你的搜索请求包含多个过滤器,他们执行的顺序可以在你的查询速度上有着显著的影响.从逻辑上来讲,如果过滤器减少最多结果集的过滤器可以优先执行,其它过滤器就更少的文档来执行,因此执行得更快.同样,某些过滤器必须执行复杂的计算,例如试图围绕一个点的半径的地理空间过滤器来过滤,这些过滤器可以在后面应用.他们应使更少的文档来执行耗资源的计算.你知道前面那些执行最耗资源的过滤器的情况下,solr允许你强制使他们通过提供相关的成本来过滤在后面执行.提供一个过滤成本的语法如下:
fq={!cost=1}category:technology&
fq={!cost=2}date:[NOW/DAY-1YEAR TO *]&
fq={!geofilt pt=37.773,-122.419 sfield=location d=50 cost=3}&
fq={!frange l=90 u=100 cache=false cost=100}
scale(query({!v="content:(solr OR lucene)"}),0,100)
过滤器的成本越高,它就会在越后面执行.此例,category:technology过滤器先执行,因为它的成本最低.此成本最低的过滤器,可能是有意义的,因为它可以快速执行并且可能大大减少单个类别下的文档数.下一个过滤器(带2的成本)限制那些日期在过去一年的所有结果.下一个最耗资源过滤器是geofilt操作,必须计算半径并限制半径在50英里的所有结果(耗资源操作).成本为100的最后过滤器,既耗资源(由于数学问题)又高度可变(因为它接受关键词),所以指定cost=100和cache=false.成本从3跳到100,这看来起似乎很奇怪,虽然成本不必须是连续的(他们以相对的顺序被应用),成本大于或等于100调用一个在Solr称为后置过滤器的特殊功能.
后置过滤器
在某些情况下,过滤器可能是如此的耗资源执行,你不希望它计算,直到所有其他的查询和过滤器之后.Solr的已经实现了特殊类型的过滤器,称为后置过滤器,这样使一个过滤器可以在文件的收集处理文档,当查询和过滤器已相交的时候再应用.
回顾7.3.1节,当查询和组合过的过滤器并行计算(跨越处理),那是在查询和过滤器找到的每个文件被收集起来.后置滤波器是一种特殊的过滤器.它只有在那些"收集"被调用过的文档基础上进行计算.这允许所有其它较低成本的过滤器先被应用来限制结果集的总数,对于耗更多资源的后置过滤器,只有在较小的文档集最后被应用.事实证明,为一个过滤器定义成本参数,也用来把一个过滤器转换成后置滤波器的机制.任何成本大于或等于100的过滤器将作为一个后置过滤器被应用,只要过滤器类型实现了后置过滤器的接口
在solr,后置过滤器不必为各类查询/过滤器工作;它仅适用于那些实现了PostFilter接口的过滤器.FRange查询(见7.6.3节)是后置过滤器功能查询类型的一个例子.也有可能你自己写插件实现PostFilter接口,如果您需要主查询和过滤器已执行来应用一个过滤器的功能.
通过清晰地概括查询和过滤器之间的差别和相关性,以及很好地理解性能方面,我们现在可以深入到Solr中最常用的查询分析器的机制.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值