导致哪条记录注意:单词太长而无法编入索引

时间:2013-08-28 14:51:05

标签: ruby-on-rails postgresql

在Rails应用程序中使用Postgres(使用pg_search gem),我已启用tsvector搜索。在拥有超过35,000条记录的数据库中,我收到了几条消息

NOTICE:  word is too long to be indexed
DETAIL:  Words longer than 2047 characters are ignored.

假设“单词”不包含空格,我是否正确?如何确定哪条记录导致此消息?

这是由迁移生成的SQL,它引入了索引

 ==  AddIndexForFullTextSearch: migrating ======================================
-- add_column(:posts, :tsv, :tsvector)
   -> 0.0344s
-- execute("      CREATE INDEX index_posts_tsv ON posts USING gin(tsv);\n")
   -> 0.1694s
-- execute("    UPDATE posts SET tsv = (to_tsvector('english', coalesce(title, '')) || \n                            to_tsvector('english', coalesce(intro, '')) || \n                            to_tsvector('english', coalesce(body, '')));\n")
NOTICE:  word is too long to be indexed
DETAIL:  Words longer than 2047 characters are ignored.
NOTICE:  word is too long to be indexed
DETAIL:  Words longer than 2047 characters are ignored.
NOTICE:  word is too long to be indexed
DETAIL:  Words longer than 2047 characters are ignored.
NOTICE:  word is too long to be indexed
DETAIL:  Words longer than 2047 characters are ignored.
   -> 343.0556s
-- execute("      CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE\n      ON posts FOR EACH ROW EXECUTE PROCEDURE\n      tsvector_update_trigger(tsv, 'pg_catalog.english', title, intro, body);\n")
   -> 0.0266s

5 个答案:

答案 0 :(得分:2)

根据the PostgreSQL documentation,“全文搜索功能包括根据您的”文本搜索配置“不仅仅基于空格进行解析的功能”。因此,您必须检查您的配置,以找出“单词”的含义。

您可以使用regular expression搜索长的以空格分隔的单词:

SELECT regexp_matches(the_text_col, '\S{2047,}') FROM the_table

该正则表达式搜索2047个或更多连续的非空白字符。

答案 1 :(得分:1)

如果您想在匹配的摘录之外的匹配行中获取更多信息,您可以执行类似

的操作

select id, text_col from table where text_col ~ '\S{255,}';

如果您尝试使用'\S{256,}'匹配256个或更多字符,则会给出 postgres上的ERROR: invalid regular expression: invalid repetition count(s) 9.3.5

答案 2 :(得分:0)

不,您在假设“假设“单词”不包含空格”时不正确。我以为你是,但稍作试验就可以证明这不是事实。因此,其他引用正则表达式的答案可能对您有帮助,如果确实有一个大于2047个字符的单词,但如果没有大于2047个字符的单词,则不会有帮助。

以下内容有望使这一点更加清楚:

sophia=> select version();
                                   version                                   
-----------------------------------------------------------------------------
 PostgreSQL 10.2 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 5.3.0, 64-bit
(1 row)

sophia=> select to_tsvector(repeat(' ', 1000));
 to_tsvector 
-------------

(1 row)

sophia=> select to_tsvector(repeat(' ', 3000));
NOTICE:  word is too long to be indexed
DETAIL:  Words longer than 2047 characters are ignored.
 to_tsvector 
-------------

(1 row)

sophia=> select to_tsvector('Bob' || repeat(' ', 1000) || ' the builder');
     to_tsvector     
---------------------
 'bob':1 'builder':3
(1 row)

sophia=> select to_tsvector('Bob' || repeat(' ', 3000) || ' the builder');
NOTICE:  word is too long to be indexed
DETAIL:  Words longer than 2047 characters are ignored.
     to_tsvector     
---------------------
 'bob':1 'builder':3
(1 row)

sophia=> 

我遇到了同样的问题,并且不知道在Postgres中解决这个问题的好方法,所以我将数据转储了。

sophia=> \pset tuples_only true
sophia=> \o foo
sophia=> select 'Bob' || repeat(' ', 3000) || ' the builder';
sophia=> 

然后,使用grep很容易找到有问题的文本:

grep -E "[ ]{2047,}" foo

答案 3 :(得分:0)

Postgres全文搜索limits the maximum token length至2047字节:

  

每个词素的长度必须小于2K字节

如果使用的文本解析器返回更长的令牌,那么您会收到像问题中引用的“通知”消息,例如:

select to_tsvector('english', repeat('x', 2*1024));
NOTICE:  word is too long to be indexed
DETAIL:  Words longer than 2047 characters are ignored.

在这种情况下,“单词”实际上表示令牌,默认的Postgres文本解析器可能会返回达到此限制的空白令牌。

您可以通过查询来标识产生此通知的记录,该查询仅调用文本解析器并选择所有过长的标记。例如:

select t.id, tt.tokid, tt.alias, length(t.token), t.token from (
    select  id, (ts_parse('default', payload)).* from content) t
    inner join ts_token_type('default') tt
       on t.tokid = tt.tokid
    where length(token) >= 2*1024;

在尚不清楚为什么解析器产生长令牌的情况下,可以在示例记录中查看前面/后面的令牌,如下所示:

select case when length(token)>128 then '###' else '' end,
       t.tokid, tt.alias, length(token), token
       from ts_parse('default',
                     (select payload from content where id = 'foobar')) t
    inner join ts_token_type('default') tt
        on t.tokid = tt.tokid;

您可以在输出中搜索###,然后查看上下文标记以了解语法错误。

例如,默认的Postgres文本解析器还返回HTML / XML样式标签,并将它们作为单独的标签标记返回。从Postgres 11开始,如果它只是看到一个打开的“标签”而没有后面的结束标签,则有时会返回以下一些文本作为人工空白标记。例如:

select case when length(token)>128 then '###' else '' end, t.tokid, tt.alias,
    length(token), token from ts_parse('default', (select $$<script> 

           We should forget about small efficiencies, say about 97% of the time

       <script>

           premature optimization is the root of all evil.

$$)) t inner join ts_token_type('default') tt on t.tokid = tt.tokid;

哪个解析为4个标记,其中空白标记甚至包含一些文本:

case tokid alias length
──── ───── ───── ──────
        13 tag        8
        12 blank     90                           
        13 tag        8
        12 blank     62
(4 rows)

(为简便起见,省略最后一列)

如果在这些伪“标记”之间存在真实的段落,则很容易达到此类伪“空白/空白”标记的2k限制。

对此的快速解决方案是替换<>文本参数中的to_tsvector()/ts_parse()个字符,以使默认的Postgres解析器不会将<>包含的单词误识别为标签,例如:

... regexp_replace($$...$$, '[<>]', '', 'g') ...

不幸的是,默认Postgres文本解析器的功能(例如标记检测)无法参数化(自版本11开始)。可以使用自定义解析器,但是creating a custom parser目前基本上是指用C编写一个新的解析器并将其加载为额外的扩展-可以说是乏味且容易出错。

答案 4 :(得分:-3)

Postgres有bug, 偶数{150,300}导致错误 - 无效的重复计数,并且这种其他有效的正则表达式无法在Postgres中运行。 希望有人能够纠正这个错误。