我正在尝试对文档进行索引,以便在其标签数组上进行搜索。
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
,因为它并不总是不可变的。
PG::InvalidObjectDefinition: ERROR: functions in index expression must be marked IMMUTABLE
我尝试过创建一个自制的
array_to_string
不可变函数,但我感觉就像在玩火,因为我不知道自己在做什么。有什么办法不重新实现它吗?
看起来我可以重新打包相同的函数并将其标记为不可变,但看起来这样做有风险。
如何为数组建立索引以进行全文搜索?
在我最初的回答中,我建议将纯文本转换为文本:
tags::text
。然而,虽然大多数从基本类型到文本的转换都被定义为 IMMUTABLE
,但数组类型的情况并非如此。显然是因为(在 pgsql-general 的帖子中引用 Tom Lane ):
我的粗体强调。因为它是通过 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,'')));
我确实没有使用多态类型ANYARRAY
,就像你的答案中的一样,因为我知道
text[]
或citext[]
是安全的,但我不能保证所有其他数组类型。 在 Postgres 9.4 中测试并且对我有用。
我在两个字符串之间添加了一个空格,以避免连接字符串之间出现误报匹配。手册中有一个
示例tags
或只是
notes
,请考虑使用多列索引:CREATE INDEX doc_search_idx ON documents USING gin (
to_tsvector('english', COALESCE(f_ciarr2text(tags), '')
, to_tsvector('english', COALESCE(notes,''));
您所指的风险主要适用于时间函数,这些函数在参考问题中使用。如果涉及时区(或仅类型timestamptz
),结果实际上并不是一成不变的。我们不会在这里对不变性撒谎。我们的功能是
实际上
IMMUTABLE
。 Postgres 只是无法从它使用的一般实现中看出。相关
citext
时,请考虑这一点:
CREATE FUNCTION immutable_array_to_string(arr ANYARRAY, sep TEXT)
RETURNS text
AS $$
SELECT array_to_string(arr, sep);
$$
LANGUAGE SQL
IMMUTABLE
;