2

Having a table with a column containing jsonb data, I want to query those data with multiple fields. The jsonb structure looks like this:

{
  "https://example.tld/": 
  [
    {
      "content-length": 312,
      "path": "page.log",
      "redirect": null,
      "status": 200
    },
    {
      "content-length": 312,
      "path": "pages/",
      "redirect": null,
      "status": 200
    },
    ...
  ]
}

I want to query all log files with status 200, so I did

SELECT json_data -> 'path' AS path
FROM table
WHERE json_data->'status' = 200 AND json_data->'path' ILIKE '%.log%'

However, it does not work. The closest variant I get to work is if I cast the json content into text and search for log, but it returns the whole content.

SELECT *
FROM table
WHERE json_data::text ILIKE '%.log%'

Thanks for your help!

2
  • Which Postgres version are you using? Commented Aug 14, 2020 at 11:43
  • (PostgreSQL) 10.12 Commented Aug 14, 2020 at 11:48

2 Answers 2

3

If I understand correctly, you want the paths from the underlying array in JSON. If so:

select rs.path
from t cross join
     jsonb_to_recordset(t.json -> 'https://example.tld/') rs(status int, path text)
 where rs.path ilike '%.log%' and rs.status = 200

Here is a db<>fiddle.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks a lot! How can I make the query independent from a specific domain? Is there something like t.json->[0] possible? I mean to query the first array entry without specifying domain name like example.tld.
@a_horse_with_no_name i get "ERROR: cannot call jsonb_to_recordset on a non-array"
@nullpointr . . . (1) I would suggest that you ask a new question. (2) The simplest method is to include the domain in the array, rather than having it be a key.
2

Assuming the top-level key name (https://example.tld/ in this case) changes from row to row in your table, you will not be able to use a fixed path and will have to expand the object with jsonb_each() before expanding the array elements with jsonb_array_elements().

with indat as ( select 1 as id, '{
  "https://example.tld/":
  [
    {
      "content-length": 312,
      "path": "page.log",
      "redirect": null,
      "status": 200
    },
    {
      "content-length": 312,
      "path": "pages/",
      "redirect": null,
      "status": 200
    }
  ]
}'::jsonb as json_data
)
select i.id, j.key, e.element
  from indat i
 cross join lateral jsonb_each(json_data) as j(key, value)
 cross join lateral jsonb_array_elements(j.value) as e (element)
 where e->>'path' ilike '%.log%'
   and e->>'status' = '200';

 id |         key          |                                   element
----+----------------------+------------------------------------------------------------------------------
  1 | https://example.tld/ | {"path": "page.log", "status": 200, "redirect": null, "content-length": 312}
(1 row)


Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.