【问题标题】:Effectively searching through entire 1 level nested JSONB in Postgres在 Postgres 中有效地搜索整个 1 级嵌套 JSONB
【发布时间】:2016-08-22 02:59:33
【问题描述】:

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

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

在转换为文本的 jsonb 数据类型上执行 ILIKE %val% 有什么好的选择吗?

jsonb_each_text(jsonb_column) ILIKE '%val%'

以这些数据为例:

SELECT 
  '{
   "col1": "somevalue", 
   "col2": 5.5, 
   "col3": 2016-01-01, 
   "col4": "othervalue", 
   "col5": "yet_another_value"
  }'::JSONB

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

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

问题

除了将每个键值对提取到文本并执行 ILIKE/POSIX 搜索之外,还有更好的替代方法吗?

【问题讨论】:

  • 这可能更适合 dba.stackexchange.com,我只是想为这件事吸引更多的观众。
  • pg_trgm 可能是最好的选择(ilike/posix 类型),因为您仍在 jsonb 列中使用模式匹配标准类型
  • @DmitrySavinkov 你能详细说明一下吗?我相信我仍然需要将 json 数据解压缩到单独的行中。
  • 是的,你需要解压值,所以gin_trgm_ops操作符类可以应用,你也可以检查检查这个answer
  • somethink LIKE '%<somevalue>%' 之类的过滤器在默认情况下效率低下,因为它总是会导致对数据进行全面扫描。所以@DmitrySavinkov 的建议几乎是最好的解决方案。 IMO 它应该是答案,并附有简短的解释。

标签: postgresql lookup jsonb postgresql-9.5


【解决方案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}'));

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

例如:如果您只需要不区分大小写的匹配,那么这样做就足够了:

-- 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');

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

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

如果您需要优化以下查询:

select * from foo where bar #>> '{col1}' ilike '%MEvaL%';

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

【讨论】:

  • 感谢您的回答。不幸的是,我担心你误解了这个问题的重点。我要求在第一级搜索整个 jsonb 而不是解包更有效的方法。我的意思是每个键。这是针对搜索引擎的。我不能说特定列有哪些键。
  • 这就是为什么我开始说“如果你知道……”。如果您需要能够通过任何(非预先确定的)键进行搜索,则优化起来会更加困难。
  • 另外,我敢说尝试“以更有效的方式”解析 JSON 并不能解决您的问题。首先,因为实际上 jsonb 并没有在内部存储为 json 以便更有效地访问(我认为你不能比本机访问函数更快地实现这一点)。
  • ...但最重要的是,因为当您在数据库中搜索任何内容时,无论您搜索的是哪种数据类型,特别是当数据量变得巨大时,重点不是解析或比较它更快,但根本不这样做。这是索引的目标。优化解析和/或比较最多会稍微延迟实际问题(假设您的数据库像大多数人一样随着时间增长)。
  • 注意:不是实际的解决方案,而是同时作为一个合理的补丁:记住 postgres 不仅可以索引一个(或多个)列值,还可以索引复杂的表达式,包括函数调用(如果函数位于最不稳定!),因此您可以实现,例如,一个函数重新调整 json 键的排序列表。同样,仅使用简单的 btree 是不够的,但是使用 tgrm(更不用说实现更多的 specifig gin/gist 之一)可以过滤具有我们要查找的键的第一行。
猜你喜欢
  • 2018-08-27
  • 1970-01-01
  • 1970-01-01
  • 2023-04-07
  • 1970-01-01
  • 2018-02-10
  • 2017-07-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多