mysql全文索引需手动在迁移中创建,innodb要求mysql≥5.6.4;eloquent无原生全文搜索方法,须用whereraw配合match against并绑定参数防注入;中文搜索效果差,推荐改用meilisearch等外部引擎。

MySQL全文索引必须手动启用,Eloquent默认不创建
Laravel 的 Model 不会自动为字段添加 FULLTEXT 索引,即使你后续用 match against 查询,也会报错 Can't find FULLTEXT index matching the column list。必须在迁移中显式声明:
- 使用
fullText()方法(Laravel 9.2+):Schema::table('posts', function (Blueprint $table) { $table->fullText(['title', 'content']); }); - 低版本需手写 SQL:
DB::statement('ALTER TABLE posts ADD FULLTEXT(title, content)'); - 注意:InnoDB 表支持全文索引,但 MySQL 版本需 ≥5.6.4;MyISAM 虽支持更早,但已不推荐
whereRaw + match against 是最直接的写法,但要防注入
Eloquent 没有原生 fullTextSearch() 方法,得用 whereRaw() 拼接 MATCH ... AGAINST。常见错误是把用户输入直接塞进 SQL:
- ❌ 危险写法:
->whereRaw("MATCH(title, content) AGAINST('{$_GET['q']}')") - ✅ 安全写法(绑定参数 + 预处理):
->whereRaw("MATCH(title, content) AGAINST(? IN NATURAL LANGUAGE MODE)", [$searchTerm]) - 模式可选:
NATURAL LANGUAGE MODE(默认,适合普通搜索)、BOOLEAN MODE(支持+/-/*等操作符),但注意布尔模式下短词(ft_min_word_len
orderByRaw 排序必须跟 MATCH 保持字段一致
全文搜索相关性得分由 MATCH() 函数返回,不能用 orderBy('relevance') 这种虚构字段——它只在当前查询里存在:
- ❌ 错误排序:
->orderBy('relevance', 'desc') - ✅ 正确写法(复用相同 MATCH 表达式):
->selectRaw("*, MATCH(title, content) AGAINST(?) AS relevance", [$searchTerm]) ->orderByRaw("MATCH(title, content) AGAINST(?) DESC", [$searchTerm]) - 否则会报错
Unknown column 'relevance' in 'order clause',或排序失效
中文搜索基本不可用,别硬扛
MySQL 原生 FULLTEXT 对中文支持极差——按空格分词,而中文无空格。哪怕加了 ngram 插件(MySQL 5.7.6+),也仅支持固定长度切分(如 ngram_token_size=2),无法处理“人工智能”和“人工”这种歧义。
- 实际效果:搜“数据库”可能匹配不到“数据库原理”,因为 ngram 切成“数据”“库原”“原理”等碎片
- 替代方案不是优化 SQL,而是换引擎:用
MeiliSearch或Algolia做外部索引,Laravel 有meilisearch/laravel-meilisearch包可快速接入 - 如果坚持用 MySQL,至少把标题/关键词单独抽成 tags 字段,用
LIKE或IN做辅助匹配,别指望全文索引扛中文主搜
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











