tp6.0需手动启用mysql全文索引:确保innodb引擎、mysql≥5.6、字段加fulltext索引,并用原生match against查询,不可依赖wherelike或withsearch自动转换。

TP6.0 里怎么让搜索走 FULLTEXT 而不是 LIKE
直接用 where('title', 'like', "%{$keyword}%") 是最常见也最危险的写法——慢、不精准、易 SQL 注入。TP6.0 本身不自动启用 MySQL 全文索引,必须手动改查询逻辑。
关键点有三个:
- 确保表引擎是
InnoDB,MySQL 版本 ≥ 5.6,字段字符集为utf8mb4 - 给目标字段(如
title、content)加FULLTEXT索引:ALTER TABLE articles ADD FULLTEXT(title, content) - 在模型或控制器中,用原生 SQL 或 Query 类拼出
MATCH() AGAINST()语句,不能依赖where链式调用自动转换
示例(在模型中):
$result = $this->query("SELECT *, MATCH(title, content) AGAINST(? IN NATURAL LANGUAGE MODE) AS score
FROM articles
WHERE MATCH(title, content) AGAINST(? IN NATURAL LANGUAGE MODE)
ORDER BY score DESC", [$keyword, $keyword]);
注意:TP6.0 的 withSearch 默认不支持 MATCH/AGAINST,它只处理普通字段条件,所以别指望 withSearch(['title'], ['title' => $kw]) 自动变全文查询。
Scws 分词怎么嵌进 TP6.0 搜索流程
Scws 不是开箱即用的“插件”,它只负责把用户输入的中文句子切分成词,比如把“苹果手机怎么拍照”变成 ['苹果', '手机', '怎么', '拍照'],后续怎么查、查什么字段,得你自己组织。
典型做法是:先分词 → 再拼布尔模式查询 → 最后喂给 MySQL 或 Sphinx。在 TP6.0 中建议封装成一个服务类,而非塞进模型搜索器。
容易踩的坑:
- Scws 默认字典不含新词或专业词,遇到“大模型”“RAG”这类词会切错,必须配合
dict_user.txt加自定义词库 - 不要对每个请求都调用
scws_new()创建实例,应复用单例,否则性能暴跌 - 分词结果里常含停用词(如“的”“了”),需提前用
stopwords.txt过滤,否则MATCH AGAINST会报语法错误
简单调用示意:
Perplexity SEO 优化指南 — 如何获得 Perplexity AI 的引用。涵盖 Perplexity 引用行为、面向 Perplexity 答案的内容结构、Perplex...
$scws = scws_open();
scws_set_dict($scws, '/usr/local/xunsearch/etc/dict_user.txt');
scws_send_text($scws, $keyword);
while ($word = scws_get_result($scws)) {
if (!in_array($word['word'], $stopwords)) {
$terms[] = $word['word'];
}
}
// 后续用 $terms 构建布尔查询:'+苹果 +拍照'...
为什么不能只靠 Scws + MySQL FULLTEXT 做 SEO 友好搜索
因为 MySQL 的 IN BOOLEAN MODE 对 SEO 场景有硬伤:不支持同义词扩展、不支持拼音容错、不支持词权重动态调整,更无法处理 site:example.com 这类语法。
真实 SEO 搜索需求往往包含:
- 用户搜“php教程”,希望匹配标题含“PHP”(大小写不敏感)、正文中含“入门教程”的文档
- 搜“tp6 搜索器”,要能命中“thinkphp6”“tp6.0”“搜索器用法”等变体
- 需要按点击率、停留时长等行为数据动态调权,而 MySQL FULLTEXT 只认 TF-IDF
这时 Scws 只能当预处理器,真正扛压和排序得交给 Elasticsearch 或 XunSearch。TP6.0 可通过 elasticsearch/elasticsearch 官方客户端或 hightman/xunsearch SDK 接入,搜索器方法里做适配即可:
public function searchKeywordAttr($query, $value, $data) {
$client = new \XSDS('/path/to/xunsearch/sdk');
$results = $client->search($value);
// 把 doc_id 返回,再用 in() 查关联数据
$ids = array_column($results, 'id');
$query->whereIn('id', $ids);
}
TP6.0 搜索器里怎么安全传入分词结果
TP6.0 的 withSearch() 第二个参数是纯数组,不能直接传对象或资源句柄。如果想把 Scws 分词后的词数组传进去,必须扁平化、过滤、转字符串。
推荐结构:
- 前端提交关键词,后端先过 Scws,得到干净词数组
$terms - 把
$terms序列化为 JSON 字符串,存进$_GET或 POST body(避免 URL 过长) - 在搜索器方法里反解:
$terms = json_decode($value, true) ?: [] - 严禁把原始用户输入直接丢进
scws_send_text(),必须trim()+htmlspecialchars()防 XSS
特别注意:TP6.0 搜索器第三个参数 $data 是整个 withSearch() 的第二参数全量,不是单个字段值。所以别写成 searchKeywordAttr($q, $term, $data) 就以为 $term 是分词结果——它只是你传进来的那个键对应的原始字符串。
Scws 和 MySQL FULLTEXT 都只是工具链的一环,真正决定搜索体验的是你怎么串起分词、查询、排序、缓存这四步。漏掉任意一环,比如忘了清空停用词就扔进布尔查询,或者没设 ft_min_word_len=2 导致“AI”被忽略,结果都会断崖式下跌。










