有的時候,我們一開始不可能准確地知道搜索的關鍵字在 Solr 中查詢出的結果是什么,因此,Solr 還提供了幾種類型的模糊查詢。模糊匹配會在索引中對關鍵字進行非精確匹配。例如,有的人可能想要搜索某個前綴開始的單詞(稱為通配符查詢),或者想要查詢和關鍵字有一兩個字母不相同的單詞(稱為模糊查詢或編輯距離查詢),或者你想要查詢兩個關鍵字,並且這兩個關鍵字之間的距離不會大於某個最大值(稱為臨近查詢)。總的說來,模糊匹配是查詢中的一個強大的工具。
通配符查詢
在 Solr 中最普遍使用的模糊查詢就是使用通配符。假設你想要查詢以 offic 開始的文檔。下面列舉出這個查詢的幾個版本:
-
查詢語句: office OR officer OR official OR officiate OR … 這個列表中的單詞是所有你以 offic 開頭的單詞。
因為你需要找到的所有匹配都在 Solr 索引中。因此,你可以使用星號(*)作為通配符來執行相同的功能:
-
查詢語句: offi* 匹配 office, officer, official 等等。
除了放在關鍵字的最后,通配符也可以放到關鍵字中間,例如,如果你想要同時匹配 officer 和 offer:
-
查詢語句: off*r 匹配 offer,officer,officiator 等。
星號通配符(*)表示匹配 0 個或多個字符。如果你只需要匹配一個字符,那么可以使用問號(?)通配符:
- 查詢語句: off?r 匹配 offer 但是不匹配 officer。
以通配符為頭進行查詢
在 Solr 中使用通配符相當強大。但是,使用通配符進行查詢也會帶來很大的開銷。一旦使用統配符的查詢,那么在關鍵字中第一個通配符之前的部分需要在反向索引中全部查詢出來。那后,每個查詢出來的結果在逐一進行檢查,看是否符合查詢條件。正是因為這樣,所以在統配符之前的字符越多,那么查詢將會越快。例如,使用 engineer* 進行查詢將不會帶來很高的開銷(因為這個查詢在反向索引中不會找到太多的匹配),但是 e* 進行查詢的開銷就相當大,它將會匹配所有 e 開頭的單詞。
如果使用通配符開頭的話,開銷也會相當大。例如,你需要查詢 ing 結尾的單詞(像 caring,liking 和 smiling),那么將會帶來嚴重的性能問題:
- 查詢語句: *ing
如果你真的需要進行這樣的查詢,那么有一個現成的解決方案,這個解決方案就是添加 ReversedWildcardFilterFactory 到你的字段類型的分析鏈中(詳細內容以后會講到)。
ReversedWildcardFilterFactory 將會插入兩條記錄到 Solr 的索引中(一條是單詞的正向文本內容,一條是單詞的反向文本內容):
- 索引:caring/#gnirac liking/#gnikil smiling/#gnilims
當提交了 *ing 的查詢之后,Solr 知道使用索引中的反向內容去查詢,這樣由通配符開頭帶來的性能問題就轉換成了普通的通配符查詢問題。
但是要注意,如果將這個特性打開的話,那么在 Solr 索引中的所有關鍵字都將會由兩條索引記錄,這無疑增加了索引的大小並且降低了整個查詢的速度。因此,不建議打開這個功能,除非你的應用程序真的非常需要。
關於使用統配符查詢的最后一點就是使用通配符只能對單獨的關鍵字進行插敘,而不能對短語進行查詢,例如:
- 正常: softwar* eng?neering
- 不正常:”softwar* eng?neering”
如果你需要在短語中使用通配符,你將要把整個短語作為一個關鍵字存儲到索引中,我們將會在以后講解這個功能。
范圍查詢
Solr 也提供了在已知值之間的范圍查詢。當你需要查詢某個范圍之間的子集的時候,這個功能非常有用。例如,如果你只想查詢 2012 年 2 月 2 日到 2012 年 8 月 2 日這六個月之間的文檔,那么可以執行下面的查詢:
- 查詢語句:created:[2012-02-01T00:00.0Z TO 2012-08-02T00:00.0Z]
范圍查詢的語法結構也可以用在其它字段上:
- 查詢語句: yearsOld:[18 TO 21] 匹配:18, 19, 20, 21
- 查詢語句:title:[boat TO boulder] 匹配:boat, boil, book, boulder,等
- 查詢語句:price:[12.99 TO 14.99] 匹配: 12.99, 13.000009, 14.99, 等
上面的范圍查詢都是放在一對方括號中,這成為被包含的范圍查詢語法(閉區間)。Solr 也支持不被包含的范圍查詢語法(開區間),這需要把查詢范圍放到大括號中:
- 查詢語句:yearsOld:{18 TO 21} 匹配:19 和 20 但是不匹配 18 或 21
- 查詢語句:yearsOld:{18 TO 21} Matches 19 and 20 but not 18 or 21
雖然看起來有點奇怪,但是 Solr 也提供了半包含的范圍查詢語法(半開區間):
- 查詢語句: yearsOld:[18 TO 21} 匹配:18, 19, 20, 但是不匹配 21
范圍查詢的效率比查詢單個關鍵字要低,但是為某個特定的范圍進行檢索提供了巨大的靈活性。需要注意的是,使用范圍查詢的返回結果是按照 Solr 索引進行排序的,也就是說是按照字典順序。如果你創建了一個文本字段來存儲數字,那么這些數字的返回順序應該是這樣:1, 11, 111, 12, 120, 13, 等。如果是數字類型的字段,那么將使用特殊的方式來進行索引這樣可以彌補這個問題,但是要明白一點,在 Solr 索引中進行排序依賴於寫入索引的時候,字段中的數據是如何被處理的。詳細內容以后會說明。
模糊/編輯距離查詢
對於很多搜索應用來說,很重要的功能是不僅僅需要精確匹配用戶的文本內容。而且還允許一些靈活的變化,比如一些用戶的拼寫錯誤或相同單詞的其它變體。Solr 通過基於 Damerau-Levenshtein 距離的編輯距離測量來支持這個功能,它將容忍 80% 以上的拼寫錯誤。
Solr 提供的模糊編輯距離查詢需要用到波浪符號(~):
- 查詢語句: administrator~ 匹配: adminstrator, administrater, administratior,等
這個查詢不僅匹配原始的關鍵字(administrator),還有其它與原始關鍵字有 2 個編輯距離的關鍵字。一個編輯距離表示增加,刪除,取代或交換一個任意字符。關鍵字 adminstrator (在第六個字母出少了字符“i”)和原始關鍵字之間相差一個編輯距離,因為它刪除了一個字符。同樣 sadministrator 和原始關鍵字之間也是相差一個編輯距離,因為它在前面添加了一個字符。administratro 也與原始關鍵字有一個編輯距離,因為它將最后兩個字符交換了順序。
在編輯距離查詢中也可以精確指定編輯距離:
- 查詢語句:administrator~1 匹配一個編輯距離以內的內容。
- 查詢語句:administrator~2 匹配兩個編輯距離以內的內容(如果沒有提供編輯距離的話,這個就是默認值)。
- 查詢語句:administrator~N 匹配 N 個編輯距離以內的內容。
注意,任何編輯距離大於 2 的查詢將會使查詢速度變得很慢。如果編輯距離在 2 以內,那么將會使用很高效率的 Levenshtein 自動機(Levenshtein automaton),但是如果編輯距離大於 2,將會退回到更慢的編輯距離實現。
臨近查詢
在前面,我們看到了編輯距離查詢是如何查找相似的關鍵字,而不是進行精確匹配。編輯距離的概念適用於關鍵字中字符的變換或短語中各個單詞之間的變化。
如果你想要通過 Solr 的索引查詢公司中所有員工的檔案。一種方法是枚舉出公司中所有可能的職位:
-
查詢語句:”chief executive officer” OR “chief financial officer” OR “chief
marketing officer” OR “chief technology officer” OR …
當然,這種查詢的前提是你需要知道公司中所有可能的職位,這當然不現實。另外的一種解決方案是單獨搜索每個關鍵字:
- 查詢語句: chief AND officer
這將會匹配所有可能的用例,但是同時也會匹配所有包含了這兩個關鍵字的文檔。例如:One chief concern arising from the incident was the safety of the police officer on duty。這個文檔明顯不符合我們的要求,但是如果使用上面的查詢語句,那么將會返回這個文檔。
Solr 提供了解決這種問題的方案:臨近插敘。在上面的例子中,比較好的策略是請求 Solr 返回所有包含了關鍵字 chief 和關鍵字 officer 臨近的文檔。這可以通過下面的查詢語句樣例來實現:
- 查詢語句: “chief officer”~1
解釋:chief 和 officer 之間最多只能有一個距離
例子:”chief executive officer”, “chief financial officer” - 查詢語句:”chief officer”~2
解釋:chief 和 officer 之間最多只能有兩個編輯距離
例子:”chief business development officer”, “officer chief” - 查詢語句:”chief officer”~N
解釋:查詢 chief 和 officer 之間有 N 個編輯距離。
事實上,對短語進行精確匹配的查詢語句 “chief development officer” 很容易改寫成 “chief development officer”~0。這兩個查詢都返回相同的結果,因為在第二個查詢語句中,編輯距離設置為 0,所以和精確查詢得到的結果是相同的。這兩種機制都需要使用到 Solr 中存儲的關鍵字位置(前面的文章介紹過)來計算編輯距離。還有一點需要注意的是,臨近查詢並不是完全按照編輯距離的定義來進行查詢,因為它的查詢結果中,所有的關鍵字都必須存在。而編輯距離查詢的定義中,可以對關鍵字進行刪除和修改。
但是其它的編輯距離定義依舊保留,例如增加和換位。順着這條線,你可能會注意到,你需要設置 2 進行臨近查詢的時候(”chief officer”~2)才能查詢出文本 officer chief。這是因為第一次編輯將 chief 和 officer 修改成相同的位置;第二次編輯將 chief 才能將 chief 編輯到 officer 后面。這也再次說明了臨近查詢使用的並不是真正的編輯距離(在編輯距離中,位置互換的編輯距離只能算 1)。