48

I am using PostgreSQL 9.6, and I have a table named "ItemDbModel" with two columns looks like:

No integer,
Content jsonb

Say I put many records like:

 "No": 2, {"obj":"x","Item": {"Name": "BigDog", "Model": "NamedHusky", "Spec":"red dog"}}
 "No": 4, {"obj":"x","Item": {"Name": "MidDog", "Model": "NamedPeppy", "Spec":"no hair"}}
 "No": 5, {"obj":"x","Item": {"Name": "BigCat", "Model": "TomCat", "Spec":"blue color"}}

How can I query the table for:

  1. Records where "Content.Item.Name" contains "Dog" And "Content.Item.Spec" contains "red".
  2. Records where "Content.Item.Name" contains "Dog" OR "Content.Item.Spec" contains "red".
  3. Records where Any json fields in "Content.Item" contains "dog".

And order by "Content.Item.Name.length"?

Thank you!

klin
  • 112,967
  • 15
  • 204
  • 232
ifdog
  • 541
  • 1
  • 4
  • 6

1 Answers1

75

You should become familiar with JSON Functions and Operators.

-- #1
select *
from example
where content->'Item'->>'Name' ilike '%dog%'
and content->'Item'->>'Spec' ilike '%red%'

-- #2
select *
from example
where content->'Item'->>'Name' ilike '%dog%'
or content->'Item'->>'Spec' ilike '%red%'

-- #3
select distinct on(no) t.*
from example t,
lateral jsonb_each_text(content->'Item')
where value ilike '%dog%';

-- and
select *
from example t
order by length(content->'Item'->>'Name');

Postgres 12 introduces new features implementing the SQL/JSON Path Language. Alternative queries using the jsonpath may look like this:

-- #1
select *
from example
where jsonb_path_exists(
    content, 
    '$ ? ($.Item.Name like_regex "dog" flag "i" && $.Item.Spec like_regex "red" flag "i")');

-- #2
select *
from example
where jsonb_path_exists(
    content, 
    '$ ? ($.Item.Name like_regex "dog" flag "i" || $.Item.Spec like_regex "red" flag "i")');

-- #3
select *
from example
where jsonb_path_exists(
    content, 
    '$.Item.* ? (@ like_regex "dog" flag "i")');

The first two queries are basically similar to the previous ones and the -> syntax may seem simpler and more pleasant than jsonpath one. Particular attention should be paid to the third query, which uses a wildcard so it eliminates the need for using the expensive function jsonb_each_text () and should be significantly faster.

Read in the documentation:

klin
  • 112,967
  • 15
  • 204
  • 232
  • Huge amount of Thanks! and yes Im reading given article now! – ifdog Feb 09 '17 at 08:23
  • 4
    May I know how do we choose between '->' and '->>' ? – Cheok Yan Cheng Mar 14 '18 at 14:51
  • 39
    The `->` operator gives a json object, while `->>` yields text. – klin Mar 14 '18 at 14:58
  • 7
    Disappointed in Postgres to be honest. These are unnecessarily confusing operators. – Damien Roche Feb 05 '19 at 16:32
  • I know this is an old post. but what is the flag "i" supposed to mean – jstuartmilne May 16 '20 at 16:50
  • 2
    @jstuartmilne - `i` stands for case-insensitive match, see [9.15.2.2. Regular Expressions](https://www.postgresql.org/docs/current/functions-json.html#JSONPATH-REGULAR-EXPRESSIONS) for details. – klin May 16 '20 at 16:59
  • flawless answer @klin – Gaurav Mar 30 '22 at 18:11
  • https://stackoverflow.com/users/1995738/klin, I have data in josnb column like this in multiple rows: {"data": [{"pval": 0.7354, "Protein": "A0A0B4J2D5", "FoldChange": -0.1676, "MinusLog10p": 0.1334, "Significance": "Non-significant"}, {"pval": 0.0689, "Protein": "A0FGR8", "FoldChange": 2.5448, "MinusLog10p": 1.1615, "Significance": "Significant"}]}. I need to query across the column containing multiple records where a certain protein has a pval or FoldChange in a certain range. Can you kindly help? – RanonKahn Dec 25 '22 at 07:43
  • @RanonKahn - there is no space in the comments to answer non-trivial questions. Please ask a new question. – klin Dec 25 '22 at 11:16
  • @klin, I have asked the question in the forum. Eagerly looking forward to your response. – RanonKahn Dec 25 '22 at 22:11