3

I think I am trying something simple, but after hours I just can't get it to work. I have a table which contains a tags column, which is a jsonb array and looks like this:

{"{\"name\": \"e-commerce\", \"weight\": 400}","{\"name\": \"management\", \"weight\": 200}","{\"name\": \"content management\", \"weight\": 200}"}

I now want to write a query which returns the full object to me, when the name matches the search string. So far I came up with this:

SELECT * FROM data
WHERE tags is not null
  AND EXISTS(
        SELECT FROM data ad WHERE (ad.tags -> 'name' ->> 'education')::text
    );

But I get this error:

[42883] ERROR: operator does not exist: jsonb[] -> unknown Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts.

I am not sure where I should do a typecast and whether that is really the problem.

I already tried solutions from these threads, but to no avail :-(

GMB
  • 216,147
  • 25
  • 84
  • 135
Gh05d
  • 7,923
  • 7
  • 33
  • 64
  • 1
    Is that really the content of your JSON column? Those escaped double quotes look wrong, it seems the value has been stored as a single scalar values, rather than a proper JSON object. –  Dec 20 '20 at 16:06
  • I am not the most familiar with arrays and objects in postgres. My colleague created the table, but when I query it, I get back an array of objects. – Gh05d Dec 20 '20 at 16:16
  • I just checked the database definition and it is indeed a jsonb: `tags jsonb[] default'{}'::jsonb[]` – Gh05d Dec 20 '20 at 16:20
  • 2
    `jsonb[]` almost never makes sense. A "plain" `jsonb` that includes a real JSON array is a much better choice –  Dec 20 '20 at 16:34

2 Answers2

7

If you want each matching object on a separate row, you can use jsonb_array_elements() to unnest the array of objects, then filter:

select o.obj
from data d
cross join lateral jsonb_array_elements(d.tags) o(obj)
where o.obj ->> 'name' = 'education'

That works in you have JSONB array (so the datatype of data is jsonb).

If, on the other hand, you have an array of json objects (so: jsonb[]), you can unnest instead:

select o.obj
from data d
cross join lateral unnest(d.tags) o(obj)
where o.obj ->> 'name' = 'education'

Note that this generates two rows when two objects match in the same array. If you want just one row, you can use exists instead:

select o.obj
from data d
where exists (
    select 1 from unnest(d.tags) o(obj) where o.obj ->> 'name' = 'education'
)
GMB
  • 216,147
  • 25
  • 84
  • 135
2

You need to query the objects within the json array. Create those objects using jsonb_array_elements and then query the json like -

SELECT d.* FROM data d, jsonb_array_elements(d.tags) tag
WHERE tag is not null and  WHERE (tag -> 'name') ? 'education'

Also, a note, in your original query

This -

WHERE (ad.tags -> 'name' ->> 'education')::text

Should be -

WHERE (ad.tags -> 'name') ? 'education'

Or

WHERE (ad.tags ->> 'name')::text = 'education'

EDIT 1:

Since your data type is not jsonb but jsonb[], you need to unnest it to jsonb using unnest -

SELECT * FROM data d, jsonb_array_elements(unnest(d.tags)) tagObj
WHERE tagObj is not null and  WHERE (tag -> 'name') ? 'education'

Or

select * from (
SELECT * FROM data d, jsonb_array_elements(unnest(d.tags)) tagobj
) a WHERE tag is not null and  WHERE (tag -> 'name') ? 'education'

First one may give error, due to tagObj not being available at that context


Nikhil Patil
  • 2,480
  • 1
  • 7
  • 20
  • The second WHERE in your answer should be removed. Unfortunately, I get this error: [42883] ERROR: function jsonb_array_elements(jsonb[]) does not exist Hint: No function matches the given name and argument types. You might need to add explicit type casts. Position: 32 – Gh05d Dec 20 '20 at 16:18
  • @Gh05d, Yes, its because your data type is `jsonb[]` instead of `jsonb`, you need to unnest it into `jsonb`. I have updated the answer. – Nikhil Patil Dec 20 '20 at 16:45