您的数组仅包含原始值,嵌套文档会更复杂。
询问
jsonb_array_elements_text()
在LATERAL
连接和计数匹配中取消嵌套找到的行的 JSON 数组:
SELECT *
FROM (
SELECT *
FROM tbl
WHERE data->'tags' ?| ARRAY['foo', 'bar']
) t
, LATERAL (
SELECT count(*) AS ct
FROM jsonb_array_elements_text(t.data->'tags') a(elem)
WHERE elem = ANY (ARRAY['foo', 'bar']) -- same array parameter
) ct
ORDER BY ct.ct DESC; -- more expressions to break ties?
与INSTERSECT
. 这是我们可以使用此基本 SQL 功能的少数情况之一:
SELECT *
FROM (
SELECT *
FROM tbl
WHERE data->'tags' ?| '{foo, bar}'::text[] -- alt. syntax w. array
) t
, LATERAL (
SELECT count(*) AS ct
FROM (
SELECT * FROM jsonb_array_elements_text(t.data->'tags')
INTERSECT ALL
SELECT * FROM unnest('{foo, bar}'::text[]) -- same array literal
) i
) ct
ORDER BY ct.ct DESC;
请注意一个细微的区别:这会在匹配时消耗每个元素,因此它不会data->'tags'
像第一个变体那样计算不匹配的重复项。有关详细信息,请参阅下面的演示。
还演示了传递数组参数的另一种方法:作为数组文字:'{foo, bar}'
。对于某些客户来说,这可能更容易处理:
或者您可以创建一个带有VARIADIC
参数的服务器端搜索函数并传递可变数量的普通text
值:
有关的:
指数
确保有一个功能性 GIN 索引来支持jsonb
存在运算符?|
:
CREATE INDEX tbl_dat_gin ON tbl USING gin (data->'tags');
重复的细微差别
根据评论中的要求进行澄清。比如说,我们有一个带有两个重复标签(总共 4 个)的 JSON 数组:
jsonb '{"tags": ["foo", "bar", "foo", "bar"]}'
并使用包含两个标签的 SQL 数组参数进行搜索,其中一个重复(共 3 个):
'{foo, bar, foo}'::text[]
考虑这个演示的结果:
SELECT *
FROM (SELECT jsonb '{"tags":["foo", "bar", "foo", "bar"]}') t(data)
, LATERAL (
SELECT count(*) AS ct
FROM jsonb_array_elements_text(t.data->'tags') e
WHERE e = ANY ('{foo, bar, foo}'::text[])
) ct
, LATERAL (
SELECT count(*) AS ct_intsct_all
FROM (
SELECT * FROM jsonb_array_elements_text(t.data->'tags')
INTERSECT ALL
SELECT * FROM unnest('{foo, bar, foo}'::text[])
) i
) ct_intsct_all
, LATERAL (
SELECT count(DISTINCT e) AS ct_dist
FROM jsonb_array_elements_text(t.data->'tags') e
WHERE e = ANY ('{foo, bar, foo}'::text[])
) ct_dist
, LATERAL (
SELECT count(*) AS ct_intsct
FROM (
SELECT * FROM jsonb_array_elements_text(t.data->'tags')
INTERSECT
SELECT * FROM unnest('{foo, bar, foo}'::text[])
) i
) ct_intsct;
结果:
data | ct | ct_intsct_all | ct_dist | ct_intsct
-----------------------------------------+----+---------------+---------+----------
'{"tags": ["foo", "bar", "foo", "bar"]}' | 4 | 3 | 2 | 2
将 JSON 数组中的元素与数组参数中的元素进行比较:
- 4 个标签匹配任何搜索元素:
ct
.
- 集合中的3 个标签相交(可以逐个匹配)
ct_intsct_all
:.
- 可以识别2 个不同的
ct_dist
匹配标签:或ct_intsct
.
如果您没有受骗者或不想排除他们,请使用前两种技术之一。另外两个要慢一些(除了不同的结果),因为他们必须检查是否有欺骗。