0

I am trying to find rows in my Postgresql Database where a json column contains a given text.

row schema:

id  |  name         |  subitems
-----------------------------------------------------------------
1   |  "item 1"     |  [{name: 'Subitem A'}, {name: 'Subitem B'}]
2   |  "item 2"     |  [{name: 'Subitem C'}, {name: 'Subitem D'}]

My wanted result for query 'Subitem B'

id  |  name         |  subitems
-----------------------------------------------------------------
1   |  "item 1"     |  [{name: 'Subitem A'}, {name: 'Subitem B'}]

I can search for the first subitem like this:

WHERE lower(subitems->0->>\'name\') LIKE '%subitem a%'

But obviously I can't find any other subitem but the first one this way.

I can get all the names of my subitems:

SELECT lower(json_array_elements(subitems)->>'name') FROM ...

But it gives me 2 rows containing the names:

lower
----------------------------------------------------------------
"subitem a"
"subitem b"

What I actually need is 1 row containing the item.

Can anyone tell me how to do that?

2
  • What you're looking for is a little unclear -- can you give us some more example data and also an example of the output you want? Commented Jul 13, 2016 at 16:07
  • Thanks for your reply, I updated my question Commented Jul 13, 2016 at 16:35

1 Answer 1

3

You're almost there. Your query:

SELECT lower(json_array_elements(subitems)->>'name') FROM foo;

That gets you what you want to filter against. If you plop that into a subquery, you get the results you're looking for:

# SELECT *
    FROM foo f1
    WHERE 'subitem a' IN
      (SELECT lower(json_array_elements(subitems)->>'name')
        FROM foo f2 WHERE f1.id = f2.id
      );
 id |  name  |                    subitems                    
----+--------+------------------------------------------------
  1 | item 1 | [{"name": "Subitem A"}, {"name": "Subitem B"}]
(1 row)

Edited to add

Okay, to support LIKE-style matching, you'll have to go a bit deeper, putting a subquery into your subquery. Since that's a bit hard to read, I'm switching to using common table expressions.

WITH all_subitems AS (
  SELECT id, json_array_elements(subitems)->>'name' AS subitem
    FROM foo),
matching_items AS (
  SELECT id
    FROM all_subitems
    WHERE
      lower(subitem) LIKE '%subitem a%')
SELECT *
  FROM foo
  WHERE
    id IN (SELECT id from matching_items);

That should get you what you need. Note that I moved the call to lower up a level, so it's alongside the LIKE. That means the filtering condition is in one spot, so you can switch to a regular expression match, or whatever, more easily.

5
  • Thank you so much, works like a charm as long as the strings match exactly! Do you also know how to get the LIKE '%query%' syntax working with this? Commented Jul 13, 2016 at 17:48
  • Edited response to add that. Commented Jul 13, 2016 at 18:05
  • Perfect, that works. Again, thank you for taking the time and helping me out! I'm a little afraid about performance as the database I'm using this for is about to get quite large. Do you know anything about that? Commented Jul 13, 2016 at 18:33
  • Well, you're essentially doing a self-join, so not much to do there. If you have problems, try making the intermediate query results into temp tables instead of CTEs (and remember to analyze and index them if they, too, are large). Commented Jul 13, 2016 at 18:42
  • Thx :) Have a great day! Commented Jul 13, 2016 at 19:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.