应使用 whereJsonContainsKey('field', '$.key') 查询 JSON 字段中键是否存在,它底层调用 JSON_CONTAINS_PATH,支持嵌套路径如 '$.user.profile.name',需严格遵循 MySQL 路径语法且字段必须为 JSON 类型。

Hyperf 3.1.66 原生支持 MySQL 的 JSON_CONTAINS 和 JSON_CONTAINS_PATH 查询,不用手写 raw SQL 或拼字符串就能安全查 JSON 字段里的键存在性——但必须用对方法,否则会 silently 返回空结果。
怎么用 whereJsonContains 查 JSON 字段里有没有某个 key
Hyperf 的 Model 和 QueryBuilder 都封装了 whereJsonContains,它对应 MySQL 的 JSON_CONTAINS,适用于判断 JSON 数组或对象中是否包含某个值(注意:是「值」,不是「键」);而查「键是否存在」得用 whereJsonContainsKey——这是 3.1.66 新增的专属方法。
-
whereJsonContains('meta', '"tag1"'):查meta字段 JSON 中是否包含字符串"tag1"(注意引号要手动加) -
whereJsonContainsKey('meta', 'tags'):查meta是否有tags这个 key(自动处理路径,支持嵌套如'user.profile.name') - 别把两者混用:
whereJsonContains对 key 无效,whereJsonContainsKey对 value 无效 - 字段类型必须是
JSON,MySQL 5.7+ 才支持;如果字段是TEXT或VARCHAR,函数会返回NULL,查询永远不匹配
whereJsonContainsKey 的路径写法和常见坑
这个方法底层调用的是 JSON_CONTAINS_PATH,路径语法必须严格遵循 MySQL 规范:根层级用 $,数组索引用 [0],对象属性用 .key。Hyperf 会帮你转义,但你传入的路径字符串不能带多余空格或非法字符。
- 正确:
whereJsonContainsKey('config', '$.features')、whereJsonContainsKey('data', '$.items[0].id') - 错误:
whereJsonContainsKey('config', 'features')(缺$.前缀)、whereJsonContainsKey('data', '$.items[0].name ')(末尾空格) - 路径中含点号或中括号但非 JSON 结构?比如字段名是
user.name,那就得写成'$.`user.name`',用反引号包裹 - 传变量时别直接拼接:
whereJsonContainsKey('meta', '$.' . $key)有注入风险,应改用参数绑定(目前 Hyperf 不支持路径参数化,所以务必校验$key只含字母、数字、下划线、点、中括号)
连表 + JSON 查询时内存和性能怎么控
JSON 查询本身不重,但一旦和大表 JOIN,又没加 limit 或游标分页,很容易触发前面提到的内存溢出问题——尤其 whereJsonContainsKey 在 WHERE 条件里,MySQL 可能无法走索引,导致全表扫描 + JSON 解析开销叠加。
- 给 JSON 字段建生成列 + 索引才是正解:
ALTER TABLE users ADD COLUMN has_tags TINYINT AS (IF(JSON_CONTAINS_KEY(meta, "$.tags"), 1, 0)) STORED;,再对has_tags加索引 - 避免在 JOIN 的被驱动表上用
whereJsonContainsKey:比如orders JOIN users,条件写在users上比写在orders上更容易命中索引 - 连表查 JSON 时,禁用
get(),改用cursor()或原生 PDO 游标遍历(参考PDO::MYSQL_ATTR_USE_BUFFERED_QUERY = false设置) - 测试时用
EXPLAIN FORMAT=TRADITIONAL看执行计划,确认type不是ALL,Extra里没有Using where; Using temporary; Using filesort
真正容易被忽略的是:JSON 查询函数在 MySQL 里区分大小写(JSON_CONTAINS_PATH 默认 case-sensitive),而 Hyperf 没做额外转换。如果你的业务依赖 key 名大小写不敏感,就得在应用层 normalize,或者用 LOWER(JSON_EXTRACT(...)) 配合模糊匹配——但这会让索引失效。


















