我有一个 Postgres 表
posts
,其中有一列类型为 jsonb
,它基本上是一个平面标签数组。
我需要做的是以某种方式对该
tags
列元素运行 LIKE 查询,以便我可以找到具有以某些部分字符串开头的标签的帖子。
在 Postgres 中这样的事情可能吗?我不断地发现超级复杂的例子,但没有人描述过如此基本和简单的场景。
我当前的代码可以很好地检查是否有具有特定标签的帖子:
select * from posts where tags @> '"TAG"'
我正在寻找一种在
的行中运行某些东西的方法select * from posts where tags @> '"%TAG%"'
SELECT *
FROM posts p
WHERE EXISTS (
SELECT FROM jsonb_array_elements_text(p.tags) tag
WHERE tag LIKE '%TAG%'
);
相关,并附说明:
@?
运算符更简单,因为 Postgres 12 实现了 SQL/JSON:
SELECT *
-- optional to show the matching item:
-- , jsonb_path_query_first(tags, '$[*] ? (@ like_regex "^ tag" flag "i")')
FROM posts
WHERE tags @? '$[*] ? (@ like_regex "TAG")';
运算符
@?
只是函数jsonb_path_exists()
的包装。所以这是等价的:
...
WHERE jsonb_path_exists(tags, '$[*] ? (@ like_regex "TAG")');
都没有指数支持。 (稍后可能会添加
@?
运算符,但尚未在第 13 页中添加)。所以对于大表来说这些查询很慢。标准化设计,就像劳伦兹已经建议的那样会更好 - 带有三元组索引:
对于前缀匹配(
LIKE 'TAG%'
,无前导通配符),您可以使其与全文索引一起使用:
CREATE INDEX posts_tags_fts_gin_idx ON posts USING GIN (to_tsvector('simple', tags));
以及匹配的查询:
SELECT *
FROM posts p
WHERE to_tsvector('simple', tags) @@ 'TAG:*'::tsquery
或者如果您想要自然英语语言的词干,请使用
english
字典而不是 simple
(或任何适合您情况的内容)。
to_tsvector(json(b))
需要 Postgres 10 或更高版本。
相关: