使用动态键在JSON字段上建立索引

Tar*_*len 5 postgresql indexing

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

例:

Visitor(id: 1, data: {name: 'Jack', age: 33, is_user: true })
Run Code Online (Sandbox Code Playgroud)

我想执行类似的查询

  • 给我所有名为Jack的访客,年龄> 25
  • 给我所有属于用户但没有指定名称的访问者(密钥不在json中)

用户指定的数据列中的键是动态的。

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

Avi*_*ish 5

您可以在jsonb列上使用GIN索引,从而为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';
Run Code Online (Sandbox Code Playgroud)

不幸的是,GIN索引不支持数字范围比较。因此,尽管您仍然可以对年龄超过25岁的杰克访客进行查询:

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

这将仅使用索引查找名称“ Jack”,并可能查找具有“ age”键的行,但是将对匹配的行进行扫描,以进行年龄超过25岁的实际测试。

请注意,如果您确实需要范围比较,但如果您希望它们出现的次数足够使之有价值,那么仍然可以在JSON值内的特定路径上添加非GIN索引。例如,您可以在data -> 'age'支持范围比较的索引上添加:

CREATE INDEX idx_visitors_data_age ON visitors ( ((data ->> 'age')::integer) );
Run Code Online (Sandbox Code Playgroud)

(请注意多余的括号;如果没有它们,您将得到一个错误)。

有关更多信息,请参见这篇出色的博客文章