laravel本身不支持向elasticsearch发送通配符查询,必须使用原生http客户端构造dsl请求;且*开头的通配符默认被禁用,需显式设置allow_leading_wildcard:true。

直接说结论:Laravel 本身不支持向 Elasticsearch 发送通配符查询(wildcard),必须绕过 Eloquent,用原生 ES HTTP 客户端构造 DSL 请求;且 * 开头的通配符(如 "*abc")在 ES 中默认被禁用,会报 illegal_argument_exception 错误。
为什么 Laravel 的 whereLike 无法用于 ES 通配符查询
Laravel 的 whereLike(实际是 where('field', 'like', '%value%'))只作用于 MySQL/PostgreSQL 等关系型数据库,底层走的是 SQL LIKE 语法。它和 Elasticsearch 完全无关——ES 没有 LIKE,也没有 % 通配符语义。
- 试图在 Eloquent 查询里写
->where('name', 'like', '%li%'),只会让 Laravel 去查 MySQL 表,不是 ES 索引 - 即使你用了 Laravel Scout + Algolia/Elasticsearch 驱动,Scout 的
search()方法默认只支持关键词全文匹配(match),不暴露wildcard、regexp等底层 DSL 能力 - Scout 的
raw()方法虽可传 DSL,但官方文档明确警告:它绕过 Scout 抽象层,需自行处理索引名、类型(ES 7+ 后已弃用 type)、路由等细节
如何正确发起 wildcard 查询(含 * 开头场景)
必须用 HTTP 客户端(如 guzzlehttp/guzzle)直连 ES,并手动构造 JSON body。关键点有三个:字段类型、wildcard 语法、以及开头 * 的特殊处理。
- 确保目标字段是
keyword类型(不是text),否则分词后wildcard无法按原始字符串匹配。例如:"name.keyword"而非"name" -
wildcard查询结构固定:{"query":{"wildcard":{"field_name":{"value":"pattern"}}}},value中*匹配任意字符(含空),?匹配单字符 -
"*abc"这类前导通配符默认被 ES 拒绝(性能风险)。要启用,必须在查询中显式加"allow_leading_wildcard": true参数,否则返回illegal_argument_exception - 示例请求(PHP + Guzzle):
{"query":{"wildcard":{"name.keyword":{"value":"*四","allow_leading_wildcard":true}}}}
容易踩的坑:中文、大小写、性能三连击
通配符在中文或混合内容下极易出错,不是写对语法就完事。
- 中文字段若用了
text类型 + 默认分词器(如standard),"李*"会去匹配分词后的“李”字 token,而非原始字符串——结果可能为空或错乱。必须确认字段映射为keyword,或改用ngram分词器配合match_phrase - ES 默认区分大小写,
"ABC*"不会匹配"abc123"。如果业务需要忽略大小写,要么在索引时用lowercasefilter,要么查询时用case_insensitive: true(ES 7.10+ 支持) -
wildcard查询没有缓存,且"a*b*c"这种多星号模式会让 Lucene 枚举所有匹配 term,数据量大时极易超时。线上环境务必限制max_determinized_states(默认 10000),并监控took时间
真正麻烦的不是怎么写这个查询,而是你得先确认:那个字段到底有没有被建模成 keyword、ES 版本是否支持 case_insensitive、以及用户输入的通配符是不是真的必要——很多时候,用 prefix 或 multi_match + edge_ngram 分词器更稳。











