【问题标题】:Indexing a jsonb array in Postgres在 Postgres 中索引 jsonb 数组
【发布时间】:2019-03-21 20:21:59
【问题描述】:

我尝试设置 GIN 索引,但无论我使用运算符还是函数,我都认为在运行请求时没有使用我的索引。

环境

在我们的表中,我们有一个 JSONB 字段 (json_aip),其中包含一个如下所示的 Json:

{
    "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' 它索引一个字符串是否是一个数组。这是我的看法。

【问题讨论】:

    标签: postgresql indexing jsonb


    【解决方案1】:

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

    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 关键字来获得与?| 相同的行为,这很奇怪但很有趣
    【解决方案2】:

    已编辑:

    我的想法正好相反,但实际上 THERE IS 运算符 (?|) 和函数 (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;
    
    

    【讨论】:

      猜你喜欢
      • 2018-02-10
      • 2018-05-29
      • 1970-01-01
      • 2020-06-01
      • 2017-08-12
      • 1970-01-01
      • 1970-01-01
      • 2017-09-11
      • 2023-03-05
      相关资源
      最近更新 更多