在Postgres中有效搜索整个1级嵌套JSONB

Kam*_*ski 7 postgresql lookup jsonb postgresql-9.5

假设我们需要检查jsonb列是否包含由任何值(非嵌套,仅第一级)中的子字符串匹配的特定值.

如何有效地优化查询以搜索值的整个JSONB列(这意味着每个键)?

是否有一些很好的替代方法可以ILIKE %val%对jsonb数据类型进行文本转换?

jsonb_each_text(jsonb_column) ILIKE '%val%'
Run Code Online (Sandbox Code Playgroud)

作为示例,请考虑以下数据:

SELECT 
  '{
   "col1": "somevalue", 
   "col2": 5.5, 
   "col3": 2016-01-01, 
   "col4": "othervalue", 
   "col5": "yet_another_value"
  }'::JSONB
Run Code Online (Sandbox Code Playgroud)

当需要%val%在包含jsonb列中不同行的不同键配置的记录中搜索模式时,您将如何优化查询?

我知道用前面和后面的%符号搜索是低效的,因此寻找更好的方法,但很难找到一个.此外,显式索引json列中的所有字段不是一个选项,因为它们对于每种类型的记录都有所不同,并且会创建一大组索引(并非每行都有相同的键集).

是否有更好的替代方法可以将每个键值对提取到文本并执行ILIKE/POSIX搜索?

bit*_*fet 1

如果您知道只需要查询几个已知键,那么您可以简单地为这些表达式建立索引。

这是一个太简单但不言自明的例子:

create table foo as SELECT '{"col1": "somevalue", "col2": 5.5, "col3": "2016-01-01", "col4": "othervalue", "col5": "yet_another_value"}'::JSONB as bar;

create index pickfoo1 on foo ((bar #>> '{col1}'));
create index pickfoo2 on foo ((bar #>> '{col2}'));
Run Code Online (Sandbox Code Playgroud)

这是基本思想,即使它对于类似的查询没有用,但您可以做更多的事情(取决于您的需要)。

例如:如果您只需要不区分大小写的匹配,那么执行以下操作就足够了:

-- Create index over lowered value:
create index pickfoo1 on foo (lower(bar #>> '{col1}'));
create index pickfoo2 on foo (lower(bar #>> '{col2}'));

-- Check that it matches:
select * from foo where lower(bar #>> '{col1}') = lower('soMEvaLUe');
Run Code Online (Sandbox Code Playgroud)

注意:这只是一个示例:如果您对之前的选择执行解释,您将看到 postgres 实际上执行顺序扫描而不是使用索引。但这是因为我们正在测试一个只有一行的表,这并不常见。但我相信你可以用更大的桌子来测试它;-)

对于巨大的表,如果第一个通配符没有出现在字符串的开头,即使是类似的查询也应该受益于索引(但这不是 jsonb 的问题,而是 btree 索引本身的问题)。

如果您需要优化查询,例如:

select * from foo where bar #>> '{col1}' ilike '%MEvaL%';
Run Code Online (Sandbox Code Playgroud)

...那么你应该考虑使用 GIN 或 GIST 索引。

  • 注意:不是作为实际的解决方案,而是作为一个合理的补丁:请记住,postgres 不仅可以索引一个(或多个)列值,还可以索引复杂的表达式,包括函数调用(如果函数至少稳定!),因此您可以例如,实现一个重新调整 json 键排序列表的函数。同样,使用简单的 btree 是不够的,但是使用 tgrm(更不用说实现更具体的 gin/gist 了),可以过滤具有我们想要查找的键的第一行。 (3认同)