【问题标题】:Index on JSON field with dynamic keys具有动态键的 JSON 字段索引
【发布时间】:2016-10-20 15:25:52
【问题描述】:

我在 PG 9.5 上,我有一张桌子 Visitors(id, data::json)

例子:

Visitor(id: 1, data: {name: 'Jack', age: 33, is_user: true })

我想执行类似的查询

  • 给我所有名叫 Jack 且年龄 > 25 岁的访客
  • 给我所有作为用户的访问者,但名称未指定(键不在 json 中)

数据列内的键是用户指定的,因此是动态的。

在这种情况下哪个索引最有意义?

【问题讨论】:

    标签: postgresql indexing


    【解决方案1】:

    你可以看看额外的扩展 JsQuery – 是一种查询 jsonb 数据类型的语言,它为 jsonb 提供了一个额外的功能(目前在 PostgreSQL 中缺少),例如在嵌套对象和数组中搜索的简单有效的方法,更多具有索引支持的比较运算符。在此处阅读更多信息:https://github.com/postgrespro/jsquery

    在您的情况下,您可以创建jsonb_path_value_ops 索引:

    CREATE INDEX idx_visitors ON visitors USING GIN (jsonb jsonb_path_value_ops);
    

    并使用下一个查询:

    select * from visitors where jsonb @@ 'name = "Jack" and age > 25';
    select * from visitors where jsonb @@ 'not name = * and is_user=true';
    

    【讨论】:

      【解决方案2】:

      您可以在 jsonb column 上使用 GIN index,它可以为 JSON 值中的键和值提供通用的动态索引。

      CREATE TABLE visitors (
        id integer,
        data jsonb
      );
      
      CREATE INDEX idx_visitors_data ON cards USING GIN (data);
      
      SELECT * FROM visitors
      WHERE data -> 'is_user' AND NOT data ? 'name';
      

      很遗憾,GIN 索引不支持数值范围比较。因此,尽管您仍然可以为 25 岁以上的名为 Jack 的访问者发出查询:

      SELECT * FROM visitors
      WHERE data @> '{"name": "Jack"}' AND ((data ->> 'age')::integer) > 25;
      

      这只会使用索引来查找名称“Jack”,并且可能会查找具有“age”键的行,但实际年龄超过 25 岁的测试将作为对匹配行的扫描来完成.

      请注意,如果您确实需要范围比较,您仍然可以在 JSON 值内的特定路径上添加非 GIN 索引,前提是您希望它们出现的频率足够高以使其值得。例如,您可以在data -> 'age' 上添加一个支持范围比较的索引:

      CREATE INDEX idx_visitors_data_age ON visitors ( ((data ->> 'age')::integer) );
      

      (注意额外的括号;没有它们你会得到一个错误)。

      更多信息请参见this excellent blog post

      【讨论】:

        【解决方案3】:

        我认为这里最好的方法是创建原始 sql 迁移:

        1. 运行./manage.py makemigrations --empty yourApp,其中yourApp 是您要为其更改索引的模型的应用程序。

        2. 编辑迁移,即

        operations = [
            migrations.RunSQL("CREATE INDEX idx_content_taxonomies_categories ON common_content((taxonomies->>'categories'));")
        ]
        

        idx_content_taxonomies_categories 是索引的名称,common_content 是您的表,taxonomies 是您的 JSONField,categories 在这种情况下是您要索引的键。

        应该这样做。干杯!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-07-20
          • 2019-02-23
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多