在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
答案 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中运行。 希望有人能够纠正这个错误。