You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

通过Postgrex适配器查询jsonb字段时遇语法错误求助

解决Postgrex查询jsonb字段的参数插值错误

问题场景

使用Ecto的Postgrex适配器查询jsonb字段时出现语法错误,相关信息如下:

原查询代码

def all_for(user_id, external_id) do
  from(n in __MODULE__,
    where: n.to == ^user_id and fragment("? @> '{\"external_id\": ?}'", n.data, ^external_id)
  )
  |> order_by(desc: :id)
end

生成的SQL语句

SELECT n0."id", n0."data", n0."to", n0."inserted_at", n0."updated_at" FROM "notifications"
AS n0 WHERE ((n0."to" = $1) AND n0."data" @> '{"external_id": $2}') ORDER BY n0."id" DESC

报错信息

↳ :erl_eval.do_apply/6, at: erl_eval.erl:680
** (Postgrex.Error) ERROR 22P02 (invalid_text_representation) invalid input syntax for type json. If you are trying to query a JSON field, the parameter may need to be interpolated. Instead of

    p.json["field"] != "value"

do

    p.json["field"] != ^"value"


    query: SELECT n0."id", n0."data", n0."to", n0."inserted_at", n0."updated_at" FROM "notifications" AS n0 WHERE ((n0."to" = $1) AND n0."data" @> '{"external_id": $2}') ORDER BY n0."id" DESC

Token "$" is invalid.
    (ecto_sql 3.9.1) lib/ecto/adapters/sql.ex:913: Ecto.Adapters.SQL.raise_sql_call_error/1
    (ecto_sql 3.9.1) lib/ecto/adapters/sql.ex:828: Ecto.Adapters.SQL.execute/6
    (ecto 3.9.2) lib/ecto/repo/queryable.ex:229: Ecto.Repo.Queryable.execute/4
    (ecto 3.9.2) lib/ecto/repo/queryable.ex:19: Ecto.Repo.Queryable.all/3

验证情况

手动替换SQL中的参数值后,在psql控制台执行可成功返回结果:

SELECT n0."id", n0."data", n0."to", n0."inserted_at", n0."updated_at" FROM "notifications" AS n0 WHERE ((n0."to" = 233) AND n0."data" @> '{"external_id": 11}') ORDER BY n0."id" DESC;

执行结果:

id |        data         | to  |     inserted_at     |     updated_at
----+---------------------+-----+---------------------+---------------------
 90 | {"external_id": 11} | 233 | 2022-12-15 14:07:44 | 2022-12-15 14:07:44
(1 row)

字段类型说明

data字段为jsonb类型:

Column    |              Type              | Collation | Nullable |                  Default
-------------+--------------------------------+-----------+----------+-------------------------------------------
 data        | jsonb                          |           |          | '{}'::jsonb

问题原因

原代码中,fragment内的JSON字符串'{"external_id": ?}'会被PostgreSQL当作完整的JSON文本解析,占位符$2被包含在JSON字符串内部后,PostgreSQL无法识别其为参数占位符,反而将$视为JSON中的无效字符,触发语法错误。

解决方案

有两种可行的修改方式:

方法1:传递完整的JSON对象作为参数

直接构造包含external_id的Map,让Ecto自动将其转换为jsonb参数:

def all_for(user_id, external_id) do
  from(n in __MODULE__,
    where: n.to == ^user_id and fragment("? @> ?", n.data, ^%{"external_id" => external_id})
  )
  |> order_by(desc: :id)
end

Ecto会将^%{"external_id" => external_id}转换为合法的jsonb参数,Postgrex能正确识别并完成匹配。

方法2:使用PostgreSQL的jsonb_build_object函数

通过PostgreSQL内置函数动态构建jsonb对象,确保参数被正确插值:

def all_for(user_id, external_id) do
  from(n in __MODULE__,
    where: n.to == ^user_id and fragment("? @> jsonb_build_object('external_id', ?)", n.data, ^external_id)
  )
  |> order_by(desc: :id)
end

jsonb_build_object会将传入的参数external_id转换为jsonb结构的一部分,避免了手动拼接JSON字符串的问题。

内容的提问来源于stack exchange,提问作者zhisme

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 05:50:22