2016-12-15 46 views
2

我有一个9.4 Postgres数据库,其中有一个包含jsonb字段的表。使用Postgres在json数组内部索引对象元素

的JSON的结构类似于这样:

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

下面的查询返回上述JSON就好:

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

的问题是性能受到了不少在更大的数据库。

我可以使用什么索引来加速此查询?


我已经试过远:

  • GIN指数(包括jsonb_opsjsonb_path_ops)。但他们似乎只在@>这样的存在运营商上工作。

  • CREATE INDEX ON persons(((jsonb_array_elements(jdoc->'products')->>'price')::numeric))。哪给我给我数据类型不匹配错误。

    • 请注意,CREATE INDEX ON persons(((jdoc->'age')::numeric))的作品,并允许我快速查询(jdoc->>'age')::numeric < 30
+0

使用关系数据模型而不是JSON。 – Abelisto

回答

2

您可以创建一个功能和使用:

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)); 

那么这个指数可以用这样的查询使用:

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) 

这不是你的查询,但它应该是等同的。

+0

但是这意味着如果我想查询一个小于一个值的价格,我还必须创建第二个使用'min(p.price)'的函数/索引。我对吗? – Kabbalah

+0

是的。这只针对你问题中的特例。答案应该让你知道如何解决像你这样的问题。 –

+0

谢谢。在接受之前,我会等待一两天,看看是否有其他选择。 – Kabbalah