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中最常用的查詢分析器的機制.
發佈了157 篇原創文章 · 獲贊 32 · 訪問量 140萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章