Search code examples
postgresqlindexingpattern-matchingjsonb

indexing pattern on jsonb column


I want to improve performance of my queries using GIN index on jsonb column for pattern matching

For example, I have a table defined as:

CREATE TABLE my_table (
  uuid text,
  doc jsonb
);

In every row of table doc has path {A,B}. B is an object, possibly empty. There is a one more optional element in path: it can be absent, can be C, D etc.

Can I create a single index for pattern matching that will be used in all cases listed below?

SELECT doc#>>'{A,B}'
FROM my_table
WHERE doc#>>'{A,B}' ILIKE '%example%';
SELECT doc#>>'{A,B,C}'
FROM my_table
WHERE doc#>>'{A,B,C}' ILIKE '%example%';
SELECT doc#>>'{A,B,D}'
FROM my_table
WHERE doc#>>'{A,B,D}' ILIKE '%example%';

I've tried to create index as:

CREATE INDEX my_index
ON my_table
USING GIN ((doc#>>'{A,B}') gin_trgm_ops)

Unfortunately, it works only for queries with path {A,B}, but not {A,B,C}


Solution

  • One index might work for all the cases, but you do have to write the query in an unnatural way for ones that don't exactly match the index expression:

    SELECT doc#>>'{A,B,C}'
    FROM my_table
    WHERE doc#>>'{A,B}' ILIKE '%example%' AND doc#>>'{A,B,C}' ILIKE '%example%';
    

    doc#>>'{A,B,C}' ILIKE '%example%' usually implies that doc#>>'{A,B}' ILIKE '%example%', but that is not something PostgreSQL will reason out for you. It also isn't perfectly true in all cases, characters that require escaping in JSON will be escaped in doc#>>'{A,B}' but not in doc#>>'{A,B,C}' if the end result of one is stringified JSON object while the other is just a plain string.

    select '{"0":{"a":"b\"c"}}'::jsonb #>> '{0}';
    ---------------
     {"a": "b\"c"}
    
    select '{"0":{"a":"b\"c"}}'::jsonb #>> '{0,a}';
    ----------
     b"c