3

在 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
4

5 回答 5

4

根据PostgreSQL 文档,“全文搜索功能包括 [...] 不仅仅基于空格进行解析的能力”,具体取决于您的“文本搜索配置”。因此,您必须检查您的配置以找出“单词”的含义。

您可以使用正则表达式搜索以空格分隔的长词:

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

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

于 2013-08-28T16:52:13.587 回答
3

Postgres 全文搜索将最大令牌长度限制为 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 限制。

对此的快速解决方法是替换text 参数<>中的字符,to_tsvector()/ts_parse()以便默认 Postgres 解析器不会错误地将<>封闭的单词识别为标签,例如:

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

不幸的是,默认 Postgres 文本解析器的功能(例如标签检测)是不可参数化的(从版本 11 开始)。可以使用自定义解析器,但当前创建自定义解析器基本上意味着用 C 编写新的解析器并将其作为额外的扩展加载 - 这可以说是乏味且容易出错的。

于 2019-08-10T10:48:04.320 回答
1

如果您想在匹配的行中获取更多信息而不是匹配的摘录,您可以执行类似的操作

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

如果您尝试匹配 256 个或更多字符'\S{256,}',它会 ERROR: invalid regular expression: invalid repetition count(s)在 postgres 9.3.5 上提供

于 2015-06-17T05:14:29.860 回答
0

不,您假设“假设一个“单词”不包括空格是不正确的。我会以为你只是一个小小的实验表明这不是真的。因此,如果确实有一个大于 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
于 2019-03-11T16:05:32.057 回答
-3

Postgres 有错误,即使是 {150,300} 也会导致错误 - 重复计数无效,并且这个原本有效的正则表达式无法在 Postgres 中运行。希望有人能纠正这个错误。

于 2017-08-03T06:45:14.790 回答