Reputation: 20043
Let's say we have a table items
which has columns name
and attributes
:
CREATE TABLE students (
name VARCHAR(100),
attributes JSON
)
where attributes is an array of (always equally-structured) JSON documents such as
[{"name":"Attribute 1","value":"Value 1"},{"name":"Attribute 2","value":"Value 2"}]
I now want to find all students where any attribute value matches something (such as Foo%
). Here's a playground example.
I realize that this isn't exactly the most straight-forward design, but for now it's what I have to work with, though performance of such a search being categorically terribly inefficient would of course be a valid concern.
Upvotes: 18
Views: 52533
Reputation: 19
I searched a lot for an easier solution for a clean where clauses in Laravel and and couldn't find it, so I came up with bellow idea.
SELECT * FROM students where lower(attributes::text) ilike '%"<key>"%"<value>"%'
If your parse json properly key and values are all stored in (") double quotes.
Notice I added (") double quotes to kay and value so it matches the exact key and value.
Upvotes: 1
Reputation: 170
You can use EXISTS
function to filter sub json data
SELECT s.* FROM
students s
WHERE EXISTS(
SELECT 1 FROM json_array_elements ( attributes ) as att WHERE att ->> 'value' ILIKE 'Foo%')
Upvotes: 1
Reputation: 31656
You may use json_array_elements
to access the elements and then use ->> json operator to search using some value.
select s.*,j from
students s
cross join lateral json_array_elements ( attributes ) as j
WHERE j->>'value' like 'Foo%'
Edit
The problem here now is that the cross join will "duplicate" rows. Is there a better way to avoid this
use WITH ORDINALITY
to generated id per element and then use DISTINCT ON
to get the first / last match per student.
select DISTINCT ON (name) s.*,j.attr from
students s
cross join lateral json_array_elements ( attributes ) WITH ORDINALITY as j(attr,id)
WHERE j.attr->>'value' like 'Value%'
ORDER BY name,j.id
Upvotes: 30