【问题标题】:Indexing jsonb data for pattern matching searches为模式匹配搜索索引 jsonb 数据
【发布时间】:2016-01-06 16:07:27
【问题描述】:

这是对以下内容的跟进:
Pattern matching on jsonb key/value

我有一张如下表

CREATE TABLE "PreStage".transaction (
  transaction_id serial NOT NULL,
  transaction jsonb
  CONSTRAINT pk_transaction PRIMARY KEY (transaction_id)
);

我的交易 jsonb 列中的内容看起来像

{"ADDR": "abcd", "CITY": "abcd", "PROV": "",
 "ADDR2": "",
 "ADDR3": "","CNSNT": "Research-NA", "CNTRY": "NL", "EMAIL": "@.com",
             "PHONE": "12345", "HCO_NM": "HELLO", "UNQ_ID": "", 
             "PSTL_CD": "1234", "HCP_SR_NM": "", "HCP_FST_NM": "",
             "HCP_MID_NM": ""}

我需要像这样的搜索查询:

SELECT transaction AS data FROM   "PreStage".transaction
WHERE  transaction->>'HCP_FST_NM' ILIKE '%neer%';

但我需要让我的用户灵活地即时搜索任何键/值。

上一个问题的答案建议将索引创建为:

CREATE INDEX idxgin ON "PreStage".transaction
USING gin ((transaction->>'HCP_FST_NM') gin_trgm_ops);

这可行,但我也想索引其他键。因此尝试了类似的东西:

CREATE INDEX idxgin ON "PreStage".transaction USING gin
((transaction->>'HCP_FST_NM'),(transaction->>'HCP_LST_NM') gin_trgm_ops) 

这不起作用。这里最好的索引方法是什么,或者我必须为每个键创建一个单独的索引,在这种情况下,如果将新的键/值对添加到数据中,该方法将不是通用的。

【问题讨论】:

  • 在尝试使用两个键创建索引时,如上所示,我收到如下错误:数据类型文本没有访问方法 gin 的默认运算符类。您必须为索引指定一个运算符类或为数据类型定义一个默认运算符类
  • 您需要为索引的两个部分指定 gin_trgm_ops,而不仅仅是最后一个。
  • 虽然这里是关于 SO 的话题,但它更适合 dba.SE

标签: postgresql indexing pattern-matching jsonb


【解决方案1】:

没有内置索引可以精确地执行您想要的操作,搜索确切的键和相应的通配符匹配值,而无需提前指定要使用的键。应该可以创建一个可以执行此操作的扩展程序,但这将是一项非常繁重的工作,而且我不知道是否存在任何工作。

开箱即用的最佳选择可能是将 jsonb 转换为文本并索引该文本:

create index on transaction using gin ((transaction::text) gin_trgm_ops);

然后在您的查询中添加一个次要条件:

SELECT transaction AS data FROM transaction
WHERE  transaction->>'HCP_FST_NM' ILIKE '%neer%'
AND transaction::text ilike '%neer%';

现在它可以使用索引查找包含“neer”的任何内容,然后重新检查“neer”是否出现在“HCP_FST_NM”键的值中,而不是JSONB 中的其他位置.

如果您的查询词出现在许多地方,而不是所需键的值中,那么这可能不会给您带来很好的性能。例如,如果有人搜索:

transaction->>'EMAIL' ilike '%ADDR%'
AND transaction::text ilike '%ADDR%';

索引将返回每一行,假设所有记录的结构与您显示的相同,因为每一行都包含“ADDR”,因为它用作键。然后每一行都会通过其他条件检查,但只有在做了很多工作之后。

【讨论】:

  • 你好 jjanes,最近我发现了用于 postgresql 的 jsquery,它可以使用 GIN 索引提供各种不同的过滤器查询。您对此有何看法。
【解决方案2】:

抛开@jjanes pointed out的语法错误,
一些常用键(包含在许多行和/或经常搜索)加上更多 em> 稀有键(包含在几行中和/或很少搜索,新键可能会动态弹出)我建议这种组合:

流行键的三元索引

您似乎不会经常在一次搜索中组合多个键,并且具有多个键的单个索引会变得非常大而且速度很慢。所以我会为每个流行的键创建一个单独的索引。使其成为大多数行中不包含的键的部分索引:

CREATE INDEX trans_idxgin_HCP_FST_NM ON transaction  -- contained in most rows
USING gin ((transaction->>'HCP_FST_NM') gin_trgm_ops);

CREATE INDEX trans_idxgin_ADDR ON transaction  -- not in most rows
USING gin ((transaction->>'ADDR') gin_trgm_ops)
WHERE transaction ? 'ADDR';

等等。就像我之前的回答中详述的那样:

基本 jsonb GIN 索引

如果您有许多不同的键和/或动态添加新键,您可以使用基本(默认)jsonb_ops GIN 索引覆盖其余部分:

CREATE INDEX trans_idxgin ON "PreStage".transaction USING gin (transaction);

此外,它还支持搜索。但您不能将其用于值的模式匹配。

查询

结合处理两个索引的谓词:

SELECT transaction AS data
FROM   "PreStage".transaction
WHERE  transaction->>'HCP_FST_NM' ILIKE '%neer%'
AND    transaction ? 'HCP_FST_NM';  -- even if that seems redundant.

第二个条件发生也匹配我们的部分索引。

所以或者对于给定的(流行/常用)键,或者都有一个特定的三元组索引至少有一个索引可以找到(少数)包含稀有键的行 - 然后过滤匹配值。相同的查询应该让您两全其美。

请务必运行最新版本的 Postgres,最近有各种成本估算更新。 Postgres 使用良好的估计和当前表统计信息来选择最佳查询计划至关重要。

【讨论】:

  • 再次感谢欧文。我们使用的是 PostgreSQL 9.4。已经用 PostgreSQL Json 构建了一个动态框架。将看到一切在生产中的表现。
  • Erwin 关于使用 jsquery 用于 postgresql 的任何想法。似乎它可以使用 GIN 索引,但在查询过滤器上提供了更多选项。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-14
  • 1970-01-01
  • 1970-01-01
  • 2019-05-08
  • 2015-03-12
  • 2019-08-25
相关资源
最近更新 更多