Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to filter rows on nested values in a json column?

Here is my table (simplified, only significant columns):

CREATE TABLE things (
  id serial primary key
, name varchar
, blueprint json default '{}'
);

And some sample data:

# select * from things;

 id |  name   |                                  blueprint
----+---------+-----------------------------------------------------------------------------
  1 | Thing 1 | {}
  2 | Thing 2 | {"1":{"name":"Iskapola","wight":"2"}}
  3 | Thing 3 | {"1":{"name":"Azamund","weight":"3"}, "2":{"name":"Iskapola","weight":"1"}}
  4 | Thing 4 | {"1":{"name":"Ulamir","weight":"1"}, "2":{"name":"Azamund","weight":"1"}}

I'd like to select rows that have 'Azamund' anywhere under the name key. Something like this:

# select * from things where * ->> 'name' = 'Azamund';

 id |      blueprint
----+----------------------------------------------------------------------------
  7 | {"1":{"name":"Azamund","weight":"3"}, "2":{"name":"Iskapola","weight":"1"}}
  8 | {"1":{"name":"Ulamir","weight":"1"}, "2":{"name":"Azamund","weight":"1"}}

Data is nested exactly like in the sample (only one level).
Currently we are using PostgreSQL 9.3.5.

Is it possible in PostgreSQL 9.3? Maybe 9.4?

like image 203
NilColor Avatar asked Sep 28 '22 18:09

NilColor


1 Answers

Your query is close. json_each() is the key function. Or jsonb_each() for jsonb. A couple of improvements:

SELECT *
FROM   things t
WHERE  EXISTS (
   SELECT FROM json_each(t.blueprint) b
   WHERE  b.value->>'name' ILIKE 'azamund'
   );

Old sqlfiddle
db<>fiddle here

  • json_each() already returns the value as json data type. No need for an additional cast.

  • Better, yet, use a LATERAL reference in EXISTS. This is much cleaner than unnesting with a set-returning function in the SELECT list. Related:

    • Call a set-returning function with an array argument multiple times
  • Use ILIKE (~~*) for the pattern match. Regular expression matches (~, ~*) are more powerful, but also more expensive. So use the basic LIKE / ILKE where you can. Details:

    • Pattern matching with LIKE, SIMILAR TO or regular expressions in PostgreSQL

Alternative with JSON array

You have already seen my related answer for JSON arrays:

  • How do I query using fields inside the new PostgreSQL JSON datatype?

While the query for nested JSON objects seems just as simple, there is superior index support for the array:

  • Index for finding an element in a JSON array

May get simpler / more efficient with SQL/JSON in Postgres 12 ...

like image 164
Erwin Brandstetter Avatar answered Oct 06 '22 01:10

Erwin Brandstetter