使用php和mysql创建一个有效的方法来构建相关的文章功能

时间:2012-02-27 15:41:02

标签: 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_dump只是为了看看我得到了什么样的数据。但是,你必须有一个更好的方法来使用嵌套的东西或临时表在单个mySQL语句中编写所有这些。我无法理解!

1 个答案:

答案 0 :(得分: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

你还想要一个关于colum“word”的索引:

create index word on bagofwords(word)