lumera
lumera

Reputation: 3

How can I find a outer (wildcard) key that contains certain parameters within a JSONB field in PostgreSQL?

I have a PostgreSQL (V14) database containing info in JSONB format. The info of one cell could be something like this:

{
  "Car23": {
    "color": "blue",
    "year": 1982,
    "engine": [
      12,
      23.3
    ],
    "broke": [
      2,
      8.5
    ]
  },
  "Banana": {
    "color": "yellow",
    "year": 2022,
    "taste": "ok"
  },
  "asdf": {
    "taste": "bad",
    "year": [
      1945,
      6
    ],
    "engine": [
      24,
      53.534
    ]
  },
  "Unique": {
    "broke": [
      342,
      2.5
    ]
  }
}

The outer key, i.o "Car23" or "Banana" has a random name created by an outside program. I want to do queries that allow me to get where the outer key contains a certain key:value.

For instance:

In Sql this seems pretty standard stuff, however I don't know how to do this within JSONB when the outer keys have random names...

I red that outer wildcard keys aren't possible, so I'm hoping there's another way of doing this within Postgresql.

Upvotes: 0

Views: 55

Answers (1)

user330315
user330315

Reputation:

You will need to unnest the JSON elements and then pick the ones you want. The fact that some values are sometimes stored in an array, and sometimes as a plain value makes things even more complicated.

I assume that "things that broke" just means those, that have a key broke:

select j.key
from the_table t
 cross join lateral (
    select *
    from jsonb_each(t.the_column) as j(key, item)
    where j.item ? 'broke'
 ) j;

To find those with a year > 1988 is tricky because of the two different ways of storing the year:

select j.key
from the_table t
 cross join lateral (
    select *
    from jsonb_each(t.the_column) as j(key, item)
    where case 
            when jsonb_typeof(j.item -> 'year') = 'array' then (j.item -> 'year' -> 0)::int
             else (j.item ->> 'year')::int
          end > 1988
 ) j;

When checking for the "engine" array item, you probably should also check if it's really an array:

select j.key
from the_table t
 cross join lateral (
    select *
    from jsonb_each(t.the_column) as j(key, item)
    where jsonb_typeof(j.item -> 'engine') = 'array'
      and (j.item -> 'engine' ->> 1)::numeric > 50
 ) j;

Upvotes: 1

Related Questions