Postgres 全文搜索 Jsonb 数组列不显示第一行
Postgres Full Text Search Jsonb Array column does not show first row
当查找 table 中的第一行时,Pgsql 无法对包含数组的 jsonb 列执行索引全文搜索。
Table 只不过是 Id
和 Foo
,即 jsonb
列。
情况是我有一个空数据库,在 make_tsvector
函数上具有预定义的杜松子酒索引 repro_fts_idx
。 make_tsvector
从给定的 jsonb
列创建 tsvector
。
当我向 table 添加新项目时,我希望它以 tsvector
的形式出现在 make_tsvector
函数中。在那。
另外,我希望如果我对它进行 运行 全文搜索查询,它就会出现在搜索结果中。但是,情况并非如此,因为它 returns 专门为第一行留空。它根本不考虑它。
如果我再添加一行完全相同的行,系统可以使用相同的查询找到它。
这里有一个小的复现案例:
-- drop table cp."Repro" cascade
CREATE TABLE cp."Repro" (
"Id" serial NOT NULL,
"Foo" jsonb NULL
);
CREATE OR REPLACE FUNCTION cp.make_tsvector(in_t cp."Repro")
RETURNS tsvector
LANGUAGE plpgsql
IMMUTABLE
AS $function$ begin
return to_tsvector(jsonb_agg(x.prop))
from (SELECT CONCAT( jsonb_array_elements(in_t."Foo") ->> 'Name', ' ', jsonb_array_elements(in_t."Foo") ->> 'Address' ) as prop from cp."Repro" f) as x;
END;
$function$
;
CREATE INDEX repro_fts_idx ON cp."Repro" USING gin (cp.make_tsvector(cp."Repro".*)) WITH (fastupdate=off, gin_pending_list_limit='64');
INSERT INTO cp."Repro"
("Foo")
VALUES('[{"Name": "Sup", "Address": "Adress", "IsCurrent": true}]');
-- just in case it's the indexing issue
-- REINDEX INDEX cp.repro_fts_idx;
select * from cp."Repro"
select cp.make_tsvector(x) from cp."Repro" x
select * from ts_stat('select cp.make_tsvector(x) from cp."Repro" x')
-- explain analyze
SELECT *
FROM "cp"."Repro" x where cp.make_tsvector(x) @@ 'sup:*'::tsquery
INSERT INTO cp."Repro"
("Foo")
VALUES('[{"Name": "Sup", "Address": "Adress", "IsCurrent": true}]');
-- explain analyze
SELECT *
FROM "cp"."Repro" x where cp.make_tsvector(x) @@ 'sup:*'::tsquery
UPD:答案
该函数是错误的,因为它引用了输入行和整个 table。
正确的函数是:
CREATE OR REPLACE FUNCTION cp.make_tsvector(in_t cp."Repro")
RETURNS tsvector
LANGUAGE plpgsql
IMMUTABLE
AS $function$
BEGIN
return string_agg(lower(regexp_replace(coalesce(x::text, ''), '[|&\*:()'']+', ' ', 'g')), ' ')::tsvector FROM (SELECT CONCAT( jsonb_array_elements(in_t."Foo") ->> 'Name', ' ', jsonb_array_elements(in_t."Foo") ->> 'Address' ) AS x) AS x;
END;
$function$
;
肯定是你的函数有问题。它不应同时引用其输入记录 in_t
和整个基础 table、cp."Repro"
。 (而且因为它指的是 cp."Repro"
,所以它并不是真正的 immutable。当你对系统撒谎说函数的不可变性时,就会发生不好的事情。)
由于在插入第一行时 table 为空,因此从函数内部的空 table 中进行选择不会给出任何结果,从而产生要索引的 NULL 结果。这可以通过以下方式看到:
truncate cp."Repro" ;
select cp.make_tsvector(row(1,'[{"Name": "Sup", "Address": "Adress", "IsCurrent": true}]'));
make_tsvector
---------------
(null)
就算你的make_tsvector没坏,好像也没用。它基本上只是 to_tsvector 的一个失败的重新实现。它应该完成什么?
当查找 table 中的第一行时,Pgsql 无法对包含数组的 jsonb 列执行索引全文搜索。
Table 只不过是 Id
和 Foo
,即 jsonb
列。
情况是我有一个空数据库,在 make_tsvector
函数上具有预定义的杜松子酒索引 repro_fts_idx
。 make_tsvector
从给定的 jsonb
列创建 tsvector
。
当我向 table 添加新项目时,我希望它以 tsvector
的形式出现在 make_tsvector
函数中。在那。
另外,我希望如果我对它进行 运行 全文搜索查询,它就会出现在搜索结果中。但是,情况并非如此,因为它 returns 专门为第一行留空。它根本不考虑它。
如果我再添加一行完全相同的行,系统可以使用相同的查询找到它。
这里有一个小的复现案例:
-- drop table cp."Repro" cascade
CREATE TABLE cp."Repro" (
"Id" serial NOT NULL,
"Foo" jsonb NULL
);
CREATE OR REPLACE FUNCTION cp.make_tsvector(in_t cp."Repro")
RETURNS tsvector
LANGUAGE plpgsql
IMMUTABLE
AS $function$ begin
return to_tsvector(jsonb_agg(x.prop))
from (SELECT CONCAT( jsonb_array_elements(in_t."Foo") ->> 'Name', ' ', jsonb_array_elements(in_t."Foo") ->> 'Address' ) as prop from cp."Repro" f) as x;
END;
$function$
;
CREATE INDEX repro_fts_idx ON cp."Repro" USING gin (cp.make_tsvector(cp."Repro".*)) WITH (fastupdate=off, gin_pending_list_limit='64');
INSERT INTO cp."Repro"
("Foo")
VALUES('[{"Name": "Sup", "Address": "Adress", "IsCurrent": true}]');
-- just in case it's the indexing issue
-- REINDEX INDEX cp.repro_fts_idx;
select * from cp."Repro"
select cp.make_tsvector(x) from cp."Repro" x
select * from ts_stat('select cp.make_tsvector(x) from cp."Repro" x')
-- explain analyze
SELECT *
FROM "cp"."Repro" x where cp.make_tsvector(x) @@ 'sup:*'::tsquery
INSERT INTO cp."Repro"
("Foo")
VALUES('[{"Name": "Sup", "Address": "Adress", "IsCurrent": true}]');
-- explain analyze
SELECT *
FROM "cp"."Repro" x where cp.make_tsvector(x) @@ 'sup:*'::tsquery
UPD:答案
该函数是错误的,因为它引用了输入行和整个 table。 正确的函数是:
CREATE OR REPLACE FUNCTION cp.make_tsvector(in_t cp."Repro")
RETURNS tsvector
LANGUAGE plpgsql
IMMUTABLE
AS $function$
BEGIN
return string_agg(lower(regexp_replace(coalesce(x::text, ''), '[|&\*:()'']+', ' ', 'g')), ' ')::tsvector FROM (SELECT CONCAT( jsonb_array_elements(in_t."Foo") ->> 'Name', ' ', jsonb_array_elements(in_t."Foo") ->> 'Address' ) AS x) AS x;
END;
$function$
;
肯定是你的函数有问题。它不应同时引用其输入记录 in_t
和整个基础 table、cp."Repro"
。 (而且因为它指的是 cp."Repro"
,所以它并不是真正的 immutable。当你对系统撒谎说函数的不可变性时,就会发生不好的事情。)
由于在插入第一行时 table 为空,因此从函数内部的空 table 中进行选择不会给出任何结果,从而产生要索引的 NULL 结果。这可以通过以下方式看到:
truncate cp."Repro" ;
select cp.make_tsvector(row(1,'[{"Name": "Sup", "Address": "Adress", "IsCurrent": true}]'));
make_tsvector
---------------
(null)
就算你的make_tsvector没坏,好像也没用。它基本上只是 to_tsvector 的一个失败的重新实现。它应该完成什么?