Sphinx添加了至關多的匹配和rank模式,而且將添加更多。一些不一樣的問題常常被提出,從「我怎樣讓指定的文檔排在第一位」到 「我怎麼根據匹配度來評定星級」,實際處理要歸結於內在的匹配和排序。算法
匹配方式有基礎匹配模式和拓展的匹配模式。數組
Sphinx 1.10版本中使用的兩個最重要的權重因子是:dom
1)經典統計學BM25因子,從80年代開始被大部分的搜索引擎使用,函數
2)Sphinx特有的短語類似因子。性能
根據Sphinx排序使用的準確算法,能夠經過調整字段權重微調排序。測試
雖然排序因子多是整型,布爾型,浮點數或者其餘任何可能的值,但權重值必定是個單標量值。在Sphinx裏,權重值是一個整數,浮點數權重值能夠經過各類各樣方法映射到一個整數值。this
SphinxAPI 提供兩個不一樣的方法,分別是 MatchMode 和RankingMode。搜索引擎
MatchMode 匹配模式都是爲了遺留和兼容。而RankingMode模式是關於Sphinx計算相關度的。spa
在版本0.9.8以前,Sphinx只有匹配模式,而且每一個匹配模式是用不一樣的代碼路徑實現。每一個代碼路徑實現一組不一樣類型的匹配和排序。例如,SPH_MATCH_ALL要求全部的關鍵字都出現,而且只用phrase proximity計算文檔的權重。SPH_MATCH_ANY要求任何關鍵字中的一個,而且用不一樣的方式計算文檔權重等等。.net
在版本0.9.8中,開始啓用一個全新的,統一的匹配引擎。爲了不在使用它工做時破壞兼容性,在版本0.9.8中,只提供一個獨立的匹配模式,叫作SPH_MATCH_EXTENDED2。到版本0.9.9時,顯然新的引擎已經變的穩定而且表現的足夠好,於是咱們同意重新引擎中移除全部遺留的代碼路徑。所以從版本0.9.9開始,全部的查詢都用統一的引擎處理,這跟以前的狀況不一樣,並且維護困難(指以前維護困難)。所以實際上如今全部的匹配模式都只是歷史遺留。
固然Sphinx仍然繼續兼容那些遺留的模式,而且當你使用其中的一種時,它會自動轉換成一個簡單的查詢短語代碼(徹底忽略查詢語法)而後自動選擇一種適當的排序模式。但其實原本就是這樣。一切都是由統一的引擎處理,所以,文檔權重(即@weight)只跟選擇的rank模式有關。
例如,下面兩個查詢將獲得徹底同樣的權重(一樣同樣的處理時間):
// 1st route
$cl->SetMatchMode ( SPH_MATCH_ALL );
$cl->Query ( "helloworld" );
// 2nd route
$cl->SetMatchMode ( SPH_MATCH_EXTENDED2 );
$cl->SetRankingMode ( SPH_RANK_PROXIMITY );
$cl->Query ("helloworld" );
注意第二種方法容許使用(@titlehello world)語法,由於這種匹配模式容許這樣作。第一種是不容許的,由於在那種匹配模式中全部的特殊操做符會被忽略,@title會被當成一個關鍵字。
MatchMode 功能是過濾關鍵字和選擇一個合適的排序。它至關於一個歷史調用,由於將不會再有新的匹配模式。
RankingMode只是讓你明確的指定一個排序方式。
rank模式,或者簡稱排序,能夠正式的定義成函數:對一個給定的查詢和文檔參數計算相關值(權重)。
相關度基本上是主觀的,所以沒有一個適合全部的排序模式,未來也不會有。所以有多個不一樣的因子用於計算最終的權重,有無數的方法合併這些因子爲一個權重,討論這些是另外單獨帖子的主題。
以官方C的API爲例,sphinx自帶的一些匹配模式以下:
匹配查詢詞中的任意一個。
一般想搜索到儘量多的一句話中的內容,使用的是SPH_MATCH_ANY,但使用它以後,任何關鍵詞中的字均可能作爲一個單獨的詞進行搜索。這種匹配模式對詞頻也頗有權重,這種搜索結果通常不是很準確
將整個查詢參數看做一個詞組,要求按順序完整匹配纔會搜索出結果,搜索時會過濾掉特殊符號,比較符合須要搜索出相關度最高的結果。
在建立鏈接對象時,設置:
sphinx_client *client;
client->mode=SPH_MATCH_PHRASE;
匹配全部查詢詞(默認模式)
在建立鏈接對象時,設置:
sphinx_client * client;
client->mode= SPH_MATCH_ALL;
將查詢看做一個布爾表達式
將查詢看做一個Sphinx內部查詢語言的表達式
要搜索的關鍵詞同時存在纔會被搜索出來。由於SPHINX默認不是經過空格分詞的。而是經過""來分。好比兩個關鍵詞:咱們他是。
若是單這樣寫sphinx_query (client, 「咱們他是「, index, NULL );使用any模式會折成我 們 他 是 。彷佛是一元分詞法。而使用extended2則要搜索的字段同時存在這2個詞才能夠被搜索到。若是寫成 sphinx_query ( client,("\"咱們\"|\"他是\"",index);那麼他就會分紅咱們和他是2個詞。並且同時存在的權重會較高。
在建立鏈接對象時,設置:
sphinx_client *client;
client->mode=SPH_MATCH_EXTENDED2;
使用徹底掃描,忽略查詢詞彙
Rank模式爲拓展的匹配模式。
以官方C的API爲例,設置爲拓展匹配模式SPH_MATCH_EXTENDED2後,請求的匹配方式將由其rank模式來決定。
可以使用以下模式對匹配搜索結果:
禁用排序的模式,這是最快的模式。實際上這種模式與布爾搜索相同。全部的匹配項都被賦予權重1
排序模式只是簡單的給每一個文檔賦權重爲1.
weight = 1
爲何這樣而且實際跳過全部的排序呢?答案就是性能。
若是你須要搜索結果按價格排序,那爲何要浪費CPU週期來處理耗時而你並不須要的排序呢。
排序模式計算全部的關鍵字出現的次數並乘以用戶設置的字段權重。
weight = 0
foreach ( field inmatching_fields )
weight += num_keyword_occurrences ( field )
注意它計算全部關鍵字出現的次數,而不僅是惟一的關鍵字。所以1個匹配的關鍵字出現3次和3個不一樣關鍵字出現1次是同樣的。
排序模式返回一個匹配字段的位標識。
weight = 0
foreach ( field inmatching_fields )
set_bit ( weight, index_of ( field ) )
// or in otherwords, weight |= ( 1 << index_of ( field ) )
經過簡單的短語類似算法獲得一個權重值:
weight = doc_phrase_weight
由短語權重的定義可知,當文檔匹配了查詢可是沒有保持匹配關鍵字的順序,全部這樣的文檔的權重都爲1.很顯然,它跟建議使用的PROXIMITY_BM25排序模式獲得的結果並無區別。相關的搜索性能影響能夠忽略不計。
短語類似,與上面BM25截然相反,根本不關心關鍵詞出現的頻率和查詢關鍵詞在文檔中的位置。代替BM25使用的關鍵詞頻率,Sphinx分析關鍵詞在每一個字段的位置,而且用最長公共子串算法(LCS)計算關鍵詞和文檔的短語類似度。
基本上,每一個字段的短語類似度就是一些關鍵詞在文檔中出現而且順序和查詢一致。這裏是一些例子:
1) query = one twothree, field = one and two three
field_phrase_weight = 2 (because 2-keywordlong "two three" subphrase matched)
2) query = one twothree, field = one and two and three
field_phrase_weight = 1 (becausesingle keywords matched but no subphrase did)
3) query = one twothree, field = nothing matches at all
field_phrase_weight = 0
每一個字段的短語權重將乘以每一個字段的權重值,字段權重值經過調用SetFieldWeights() API或者在SphinxQL中的field_weights選項設置的,而後再所有相加起來生成每一個文檔的短語權重。
字段的默認權重值爲1,不能設成小於1的值。整個短語類似算法的僞代碼以下所示:
doc_phrase_weight = 0
foreach ( field inmatching_fields )
{
field_phrase_weight = max_common_subsequence_length ( query, field )
doc_phrase_weight += user_weight ( field ) * field_phrase_weight
}
Example:
doc_title = hello world
doc_body = the world is awonderful place
query = hello world
query_title_weight = 5
query_body_weight = 3
title_phrase_weight = 2
body_phrase_weight = 1
doc_phrase_weight = 2*5+3*1 = 13
正是因爲短語類似因子保證了越類似的短語將排在前面,而精確匹配的短語將排在很是前面。可使用字段權重值來調整排序,例如,上面例子中,匹配單個關鍵字的標題的權重值和匹配兩個關鍵字短語的內容同樣。
用來模擬匹配模式的MATCH_ANY模式,結合了短語類似算法和匹配關鍵字次數,所以每一個字段默認權重,a)較長子短語匹配(即更大短語類似)在任何字段將得到更高的排序,b)與短語類似一致,文檔匹配不一樣關鍵字越多則排名越高。換句話說,先看最大匹配子短語的長度,再看匹配不一樣關鍵字的數量。
僞代碼以下:
k = 0
foreach ( field inall_fields )
k += user_weight ( field ) * num_keywords ( query )
weight = 0
foreach ( field inmatching_fields )
{
field_phrase_weight = max_common_subsequence_length ( query, field )
field_rank = ( field_phrase_weight * k + num_matching_keywords ( field ) )
weight += user_weight ( field ) * field_rank
}
它不使用BM25,由於遺留的模式沒有使用,要保持兼容。
排序模式計算匹配字段用戶設置的權重和BM25的總和.
field_weights = 0
foreach ( field inmatching_fields )
field_weights += user_weight ( field )
weight = field_weights*1000 + integer(doc_bm25*999)
和PROXIMITY_BM25模式基本類似,除了用戶權重沒有乘以每一個字段的短語類似值。不使用短語類似容許引擎只使用文檔列表來評估搜索,跳過處理關鍵字出現。除非你的文檔很是短(think tweets, titles, etc),關鍵字出現列表比文檔列表大,而且須要更多的時間去處理。所以BM25比其餘任何類似算法快。
一樣,不少其餘搜索系統默認使用BM25排序模式,或者有的只提供它作爲惟一選擇。所以當作性能測試展現的時候使用BM25排序可能有意義。
BM25是一個只依賴於匹配關鍵字出現頻率的浮點數值。Frequencies in question are in-document and in-collectionfrequencies.基本上,關鍵字和/或在文檔字段中出現屢次,那個文檔的權重越大,這是很罕見的。
統計相關度計算模式,僅使用 BM25 評分計算(與大多數全文檢索引擎相同)。這個模式比較快,可是可能使包含多個詞的查詢的結果質量降低。
標準的BM25實如今Wikipedia article on BM25解釋的很是明白,可是Sphinx使用的是稍微修改過的變體。首先,考慮到性能緣由,咱們計算全部的關鍵字在文檔中出現的次數,而不僅是計算匹配的關鍵字。例如(@title 「hello world」)查詢只在標題中匹配「hello world」的單一實例,它的BM25的計算結果和(hello world)查詢同樣,(hello world)查詢文檔中匹配全部同時出現關鍵字的實例。第二,咱們不強制任何文檔屬性,所以不須要文檔的長度,這樣咱們也忽略了文檔長度(等於在原始的BM25中設置 b=0)。所有的變化都是內部的,在咱們的測試中,使用原始BM25獲得的計算結果不足夠說明排序關聯性能做用的改善。在Sphinx中使用的BM25計算算法的僞代碼以下:
BM25 = 0
foreach ( keyword inmatching_keywords )
{
n = total_matching_documents ( keyword )
N = total_documents_in_collection
k1 = 1.2
TF = current_document_occurrence_count ( keyword )
IDF = log((N-n+1)/n) / log(1+N)
BM25 = BM25 + TF*IDF/(TF+k1)
}
// normalize to 0..1 range
BM25 = 0.5 + BM25 / ( 2*num_keywords ( query ) )
TF是指在一個文檔中被排序的檢索詞頻。它是基於在一個文檔內關鍵字出現的次數,可是由於用對數函數平滑處理,所以出現1000次並不會獲得1000倍的影響,而是1。
TF通常在0到1之間變化,可是在條件k=1.2的狀況下,它實現的變化範圍是0.4545…到1之間。
IDF是指在整個文檔集中的反向文檔頻率。常見詞(如「the」 or 「to」等)的IDF值小,罕見詞的IDF值大,當一個關鍵詞只在一個文檔中出現時,達到峯值IDF=1,而當關鍵詞在每一個索引文檔都出現時,IDF=-1。
所以,就像你上面看到的代碼,BM25值當關鍵字出現頻率小時會增大,相反在文檔中頻繁出現的話,BM25值會減少。要注意的是當關鍵詞過分頻繁匹配索引文檔超過一半以上時會下降BM25的值!事實上,當一個關鍵詞出如今90%的文檔中而不多的文檔沒有包含關鍵詞時,或許大概會更有趣,應該獲得更大的權重。
怎樣計算最大可能的權重
怎樣計算最大可能的權重,而後根據返回的權重評定A-F等級,或者百分比,或者其餘任何東西?
從前面的章節能夠看到沒有簡單的辦法能夠實現。最大權重依靠於選擇的排序模式和特定的查詢。
例如,PROXIMITY_BM25模式權重的上界應該是
max_weight = num_keywords * sum ( user_field_weights ) * 1000 + 999
但這個上界能夠達到嗎?實際上幾乎不可能,由於那須要a)精確短語匹配b)在全部的字段c)附加的BM25峯值達到999。
此外,若是查詢使用字段權限符將會怎樣?例如:@title hello world? 在那種狀況咱們的上界將永遠不會被達到,由於咱們除了標題字段外的其餘字段都不會匹配。
強調須要字段
便可以使用一個適合你需求的排序模式,或者使用Sphinx運行時表達式來計算所須要的結果集並排序。
例子以下,把精確匹配排在前面能夠用表達式模擬排序:
SELECT *, @weight+IF(fieldcrc==$querycrc,1000,0) AS myweight ...
ORDER BY myweight DESC
fieldcrc是CRC(field)屬性在索引時計算並存在索引文件裏,querycrc是在搜索時計算CRC(query)。
例子以下,代替嚴格檢查CRC值匹配,你能夠索引並保存字段長度,而後經過表達式把越短的字段排越前面
SELECT *, @weight+ln(len+1)*1000 AS myweight ...
例子,當搜索一個關鍵字時爲了強制一個文檔排的更靠前,你能夠建立一個單獨的字段,放超級重要的關鍵字,而後給這個字段賦一個很高的權重。(不要把權重設置超過1000000)
使用詞組評分和 BM25 評分。
計算權重以下:
weight = doc_phrase_weight*1000 + integer(doc_bm25*999)
所以文檔短語類似度是主要因子,BM25是輔助部分,當相同的短語類似時進行附加的文檔排序。BM25在0到1之間,所以最終權重包含的最後3個數字是由BM25決定的,全部其餘的數字用於短語權重。
若是須要要把結果精確匹配排在前面,更進一步改善PROXIMITY_BM25模式,版本1.10-beta中加入SPH04模式。
短語類似仍然是主導因素,可是當給定一個短語類似的時候,在字段最開始匹配將排序更高,若是是整個字段徹底匹配的話將排到最高處。
例如,當查詢「Market Street」,SPH04模式基本上將某個字段徹底匹配「Market Street」的文檔排序在最前面,接着排像「Market Street Grocery」這樣在字段最開始匹配的文檔,而後排像「WestMarket Street」這樣在字段某處有與短語相匹配的文檔,最後排那些有包含短語全部關鍵字但不是一個短語的文檔(例如,「Flea Market on 26th Street」)。
僞代碼以下,
field_weights = 0
foreach ( field inmatching_fields )
{
f = 4*max_common_subsequence_length ( query, field )
if ( exact_field_match ( query, field ) )
f += 3
else if ( first_keyword_matches ( query, field ) )
f += 2
field_weights += f * user_weight ( field )
}
weight = field_weights*1000 + integer(doc_bm25*999)
在1.10-beta版本,Sphinx有8種不一樣的排序模式,而且在未來還會添加更多的。每一個排序模式計算獲得不一樣的權重值,所以可能不會適合一個特殊的方案。
不一樣的rank模式使用一個或多個rank因子。
(1)短語類似度和BM25
短語類似度設計成比BM25須要更多的計算,由於它須要計算全部在文檔中匹配的關鍵詞,而不只僅只計算文檔自己。Sphinx默認使用短語類似算法,由於咱們相信這個產生更好的搜索質量。固然你也能夠選擇使用一個更輕量級的排序器來省掉這些昂貴的類似計算。
短語類似和BM25是兩個最重要的因子,最終的權重值是由排序模式決定的,一個或者多個因子通過特殊函數的處理獲得一個值。
有兩種遺留的排序模式(PROXIMITY,MATCHANY)是隻依靠短語類似算法,並分別用於模擬MATCH_ALL 和MATCH_ANY兩種遺留模式。
有三種排序模式(BM25,PROXIMITY_BM25, SPH04)是能夠合併短語類似、BM25還有其餘。SphinxQL如今默認是用PROXIMITY_BM25。BM25被推薦作爲一個合適的快速排序模式,不亞於其餘系統。SPH04是創建在PROXIMITY_BM25之上,但另外排序精確字段匹配,字段開頭匹配比僅僅只是匹配等級高。
PROXIMITY_BM25 和 SPH04被指望產生最佳的質量,可是有可能須要特殊的結果。
(2)關鍵字次數
有三種簡單的排序模式(NONE,WORDCOUNT, FIELDMASK)不作任何事,只統計關鍵字出現的次數,而後分別的返回匹配字段的位標識。它們在根本不須要排序或者因爲應用端以某種方式計算時頗有用。
(3)性能比較
選擇的排序模式會嚴重影響搜索的性能。
NONE模式顯明是最快的排序模式。
處理關鍵字位置(出現次數)是典型的最耗時的部分,所以不須要處理這部分的排序模式(FIELDMASK, BM25)老是比其餘的快,也須要較少的磁盤IO(不須要讀取位置)。
處理關鍵字位置的排序模式(WORDCOUNT,PROXIMITY, MATCHANY, PROXIMITY_BM25, SPH04)只在CPU影響上有所區別。
使用排序模式和文檔屬性對搜索結果排序。
已知的文檔的內置屬性:
@id (匹配文檔的 ID)
@weight (匹配權值)
@rank (等同 weight)
@relevance (等同 weight)
@random (隨機順序返回結果)
其餘的屬性爲用戶設置屬性。
以官方C的API爲例,模式以下:
按相關度降序排列(最好的匹配排在最前面)
SPH_SORT_RELEVANCE忽略任何附加的參數,永遠按相關度評分排序。全部其他的模式都要求額外的排序子句,子句的語法跟具體的模式有關。
SPH_SORT_RELEVANCE模式等價於在擴展模式中按"weightDESC, id ASC"排序。
按屬性降序排列 (屬性值越大的越是排在前面)
SPH_SORT_ATTR_DESC等價於"attribute DESC, weight DESC, id ASC"。
按屬性升序排列(屬性值越小的越是排在前面)
SPH_SORT_ATTR_ASC模式等價於"attribute ASC, weight DESC, id ASC"
先按時間段(最近一小時/天/周/月)降序,再按相關度降序
在SPH_SORT_TIME_SEGMENTS模式中,屬性值被分割成「時間段」,而後先按時間段排序,再按相關度排序。
時間段是根據搜索發生時的當前時間戳計算的,所以結果隨時間而變化。
時間段的分法固化在搜索程序中了,但若是須要,也能夠比較容易地改變(須要修改源碼)。
這種模式是爲了方便對Blog日誌和新聞提要等的搜索而增長的。使用這個模式時,處於更近時間段的記錄會排在前面,可是在同一時間段中的記錄又根據相關度排序-這不一樣於單純按時間戳排序而不考慮相關度。
SPH_SORT_ATTR_ASC,SPH_SORT_ATTR_DESC以及SPH_SORT_TIME_SEGMENTS這三個模式僅要求一個屬性名。
按一種相似SQL的方式將列組合起來,升序或降序排列。
在 SPH_SORT_EXTENDED 模式中,您能夠指定一個相似SQL的排序表達式,但涉及的屬性(包括內部屬性)不能超過5個。
例如:
sphinx_set_sort_mode(client,SPH_SORT_EXTENDED,"@relevanceDESC, price ASC, @id DESC")
relevance和id是內部屬性,而price是用戶定義屬性
只要作了相關設置,無論是內部屬性(引擎動態計算出來的那些屬性)仍是用戶定義的屬性就均可以使用。內部屬性的名字必須用特殊符號@開頭,用戶屬性按原樣使用就好了。
表達式排序模式使您能夠對匹配項按任何算術表達式排序,表達式中的項能夠是屬性值,內部屬性(id和weight),算術運算符和一些內建的函數。
例如:
sphinx_set_sort_mode(client,SPH_SORT_EXPR,"weight+ ( user_karma + ln(pageviews) )*0.1")
支持的運算符和函數爲:運算符: +, -,*, /, <, >
算術表達式是模仿MySQL設計的。函數接受參數,參數的數目根據具體函數的不一樣而不一樣。
若是須要按照權重排序,可使用如下方式:
按照字段命中計算權值
sphinx_set_match_mode( client,SPH_MATCH_EXTENDED2 );//設置匹配模式
sphinx_set_ranking_mode ( client, SPH_RANK_PROXIMITY);//設置評分模式
const char * field_names[3];
int field_weights[3];
field_names[0] = "industry";
field_weights[0] = 1;
field_names[1] = "area";
field_weights[1] = 2;
field_names[2] = "expr";
field_weights[2] = 2;
//設置字段的權重,若是area命中,那麼權重算2
sphinx_set_field_weights ( client, 3, field_names, field_weights );
//按照權重排序
sphinx_set_sort_mode ( client, SPH_SORT_EXPR, "@weight");//內部屬性的名字必須用特殊符號@開頭
可以使用以下模式對搜索結果排序:
在 SPH_SORT_EXTENDED 模式中,您能夠指定一個相似SQL的排序表達式,但涉及的屬性(包括內部屬性)不能超過5個,例如:
@relevance DESC, price ASC, @id DESC
只要作了相關設置,無論是內部屬性(引擎動態計算出來的那些屬性)仍是用戶定義的屬性就均可以使用。內部屬性的名字必須用特殊符號@開頭,用戶屬性按原樣使用就好了。在上面的例子裏,@relevance
和@id
是內部屬性,而price
是用戶定義屬性。
已知的內置屬性:
@rank
和 @relevance
只是 @weight
的別名.
PHP 的 API 中只有 SetSortMode($mode, $sortBy) 方法.從上面的文檔說明中可知,若是須要設置多個字段依次排序。須要使用下列代碼:
$SphinxClient->SetSortMode(SPH_SORT_EXTENDED, "columnA ASC, columnB DESC, columnC ASC");
沒法方便的同時設置多個排序。經過繼承 SphinxClient 類添加了一個新方法,實現了經過數組設置多個排序。
/** * 設定多重排序條件 * @param Array $orders 排序條件數組 * @example $order = array ( * '0' => array ('attribute' => 'columnname', 'order' => RentSearch::DESC), * '1' => array (//...), * ); */ public function multiOrder($orders) { $orderType = SPH_SORT_EXTENDED; $orderString = ""; foreach ($orders as $order) { switch ($order['order']) { case self::ASC : $orderString += $order['attribute'] . ' ASC, '; break; case self::DESC : $orderString += $order['attribute'] . ' DESC, '; break; default : $orderString += $order['attribute'] . ' DESC, '; break; } } $orderString = substr($orderString, 0, strlen($orderString)-2); $this->SetSortModel($orderType, $orderString); }