索引数组以进行全文搜索

Jon*_*ard 6 postgresql indexing full-text-search

我正在尝试索引可在其标记数组上搜索的文档.

CREATE INDEX doc_search_idx ON documents
      USING gin( 
    to_tsvector('english', array_to_string(tags, ' ')) ||
    to_tsvector('english', coalesce(notes, '')))
)
Run Code Online (Sandbox Code Playgroud)

哪里tags(ci)text[].但是,PG会拒绝索引,array_to_string因为它并不总是不可变的.

PG::InvalidObjectDefinition: ERROR:  functions in index expression must be marked IMMUTABLE
Run Code Online (Sandbox Code Playgroud)

我已经尝试创建一个自制的array_to_string不可变函数,但我觉得我喜欢玩火,因为我不知道我在做什么.有什么办法不重新实施吗?

看起来我可以重新打包相同的功能并将其标记为不可变,但看起来这样做有风险.

如何索引数组以进行全文搜索?

Erw*_*ter 8

在我最初的回答中,我提出了一个简单的演员文字:tags::text.但是,虽然定义了大多数来自基本类型的文本转换IMMUTABLE,但对于数组类型却不是这种情况.显然是因为(引用Tom Lane的帖子中的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';
Run Code Online (Sandbox Code Playgroud)

这更快.
或者(使用array_to_string()没有花括号的结果):

CREATE OR REPLACE FUNCTION f_ciarr2text(citext[]) 
  RETURNS text LANGUAGE sql IMMUTABLE AS $$SELECT array_to_string($1, ',')$$;
Run Code Online (Sandbox Code Playgroud)

这是更正确的一点.
然后:

CREATE INDEX doc_search_idx ON documents USING gin (
   to_tsvector('english', COALESCE(f_ciarr2text(tags), '')
                || ' ' || COALESCE(notes,'')));
Run Code Online (Sandbox Code Playgroud)

我并没有使用多态类型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,''));
Run Code Online (Sandbox Code Playgroud)

您所指的风险主要适用于时间函数,这些函数在引用的问题中使用.如果timestamptz涉及时区(或仅涉及类型),结果实际上不是不可变的.我们不在乎这里的不变性.我们的功能实际上是 IMMUTABLE.Postgres无法从它使用的一般实现中分辨出来.

有关

通常人们认为他们需要文本搜索,而使用trigram索引进行相似性搜索会更合适:

在这个确切的情况下不相关,但在使用时citext,请考虑以下事项:


Jon*_*ard 2

这是我天真的解决方案,将其包装起来并称其为不可变的,正如所怀疑的那样。

  CREATE FUNCTION immutable_array_to_string(arr ANYARRAY, sep TEXT)
    RETURNS text
    AS $$
      SELECT array_to_string(arr, sep);
    $$
    LANGUAGE SQL
    IMMUTABLE
  ;
Run Code Online (Sandbox Code Playgroud)