目錄
1. Sphinx簡介
1.1. 什么是全文檢索
1.2. 介紹
1.3. Sphinx的特性
2. Sphinx安裝(For MySQL)
2.1. Windows下安裝
2.2. Linux下安裝 3. 實例說明
4. Sphinx配置
5. 運行Sphinx
6. 搜索(翻譯)
6.1. 匹配模式
6.2. 布爾查詢語法(Boolean query syntax)
6.3. 擴展查詢語法(Extended query syntax)
6.4. 權重(匹配度,Weight)
7. 如何調用Sphinx
8. SphinxSE的SQL查詢例子演練
9. 如何自動重建索引
10. 相關資源
1. Sphinx簡介
1.1. 什么是全文檢索
全文檢索是指以文檔的全部文本信息作為檢索對象的一種信息檢索技術。檢索的對象有可能是文章的標題,也有可能是文章的作者,也有可能是文章摘要或內容。
1.2. 介紹
Sphinx是一個基於SQL的全文檢索引擎,可以結合MySQL,PostgreSQL做全文搜索,它可以提供比數據庫本身更專業的搜索功能,使得應用程序更容易實現專業化的全文檢索。Sphinx特別為一些腳本語言設計搜索API接口,如PHP,Python,Perl,Ruby等,同時為MySQL也設計了一個存儲引擎插件。
1.3. Sphinx的特性
高速索引 (在新款CPU上,近10 MB/秒); 高速搜索 (2-4G的文本量中平均查詢速度不到0.1秒); 高可用性 (單CPU上最大可支持100 GB的文本,100M文檔); 提供良好的相關性排名 支持分布式搜索; 提供文檔摘要生成; 提供從MySQL內部的插件式存儲引擎上搜索 支持布爾,短語, 和近義詞查詢; 支持每個文檔多個全文檢索域(默認最大32個); 支持每個文檔多屬性; 支持斷詞; 支持單字節編碼與UTF-8編碼;
支持英文,俄文詞干提取和音標查詢 supports English stemming, Russian stemming, and Soundex for morphology;
支持MySQ(MyISAM和InnoDB 表都支持); 支持PostgreSQL.
2. Sphinx安裝(For MySQL) 2.1. Windows下安裝 從http://dev.mysql.com上下載MySQL5.0.45版安裝配置好MySQL,采用utf-8字符集
從Sphinx官網上http://www.sphinxsearch.com/downloads.html下載mysql-5.0.45-sphinxse-r871-win32.zip和sphinx-0.9.8-svn-r985-win32.zip
如果您的MySQL服務已啟動請先停止掉
解壓mysql-5.0.45-sphinxse-r871-win32.zip,將里面bin與share目錄覆蓋掉你的mysql安裝目錄下的相應目錄
解壓sphinx-0.9.8-svn-r985-win32.zip ,將里面的文件解壓到D:\sphinx
sphinx的配置與實際應用是相關的,因此以下我以例子進行說明,至此sphinx安裝部分結束
2.2. Linux下安裝 下載mysql-5.1.22-rc.tar.gz解壓至/root/mysql-5.1.22
下載sphinx-0.9.8-svn-r985.tar.gz,解壓至/root/sphinx-0.9.8-svn-r985
將/root/sphinx-0.9.8-svn-r985/mysqlse下的文件復制至/root/mysql-5.1.22/storage/sphinx
在/root/mysql-5.1.22目錄下運行
sh BUILD/autorun.sh ./configure --prefix=/usr/local/mysql --with-charset=utf8 --with-extra-charsets=all \ --enable-thread-safe-client --enable-assembler --with-readline --with-big-tables --with-plugins=sphinx make && make install groupadd mysql useradd –g mysql mysql chown mysql:mysql /usr/local/mysql -R cd /usr/local/mysql bin/mysql_install_db –user=mysql
(此時系統可能會提示:
[Warning] Storage engine 'SPHINX' has conflicting typecode. Assigning value 42.可忽略,不影響使用) cp /root/mysql-5.1.22/support-files/mysql.server /etc/init.d/mysqld chmod 700 /etc/init.d/mysqld cp /root/mysql-5.1.22/support-files/my-medium.cnf /etc/my.cnf /etc/init.d/mysqld start
(至些mysql啟動了)
然后進入mysql命令行,運行show engines,看是不是有一個叫sphinx的engine,有的話就表示sphinxSE(mysql的sphinx引擎)安裝正常了
進入/root/sphinx-0.9.8-svn-r985,運行
ldconfig /usr/local/mysql/lib/mysql ldconfig /usr/local/mysql/include/mysql ./configure --prefix=/usr/local/sphinx --with-mysql=/usr/local/mysql make && make install
3. 實例說明
為更好說明如何應用Sphinx,現結合實例說明,我們以網站的新聞文章表為例。我們想要對新聞文章表進行全文檢索(主要是標題與內容),新聞文章表的相關信息如下:
CREATE TABLE `eht_articles` ( `ARTICLESID` int(11) NOT NULL auto_increment, `TITLE` varchar(100) NOT NULL default '', `TITLECOLOR` varchar(20) default NULL, `AUTHOR` varchar(200) default NULL, `COMEFROM` varchar(200) default NULL, `KEYWORD` varchar(200) default NULL, `HTMLURL` varchar(200) default NULL, `CATALOGID` int(6) default NULL, `CONTENTS` mediumtext, `EDITUSERID` int(6) default NULL, `ADDTIME` int(10) default NULL, `UPDATETIME` int(10) default NULL, `HITS` int(6) default NULL, PRIMARY KEY (`ARTICLESID`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
在這個表中,我主要想對標題(TITLE)與內容(CONTENTS)字段進行全文檢索,在檢索過程中可能我會根據文章的欄目(CATALOGID),編輯(EDITUSERID),時間段(ADDTIME)進行條件性的全文檢索,然后可能會根據主鍵ID(ARTICLESID),人氣(HITS)進行排序顯示,如何配置Sphinx來實現呢?
4. Sphinx配置
sphinx是以sphinx.conf為配置文件,索引與搜索均以這個文件為依據進行,要進行全文檢索,首先就要配置好sphinx.conf,告訴sphinx哪些字段需要進行索引,哪些字段需要在where,orderby,groupby中用到。
安裝完Sphinx后,在D:/sphinx目錄有一個sphinx.conf.in,這個相當於sphinx的配置例子文件,我們以這個文件為藍本,重新創建一個空白內容的sphinx.conf,存放在d:/sphinx根目錄。
sphinx.conf的內容組成
source 源名稱1{ … } index 索引名稱1{ source=源名稱1 … } source 源名稱2{ … } index 索引名稱2{ source = 源名稱2 … } indexer{ … } searchd{ … }
提示
從組成我們可以發現sphinx可以定義多個索引與數據源,不同的索引與數據源可以應用到不同表或不同應用的全文檢索。
根據前面的實例,我們配置出我們需要的sphinx.conf,如下:
source cgfinal { type = mysql strip_html = 0 index_html_attrs = sql_host = localhost sql_user = root sql_pass = admin sql_db = test sql_port= 3306 # optional, default is 3306 sql_query_pre= SET NAMES utf8 sql_query = SELECT ARTICLESID,TITLE,CONTENTS,AUTHOR,CATALOGID,ADDTIME,EDITUSERID,\ HITS FROM a.eht_news_articles #sql_query = SELECT * FROM a.eht_news_articles sql_attr_uint= CATALOGID sql_attr_uint= EDITUSERID sql_attr_uint = HITS sql_attr_timestamp = ADDTIME sql_query_post = sql_ranged_throttle= 0 #sql_query_info = SELECT * FROM a.eht_news_articles WHERE ARTICLESID=$id } index cgfinal { source = cgfinal path = d:/sphinx/data/cgfinal docinfo = extern mlock = 0 morphology = none stopwords = min_word_len = 1 charset_type = utf-8 charset_table = U+FF10..U+FF19->0..9, 0..9, U+FF41..U+FF5A->a..z, U+FF21..U+FF3A->a..z,\ A..Z->a..z, a..z, U+0149, U+017F, U+0138, U+00DF, U+00FF, U+00C0..U+00D6->U+00E0..U+00F6,\ U+00E0..U+00F6, U+00D8..U+00DE->U+00F8..U+00FE, U+00F8..U+00FE, U+0100->U+0101, U+0101,\ U+0102->U+0103, U+0103, U+0104->U+0105, U+0105, U+0106->U+0107, U+0107, U+0108->U+0109,\ U+0109, U+010A->U+010B, U+010B, U+010C->U+010D, U+010D, U+010E->U+010F, U+010F,\ U+0110->U+0111, U+0111, U+0112->U+0113, U+0113, U+0114->U+0115, U+0115, \ U+0116->U+0117,U+0117, U+0118->U+0119, U+0119, U+011A->U+011B, U+011B, U+011C->U+011D,\ U+011D,U+011E->U+011F, U+011F, U+0130->U+0131, U+0131, U+0132->U+0133, U+0133, \ U+0134->U+0135,U+0135, U+0136->U+0137, U+0137, U+0139->U+013A, U+013A, U+013B->U+013C, \ U+013C,U+013D->U+013E, U+013E, U+013F->U+0140, U+0140, U+0141->U+0142, U+0142, \ U+0143->U+0144,U+0144, U+0145->U+0146, U+0146, U+0147->U+0148, U+0148, U+014A->U+014B, \ U+014B,U+014C->U+014D, U+014D, U+014E->U+014F, U+014F, U+0150->U+0151, U+0151, \ U+0152->U+0153,U+0153, U+0154->U+0155, U+0155, U+0156->U+0157, U+0157, U+0158->U+0159,\ U+0159,U+015A->U+015B, U+015B, U+015C->U+015D, U+015D, U+015E->U+015F, U+015F, \ U+0160->U+0161,U+0161, U+0162->U+0163, U+0163, U+0164->U+0165, U+0165, U+0166->U+0167, \ U+0167,U+0168->U+0169, U+0169, U+016A->U+016B, U+016B, U+016C->U+016D, U+016D, \ U+016E->U+016F,U+016F, U+0170->U+0171, U+0171, U+0172->U+0173, U+0173, U+0174->U+0175,\ U+0175,U+0176->U+0177, U+0177, U+0178->U+00FF, U+00FF, U+0179->U+017A, U+017A, \ U+017B->U+017C,U+017C, U+017D->U+017E, U+017E, U+0410..U+042F->U+0430..U+044F, \ U+0430..U+044F,U+05D0..U+05EA, U+0531..U+0556->U+0561..U+0586, U+0561..U+0587, \ U+0621..U+063A, U+01B9,U+01BF, U+0640..U+064A, U+0660..U+0669, U+066E, U+066F, \ U+0671..U+06D3, U+06F0..U+06FF,U+0904..U+0939, U+0958..U+095F, U+0960..U+0963, \ U+0966..U+096F, U+097B..U+097F,U+0985..U+09B9, U+09CE, U+09DC..U+09E3, U+09E6..U+09EF, \ U+0A05..U+0A39, U+0A59..U+0A5E,U+0A66..U+0A6F, U+0A85..U+0AB9, U+0AE0..U+0AE3, \ U+0AE6..U+0AEF, U+0B05..U+0B39,U+0B5C..U+0B61, U+0B66..U+0B6F, U+0B71, U+0B85..U+0BB9, \ U+0BE6..U+0BF2, U+0C05..U+0C39,U+0C66..U+0C6F, U+0C85..U+0CB9, U+0CDE..U+0CE3, \ U+0CE6..U+0CEF, U+0D05..U+0D39, U+0D60,U+0D61, U+0D66..U+0D6F, U+0D85..U+0DC6, \ U+1900..U+1938, U+1946..U+194F, U+A800..U+A805,U+A807..U+A822, U+0386->U+03B1, \ U+03AC->U+03B1, U+0388->U+03B5, U+03AD->U+03B5,U+0389->U+03B7, U+03AE->U+03B7, \ U+038A->U+03B9, U+0390->U+03B9, U+03AA->U+03B9,U+03AF->U+03B9, U+03CA->U+03B9, \ U+038C->U+03BF, U+03CC->U+03BF, U+038E->U+03C5,U+03AB->U+03C5, U+03B0->U+03C5, \ U+03CB->U+03C5, U+03CD->U+03C5, U+038F->U+03C9,U+03CE->U+03C9, U+03C2->U+03C3, \ U+0391..U+03A1->U+03B1..U+03C1,U+03A3..U+03A9->U+03C3..U+03C9, U+03B1..U+03C1, \ U+03C3..U+03C9, U+0E01..U+0E2E,U+0E30..U+0E3A, U+0E40..U+0E45, U+0E47, U+0E50..U+0E59, \ U+A000..U+A48F, U+4E00..U+9FBF,U+3400..U+4DBF, U+20000..U+2A6DF, U+F900..U+FAFF, \ U+2F800..U+2FA1F, U+2E80..U+2EFF,U+2F00..U+2FDF, U+3100..U+312F, U+31A0..U+31BF, \ U+3040..U+309F, U+30A0..U+30FF,U+31F0..U+31FF, U+AC00..U+D7AF, U+1100..U+11FF, \ U+3130..U+318F, U+A000..U+A48F,U+A490..U+A4CF min_prefix_len = 0 min_infix_len = 1 ngram_len = 1 ngrams_chars = U+4E00..U+9FBF, U+3400..U+4DBF, U+20000..U+2A6DF, U+F900..U+FAFF,\ U+2F800..U+2FA1F, U+2E80..U+2EFF, U+2F00..U+2FDF, U+3100..U+312F, U+31A0..U+31BF,\ U+3040..U+309F, U+30A0..U+30FF, U+31F0..U+31FF, U+AC00..U+D7AF, U+1100..U+11FF,\ U+3130..U+318F, U+A000..U+A48F, U+A490..U+A4CF } indexer { mem_limit = 32M } searchd { # address = 0.0.0.0 port = 3312 log = d:/sphinx/log/searchd.log query_log = d:/sphinx/log/query.log read_timeout = 5 max_children = 30 pid_file = d:/sphinx/log/searchd.pid max_matches = 1000 seamless_rotate = 1 }
相關配置項說明:
Source部分配置項說明
#type 數據庫類型,目前支持mysql與pgsql #strip_html 是否去掉html標簽 #sql_host 數據庫主機地址 #sql_user 數據庫用戶名 #sql_pass 數據庫密碼 #sql_db 數據庫名稱 #sql_port 數據庫采用的端口 #sql_query_pre 執行sql前要設置的字符集,用utf8必須SET NAMES utf8 #sql_query 全文檢索要顯示的內容,在這里盡可能不使用where或group by,將where與groupby的內容交給sphinx,由sphinx進行條件過濾與groupby效率會更高 #注意:select 出來的字段必須至少包括一個唯一主鍵(ARTICLESID)以及要全文檢索的字段,你計划原本在where中要用到的字段也要select出來 #這里不用使用orderby #sql_attr_開頭的表示一些屬性字段,你原計划要用在where,orderby,groupby中的字段要在這里定義 #根據我們原先的SQL: #select * from eht_articles where title like ? and catalogid=? And edituserid=? And addtime between ? and ? order by hits desc #我們需要對catalogid,edituserid,addtime,hits進行屬性定義(這四個字段也要在select的字段列表中),定義時不同的字段類型有不同的屬性名稱,具體可以見sphinx.conf.in中的說明 index部分配置項說明 #source 數據源名 #path 索引記錄存放目錄,如d:/sphinx/data/cgfinal,實際存放時會存放在d:/sphinx/data目錄,然后創建多個cgfinal名稱,不同擴展名的索引文件。 #其他的配置如min_word_len,charset_type,charset_table,ngrams_chars,ngram_len這些則是支持中文檢索需要設置的內容。 #如果檢索的不是中文,則charset_table,ngrams_chars,min_word_len就要設置不同的內容,具體官方網站的論壇中有很多,大家可以去搜索看看。
5. 運行Sphinx
首先要對數據進行索引或重建索引
進入命令行,運行d:/sphinx/bin/release/indexer –config d:/sphinx/sphinx.conf cgfinal
如果您在sphinx.conf中配置了多個數據源,想一次性全部索引則d:/sphinx/bin/release/indexer –config d:/sphinx/sphinx.conf –all
如果只是想對某個數據源進行索引,則d:/sphinx/bin/release/indexer –config d:/sphinx/sphinx.conf 索引名稱(這里的索引名稱是你在sphinx.conf中定義的索引名稱)
運行檢索守護進程searchd
進入命令行,運行d:/sphinx/bin/release/searchd –config d:/sphinx/sphinx.conf,此時系統會在3312端口偵聽mysql的全文檢索請求,所以如果您的mysql與sphinx不在同一台機器,要保證3312端口不被防火牆阻隔。
6. 搜索(翻譯)
6.1. 匹配模式
SPH_MATCH_ALL,匹配所有查詢詞(缺省模式)
SPH_MATCH_ANY,匹配任意查詢詞
SPH_MATCH_PHRASE,短語匹配
SPH_MATCH_BOOLEAN,布爾表達式匹配
SPH_MATCH_EXTENDED,查詢匹配一個Sphinx內部查詢語言表達式
6.2. 布爾查詢語法(Boolean query syntax)
布爾查詢允許使用下面特殊操作符:
AND:hello & world
OR:hello | world
NOT:hello -world或hello !world
Grouping:(hello world)
舉一個使用這些操作符的例子:
( cat -dog ) | ( cat -mouse)
AND是一個隱式操作符,“hello world”就相當於“hello & world”。
OR的優先級高於AND,所以“looking for cat | dog | mouse”的意思是“looking for (cat | dog | mouse)”而不是“(looking for cat) | dog | mouse”
象“-dog”這種隱式地包含了所有查詢記錄,是不會被執行的。這主要是考慮到技術上與性能上的原因,從技術上來說,sphinx不能總保持所有文章的ID列表,性能上來說,當結果集巨大(10-100M),執行這樣的查詢將費耗較長時間。
6.3. 擴展查詢語法(Extended query syntax)
擴展查詢允許合我下面特殊操作符:
操作符OR:hello | world
操作符NOT:hello -world或hello !world
字段搜索操作符:@title hello @body world
短語(phrase)搜索符:“hello world”
臨近(proximity)搜索符:“hello world”~10
舉例:
“hello world” @title “example program”~5 @body python -(php|perl)
AND是一個隱式操作符,“hello world”表示hello與world都要出現在匹配的記錄中。
OR的優先級高於AND,所以“looking for cat | dog | mouse”的意思是“looking for (cat | dog | mouse)”而不是“(looking for cat) | dog | mouse”
臨近距離在串中標明了,主要是用來調整單詞數量,應用在引號中的所有查詢字串。“cat dog mouse”~5表示包括這三個單詞在內,總共不能多於8個單詞的間隔。比如“CAT aaa bbb ccc DOG eee fff MOUSE”就不能匹配這個查詢,因為單詞間隔剛好是8個。
象aaa | ( bbb ccc | ( ddd eee ) )這樣的括號嵌套查詢目前還不支持,但以后會修正的。
否定(如NOT)只允許出現在頂層,不允許出現在括號內(如groups)。這點是不會改變的。因為支持否定嵌套查詢會讓短語排序(phrase ranking)的實現變得過於復雜。
6.4. 權重(匹配度,Weight)
采用什么權重功能取決於搜索模式(Search mode)
在權重函數中,有兩個主要部分:(短語排名)phrase rank和statistical rank(統計排名)
短語排名是基於搜索詞在文檔和查詢短語中的最長公共子序列(LCS)的長度。所以如果在記錄中有切確的短語匹配,記錄的短語排名將有可能是最高的,等於查詢單詞的總個數。
統計排名是建立在經典的BM25算法基礎之上,它只考慮詞頻。詞在全部文檔集合中以低的頻度出現或高頻度出現在匹配的文檔中,那么它獲得的權重就越大,最終的BM25權重是一個介於0到1之間的小數。
好的子短語匹配得到好的排名,最好的匹配放到最頂端。Sphinx作者的經驗是:基於排名的密切短語比其它任何單獨的統計方式表現出較好的搜索質量。
在SPH_MATCH_BOOLEAN 模式中,不需要計算權重,每條匹配記錄的權重都是1
在SPH_MATCH_ALL和SPH_MATCH_PHRASE模式中,最終的權重是短語排名權重的總和
(TOFIX:翻譯不暢)在SPH_MATCH_ANY模式中,本質上是一樣的,但它也增加了每個字段的匹配單詞數量,在這之前,短語排名權重乘以一個足夠大的值以保證在任意一個字段的較高短語排名可以匹配排名較高者,即使它的字段權重比較低。
在SPH_MATCH_EXTENDED模式中,最終的權重是短語權重和BM25權重的總和,再乘以1000取整。
7. 如何調用Sphinx
按上面配置,第5節點對數據庫進行了索引,通過Sphinx自帶的search(在bin/release目錄)就可以在命令行進行搜索:
(搜索CGArt)
windows上: search -c d:/sphinx/sphinx.conf CGArt Linux上: cd /usr/local/sphinx ./bin/search -c sphinx.conf CGArt
運行后,系統提示一堆信息: .... .... words: 1. 'cgart': 36 documents, 189 hits 這個表示庫中有36條記錄符合要求,出現CGArt的有189處。
應用程序如果想調用Sphinx,可以從兩個方面:
一是通過Sphinx官方提供的API接口(接口有Python,Java,Php三種版本)
二是通過安裝SphinxSE(具體見1.2部分),然后創建一個中介sphinxSE類型的表,再通過執行特定的SQL語句實現。
通過官方API調用Sphinx(以PHP為例)
在sphinx安裝目錄有一個API目錄,里面有三個PHP文件:test.php,test2.php和sphinxapi.php。sphinxapi.php是sphinx調用接口封裝文件,test.php是一個在命令行下執行的查詢例子文件,test2.php是一個生成摘要的例子文件。
在命令下行運行test.php(Linux上沒有API目錄,需要從源程序包中復制api目錄至/usr/local/sphinx)
Windows上: D:\sphinx\bin\release>c:\php5.2\php.exe -c c:\php5.2\php.ini ..\..\api\test.php -i cgfinal CGart
Linux上(php在/usr/local/php目錄,sphinx.conf在/usr/local/sphinx目錄):
cd /usr/local/sphinx
/usr/local/php/bin/php api/test.php -i cgfinal CGArt
Sphinx的API查詢接口主要有這些內容(其實對照 一下sphinxapi.php就清楚了):
//創建Sphinx的客戶端接口對象 $cl = new SphinxClient (); //設置連接Sphinx主機名與端口 $cl->SetServer('localhost',3312); //可選,為每一個全文檢索字段設置權重,主要根據你在sql_query中定義的字段的順序,Sphinx系統以后會調整,可以按字段名稱來設定權重 $cl->SetWeights ( array ( 100, 1 ) ); //設定搜索模式,SPH_MATCH_ALL,SPH_MATCH_ANY,SPH_MATCH_BOOLEAN,SPH_MATCH_EXTENDED,SPH_MATCH_PHRASE $cl->SetMatchMode(SPH_MATCH_ALL); //設定過濾條件$attribute是屬性名,相當於字段名(用SPH_MATCH_EXTENDED時),$value是值,$exclude是布爾型, 當為true時,相當於$attribute!=$value,默認值是false $cl->SetFilter($attribute, $values, $exclude); //設定group by //根據分組方法,匹配的記錄集被分流到不同的組,每個組都記錄着組的匹配記錄數以及根據當前排序方法本組中的最佳匹配記錄。 //最后的結果集包含各組的一個最佳匹配記錄,和匹配數量以及分組函數值 //結果集分組可以采用任意一個排序語句,包括文檔的屬性以及sphinx的下面幾個內部屬性 //@id--匹配文檔ID //@weight, @rank, @relevance--匹配權重 //@group--group by 函數值 //@count--組內記錄數量 //$groupsort的默認排序方法是@group desc,就是按分組函數值大小倒序排列 $cl->SetGroupBy($attribute, $func, $groupsort); //設定order by的內容,第一個參數是排序方法名,值有 // SPH_SORT_RELEVANCE,SPH_SORT_ATTR_DESC,SPH_SORT_ATTR_ASC,SPH_SORT_TIME_SEGMENTS,SPH_SORT_EXTENDED //$sortby的值如"HITS desc" $cl->SetSortMode(SPH_SORT_EXTENDED, $sortby); //set count-distinct attribute for group-by queries,$distinct為字符串 $cl->SetGroupDistinct ( $distinct ); //相當於mysql的limit $offset,$limit $cl->SetLimits($start,$limit) //$q是查詢的關鍵字,$index是索引名稱,當等於*時表查詢所有索引 $res = $cl->Query ( $q, $index );
$cl→Query()返回的內容print_r后大概是:
Array ( [error] => [warning] => [status] => 0 [fields] => Array ( [0] => title [1] => contents [2] => author ) [attrs] => Array ( [catalogid] => 1 [addtime] => 2 [edituserid] => 1 [hits] => 1 ) [matches] => Array ( [380] => Array ( [weight] => 1 [attrs] => Array ( [catalogid] => 7 [addtime] => 1112677492 [edituserid] => 1 [hits] => 1470 ) ) [599] => Array ( [weight] => 101 [attrs] => Array ( [catalogid] => 7 [addtime] => 1115910729 [edituserid] => 1 [hits] => 1749 ) ) [850] => Array ( [weight] => 1 [attrs] => Array ( [catalogid] => 2 [addtime] => 1118741392 [edituserid] => 1 [hits] => 289 ) ) [877] => Array ( [weight] => 1 [attrs] => Array ( [catalogid] => 2 [addtime] => 1118898869 [edituserid] => 1 [hits] => 9870 ) ) [1040] => Array ( [weight] => 101 [attrs] => Array ( [catalogid] => 2 [addtime] => 1120708579 [edituserid] => 1 [hits] => 318 ) ) ) [total] => 129 [total_found] => 129 [time] => 0.000 [words] => Array ( [design] => Array ( [docs] => 129 [hits] => 265 ) ) )
從上面可以看出Query並不能全部取得我們想要的記錄內容,比如說Title,Contents字段就沒有取出來,根據官方的說明是sphinx並沒有連到mysql去取記錄,只是根據它自己的索引內容進行計算,因此如果想用sphinxAPI去取得我們想要的記錄,還必須將Query的結果為依據去查詢MySQL才可以得到最終我們想要的結果集。
test2.php是一個摘要生成的例子文件,如果你的本地機器已裝好sphinx,php運行環境,你可以通過瀏覽器看查看test2.php的運行效果。
假設我要搜索關鍵詞“test”,通過sphinx可以取到搜索結果,在顯示搜索結果時,我希望將含有“test”的進行紅色或加粗顯示,同時,我不希望全部都顯示出來,只需要顯示一段摘要,就象google或百度那樣,搜出來的結果不是全篇顯示,只是部分顯示,這個就是摘要的作用。
以test2.php中為例,以下是test2.php的代碼:
require ( "sphinxapi.php" ); $docs = array ( "this is my test text to be highlighted, and for the sake of the testing we need to pump its length somewhat", "another test text to be highlighted, below limit", "test number three, without phrase match", "final test, not only without phrase match, but also above limit and with swapped phrase text test as well", ); $words = "test"; $index = "cgfinal"; $opts = array ( "before_match" => "<span style='font-weight:bold;color:red'>", "after_match" => "</span>", "chunk_separator" => " ... ", "limit" => 60, "around" => 3, ); foreach ( array(0,1) as $exact ) { $opts["exact_phrase"] = $exact; print "exact_phrase=$exact\n"; $cl = new SphinxClient (); $res = $cl->BuildExcerpts ( $docs, $index, $words, $opts ); if ( !$res ) { die ( "ERROR: " . $cl->GetLastError() . ".\n" ); } else { $n = 0; foreach ( $res as $entry ) { $n++; print "n=$n, res=$entry<br/>"; } print "\n"; } }
在實際環境中,上面代碼的$docs是我們用sphinx搜索出來的結果,這個結果利用BuildExcerpts方法可以實現摘要的功能。
采用SphinxSE方式調用Sphinx
采用sphinxSE必須要求為mySQL安裝sphinxSE Engine驅動,方法在第1節中我已講到
要創建一張sphinx 專用表,你可以這樣建
CREATE TABLE `sphinx` ( `id` int(11) NOT NULL, `weight` int(11) NOT NULL, `query` varchar(255) NOT NULL, `CATALOGID` INT NOT NULL, `EDITUSERID` INT NOT NULL, `HITS` INT NULL, `ADDTIME` INT NOT NULL, KEY `Query` (`Query`) ) ENGINE=SPHINX DEFAULT CHARSET=utf8 CONNECTION='sphinx://localhost:3312/cgfinal';
警告
注:與一般mysql表不同的是ENGINE=SPHINX DEFAULT CHARSET=utf8 CONNECTION='sphinx:localhost:3312/cgfinal';,這里表示這個表采用SPHINXSE引擎,字符集是utf8,與sphinx的連接串是'sphinx:localhost:3312/cgfinal,cgfinal是索引名稱
根據sphinx官方說明,這個表必須至少有三個字段,字段起什么名稱無所謂,但類型的順序必須是integer,integer,varchar,分別表示記錄標識document ID,匹配權重weight與查詢query,同時document ID與query必須建索引。另外這個表還可以建立幾個字段,這幾個字段的只能是integer或TIMESTAMP類型,字段是與sphinx的結果集綁定的,因此字段的名稱必須與在sphinx.conf中定義的屬性名稱一致,否則取出來的將是Null值。
比如我在上面有定義了sql_attr_uint= CATALOGID,sql_attr_uint= EDITUSERID,sql_attr_uint = HITS,sql_attr_timestamp = ADDTIME,那么在這個表里頭,你就可以再定義CATALOGID,EDITUSERID,HITS,ADDTIME四個字段。
通過sql語句實現查詢。通過select * from sphinx where query='sphinx表達式' 的方式可以實現查詢,通過讓sphinx表與eht_articles或其他表並聯查詢(條件是sphinx.id=eht_articles.Articlesid)還可以實現更為復雜的sql,基本上可以符合我們日常的要求。
sphinx表達式在sphinx的手冊中也提到了,這里我簡單說明幾條:
query='關鍵字' ,關鍵字就是你要搜索的關鍵字,如query='CGArt'表示你要全文搜索CGArt mode,搜索模式,值有:all,any,phrase,boolean,extended,默認是all sort,排序模式,必須是relevance,attr_desc,attr_asc,time_segments,extended中的一種,在所有模式中除了relevance外, 屬性名(或用extended排序)前面都需要一個冒號。 ... where query='test;sort=attr_asc:hits'; ... where query='test;sort=extended:@weight desc,hits asc'; offset,結果記錄集的起始位置,默認是0 limit,從結果記錄集中取出的數量,默認是20條 index,要搜索的索引名稱 ... where query='test;index=cgfinal'; ... where query='test;index=test1,test2,test3;'; minid,maxid,匹配最小與最大文檔ID weights,以逗號分割的分配給sphinx全文檢索字段的權重列表 ... where query='test;weights=1,2,3;'; filter,!filter,以逗號分隔的屬性名與一堆要匹配的值 #只包括1,5,19的組 ... where query='test;filter=group_id,1,5,19;'; #不包括3,11的組 ... where query='test;!filter=group_id,3,11'; range,!range,逗號分隔的屬性名一最小與最大要匹配的值 #從3至7的組 ... where query='test;range=group_id,3,7;'; #不包括從5至25的組 ... where query='test;!range=group_id,5,25;'; maxmatches,每個查詢最大匹配的值 ... where query='test;maxmatches=2000;'; groupby,group by 方法與屬性 ... where query='test;groupby=day:published_ts;'; ... where query='test;groupby=attr:group_id;'; groupsort,group by 的排序 ... where query='test;gropusort='@count desc';
需要注意的重要一點是讓sphinx進行排序,過濾,切分結果記錄集比用MySQL的where,orderby 和limit將有更好的效率。有兩個原因,首先sphinx做了很多優化,在這些任務上它比mySQL做得更出色,其次searchd在打包,sphinxSE在傳輸與解包上需要的數據量更少。
你可以通過運用join在sphinxSE的搜索表和其他引擎類型的表做並聯查詢。這有一個從example.sql中documents表的例子:
mysql> SELECT content, date_added FROM test.documents docs -> JOIN t1 ON (docs.id=t1.id) -> WHERE query="one document;mode=any"; +-------------------------------------+---------------------+ | content | docdate | +-------------------------------------+---------------------+ | this IS my test document NUMBER two | 2006-06-17 14:04:28 | | this IS my test document NUMBER one | 2006-06-17 14:04:28 | +-------------------------------------+---------------------+ 2 ROWS IN SET (0.00 sec) mysql> SHOW ENGINE SPHINX STATUS; +--------+-------+---------------------------------------------+ | TYPE | Name | STATUS | +--------+-------+---------------------------------------------+ | SPHINX | stats | total: 2, total found: 2, TIME: 0, words: 2 | | SPHINX | words | one:1:2 document:2:2 | +--------+-------+---------------------------------------------+ 2 ROWS IN SET (0.00 sec)
8. SphinxSE的SQL查詢例子演練
從eht_articles中查詢標題含有“動畫”關鍵字的記錄。
SELECT c.* FROM eht_articles AS c,sphinx AS t WHERE c.articlesid=t.id AND query='@title 動畫;mode=extended'
提示
說明:要指定某個字段進行搜索,要用@字段名+空格+關鍵字+分號+mode=extended 如果不指定字段,則系統會對TITLE,CONTENTS進行搜索 ,對什么字段進行全文檢索取決於在sphinx.conf中sql_query定義的select 中的字段(文本類型)
從eht_articles中查詢文章內容或標題含有“CGArt”關鍵字的記錄。
SELECT c.* FROM eht_articles AS c,sphinx AS t WHERE c.articlesid=sphinx.id AND query='動畫'
若AUTHOR,TITLE,CONTENTS三個字段都全文索引了,但只想搜title,或contents中含有“動畫”關鍵字的文章
SELECT c.* FROM eht_articles AS c,sphinx AS t WHERE c.articlesid=t.id AND query='@title 動畫 | @contents 動畫; mode=extended'
查詢標題含有“動畫”關鍵字,catalogid為7,edituserid為1的記錄
SELECT c.* FROM eht_articles AS c,sphinx AS t WHERE c.articlesid=t.id AND query='@title 動畫; filter=edituserid,1;filter=catalogid,7;mode=extended'
提示
采用filter=字段名稱,值就相當於where中的 字段名=值,filter提到的字段必須在sphinx的source部分的字段屬性定義中定義,如
sql_attr_uint = CATALOGID sql_attr_uint = EDITUSERID sql_attr_uint = HITS sql_attr_timestamp = ADDTIME
查詢標題含有“動畫”關鍵字,按人氣Hits從大至小,欄目ID從大至小排序
SELECT c.* FROM eht_articles AS c,sphinx AS t WHERE c.articlesid=t.id AND query='@title 動畫;mode=extended; sort=extended:hits desc,catalogid desc'
在sphinx中,select出來的內容是按weight從大至小排序的,weight是根據sphinx內部一定的算法算出來的,越大就表示越匹配,如果想按匹配度從大至小排序,則可以:
SELECT c.* FROM eht_articles AS c,sphinx AS t WHERE c.articlesid=t.id AND query='@title 動畫;mode=extended; sort=@weight desc'
搜內容或標題含有優秀或Icon或設計,按catalogid分組,按匹配度從高至低排序
SELECT t.*,c.* FROM eht_articles AS c,sphinx AS t WHERE c.articlesid=t.id AND query='優秀 | Icon | 設計; mode=extended;groupby=attr:catalogid;groupsort=@weight;'
9. 如何自動重建索引
10. 相關資源