我正在尝试索引可在其标记数组上搜索的文档。
CREATE INDEX doc_search_idx ON documents
USING gin(
to_tsvector('english', array_to_string(tags, ' ')) ||
to_tsvector('english', coalesce(notes, '')))
)
tags
是(ci)text[]
的位置。但是,PG将拒绝对array_to_string
进行索引,因为它is not always immutable。
PG::InvalidObjectDefinition: ERROR: functions in index expression must be marked IMMUTABLE
我尝试过创建一个自制的array_to_string
不可变函数,但我觉得我喜欢玩火,因为我不知道自己在做什么。有什么方法不重新实现它?
看起来我可以重新打包相同的函数并将其标记为不可变,但在执行此操作时看起来像there are risks。
如何为全文搜索索引数组?
答案 0 :(得分:8)
在我最初的回答中,我提出了一个简单的演员文字:tags::text
。但是,虽然大多数基于类型的文本转换都是IMMUTABLE
定义的,但数组类型不是这种情况。显然是因为(quoting Tom Lane in a post to pgsql-general):
因为它是通过array_out / array_in实现的,而不是更多 直接方法,那些标记稳定,因为它们可能 调用非不可变元素I / O函数。
大胆强调我的。
我们可以与之合作。一般情况不能标记为IMMUTABLE
。但对于手头的情况(演员citext[]
或text[]
到text
),我们可以安全地承担不变性。创建一个包装函数的简单IMMUTABLE
SQL函数。然而,我的简单解决方案的吸引力现在大部分消失了。您也可以将array_to_string()
(就像您已经考虑过的那样)包装起来,以适用类似的考虑因素。
对于citext[]
(如果需要,为text[]
创建单独的函数):
(基于普通演员到text
):
CREATE OR REPLACE FUNCTION f_ciarr2text(citext[])
RETURNS text LANGUAGE sql IMMUTABLE AS 'SELECT $1::text';
这更快。
或者(使用array_to_string()
获得没有花括号的结果):
CREATE OR REPLACE FUNCTION f_ciarr2text(citext[])
RETURNS text LANGUAGE sql IMMUTABLE AS $$SELECT array_to_string($1, ',')$$;
这更正确一点 然后:
CREATE INDEX doc_search_idx ON documents USING gin (
to_tsvector('english', COALESCE(f_ciarr2text(tags), '')
|| ' ' || COALESCE(notes,'')));
我不使用类似in your answer的多态类型ANYARRAY
,因为我知道text[]
或citext[]
是安全的,但我可以'保证所有其他数组类型。
在Postgres 9.4中测试并为我工作。
我在两个字符串之间添加了一个空格,以避免在连接字符串中出现误报。有example in the manual。
如果您有时只想搜索tags
或仅搜索notes
,请考虑使用多列索引:
CREATE INDEX doc_search_idx ON documents USING gin (
to_tsvector('english', COALESCE(f_ciarr2text(tags), '')
, to_tsvector('english', COALESCE(notes,''));
您所指的风险主要适用于referenced question中使用的时间函数。如果涉及时区(或只是类型timestamptz
),结果实际上不是不可变的。我们不在乎这里的不变性。我们的功能实际上 IMMUTABLE
。 Postgres无法从它使用的一般实现中分辨出来。
通常人们认为他们需要text search,而使用trigram索引进行相似性搜索会更合适:
在这种情况下不相关,但在使用citext
时,请考虑以下事项:
答案 1 :(得分:1)
这是我天真的解决方案,包装它并将其称为不可变的,如怀疑的那样。
CREATE FUNCTION immutable_array_to_string(arr ANYARRAY, sep TEXT)
RETURNS text
AS $$
SELECT array_to_string(arr, sep);
$$
LANGUAGE SQL
IMMUTABLE
;