通過MySQL全文搜索實現(xiàn)中文的相關(guān)搜索.doc_第1頁
通過MySQL全文搜索實現(xiàn)中文的相關(guān)搜索.doc_第2頁
通過MySQL全文搜索實現(xiàn)中文的相關(guān)搜索.doc_第3頁
通過MySQL全文搜索實現(xiàn)中文的相關(guān)搜索.doc_第4頁
通過MySQL全文搜索實現(xiàn)中文的相關(guān)搜索.doc_第5頁
已閱讀5頁,還剩12頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

通過MySQL內(nèi)置全文檢索實現(xiàn)中文的相關(guān)檢索關(guān)鍵字:MySQL 全文檢索 全文索引 中文分詞 二元分詞 區(qū)位碼 相似度注:本文使用的MySQL版本為:MySQL 4.0.x在MySQL4中,是已經(jīng)開始支持全文檢索(索引)的了。但是只是對英文支持全文檢索。由于英文在書寫上的特殊性,使得分詞算法相對中文來說,簡單得多。一般來說,我們可以通過單詞與單詞之間的空格,以及標點符號來完成這個分詞過程。但是就中文來說,就沒有那么簡單。MySQL無法對中文做出正確的分詞,假設(shè)有如下英文句子:Hello world! Hello PHP!通過上面提及的方法,可以很簡單的把這個句子分詞為:1 Hello2 world3 PHP我們再來看看中文的句子:你好世界,你好PHP!按照英文的算法,分詞如下:1 你好世界2 你好PHP顯然是不能滿足我們的需要的。所以,首先我們要做的是,把中文的句子轉(zhuǎn)變?yōu)镸ySQL眼中的英文,以便使得它能以英文分詞算法去對句子進行正確的分詞處理。先將上面中文句子進行標點過濾處理,得到以下句子:你好世界 你好PHP接著再使用中文分詞中較簡單實現(xiàn)的二元分詞算法對句子進行二元分詞,得到以下句子:你好 好世 世界 你好 PHP因為把標點符號替換為空格,以及PHP本身為英文字母的關(guān)系,可以不用進行二元切分,所以得到上面句子。這個時候,我們來看看處理過后的句子,會發(fā)現(xiàn),就其書寫格式上來說,已經(jīng)符合英文的書寫格式,既以空格,標點來對單詞形成自然間隔。只是上面句子沒有標點,只有空格而已。到此,我們已經(jīng)成功的將中文“翻譯”為MySQL能理解的“英文”書寫格式。但是,問題還沒解決,首先,MySQL中,ft_min_word_len(分詞詞匯最小長度)這個參數(shù)的默認值為4,也就是4個字母以上長度的單詞,才會被考慮,小于4個的,將會被忽略。如果不改變這個長度,按照上面的分詞結(jié)果,我們將無法通過 你好,世界,PHP等檢索到相關(guān)的結(jié)果,因為分出來的詞太短了,不在MySQL的選擇范圍內(nèi)。我們可以通過修改ft_min_word_len的值,將其設(shè)置為2來解決上面問題,但是這樣做的話,在檢索列表中的原本就為英文的短小詞匯,如:PHP,MP3,也會被劃入檢索范圍內(nèi),這樣做的結(jié)果是,出現(xiàn)很多無意義的相關(guān)結(jié)果。請看以下列表:MP3 the lookMP3 because of you因為他們都同有MP3在標題中,所以會出現(xiàn)上述提到的問題。回到ft_min_word_len值的問題,我們之所以要修改他,是為了能讓MySQL找到我們的二元分詞,但是短小的英文又被“無辜”的卷入,我們目前要解決的問題就是,如何使得MySQL能檢索到二個字的中文詞匯,又能忽略掉原本的英數(shù)?第一個反應(yīng)是把中文MD5,這樣以上分詞就將轉(zhuǎn)化為以下結(jié)果:你好 好世 世界 你好 PHP = b94ae3c6d892b29cf48d9bea819b27b9 f5625345be46432fb0fd51340fcf6679 9067de5206278a93823f9c5dc2c737fd b94ae3c6d892b29cf48d9bea819b27b9 PHP這樣做,首先是使得中文分詞的長度超越了默認的2個字,同時消除了中文的歧義性。(MySQL4對中文的處理有問題),搜索“車輪”時候,不再會出現(xiàn)類似“發(fā)動機”結(jié)果的問題。(車輪的例子只是為了方便理解而做出的假設(shè))通過上面的做法,已經(jīng)解決了分詞最小長度的問題,順利的把中文詞匯長度升級,從而達到把中文詞匯劃入檢索范圍,把較短的英數(shù)劃出檢索范圍。休息一下,然后發(fā)現(xiàn)這個MD5后的字符串是否太長了點比較占用空間,要不,于是想到區(qū)位碼,4位數(shù)的區(qū)位碼能表示一個GB漢字,一個詞有二個漢字組成,轉(zhuǎn)換為區(qū)位碼后是8個數(shù)字。不但能確定惟一性,也就MD5而已減少了長度。下面是轉(zhuǎn)換后的:你好 好世 世界 你好 PHP = b94ae3c6d892b29cf48d9bea819b27b9 f5625345be46432fb0fd51340fcf6679 9067de5206278a93823f9c5dc2c737fd b94ae3c6d892b29cf48d9bea819b27b9 PHP = 36672635 26354232 42322971 36672635 PHP呵呵,是不是比MD5的小了很多呢?最后我們把相同的詞匯留一個,多余的刪除。得到36672635 26354232 42322971 PHP于是就完成了 你好世界,你好PHP! 到 36672635 26354232 42322971 PHP 的轉(zhuǎn)換。通過上面方法結(jié)合MySQL全文檢索語句,我們可以通過給出一個標題例如:邁克爾杰克遜 -危險之旅之布加勒斯特站找出類似以下的相關(guān)標題邁克爾杰克遜 -邁克爾杰克遜危險布加勒斯特演唱會Michael Jackson -邁克爾杰克遜 羅馬尼亞 危險演唱會邁克爾杰克Michael Jackson -危險之旅邁克爾杰克遜 -邁克爾杰克遜 美國50annive演唱會危險片段邁克爾杰克遜 -邁克爾杰克遜 終極收藏 原版DVD危險演唱會邁克爾杰克遜 杰克遜五兄弟 -The Jackson Motown 25 演唱會邁克爾杰克遜 -邁克爾杰克遜BAD曰本Yokohama演唱會邁克爾杰克遜 -邁克爾杰克遜曰本大阪演唱會邁克爾杰克遜 -邁克爾杰克遜之勝利-達拉絲演唱會邁克爾杰克遜 -邁克爾杰克遜之勝利演唱會 比麗珍 片段邁克爾杰克遜 -邁克爾杰克遜德國危險演唱會之 billie jean片段邁克爾杰克遜 -Michael Jackson -30周年演唱會Michael Jackson -邁克爾杰克遜 馬尼拉 歷史演唱會邁克爾杰克遜 -1993年美國橄欖球中場休息精彩表演表結(jié)構(gòu) articletitle varchar 200 - 用于存放標題 (顯示用)ft text - fulltext 用于存放標題分詞結(jié)果 (檢索用)首先我們在把標題保存到數(shù)據(jù)庫時候,就已經(jīng)對標題進行分詞轉(zhuǎn)區(qū)位碼,保存到ft字段中,用于相關(guān)性的檢索。然后把給出的標題邁克爾杰克遜 -危險之旅之布加勒斯特站轉(zhuǎn)為34853143 31432291 22910104 01042960 29603143 31434923 46034753 47535414 54143435 34355414 54141828 18282851 28513253 32534325 43254456 44565330,最后進行全文檢索查詢:SELECT title, MATCH( ft ) AGAINST( 34853143 31432291 22910104 01042960 29603143 31434923 46034753 47535414 54143435 34355414 54141828 18282851 28513253 32534325 43254456 44565330 IN BOOLEAN MODE ) AS scoreFROM articleWHERE MATCH( ft ) AGAINST( 34853143 31432291 22910104 01042960 29603143 31434923 46034753 47535414 54143435 34355414 54141828 18282851 28513253 32534325 43254456 44565330 IN BOOLEAN MODE )ORDER BY score DESCLIMIT 0, 5從SQL Query上來看,進行了兩次全文檢索,其實不然,MySQL會將其視為一次,所以不比擔(dān)心。同時使用了AS score,這個score是相似度,分值越高,自然越與給出的標題相近。二點建議:1.在實際使用中,挑選score大于1的作為檢索結(jié)果。2.檢索結(jié)果會將本身標題也算入其中,根據(jù)score排序,為第一條,別忘記過濾哦 _。站在用戶的立場來說,我們給用戶提供了更多的相關(guān)內(nèi)容,站在搜索引擎立場上來說,給關(guān)鍵字提供了更多的相關(guān)鏈接,形成了良好的站內(nèi)互聯(lián)結(jié)構(gòu),提高了搜索引擎對網(wǎng)頁的評價。如果各位碰到錯誤的不合理的地方,懇請指正,共同進步。謝謝!=參考資料:=1.Monkey的二元分詞首先,我們來想想MySQL不支持中文索引的關(guān)鍵原因還是中文是雙字節(jié)的,如果能把中文轉(zhuǎn)換成單字節(jié)的字母或數(shù)字,那不就可以使用全文索引了嗎基于這個目的,我們首先需要做的就是分詞,如果要實現(xiàn)比較完美的分詞的話,還是需要安裝相應(yīng)的插件,但我們很多是虛擬主機,根本沒有條件來安裝,所以只能采取比較原始的分詞方法,二元分詞法。所謂二元分詞法,就是將一句話從頭到尾,兩個字兩個字地分開,比如:我們的祖國是花園。就可以劃分為:我們,們的,的祖,祖國,國是,是花,花園。雖然有點浪費,但至少面面俱到了。PHP的相應(yīng)函數(shù)/Monkeys 二元分詞 function sp_str($str) /所有漢字后添加ASCII的0字符,此法是為了排除特殊中文拆分錯誤的問題 $str=preg_replace(/x80-xff2/,0.chr(0x00),$str); /拆分的分割符 $search = array(, /, , ., ;, :, , !, , , , (, ), ?, -, t, n, , , r, rn, $, &, %, #, , +, =, , , , , :, ), (, , 。, ,, !, ;, “, ”, , , , , 、, , , , , , , 【, 】,); /替換所有的分割符為空格 $str = str_replace($search, ,$str); /用正則匹配半角單個字符或者全角單個字符,存入數(shù)組$ar preg_match_all(/x80-xff?./,$str,$ar);$ar=$ar0; /去掉$ar中ASCII為0字符的項目 for ($i=0;$icount($ar);$i+) if ($ar$i!=chr(0x00) $ar_new=$ar$i; $ar=$ar_new;unset($ar_new);$oldsw=0; /把連續(xù)的半角存成一個數(shù)組下標,或者全角的每2個字符存成一個數(shù)組的下標 for ($ar_str=,$i=0;$i0 and $sw!=$oldsw) $ar_str.= ; if ($sw=1) $ar_str.=$ar$i; else if (strlen($ar$i+1)=2) $ar_str.=$ar$i.$ar$i+1. ; elseif ($oldsw=1 or $oldsw=0) $ar_str.=$ar$i; $oldsw=$sw; /去掉連續(xù)的空格 $ar_str=trim(preg_replace(# 1,#i, ,$ar_str);/$ar_str = Monkey s 二元 元分 分詞 /返回拆分后的結(jié)果 return explode( ,$ar_str); 接下來,就該考慮如何把分好的詞轉(zhuǎn)換成單字節(jié)的,可以使用base64,sha1,md5。但有個問題就是轉(zhuǎn)換后的字符有點長,那如何才能縮短字符呢,對了,就是使用區(qū)位碼,因為區(qū)位碼短啊,一個中文只占四個字節(jié)。每個中文都有對應(yīng)的區(qū)位碼(除了標點符號和特殊符號),這樣只要將上面分詞的結(jié)果通過區(qū)位碼轉(zhuǎn)換后,然后存儲到數(shù)據(jù)庫里,就可以了。PHP區(qū)位碼函數(shù)function quweima($str) if(preg_match(/a-z0-9 +$/i,$str) return $str; else $str1 = substr($str,0,2); /echo $str1; $str_qwm = sprintf(%02d%02d,ord($str0)-160,ord($str1)-160); $str2 = substr($str,2,4); /echo $str2; $str_qwm .= sprintf(%02d%02d,ord($str0)-160,ord($str1)-160); return $str_qwm; 這里我加了判斷,如果是英文或數(shù)字直接返回不做處理經(jīng)過這兩步處理后,準備工作就基本完成了,下面就是建立數(shù)據(jù)庫我的數(shù)據(jù)庫結(jié)構(gòu)是這樣的id,title,title_ft(fulltext)添加數(shù)據(jù)的時候,title存放標題,ft_title存放處理后的標題,內(nèi)容應(yīng)該是像這樣的:43557401 54903471 SQL代碼$query = SELECT title, MATCH( title_ft ) AGAINST( $title_ft IN BOOLEAN MODE ) AS score FROM info WHERE MATCH( title_ft ) AGAINST( $title_ft IN BOOLEAN MODE ) ORDER BY score DESC ;其中 $title_ft是經(jīng)過兩個函數(shù)處理后的字符串,用它去匹配title_ft。解決monkey的二元分詞,分utf編碼的中文時出現(xiàn)亂碼momoca test?phpfunction dualDecom($str)/所有漢字后添加ASCII的0字符,此法是為了排除特殊中文拆分錯誤的問題$str = preg_replace(/x80-xff3/,0.chr(0x00),$str);/拆分的分割符$search = array(, /, , ., ;, :, , !, , , , (, ), ?, -, t, n, , , r, rn, $, &, %, #, , +, =, , , , , :, ), (, , 。, ,, !, ;, “, ”, , , , , 、, , , , , , , 【, 】,);/替換所有的分割符為空格$str = str_replace($search, ,$str);/用正則匹配半角單個字符或者全角單個字符,存入數(shù)組$arpreg_match_all(/x80-xff+?x00/,$str,$ar);$ar = $ar0;/去掉$ar中ASCII為0字符的項目for ( $i = 0; $i count($ar); $i+ )if ($ar$i != chr(0x00) $ar_new=$ar$i;$ar = $ar_new;unset($ar_new);$oldsw = 0;/把連續(xù)的半角存成一個數(shù)組下標,或者全角的每2個字符存成一個數(shù)組的下標for ( $ar_str = , $i = 0; $i 0 and $sw != $oldsw) $ar_str.= ; if ( $sw = 1 )$ar_str.= $ar$i;elseif ( strlen($ar$i+1) = 2 )$ar_str.= $ar$i.$ar$i+1. ; elseif ( $oldsw = 1 OR $oldsw = 0 ) $ar_str.= $ar$i;$oldsw=$sw;/去掉連續(xù)的空格$ar_str = trim(preg_replace(# 1,#i, ,$ar_str);return explode( ,$ar_str);print_r(dualDecom(比如有一個字符串是“你好PHP!”就只能分出“你好”一個詞 );?2.PHP里如何實現(xiàn)漢字轉(zhuǎn)區(qū)位碼無標題文檔漢字區(qū)位碼查詢系統(tǒng)form id=form1 name=form1 method=post action=輸入漢字input name=textfield1 type=text value= /input name=textfield2 type=text value= /form id=form2 name=form2 method=post action=輸入?yún)^(qū)位碼input name=textfield3 type=text value= /input name=textfield4 type=text value= /3.對dvbbs.php全文搜索的完全分析這次給大家滲透些比較高級些的.關(guān)于搜索的東西.首先,大家先去下載一份dvbbs.php beta1的代碼.解壓放在手頭上已經(jīng)有的同學(xué)們就可以繼續(xù)往下看了.首先拋開php代碼.找出你的mysql手冊.(沒有?那直接看下面的吧.)mysql全文搜索,sql的寫法:MATCH (col1,col2,) AGAINST (expr IN BOOLEAN MODE | WITH QUERY EXPANSION)比如:SELECT * FROM articles WHERE MATCH (title,body) AGAINST (database);MATCH()函數(shù)對于一個字符串執(zhí)行資料庫內(nèi)的自然語言搜索。一個資料庫就是1套1個或2個包含在FULLTEXT內(nèi)的列。搜索字符串作為對 AGAINST()的參數(shù)而被給定。對于表中的每一行, MATCH() 返回一個相關(guān)值,即, 搜索字符串和 MATCH()表中指定列中該行文字之間的一個相似性度量。下面的例子則更加復(fù)雜。詢問返回相關(guān)值,同時對行按照相關(guān)性漸弱的順序進行排序。為實現(xiàn)這個結(jié)果,你應(yīng)該兩次指定 MATCH(): 一次在 SELECT 列表中而另一次在 WHERE子句中。這不會引起額外的內(nèi)務(wù)操作,原因是MySQL 優(yōu)化程序注意到兩個MATCH()調(diào)用是相同的,從而只會激活一次全文搜索代碼。mysql SELECT id, body, MATCH (title,body) AGAINST- (Security implications of running MySQL as root) AS score- FROM articles WHERE MATCH (title,body) AGAINST- (Security implications of running MySQL as root);所以,到這里你應(yīng)該會mysql 英文全文搜索了.=請注意一個問題.一些詞在全文搜索中會被忽略:* 任何過于短的詞都會被忽略。 全文搜索所能找到的詞的默認最小長度為 4個字符。* 停止字中的詞會被忽略。=mysql還自帶查詢擴展功能.這里不做過多討論.=下面進行php中文全文搜索的分析.曾經(jīng)有一個版本的mysql支持中文全文搜索(海量 mysql chinese+,說是GPL但是最終沒有開源)中文全文搜索的關(guān)鍵是在分詞上.mysql本身不支持cjk的分詞(cjk:chinese,japanese,korean),所以!*如何用php模擬分詞是mysql全文索引的關(guān)鍵*!中文分詞是語言分詞中最困難的.現(xiàn)在也沒有人能夠徹底完美的解決(雖然這些搜索引擎做的都還不錯.)/fcicq:下面給大家看看這里php的分詞是怎么做的.function &DV_ChineseWordSegment($str,$encodingName=gbk)static $objEnc = null;if( $objEnc = null )if( !class_exists(DV_Encoding) )require_once ROOT_PATH.inc/DV_Encoding.class.php;$objEnc =& DV_Encoding:GetEncoding($encodingName);$strLen = $objEnc-StrLength($str);$returnVal = array();if( $strLen = 1 )return $str;$arrStopWords =& DV_GetStopWordList();/print_r($arrStopWords);/過濾所有HTML標簽$str = preg_replace(#|#is, ”, $str);/過濾所有stopword$str = str_replace($arrStopWordsStrRepl, ,$str);$str = preg_replace($arrStopWordsPregRepl, ,$str);/echo “$str:$str“;$arr = explode( ,$str);/fcicq:好了,這下面的才是php分詞關(guān)鍵 *foreach( $arr as $tmpStr )if ( preg_match(”/x00-x7f+$/i”,$tmpStr) = 1 ) /fcicq:全是E文,沒關(guān)系,mysql可以認識的$returnVal = .$tmpStr; else /fcicq:中英混合preg_match_all(”/(a-zA-Z+)/i”, $tmpStr, $matches);if( !empty($matches) ) /fcicq:英語部分foreach( $matches0 as $matche )$returnVal = $matche;/過濾ASCII字符$tmpStr = preg_replace(”/(x00-x7f+)/i”, ”, $tmpStr); /fcicq:你看,剩下的不就全是中文了?$strLen = $objEnc-StrLength($tmpStr)-1;for( $i = 0 ; $i SubString($tmpStr,$i,2); /fcicq:注意這里的substr,不是手冊上的./fcicq:你仔細看,所有的詞都是分成兩個./比如”數(shù)據(jù)庫的應(yīng)用”,會被分成數(shù)據(jù) 據(jù)庫 庫的 的應(yīng) 應(yīng)用/全文搜索: 全文 文搜 搜索/這分詞自然是不怎么樣的/但是,搜索的時候同樣這么做./比如搜索數(shù)據(jù)庫,就相當(dāng)于搜索了數(shù)據(jù) 據(jù)庫./這是一種相當(dāng)傳統(tǒng)的全文搜索分詞方法.return $returnVal;/end function DV_ChineseWordSegment/fcicq:這就是傳說中的substr.偶相信許多人寫出來的php代碼都比這個好.function &SubString(&$str,$start,$length=null)if( !is_numeric($start) )return false;$strLen = strlen($str);if( $strLen = 0 )return false;if( $start 0 | $length StrLength($str); else$mbStrLen = $strLen;if( !is_numeric($length) )$length = $mbStrLen; elseif( $length 0 )$length = $mbStrLen + $length - 1;if( $start 0 )$start = $mbStrLen + $start;$returnVal = ;$mbStart = 0;$mbCount = 0;for( $i = 0 ; $i = $length )break;$currOrd = ord($str$i);if( $mbStart = $start )$returnVal .= $str$i;if( $currOrd 07f )$returnVal .= $str$i+1.$str$i+2;$i += 2;$mbCount+; elseif( $currOrd 07f )$i += 2;$mbStart+;return $returnVal;/end function SubString/插入全文搜索分詞表.一共兩個,一個 topic_ft,一個bbs_ft$arrTopicIndex =& DV_ChineseWordSegment($topic);if( !empty($arrTopicIndex) & is_array($arrTopicIndex) )$topicindex = $db-escape_string(implode( ,$arrTopicIndex);if( $topicindex != ” )$db-query(”UPD ATE $dvtopic_ft SET topicindex=$topicindex WHERE topicid=$RootID”); else$db-query(”DEL ETE FROM $dvtopic_ft WHERE topicid=$RootID”);明白了吧?這就是所謂的mysql全文搜索分詞mysql不會分詞,而php會.就這么簡單.這雖然是一種比較過時的方法,但被dv這么一炒作就成了香餑餑.很好理解的.之后,mysql把這些全文搜索分詞的結(jié)果implode( ,$arrTopicIndex)再分詞(呵呵,數(shù)據(jù),據(jù)庫),把這些詞生硬的記住了.下面回到mysql上來,下面是php+mysql實現(xiàn)的全文搜索查詢.$arrFTKeyWord =& DV_Chin

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論