2012-02-27 40 views
3

首先,讓我開始說我已經做了大量的關於這個話題的研究,並且已經投入了大量的時間在一個可行的解決方案中。就這樣說,我遇到了一些我似乎無法克服的問題,因此正在尋求一些正確的方向。使用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語句中。我無法弄清楚!

+0

你能不能動這個成cronjob每晚重新索引所有內容或因此有效創建緩存? – Treffynnon 2012-02-27 15:44:54

+0

您是要求優化您的SQL還是建築理念?如果前者,你可以請張貼模式和查詢嗎? – 2012-02-27 15:46:35

+0

這是一項令人印象深刻的工作,但它看起來像是重新實施Solr。是否有理由不能使用專用搜索服務器?很多聰明的人花費了大量的工作來構建和測試它,並且它可以爲你生成相關文檔列表。 – menacingly 2012-02-27 15:46:47

回答

1

顯而易見的是將此查詢作爲自連接運行。我需要測試產量優化,但這樣的:

select word, count(*) as article_count 
from bagofwords article, 
     bagofwords relations 
where article.article_id = '$article_id' 
and article.word  = relation.word 
group by word 
order by article.tfidf, article_count 

limit 20 

你也想在科拉姆「詞」索引:

create index word on bagofwords(word) 
+0

感謝所有的幫助,我仍然學習了很多關於基本原理之外的mysql語法的知識......我現在就試試看,並告訴您它是如何工作的 – Lyynk424 2012-02-27 16:51:38

+0

好的,我必須稍微修改一下你的代碼才能使它工作。這是我結束了:------從bagofwords文章,bagofwords關係 選擇relations.word,relations.article_id,relations.tfidf where article.article_id ='3415' and(relations.word = article .word AND relations.article_id!= article.article_id) order by relations.tfidf desc limit 20 ----不幸的是,這有幾個問題,因爲它僅基於最高tf * idf的文章關係,而不是考慮tf * idf和最高文章計數,我似乎無法計算如何將數據添加到php中。 – Lyynk424 2012-02-27 17:02:51

+0

article_id ='3415'只是我在phpmyadmin中測試代碼 – Lyynk424 2012-02-27 17:05:40