這是半年前沒有對外寫的文章,現在拿出來分享下。可能會有一些不正確或不嚴謹的地方,某些語言可能比較輕浮,請見諒。
以上一篇的email數據表為例:
數據結構:
CREATE TABLE email (
emailid mediumint(8) unsigned NOT NULL auto_increment COMMENT '郵件id',
fromid int(10) unsigned NOT NULL default '0' COMMENT '發送人ID',
toid int(10) unsigned NOT NULL default '0' COMMENT '收件人ID',
content text unsigned NOT NULL COMMENT '郵件內容',
subject varchar(100) unsigned NOT NULL COMMENT '郵件標題',
sendtime int(10) NOT NULL COMMENT '發送時間',
attachment varchar(100) NOT NULL COMMENT '附件ID,以逗號分割', PRIMARY KEY (emailid),
) ENGINE=MyISAM';
使用打開控制台,必需打開控制台PHP才能連接到sphinx(確保你已經建立好索引源):
d:\coreseek\bin\searchd -c d:\coreseek\bin\sphinx.conf
coreseek/api目錄下提供了PHP的接口文件 sphinxapi.php,這個文件包含一個SphinxClient的類
在PHP引入這個文件,new一下
$sphinx = new SphinxClient();
//sphinx的主機名和端口
$sphinx->SetServer ( 'loclahost', 9312 );
//設置返回結果集為php數組格式
$sphinx->SetArrayResult ( true );
//匹配結果的偏移量,參數的意義依次為:起始位置,返回結果條數,最大匹配條數
$sphinx->SetLimits(0, 20, 1000);
//最大搜索時間
$sphinx->SetMaxQueryTime(10);
//執行簡單的搜索,這個搜索將會查詢所有字段的信息,要查詢指定的字段請繼續看下文
$index = 'email' //索引源是配置文件中的 index 類,如果有多個索引源可使用,號隔開:'email,diary' 或者使用'*'號代表全部索引源
$result = $sphinx->query ('搜索關鍵字', $index);
echo '
';
print_r($result);
echo '
';
$result是一個數組,其中
total是匹配到的數據總數量
matches是匹配的數據,包含id,attrs這些信息
words是搜索關鍵字的分詞
你可能奇怪為什麼沒有郵件的內容這些信息,其實sphinx並不會返回像mysql那樣的數據數組,因為sphinx本來就沒有記錄完整的數據,只記錄被分詞後的數據。
具體還要看matches數組,matches中的ID就是指配置文件中sql_query SELECT語句中的第一個字段,我們配置文件中是這樣的
sql_query = SELECT emailid,fromid,toid,subject,content,sendtime,attachement FROM email
所以matches中的ID是指emailid
至於weight是指匹配的權重,一般權重越高被返回的優先度也最高,匹配權重相關內容請參考官方文檔
attrs是配置文件中sql_attr_ 中的信息,稍後會提到這些屬性的用法
說了這麼多,即使搜索到結果也不是我們想要的email數據,但事實sphinx是不記錄真實數據的,所以要獲取到真實email數據還要根據matches中的ID去搜索mysql的email表,但總體來說這樣一來一回的速度還是遠遠比mysql的LIKE快得多,前提是幾十萬數據量以上,否則用sphinx只會更慢。
接下來介紹sphinx一些類似mysql條件的用法
//emailid的范圍
$sphinx->SetIdRange($min, $max);
//屬性過濾,可過濾的屬性必需在配置文件中設置sql_attr_ ,之前我們定義了這些
sql_attr_uint = fromid
sql_attr_uint = toid
sql_attr_timestamp = sendtime
//如果你想再次修改這些屬性,配置完成後記得重新建立索引才能生效
//指定一些值
$sphinx->SetFilter('fromid', array(1,2)); //fromid的值只能是1或者2
//和以上條件相反,可增加第三個參數
$sphinx->SetFilter('fromid', array(1,2), false); //fromid的值不能是1或者2
//指定一個值的范圍
$sphinx->SetFilterRange('toid', 5, 200); //toid的值在5-200之間
//和以上條件相反,可增加第三個參數
$sphinx->SetFilterRange('toid', 5, 200, false); //toid的值在5-200以外
//執行搜索
$result = $sphinx->query('關鍵字', '*');
排序模式
可使用如下模式對搜索結果排序:
SPH_SORT_RELEVANCE 模式, 按相關度降序排列(最好的匹配排在最前面)
SPH_SORT_ATTR_DESC 模式, 按屬性降序排列 (屬性值越大的越是排在前面)
SPH_SORT_ATTR_ASC 模式, 按屬性升序排列(屬性值越小的越是排在前面)
SPH_SORT_TIME_SEGMENTS 模式, 先按時間段(最近一小時/天/周/月)降序,再按相關度降序
SPH_SORT_EXTENDED 模式, 按一種類似SQL的方式將列組合起來,升序或降序排列。
SPH_SORT_EXPR 模式,按某個算術表達式排序
//使用屬性排序
//以fromid倒序排序,注意當再次使用SetSortMode會覆蓋上一個排序
$sphinx->SetSortMode ( "SPH_SORT_ATTR_DESC", 'fromid');
//如果要使用多個字段排序可使用SPH_SORT_EXTENDED模式
//@id是sphinx內置關鍵字,這裡指emailid,至於為什麼是emailid,自己思考一下
$sphinx->SetSortMode ( "SPH_SORT_ATTR_DESC", 'fromid ASC, toid DESC, @id DESC');
//執行搜索
$result = $sphinx->query('關鍵字', '*');
//更多請查看官方文檔排序模式的說明
匹配模式
有如下可選的匹配模式:
SPH_MATCH_ALL, 匹配所有查詢詞(默認模式);
SPH_MATCH_ANY, 匹配查詢詞中的任意一個;
SPH_MATCH_PHRASE, 將整個查詢看作一個詞組,要求按順序完整匹配;
SPH_MATCH_BOOLEAN, 將查詢看作一個布爾表達式
SPH_MATCH_EXTENDED, 將查詢看作一個CoreSeek/Sphinx內部查詢語言的表達式 . 從版本Coreseek 3/Sphinx 0.9.9開始, 這個選項被選項SPH_MATCH_EXTENDED2代替,它提供了更多功能和更佳的性能。保留這個選項是為了與遺留的舊代碼兼容——這樣即使Sphinx及其組件包括API升級的時候,舊的應用程序代碼還能夠繼續工作。
SPH_MATCH_EXTENDED2, 使用第二版的“擴展匹配模式”對查詢進行匹配.
SPH_MATCH_FULLSCAN, 強制使用下文所述的“完整掃描”模式來對查詢進行匹配。注意,在此模式下,所有的查詢詞都被忽略,盡管過濾器、過濾器范圍以及分組仍然起作用,但任何文本匹配都不會發生.
我們要關注的主要是SPH_MATCH_EXTENDED2擴展匹配模式,擴展匹配模式允許使用一些像mysql的條件語句
//設置擴展匹配模式
$sphinx->SetMatchMode ( "SPH_MATCH_EXTENDED2" );
//查詢中使用條件語句,字段用@開頭,搜索內容包含測試,toid等於1的郵件:
$result = $sphinx->query('@content (測試) & @toid =1', '*');
//用括號和&(與)、|、(或者)、-(非,即!=)設置更復雜的條件
$result = $sphinx->query('(@content (測試) & @subject =呃) | (@fromid -(100))', '*');
//更多語法請查看官方文檔匹配模式的說明
擴展匹配模式中值得一提的是搜索的字段,如果該字段被設置屬性,那麼擴展匹配搜索的字段默認是不包含這些屬性的,只能用SetFilter()或者SetFilterRange()之類
之前我們設置了fromid、toid、sendtime為屬性,但又想在擴展匹配模式中又想用作條件該怎麼辦?
只要在sql_query語句中再選擇多一次該字段就可以了
sql_query = SELECT emailid,fromid,fromid,toid,toid,subject,content,sendtime,sendtime,attachement FROM email
//設置完成記得重新建立索引
更多條件技巧
只是一些技巧,但不建議使用的部署環境中,至於為什麼,請看文章結尾
<、<=、>、>=
默認sphinx沒有這些比較符。
假如我想郵件的發送時間大於某一日期怎麼辦?用SetFilterRange()方法模擬一下
//大於等於某一時間截$time
$sphinx->SetFilterRange('sendtime', $time, 10000000000) //時間截最大是10個9,再加1是不可超越了。。
//大於某一時間截$time
$sphinx->SetFilterRange('sendtime', $time+1, 10000000000)
//小於等於某一時間截$time
$sphinx->SetFilterRange('sendtime', -1, $time) //時間截最小是0,所以應該減1
//大於某一時間截$time
$sphinx->SetFilterRange('sendtime', -1, $time - 1)
IS NOT NULL
怎樣搜索為空的字段,比如我要搜索附件為空的郵件,有人可能會想 @attachment ('')不就可以了嗎?其實這是搜索兩個單引號。。。sphinx搜索的字符串不用加引號的
目前sphinx是沒有提供這樣的功能,其實可以在mysql語句上作手腳:
sql_query = SELECT emailid,fromid,toidsubject,content,sendtime,attachement != '' as attach is not null FROM email //這裡返回了一個新字段attachisnotnull,當attachisnotnull為1的時候附件就不為空了
//設置完成記得重新建立索引
FIND_IN_SET()
搜索包含某一附件的郵件,mysql習慣用FIND_IN_SET這麼簡單一句就搞定了,在sphinx中必需在配置裡設置屬性sql_attr_multi 多值屬性(MVA):
sql_attr_multi = attachment #attachment可以是逗號分隔的附件ID,或者是空格、分號等sphinx都能識別
//設置完成記得重新建立索引
然後PHP中可以使用SetFilter()
//搜索包含附件ID為1或2郵件,mysql語法是這樣FIND_IN_SET(`attachment`, '1,2')
$sphinx->SetFilter('attachment', array(1,2))
//可以使用SetFilterRange,搜索包含附件ID在50-100范圍的郵件
$sphinx->SetFilterRange('attachment', 50, 100)
總結
如果你想一個免費、好用、極速的全文搜索引擎,sphinx無疑是最好的選擇,但是不要忘記sphinx的目的:全文檢索。不要去想那些亂七八糟條件。你想要把sphinx搜索變得像mysql那樣靈活,可完全單獨用在一些復雜的多條件搜索,像某些郵件的高級搜索,那麼我建議你還是多花點時間在PHP或者mysql代碼的優化上,因為那樣可能會讓你的搜索變得更慢。
最好的方法是以最簡單的方法搜索到內容,將ID交還mysql數據庫搜索。