问题
I want to get all records where any value in jsonb field contains a text regardless of key.
For example: field contains json {k1: 'hello', k2: 'world'}
. I want to get this record by the text 'hello'. I don't care about keys or any json structure, only values.
One dirty hack is to cast the field to varchar where jsonb_field::varchar like ...
, but this is ugly and it will match keys and {} as well.
Another dirty hack looks like this:
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%';
but it's ugly too.
How can I do this?
回答1:
For simple JSONs you can use more appropriate query like
select *
from mytable t
where exists (
select 1
from jsonb_each_text(t.jsonbfield) j
where j.value = 'hello');
It works fine for JSONs like in your example but not helps for more complex JSONs like {"a":"hello","b":1,"c":{"c":"world"}}
I can propose to create the stored function like
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;
to list all values including recursive objects (It is up to you what to do with arrays).
Here is usage example:
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"');
Note that doublequotes around the string value.
来源:https://stackoverflow.com/questions/36250331/query-postgres-jsonb-by-value-regardless-of-keys