Reputation: 18379
Let's say I have a jsonb column called meta
with this value:
{
"foo": {
"bar": "baz"
}
}
When I want to filter by the meta.foo.bar
value, I can do this:
select * from tbl where meta->foo->>bar = 'baz';
Now let's say I want to make the foo
an array of objects instead:
{
"foo": [
{ "bar": "baz 1" },
{ "bar": "baz 2" }
]
}
How can I filter by the meta.foo.*.bar
values? Tried various combinations of this ->
syntax without any luck. Only thing that worked was searching by a specific index in the array, but I want to check all the items, not just one.
FWIW I'd also like to support multiple nested arrays. The use case is filtering by JSON properties in an ORM (http://mikro-orm.io/), so it needs to be dynamic.
Upvotes: 3
Views: 8139
Reputation: 2669
UPDATE 2: Based on additional information in the comments, if you want to use other operators (e.g. LIKE
or >=
), then you could do something around the following lines:
SELECT * FROM tbl
WHERE EXISTS (
SELECT 1
FROM jsonb_path_query(meta, '$.foo[*].bar') extracted
WHERE extracted = '"baz 1"'
);
Please note that the filter value must be JSON encoded (therefore the "
quotes around the string value).
Similar results can be achieved using JOIN
s or WITH
CTE's but one must be careful about JOIN-style row duplication.
UPDATE 1: Thinking about it, you may want something like this:
SELECT * FROM tbl
WHERE jsonb_path_query_array(meta, '$.foo[*].bar') ? 'baz 1';
My original answer:
Since Postgres 12 you should be able to use the @@
jsonpath operator:
SELECT * FROM tbl WHERE meta @@ '$.foo[*].bar == "baz 1"';
Upvotes: 0
Reputation: 246308
You can do that with the jsonb
containment operator @>
:
SELECT '{"foo": [{"bar": "baz 1"}, {"bar": "baz 2"}]}'::jsonb @>
'{"foo": [{"bar": "baz 1"}]}'::jsonb;
Alternatively, you could use a JSONPATH query:
SELECT jsonb_path_exists(
'{"foo": [{"bar": "baz 1"}, {"bar": "baz 2"}]}',
'$.foo[*] ? (@.bar == "baz 2")'
);
To search for bar
no matter how deep it is nested, you can
SELECT jsonb_path_exists(
'{"foo": [{"bar": "baz 1"}, {"bar": "baz 2"}]}',
'$.**.bar ? (@ == "baz 2")'
);
If you want to search with = 'baz 2'
, you will habe to unnest the array:
SELECT *
FROM jsonb_array_elements(
'{"foo": [{"bar": "baz 1"}, {"bar": "baz 2"}]}'::jsonb -> 'foo'
) AS elem(j)
WHERE j->>'bar' = 'baz 2';
But then you have to know exactly where in the JSON you want to search.
Upvotes: 4