如何从 JSONB 中选择具有给定键的子对象?

Ale*_*erF 5 postgresql functions json

如何从 Postgres 中的 json(b) 对象中提取/获取/选择“子对象”?

似乎有很多信息几乎可以让我到达那里,但又不完全是。很多关于转换为记录、过滤然后从中构建新对象的内容。说实话,我真的很惊讶这不是内置功能。也许有一种简单的方法可以通过组合内置 fns 来实现此目的?我正在寻找的本质上相当于select-keys;一个函数(比如jsonb_select_keys)给出了这个:

SELECT jsonb_select_keys('{"a":42,"b":43,"c":44,"d":97}', '{a,d,e}');
Run Code Online (Sandbox Code Playgroud)

会返回这个:

{"a":42,"d":97}
Run Code Online (Sandbox Code Playgroud)

有点像jsonb_path_query_array,但针对的是 kv 对而不是值。

Erw*_*ter 8

您可以使用运算符删除给定的键-(与您的想法相反)。手册:

\n
\n

jsonb - text[]\xe2\x86\x92jsonb

\n

从左操作数中删除所有匹配的键或数组元素。

\n

\'{"a": "b", "c": "d"}\'::jsonb - \'{a,c}\'::text[]\xe2\x86\x92{}

\n
\n

但我不知道内置函数或运算符会执行您所要求的操作(包括当前的 Postgres 15)。

\n

值得注意的是,SQL 语句则相反SELECT。在那里你会得到一个带有 的肯定列表SELECT a,d,e FROM tbl,但不能简单地获取“除 [a,d,e] 之外的所有列”,就像你可以获取“除 [a,d,e] 之外的所有键”一样一样。我希望每个功能都有互补的功能。

\n

也想不出使用 SQL/JSON 路径语言(Postgres 12+)的简单方法。

\n

解决方法jsonb

\n

您可以创建一个简单的函数,例如:

\n
CREATE OR REPLACE FUNCTION f_jsonb_select_keys(_js jsonb, _keys text[])\n  RETURNS jsonb\n  LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE AS\n$func$\nSELECT jsonb_object_agg(t.key, t.value)\nFROM   jsonb_each(_js) t\nWHERE  t.key = ANY (_keys);\n$func$;\n
Run Code Online (Sandbox Code Playgroud)\n

db<>在这里摆弄

\n

称呼:

\n
SELECT f_jsonb_select_keys(\'{"a":42,"b":43,"c":44,"d":97}\', \'{a,d,e}\');\n
Run Code Online (Sandbox Code Playgroud)\n

返回:

\n
{"a":42,"d":97}\n
Run Code Online (Sandbox Code Playgroud)\n

对于 NULL 或任一参数的空输入,您会得到 NULL(如小提琴所示)。所以我声明了该函数STRICT(又名RETURNS NULL ON NULL INPUT),即使嵌套函数jsonb_object_agg()不是STRICT。可能扰乱函数内联,但由于无论如何都不能内联这个函数(包含聚合函数),所以我们也可以。看:

\n\n

IMMUTABLE只是有点真实。继续阅读。

\n

键的顺序?

\n

jsonb不保留键的顺序(键在内部以确定性方式排序),因此我们无需担心聚合中输入行的顺序。除非存在重复项,否则以输入的最新副本为准。手册:

\n
\n

由于该json类型存储输入文本的精确副本,\n它将保留标记之间语义上无关紧要的空白,\以及 JSON 对象中键的顺序。此外,如果值中的 JSON 对象多次包含相同的键,则所有键/值对都会保留。(处理函数将最后一个值视为操作值。)相比之下,jsonb不保留空白、不保留对象键的顺序,并且不保留重复的对象键。如果在输入中指定了重复的键,\n则仅保留最后一个值。

\n
\n

这意味着,如果输入中可以有重复的键,则行的顺序很重要。我的函数没有显式对行进行排序,因此结果不会严格为IMMUTABLE. 但输入直接来自jsonb_each()并且不能有重复的键jsonb(与 不同json)。输入键数组中可能存在的重复项与= ANY构造无关。所以重复永远不会发生。IMMUTABLE毕竟。

\n

同样,这将禁止函数内联——如果它一开始就可以内联的话。

\n

相当于json

\n

为了完整起见:与 相同,不是jsonjsonb

\n

第一个变体不会尝试保留给定的键顺序,并且它消除了输入键数组中可能的重复项(但不会消除输入中可能的重复项json!):

\n
CREATE OR REPLACE FUNCTION f_json_select_keys(_js json, _keys text[])\n  RETURNS json\n  LANGUAGE sql STABLE STRICT PARALLEL SAFE AS\n$func$\nSELECT json_object_agg (t.key, t.value)\nFROM   json_each(_js) t\nWHERE  key = ANY (_keys);\n$func$;\n
Run Code Online (Sandbox Code Playgroud)\n

第二种变体保留给定的键顺序并保留所有可能的重复项。(如果相同的键在json输入中为 2x,在输入键数组中为 3x,则在结果中得到 6x。):

\n
CREATE OR REPLACE FUNCTION f_json_select_keys(_js json, _keys text[])\n  RETURNS json\n  LANGUAGE sql STABLE STRICT PARALLEL SAFE AS\n$func$\nSELECT json_object_agg (t.key, t.value ORDER BY ord)\nFROM   unnest(_keys) WITH ORDINALITY k(key, ord)\nJOIN   json_each(_js) t USING (key);\n$func$;\n
Run Code Online (Sandbox Code Playgroud)\n

关于WITH ORDINALITY

\n\n

我创建这些json函数是STABLE因为json_object_agg()is 只是STABLE与 相对jsonb_object_agg(),即IMMUTABLE相对。

\n
\n

可以使用json_build_object (\'a\', js->\'a\', \'d\', js->\'d\', \'e\', js->\'e\'),但这将包括所有键,如果未找到则为 NULL 值。不完全是你的要求。而且您无法区分丢失的键和具有实际 NULL 值的相同键之间的区别。

\n