2

I have a 9.4 Postgres database which has a table containing a jsonb field.

The structure of the json looks similar to this:

{
  name: 'customer1',
  age: 28,
  products: [{
    name: 'product1',
    price: 100
  }, {
    name: 'product2',
    price: 200
  }]
}

The following query returns the aforementioned json just fine:

SELECT jdoc
FROM customers, jsonb_array_elements(jdoc->'products') as products
WHERE (products->>'price')::numeric > 150

The problem is that the performance suffers quite a bit on bigger databases.

What index can I use to speed up this query?


What I've tried to far:

  • GIN indices (both jsonb_ops and jsonb_path_ops). They only seem to work on existence operators like @> though.

  • CREATE INDEX ON persons(((jsonb_array_elements(jdoc->'products')->>'price')::numeric)). Which give me give me datatype mismatch error.

    • Note that CREATE INDEX ON persons(((jdoc->'age')::numeric)) works and allows me to query (jdoc->>'age')::numeric < 30 fast.
1
  • Use relational data model instead of JSON. Commented Dec 15, 2016 at 13:37

1 Answer 1

4

You could create a function and use that:

CREATE FUNCTION max_price(jsonb) RETURNS double precision
   LANGUAGE sql IMMUTABLE AS
$$SELECT max(p.price)
FROM jsonb_to_recordset($1->'products')
        AS p(name text, price double precision)$$;

CREATE INDEX customers_ind ON customers(max_price(jdoc));

Then this index can be used with a query like this:

EXPLAIN SELECT jdoc FROM customers WHERE max_price(jdoc) > 150;

                                   QUERY PLAN
--------------------------------------------------------------------------------
 Index Scan using customers_ind on customers  (cost=0.12..8.14 rows=1 width=36)
   Index Cond: (max_price(jdoc) > '150'::double precision)
(2 rows)

This is not your query, but it should be equivalent.

Sign up to request clarification or add additional context in comments.

3 Comments

But this means I also have to create a second function/index that uses min(p.price) if I want to query for a price less than a value. Am I right?
Yes. This targets just the special case in your question. The answer should give you the idea how a problem like yours might be tackled.
Thank you. I'll wait a day or two to see if there are other options before accepting.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.