wherejsoncontains不能直接匹配数组中任一值,必须用orwherejsoncontains逐个展开;空数组需提前返回空集合,null或非法json字段会静默失败;postgresql对象数组需传关联数组而非字符串。

whereJsonContains 不能直接匹配数组,必须拆成 orWhereJsonContains
想用 whereJsonContains('fields', ['fashion', 'food']) 查 JSON 数组里含任一值?不行。这个写法实际会调用数据库的 JSON_CONTAINS(fields, '["fashion","food"]'),语义是“是否完整包含该数组”,即要求字段值同时有 fashion 和 food —— 和你想要的 whereIn 行为完全相反。
正确做法是手动展开:对每个目标值调用一次 orWhereJsonContains,并包裹在闭包里避免逻辑污染。
-
whereJsonContains('fields', 'fashion')匹配["fashion"]、["fashion","sport"]等任意含 fashion 的数组 - 多个值必须用
orWhereJsonContains拼接,不能用whereJsonContains连续调用(否则变成 AND) - 闭包内用
foreach构建 OR 链,避免外部查询条件被意外覆盖
空数组或 null 字段要提前兜底
如果传入的 $targetValues 是空数组,Eloquent 不会自动跳过查询,而是生成一个恒假的 WHERE 0 = 1,结果永远为空。更糟的是,如果 JSON 字段本身为 NULL 或不是合法 JSON,whereJsonContains 在 MySQL 中会静默返回空结果,不报错也不提示。
- 空数组时直接返回空集合:
if (empty($targetValues)) { return collect(); } - 数据库层无法校验 JSON 合法性,建议在写入时用模型的
casts属性强制转为 array,并配合json验证规则 - MySQL 8.0+ 可加
JSON_VALID(fields)条件过滤脏数据,但 Laravel 原生不支持,需whereRaw
PostgreSQL 用户注意 operator 差异
MySQL 用 JSON_CONTAINS,PostgreSQL 用 @> 操作符,但 whereJsonContains 底层已适配。不过 PostgreSQL 对 JSONB 的路径匹配更严格:如果字段是 JSONB 类型,且存的是字符串数组 ["a","b"],whereJsonContains('tags', 'a') 能正常工作;但如果存的是对象数组 [{"id":"a"},{"id":"b"}],就必须写成 whereJsonContains('tags', ['id' => 'a'])。
- 字符串数组匹配用
whereJsonContains('tags', 'a') - 对象数组匹配用
whereJsonContains('tags', ['id' => 'a']),注意是关联数组,不是 JSON 字符串 - PostgreSQL 不支持
->路径语法用于whereJsonContains,比如tags->'id'不能直接传进去
大数据量时别在 PHP 层用 intersect 做兜底
有人图省事,先 get() 全部记录再用 collect()->intersect() 过滤。这在几百条数据时看似可行,但一旦表里有上万行,就会把所有 JSON 字段拉到 PHP 内存里解析、比对,CPU 和内存双双爆炸。
-
intersect()是纯内存操作,和数据库无关,不能替代 SQL 层过滤 - 哪怕只查 1000 条,JSON 字段若含长文本,序列化/反序列化开销也远高于一次
whereJsonContains - 真要兜底(比如跨库或动态 schema),优先考虑
chunkById()分批 +whereJsonContains,而不是全量加载
whereJsonContains 的参数形式必须跟着变,而不是无脑套用字符串值。











