Let's add a JSONB field to our recipes. We'll call it meta as in metadata but we could call it anything. And keep in mind this is just a normal JSON object. You can have arrays, nested objects, whatever.

ALTER TABLE recipes
ADD COLUMN meta JSONB;

That's easy enough. Okay, let's now add a few rows to it. Keep in mind that it does have to be valid JSON or Postgres won't let you add it.

UPDATE
  recipes
SET
  meta='{ "tags": ["chocolate", "dessert", "cake"] }'
WHERE
  recipe_id=16;

UPDATE
  recipes
SET
  meta='{ "tags": ["dessert", "cake"] }'
WHERE
  recipe_id=20;

UPDATE
  recipes
SET
  meta='{ "tags": ["dessert", "fruit"] }'
WHERE
  recipe_id=45;

UPDATE
  recipes
SET
  meta='{ "tags": ["dessert", "fruit"] }'
WHERE
  recipe_id=47;

Okay, let's select all tags from any recipe that has meta data.

SELECT meta -> 'tags' FROM recipes WHERE meta IS NOT NULL;

The -> is an accessor. In JS terms, this is like saying meta.tags. If we were accessing another layer of nesting, you just use more ->. Ex. SELECT meta -> 'name' -> 'first'.

Let's try selecting just the first element of each one using that.

SELECT meta -> 'tags' -> 0 FROM recipes WHERE meta IS NOT NULL;

This should give you back only the first item of each. Notice they're still weirdly in "". This is because the -> selector gives you back a JSON object so you can keep accessing it. If you want it back as just text, you use ->>.

SELECT meta -> 'tags' ->> 0 FROM recipes WHERE meta IS NOT NULL;

Notice the first one -> because we need tags back as a JSON object (because accessors don't work on text) but we use ->> on the second one so we can get the text back. Not always important but it bit me a few times and I wanted to help you.

Alright, let's select all the cakes.

SELECT recipe_id, title, meta -> 'tags' FROM recipes WHERE meta -> 'tags' ? 'cake';
SELECT recipe_id, title, meta -> 'tags' FROM recipes WHERE meta -> 'tags' @> '"cake"';

Both of these work. The first one with the ? checks to see if 'cake' is a top level key available in which case it is.

The second with the @> is doing a "does this array contains this value. That's why we do need the extra double quotes, since it is a string in a JSON value.