如何将变量值插入到JSONPath字符串中?

huangapple go评论67阅读模式
英文:

How to insert variable value into JSONPath string?

问题

我有一个函数:

CREATE OR REPLACE FUNCTION public.new_function1(arg1 text DEFAULT 'Miha'::text)
 RETURNS SETOF declarations
 LANGUAGE sql
 STABLE
AS $function$
select * from declarations where data @? '$.** ? (@ == $1)';
$function$

如何将变量 arg1 替换为 JSONPath 表达式中的 $1

select * from declarations where data @? '$.** ? (@ == $1)';

如果我使用字符串字面值,一切都好:

select * from declarations where data @? '$.** ? (@ == "Sergey")';
英文:

I have a function:

CREATE OR REPLACE FUNCTION public.new_function1(arg1 text DEFAULT 'Miha'::text)
 RETURNS SETOF declarations
 LANGUAGE sql
 STABLE
AS $function$
select * from declarations where data @? '$.** ? (@ == $1)';
$function$

How to substitute variable arg1 for $1 in the JSONPath expression?

select * from declarations where data @? '$.** ? (@ == $1)';

If I use a string literal, all is good:

select * from declarations where data @? '$.** ? (@ == "Sergey")';

答案1

得分: 1

以下是翻译好的内容:

  • 您可以将函数参数连接成字符串并转换为jsonpath(如评论中建议的)。但在jsonpath表达式中,嵌套字符串值是双引号括起来的(而Postgres字符串类型使用单引号)。因此,format()quote_*()函数系列都无法正确处理这种情况。

  • 对于字符串连接,您可以手动添加双引号,如果有嵌套的双引号,则需要进行转义。或者您可以将其转换为json / jsonb,然后再转换为text,让Postgres为您可靠地完成工作。可以使用jsonb_build_object('arg1', arg1)构建函数调用的jsonb参数,或者使用to_json(arg1)::text进行字符串连接。

  • 函数jsonb_path_exists():这个函数提供了将参数传递给jsonpath表达式的选项。这对于操作符@?并未实现。一个干净且安全的方法是使用jsonb_path_exists()

  • 操作符@?:添加了strict,如手册建议

    为避免出现意外结果,我们建议只在严格模式下使用.**访问器。

    这在这种特定情况下不会出问题,但仍然更加安全。

  • 您还可以在PL/pgSQL函数中使用EXECUTE进行动态SQL,并在USING子句中传递jsonpath表达式(双引号的相同问题适用)。参见:

  • 索引:为您特定的jsonpath表达式创建了一个GIN索引。对于这个特定的jsonpath表达式,有一个重要的细节,需要引用手册

    jsonb_ops操作符类还支持.*.**访问器,但jsonb_path_ops操作符类不支持。

    因此,您无法为此使用更优化的操作符类jsonb_path_ops。请参考:

英文:

You could concatenate the function parameter into a string and cast to jsonpath (as suggested in comments). But nested string values are double-quoted in a jsonpath expression (while Postgres string types use single-quotes). So neither format() nor any of the quote_*() family of functions handles that properly.

For string-concatenation, you could double-quote manually, escaping nested double-quotes if any. Or you cast to json / jsonb and back to text to make Postgres do the work for you, reliably. Either with jsonb_build_object('arg1', arg1) to build the jsonb argument for the function call. Or with to_json(arg1)::text for string concatenation.

Function jsonb_path_exists()

The largely equivalent function jsonb_path_exists() offers the option to pass parameters to the jsonpath expression. That's not implemented for the operator @?. A clean & safe way is to use jsonb_path_exists()

CREATE OR REPLACE FUNCTION public.new_function1(arg1 text DEFAULT 'Miha')
  RETURNS SETOF public.declarations       -- schema-qualify to be safe
  LANGUAGE sql STABLE PARALLEL SAFE AS    -- PARALLEL SAFE in Postgres 9.6 or later
$func$
SELECT *
FROM   public.declarations
WHERE  jsonb_path_exists(target => data
                       , path   => 'strict $.** ? (@ == $arg1)'      -- can use a param ...
                       , vars   => jsonb_build_object('arg1', arg1)  -- ... which is defined in next arg
                       , silent => true);                            -- to behave exactly like original operator @?
$func$;

Using the optional, more verbose function call with named parameters for clarity. See:

Major downside: Postgres indexes are bound to operators, not functions. So this form gets no index support. No good for big tables. Related:

Operator @?

CREATE OR REPLACE FUNCTION public.new_function1(arg1 text DEFAULT 'Miha')
  RETURNS SETOF public.declarations
  LANGUAGE sql STABLE PARALLEL SAFE AS
$func$
SELECT *
FROM   public.declarations
WHERE  data @? format('strict $.** ? (@ == $s)'  -- note: strict
                    , to_json(arg1)::text        -- safe conversion
                     )::jsonpath;
$func$;

I added strict as advised in the manual:

> To avoid surprising results, we recommend using the .** accessor only in the strict mode.

Wouldn't break in this particular case, but it's still cheaper.

You could also use dynamic SQL with EXECUTE in a PL/pgSQL function and pass the jsonpath expression in a USING clause. (The same issue with double-quoting applies.) See:

Index

CREATE INDEX declarations_gin_idx ON public.declarations USING gin (data);

Important detail for your particular jsonpath expression, quoting the manual:

> The jsonb_ops operator class also supports .* and .** accessors,
> but the jsonb_path_ops operator class does not.

So you cannot use the more optimized operator class jsonb_path_ops for this. See:

huangapple
  • 本文由 发表于 2023年7月11日 04:24:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/76657101.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定