在 Postgres JSON 数组中查询
Posted
技术标签:
【中文标题】在 Postgres JSON 数组中查询【英文标题】:Querying inside Postgres JSON arrays 【发布时间】:2013-09-20 22:54:15 【问题描述】:您将如何搜索存储在json
列中的数组中的元素? (更新:另请参阅jsonb
列的 9.4 更新答案。)
如果我有一个这样的 JSON 文档,存储在名为 blob
的 json
列中:
"name": "Wolf",
"ids": [185603363281305602,185603363289694211]
我想做的是这样的:
SELECT * from "mytable" WHERE 185603363289694211 = ANY("blob"->'ids');
并取出所有匹配的行。但这不起作用,因为 "blob"->'ids'
返回 JSON 值,而不是 Postgres 数组。
如果可能的话,我还想在各个 ID 上建立一个索引。
【问题讨论】:
【参考方案1】:首先,尝试使用运算符 ->>
而不是 ->
从数组值中剥离 JSON 层。
接下来,查询可以像这样工作:How do I query using fields inside the new PostgreSQL JSON datatype?
索引可能会像这样工作:Index for finding an element in a JSON array
【讨论】:
【参考方案2】:以下原始答案仅适用于 Postgres 9.3。有关 Postgres 9.4 的答案,请参阅下面的更新。
这建立在Erwin's referenced answers 之上,但对这个问题更加明确。
本例中的 ID 为 bigint
s,因此创建一个辅助函数用于将 JSON 数组转换为 Postgres bigint
数组:
CREATE OR REPLACE FUNCTION json_array_bigint(_j json)
RETURNS bigint[] AS
$$
SELECT array_agg(elem::text::bigint)
FROM json_array_elements(_j) AS elem
$$
LANGUAGE sql IMMUTABLE;
我们可以在这里轻松地(也许更可重用)返回一个text
数组。我怀疑bigint
上的索引比text
快很多,但我很难在网上找到证据来支持这一点。
建立索引:
CREATE INDEX "myindex" ON "mytable"
USING GIN (json_array_bigint("blob"->'ids'));
对于查询,这可以工作并使用索引:
SELECT * FROM "mytable"
WHERE '185603363289694211' <@ json_array_bigint("blob"->'ids');
这样做也适用于查询,但不使用索引:
SELECT * FROM "mytable"
WHERE 185603363289694211 = ANY(json_array_bigint("blob"->'ids'));
9.4 更新
Postgres 9.4 引入了jsonb
类型。 This is a good SO answer about jsonb
and when you should use it over json
。简而言之,如果您曾经查询过 JSON,您应该使用 jsonb
。
如果您将列构建为jsonb
,则可以使用此查询:
SELECT * FROM "mytable"
WHERE blob @> '"ids": [185603363289694211]';
@>
是 Postgres 的包含运算符 documented for jsonb
here。
感谢Alain's answer 让我注意到这一点。
【讨论】:
+1 干得好。请注意,如果 JSON 数组中的元素不是唯一的,则结果中可能会出现重复的行。是的,处理bigint
通常比处理text
快。也使索引更小,但将elem::text::bigint
简化为elem::bigint
。顺便说一句,Postgres 中完全有效的标识符不需要双引号。
感谢您的帮助!不幸的是,从json
直接转换为bigint
不起作用。我必须先通过text
。
ANY(json_array_bigint("blob"->'ids');
--> 末尾缺少)
【参考方案3】:
我知道已经有一段时间了......
在 postgresql-9.5 中,现在可以轻松查询它。
select '"name": "Wolf",
"ids": [185603363281305602,185603363289694211]'::jsonb
@> '"ids":[185603363281305602]'
我认为您应该改用jsonb
字段,然后您可以对其进行索引。
CREATE INDEX idx_gin_ids ON mytable USING gin ((blob -> 'ids'));
【讨论】:
这似乎也适用于 Postgres 9.4。但需要注意的是@>
仅适用于 jsonb
列,而不适用于 json
列。 (这可能很好,我想不出你会选择json
而不是jsonb
,但这个问题最初是针对还没有jsonb
的Postgres 9.3 提出的)
另外我认为更好的例子是SELECT * from "mytable" WHERE blob @> '"ids": [185603363281305602]'
;以上是关于在 Postgres JSON 数组中查询的主要内容,如果未能解决你的问题,请参考以下文章
Postgres 9.5 - 查询嵌套 JSON 元素的数组长度
如何在 Postgres 中选择一列 json 对象,以便返回的行是 json 数组?
Postgres - 连接三个表并在查询中对数据使用聚合函数