PostgreSQL Index on JSON

Your other two indexes won’t work simply because the ->> operator returns text, while you obviously have the jsonb gin operator classes in mind. Note that you only mention json, but you actually need jsonb for advanced indexing capabilities.

To work out the best indexing strategy, you’d have to define more closely which queries to cover. Are you only interested in cows? Or all animals / all tags? Which operators are possible? Does your JSON document also include non-animal keys? What to do with those? Do you want to include rows in the index where cows (or whatever) don’t show up in the JSON document at all?

Assuming:

  • We are only interested in cows at the first level of nesting.
  • The value is always a valid integer.
  • We are not interested in rows without cows.

I suggest a functional btree index, much like you already have, but cast the value to integer. I don’t suppose you’d want the comparison evaluated as text (where ‘2’ is greater than ‘1111’).

CREATE INDEX animal_index ON farm (((animal ->> 'cow')::int));  -- !

The extra set of parentheses is required for the cast shorthand to make the syntax for the index expression unambiguous.

Use the same expression in your queries to make Postgres realize the index is applicable:

SELECT * FROM farm WHERE (animal ->> 'cow')::int > 3;

If you need a more generic jsonb index, consider:

For a known, static, trivial number of animals (like you commented), I suggest partial indexes like:

CREATE INDEX animal_index ON farm (((animal ->> 'cow')::int))
WHERE (animal ->> 'cow') IS NOT NULL;

CREATE INDEX animal_index ON farm (((animal ->> 'chicken')::int))
WHERE (animal ->> 'chicken') IS NOT NULL;

Etc.

You may have to add the index condition to the query:

SELECT * FROM farm
WHERE (animal ->> 'cow')::int > 3
AND   (animal ->> 'cow') IS NOT NULL; 

May seem redundant, but may be necessary. Test with ANALYZE!

Leave a Comment