英文:
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()
。 -
为避免出现意外结果,我们建议只在严格模式下使用
.**
访问器。这在这种特定情况下不会出问题,但仍然更加安全。
-
您还可以在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:
- https://stackoverflow.com/questions/14065271/how-to-use-execute-format-using-in-postgres-function/14066715#14066715
- https://stackoverflow.com/questions/22656716/format-specifier-for-integer-variables-in-format-for-execute/22675836#22675836
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:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论