首先,讓我開始說我已經做了大量的關於這個話題的研究,並且已經投入了大量的時間在一個可行的解決方案中。就這樣說,我遇到了一些我似乎無法克服的問題,因此正在尋求一些正確的方向。使用php和mysql創建一個有效的方法來構建相關的文章功能
小背景故事:我寫/維護一個網站的php/mysql。我們基本上是一個遊戲網站,發佈文章,評論,視頻等。
問題:我有一個mysql數據庫,存儲所有的網站內容。這個數據庫中基本上有4個字段,我可以從中抽取單詞,然後我想匹配數據庫中的所有其他文章,並確定前3個相關文章,以便它們可以顯示。最有效和最好的方式來實現這一目標?
這是我到目前爲止已經完成:
在CMS我設計,我已經基本上設計了「袋的字」型系統。該程序遍歷所有文章(大約有4,000篇),並將每個單詞分解成單獨的數據庫。在這個單獨的數據庫中,存儲了文章中的單詞計數,tf * idf(稍後更多)以及文章ID(x-ref到內容數據庫)。所以,一個詞可以不止一次地在這個數據庫中,但是對於一篇文章不會超過一次。處理完這個(大約需要4分鐘)後,在這個新的數據庫中有將近700,000個條目。
然後,我有另一個程序,通過這個新的單詞數據庫,並解析它的tf*idf。瀏覽整個700,000個條目列表需要15分鐘左右的時間。
現在,這是我堅持的部分。我正在研究它的前端部分,以實際使系統可用。前端部分針對當前正在查看的文章(article_id)進行數據庫查詢,並拉取按tf * idf排序的前20個詞。然後,我抽出這些單詞並對其他包含單詞的文章進行查詢,並有一個數組存儲要比較的文章以及它們匹配的次數。然後,對數組進行排序,並拖動比較次數最多的前3篇文章。
這最後一部分工作正常,並且我使用tf * idf和bag-of-words之間的混合實際上得到了很好的比較。問題在於前端部分發生時,需要30-45秒。顯然這是不可行的......它必須在幾分之一秒內完成,這就是我遇到我的問題的地方。
我知道這真的很長,我對此表示歉意。我基本上尋求一些幫助清理這個想法,有些地方我錯了,不同的方法。我願意接受所有建議,並樂意提供任何更多信息,如果它能使這些更清楚的話。謝謝你的時間!
每請求,表架構和前端代碼...
--
-- Table structure for table `bagofwords`
--
CREATE TABLE IF NOT EXISTS `bagofwords` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`article_id` int(11) NOT NULL,
`article_total_word_count` int(11) NOT NULL,
`word` text NOT NULL,
`count` int(11) NOT NULL,
`timestamp` int(11) NOT NULL,
`tfidf` float NOT NULL,
KEY `id` (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=660930 ;
public function related_articles($article_id, $count = 3) {
$query = "SELECT * FROM `bagofwords` WHERE `article_id` = '$article_id' ORDER BY `tfidf` DESC LIMIT 20";
$result = $this->db->query($query);
$num_rows = $this->db->num_rows($result);
$articles_list = array();
for ($i=0; $i<$num_rows; $i++) {
$word = $this->db->fetch_field($result, 'word', $i);
$query_word = "SELECT `article_id` FROM `bagofwords` WHERE `word` = '$word' AND `article_id` != '$article_id' ORDER BY `tfidf` DESC";
$result_word = $this->db->query($query_word);
$result_num_rows = $this->db->num_rows($result_word);
for ($x=0; $x<$result_num_rows; $x++) {
$article_id_word = $this->db->fetch_field($result_word, 'article_id', $x);
if (isset($articles_list["$article_id_word"])) $articles_list["$article_id_word"]++;
else $articles_list["$article_id_word"] = 1;
}
}
array_flip($articles_list);
asort($articles_list);
return $articles_list;
}
好吧,這是相當多的前端代碼部分,截至現在它返回整個陣列和var_dumps對前端剛看看我得到了什麼樣的數據。但是,你必須有更好的方法,使用嵌套的東西或臨時表將所有這些寫入單個mySQL語句中。我無法弄清楚!
你能不能動這個成cronjob每晚重新索引所有內容或因此有效創建緩存? – Treffynnon 2012-02-27 15:44:54
您是要求優化您的SQL還是建築理念?如果前者,你可以請張貼模式和查詢嗎? – 2012-02-27 15:46:35
這是一項令人印象深刻的工作,但它看起來像是重新實施Solr。是否有理由不能使用專用搜索服務器?很多聰明的人花費了大量的工作來構建和測試它,並且它可以爲你生成相關文檔列表。 – menacingly 2012-02-27 15:46:47