Dismiss
Announcing Stack Overflow Documentation

We started with Q&A. Technical documentation is next, and we need your help.

Whether you're a beginner or an experienced developer, you can contribute.

Sign up and start helping → Learn more about Documentation →

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?

share|improve this question
    
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? – jmelesky Jul 13 at 16:07
    
Thanks for your reply, I updated my question – Jesse Jul 13 at 16:35
up vote 1 down vote accepted

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.

share|improve this answer
    
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? – Jesse Jul 13 at 17:48
    
Edited response to add that. – jmelesky Jul 13 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? – Jesse Jul 13 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). – jmelesky Jul 13 at 18:42
    
Thx :) Have a great day! – Jesse Jul 13 at 19:05

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.