构建宏以生成访问JSON密钥的片段

问题描述

假设我有一个要根据其字段之一排序的jsonb字段,它也应该使用dynamic,因为在编译时无法告诉字段名称。我目前拥有的是:

def order_by_dynamic([sort_direction,%{binding_name: binding_name,json_field: json_key,field: field}]) do
    [{sort_direction |> String.to_atom(),dynamic([{^binding_name,c}],create_fragment([{c.details,^field},json_key]))}]
  end

defmacro create_fragment(fields) do
    require Ecto.Query

    query = "?->>?"
    quote do
      fragment(unquote(query),unquote_splicing(fields))
    end
  end

代码的问题是它将无法编译:

** (Ecto.Query.CompileError) Tuples can only be used in comparisons with literal tuples of the same size
    expanding macro: Ecto.Query.dynamic/2

我还试图在将json字段名称传递给字符串之前将其插入到字符串中,并且错误是:

** (Ecto.Query.CompileError) to prevent sql injection attacks,fragment(...) does not allow strings to be interpolated as the first argument via the `^` operator,got: `"?->>#{json_key}"`
    expanding macro: Ecto.Query.dynamic/2

我丢失了某些东西吗,或者使用当前的片段宏无法实现?

解决方法

好吧,看来我了解了,您需要直接从绑定中提供第一个参数,以避免sql注入:

defmacro create_dynamic_fragment(binding_name,field,json_field) do
    require Ecto.Query

    quote do
      dynamic([{^unquote(binding_name),c}],fragment("?->>?",field(c,^unquote(field)),^unquote(json_field)))
    end
  end