无论密钥如何,都可以按值查询postgres jsonb

时间:2016-03-27 17:24:38

标签: postgresql postgresql-9.4 jsonb

我想获取所有记录,其中jsonb字段中的任何包含文本而不管密钥。

例如:字段包含json {k1: 'hello', k2: 'world'}。我希望通过文字“你好”得到这个记录。我不关心键或任何json结构,只关心值。

一个肮脏的黑客是将字段强制转换为varchar where jsonb_field::varchar like ...,但这很难看,它也会匹配键和{}。

另一个肮脏的黑客看起来像这样:

SELECT * FROM mytable AS m1 WHERE (
  SELECT string_agg(value, '') FROM jsonb_each_text( (SELECT name FROM mytable AS m2 WHERE m1.id= m2.id)::jsonb ) 
) LIKE '%whatever%';

但它也很难看。

我该怎么做?

1 个答案:

答案 0 :(得分:2)

对于简单的JSON,您可以使用更合适的查询,例如

select * 
from mytable t 
where exists (
  select 1 
  from jsonb_each_text(t.jsonbfield) j 
  where j.value = 'hello');

它适用于您的示例中的JSON,但对{"a":"hello","b":1,"c":{"c":"world"}}

等更复杂的JSON没有帮助

我可以建议创建像

这样的存储函数
create or replace function jsonb_enum_values(in jsonb) returns setof varchar as $$
begin
  case jsonb_typeof($1)
    when 'object' then
      return query select jsonb_enum_values(j.value) from jsonb_each($1) j;
    when 'array' then
      return query select jsonb_enum_values(a) from jsonb_array_elements($1) as a;
    else
      return next $1::varchar;
  end case;
end
$$ language plpgsql immutable;

列出包括递归对象在内的所有值(由您决定如何处理数组)。

以下是用法示例:

with t(x) as (
  values
    ('{"a":"hello","b":"world","c":1,"d":{"e":"win","f":"amp"}}'::jsonb),
    ('{"a":"foo","b":"world","c":2}'),
    ('{"a":[{"b":"win"},{"c":"amp"},"hello"]}'),
    ('[{"a":"win"}]'),
    ('["win","amp"]'))
select * 
from t 
where exists (
  select *
  from jsonb_enum_values(t.x) j(x) 
  where j.x = '"win"');

请注意字符串值周围的双引号。