在Postgres中对一个jsonb数组进行索引

5

我尝试设置一个GIN索引,但我认为当我运行请求时我的索引没有被使用,无论我是使用操作符还是函数。

环境

在我们的表中,我们有一个包含类似于以下Json的JSONB字段(json_aip):

{
    "properties": {
        "pdi": {
            "contextInformation": {
                "tags": ["SOME_TAG"]
            },
    },
}

创建表:

create table t_aip (
    json_aip jsonb,
    [...]
);

CREATE INDEX idx_aip_tags 
ON t_aip 
USING gin ((json_aip -> 'properties' -> 'pdi' -> 'contextInformation' -> 'tags'));

操作员查询

我们不能像使用JDBC一样使用运算符?|。 但是有传言表明,当我运行这种类型的查询时,应该看到我的索引。

EXPLAIN ANALYZE SELECT count(*)  
FROM storage.t_aip 
WHERE json_aip#>'{properties,pdi,contextInformation,tags}' ?| array['SOME_TAG']

结果:

  Aggregate

  (cost=27052.16..27052.17 rows=1 width=8) (actual time=488.085..488.087 rows=1 loops=1)
  ->  Seq Scan on t_aip  (cost=0.00..27052.06 rows=42 width=0) (actual time=0.134..456.978 rows=16502 loops=1)
        Filter: ((json_aip #> '{properties,pdi,contextInformation,tags}'::text[]) ?| '{SOME_TAG}'::text[])
        Rows Removed by Filter: 17511
Planning time: 23.202 ms
Execution

time: 488.449 ms

函数式查询

EXPLAIN ANALYZE SELECT count(*)  
FROM storage.t_aip 
WHERE jsonb_exists_any(
    json_aip#>'{properties,pdi,contextInformation,tags}', 
    array['SOME_TAG']
)

结果:

QUERY PLAN
Aggregate  (cost=27087.00..27087.01 rows=1 width=8) (actual time=369.931..369.933 rows=1 loops=1)
  ->  Seq Scan on t_aip  (cost=0.00..27052.06 rows=13979 width=0) (actual time=0.173..350.437 rows=16502 loops=1)
        Filter: jsonb_exists_any((json_aip #> '{properties,pdi,contextInformation,tags}'::text[]), '{SOME_TAG}'::text[])
        Rows Removed by Filter: 17511
Planning time: 56.021 ms
Execution time: 370.252 ms

关于索引方面没有任何信息。非常感谢您的帮助!

我认为我的索引有误,因为它认为在路径json_aip -> 'properties' -> 'pdi' -> 'contextInformation' -> 'tags'的末尾索引了一个字符串,无论它是否是数组。这是我的看法。

2个回答

5

有一个通用规则,必须在索引和查询中使用完全相同的表达式才能使用索引。对于这个索引:

CREATE INDEX idx_aip_tags 
ON t_aip 
USING gin ((json_aip#>'{properties,pdi,contextInformation,tags}'));

查询将使用索引

EXPLAIN ANALYZE 
SELECT count(*)  
FROM t_aip 
WHERE json_aip#>'{properties,pdi,contextInformation,tags}' ?| array['SOME_TAG']

                                                           QUERY PLAN                                                            
---------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=149.97..149.98 rows=1 width=0) (actual time=27.783..27.783 rows=1 loops=1)
   ->  Bitmap Heap Scan on t_aip  (cost=20.31..149.87 rows=40 width=0) (actual time=1.504..25.726 rows=20000 loops=1)
         Recheck Cond: ((json_aip #> '{properties,pdi,contextInformation,tags}'::text[]) ?| '{SOME_TAG}'::text[])
         Heap Blocks: exact=345
         ->  Bitmap Index Scan on idx_aip_tags  (cost=0.00..20.30 rows=40 width=0) (actual time=1.455..1.455 rows=20000 loops=1)
               Index Cond: ((json_aip #> '{properties,pdi,contextInformation,tags}'::text[]) ?| '{SOME_TAG}'::text[])

请注意,GIN索引还支持@>运算符。
SELECT count(*)  
FROM t_aip 
WHERE json_aip#>'{properties,pdi,contextInformation,tags}' @> '["SOME_TAG"]'

但是当搜索多个标签时要小心:

SELECT count(*)  
FROM t_aip 
-- this gives objects containing both tags:
-- WHERE json_aip#>'{properties,pdi,contextInformation,tags}' @> '["SOME_TAG", "ANOTHER_TAG"]'
-- and this gives objects with any of two tags:
WHERE json_aip#>'{properties,pdi,contextInformation,tags}' @> ANY(ARRAY['["SOME_TAG"]', '["ANOTHER_TAG"]']::jsonb[])

这非常有帮助!而且使用@>ANY关键字来获得与?|相同的行为是相当奇怪但有趣的。 - Ser

1

编辑:

我原本以为操作符(?|)和函数(jsonb_exists_any)在索引使用上没有区别,但事实上它们有区别,因为当查询使用(jsonb)函数时,索引永远不会被使用。

你可以在这里了解更多信息:https://dba.stackexchange.com/a/91007

这是该主题的另一个问题。

编辑2:

你可以创建函数别名,这些别名可以使用索引,并且可以像函数一样在你的代码中使用,就像这样:

-- Define functions that calls the postgres native operator, to overpass the JDBC issue related to question mark
CREATE OR REPLACE FUNCTION rs_jsonb_exists_all(jsonb, text[])
RETURNS bool AS
'SELECT $1 ?& $2' LANGUAGE sql IMMUTABLE;


CREATE OR REPLACE FUNCTION rs_jsonb_exists(jsonb, text)
RETURNS bool AS
'SELECT $1 ? $2' LANGUAGE sql IMMUTABLE;


CREATE OR REPLACE FUNCTION rs_jsonb_exists_any(jsonb, text[])
RETURNS bool AS
'SELECT $1 ?| $2' LANGUAGE sql IMMUTABLE;


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接