关于postgresql:为模式匹配搜索索引jsonb数据

Indexing jsonb data for pattern matching searches

这是后续行动:
jsonb键/值上的模式匹配

我有一张桌子如下

1
2
3
4
5
CREATE TABLE"PreStage".transaction (
  transaction_id serial NOT NULL,
  TRANSACTION jsonb
  CONSTRAINT pk_transaction PRIMARY KEY (transaction_id)
);

我的事务jsonb列中的内容看起来像

1
2
3
4
5
6
{"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":""}

我需要搜索查询,如:

1
2
SELECT TRANSACTION AS DATA FROM  "PreStage".transaction
WHERE  transaction->>'HCP_FST_NM' ILIKE '%neer%';

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

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

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

哪个有效,但我也想索引其他键。 因此尝试了类似的事情:

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

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


@jjanes指出的语法错误,
对于一些流行密钥的混合(包含在许多行和/或经常搜索)加上许多更罕见的密钥(包含在几行和/或很少搜索,新密钥可能会动态弹出)我建议这个组合:

流行键的Trigram索引

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

1
2
3
4
5
6
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键/值上的模式匹配

基本的jsonb GIN索引

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

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

除此之外,这支持搜索键。但是你不能将它用于值的模式匹配。

  • 在Postgres jsonb中查询数组结构的正确索引是什么?

询问

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

1
2
3
4
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使用良好的估计和当前的表统计信息来选择最佳查询计划至关重要。


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

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

1
CREATE INDEX ON TRANSACTION USING gin ((TRANSACTION::text) gin_trgm_ops);

然后为查询添加辅助条件:

1
2
3
SELECT TRANSACTION AS DATA FROM TRANSACTION
WHERE  transaction->>'HCP_FST_NM' ILIKE '%neer%'
AND TRANSACTION::text ilike '%neer%';

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

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

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

假设所有记录与您显示的结构具有相同的结构,则索引将返回每一行,因为每行包含"ADDR",因为用作键。然后每一行都会失败另一个条件检查,但只有在做了很多工作之后。