真实可用的相似推荐需基于语义特征而非关键词硬匹配,推荐用TF-IDF+余弦相似度或SQLite FTS5实现轻量级方案,关键词匹配仅作兜底且须规避全表扫描等陷阱。

相似文章推荐的核心逻辑不是靠关键词硬匹配
直接用 LIKE 或 INSTR 做标题/内容关键词匹配,推荐结果基本不可用——词频、权重、停用词、同义词全被忽略,用户看到的往往是“都含‘PHP’所以推荐《PHP安装教程》给刚读完《ThinkPHP路由机制》的人”。真实可用的相似推荐,得从文本向量化或语义特征入手,哪怕只是轻量级方案。
用 TF-IDF + 余弦相似度做轻量级相似推荐(适合中小站点)
ThinkPHP 本身不内置文本相似计算,但可以借助 PHP 扩展或纯 PHP 实现。推荐用 php-science/vector(轻量)+ 自己提取关键词(避免依赖分词扩展)。关键不是“匹配哪些词”,而是“哪些词在本文中更重要”。
- 先对每篇文章标题+摘要做预处理:转小写、去标点、切词(空格/顿号/逗号分隔即可,不用复杂分词)、过滤停用词(如“的”“了”“是”)
- 用所有文章的词频构建全局
idf值,单篇文档用tf * idf算出向量 - 当前文章向量与其余文章向量逐个算余弦相似度,取 top 5 排序
- 缓存向量结果到
runtime/cache/article_vectors.php,避免每次请求都重算
示例片段(伪代码逻辑):
// $docVec 是当前文章向量,$allVecs 是其他文章向量数组
$similarities = [];
foreach ($allVecs as $id => $vec) {
$sim = vector_cosine($docVec, $vec);
if ($sim > 0.15) $similarities[$id] = $sim; // 阈值防噪声
}
arsort($similarities);
$recommendIds = array_keys($similarities);
关键词匹配只能当兜底或辅助策略(慎用 LIKE %keyword%)
如果硬要关键词匹配,必须避开三个坑:LIKE '%关键词%' 全表扫描、未建索引导致慢查、大小写和空格干扰匹配。它只适合“人工运营打标签”后的窄场景,比如后台指定“PHP”“Laravel”为关联标签,再查 WHERE tag_ids LIKE '%,3,%'。
- 绝对不要对
content字段用LIKE做实时推荐——加了全文索引(FULLTEXT)也扛不住高并发 - 若坚持用关键词,把提取结果存在独立表
article_keywords,字段为article_id、keyword、weight,并为keyword加普通索引 - ThinkPHP 查询可写成:
Db::name('article_keywords')->where('keyword', 'in', $keywords)->group('article_id')->having('count(*) >= 2'),控制最小匹配数
ES 或 SQLite FTS 是跨过“手写 TF-IDF”的最快路径
本地部署 Elasticsearch 成本高,但 SQLite 的 FTS5 扩展(PHP 8.1+ 默认启用)能直接跑在 ThinkPHP 里,支持词干、短语匹配、rank 排序,比手写稳定得多。
- 建虚拟表:
CREATE VIRTUAL TABLE article_fts USING fts5(title, content, tokenize='porter') - 插入时同步写入:
Db::execute("INSERT INTO article_fts (title, content) VALUES (?, ?)", [$title, $content]) - 查询相似:
SELECT article_id FROM article_fts WHERE article_fts MATCH ? ORDER BY rank LIMIT 5,参数传当前文章的标题+摘要 - 注意:FTS 表不支持 JOIN,需用子查询或两次查询补全字段
真正难的不是选哪种方法,而是想清楚“用户点进这篇文章,到底是因为什么才可能对另一篇感兴趣”——是主题延续?是技术栈一致?还是问题场景雷同?算法只是表达这个判断的工具,不是替代判断本身。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










