查询我们知道了如何构建一个文档,但我们的目标是搜索文档。我们对tsvector搜索时可以使用@@操作符,使用说明见此处。看几个查询文档的例子。 1 2 3 4 5 6 7 8 9 10 11 12 | > select to_tsvector( 'If you can dream it, you can do it' ) @@ 'dream' ;
? column ?
t
(1 row)
> select to_tsvector( 'It' 's kind of fun to do the impossible' ) @@ 'impossible' ;
? column ?
f
(1 row)
|
第二个查询返回了假,因为我们需要构建一个tsquery,使用@@操作符时,把字符串转型(cast)成了tsquery。下面显示了这种l转型和使用to_tsquery()之间的差别。 1 2 3 4 | SELECT 'impossible' ::tsquery, to_tsquery( 'impossible' );
tsquery | to_tsquery
'impossible' | 'imposs' (1 row)
|
但"dream"的词位与它本身相同。 1 2 3 4 | SELECT 'dream' ::tsquery, to_tsquery( 'dream' );
tsquery | to_tsquery
'dream' | 'dream' (1 row)
|
从现在开始我们使用to_tsquery查询文档。 1 2 3 4 5 6 | SELECT to_tsvector( 'It' 's kind of fun to do the impossible' ) @@ to_tsquery( 'impossible' );
? column ?
t
(1 row)
|
tsquery存储了要搜索的词位,可以使用&(与)、|(或)和!(非)逻辑操作符。可以使用圆括号给操作符分组。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | > SELECT to_tsvector( 'If the facts don' t fit the theory, change the facts ') @@ to_tsquery(' ! fact ');
?column?
----------
f
(1 row)
> SELECT to_tsvector(' If the facts don '' t fit the theory, change the facts ') @@ to_tsquery(' theory & !fact ');
?column?
----------
f
(1 row)
> SELECT to_tsvector(' If the facts don '' t fit the theory, change the facts. ') @@ to_tsquery(' fiction | theory');
? column ?
t
(1 row)
|
我们也可以使用:*来表达以某词开始的查询。 1 2 3 4 5 6 | > SELECT to_tsvector( 'If the facts don' 't fit the theory, change the facts.' ) @@ to_tsquery( 'theo:*' );
? column ?
t
(1 row)
|
既然我们知道了怎样使用全文搜索查询了,我们回到开始的表模式,试着查询文档。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | SELECT pid, p_titleFROM ( SELECT post.id as pid,
post.title as p_title,
to_tsvector(post.title) ||
to_tsvector(post.content) ||
to_tsvector(author. name ) ||
to_tsvector( coalesce (string_agg(tag. name , ' ' ))) as document
FROM post
JOIN author ON author.id = post.author_id
JOIN posts_tags ON posts_tags.post_id = posts_tags.tag_id
JOIN tag ON tag.id = posts_tags.tag_id
GROUP BY post.id, author.id) p_search WHERE p_search.document @@ to_tsquery( 'Endangered & Species' );
pid | p_title
1 | Endangered species
(1 row)
|
这个查询将找到文档中包含Endangered和Species或接近的词。
语言支持Postgres 内置的文本搜索功能支持多种语言: 丹麦语,荷兰语,英语,芬兰语,法语,德语,匈牙利语,意大利语,挪威语,葡萄牙语,罗马尼亚语,俄语,西班牙语,瑞典语,土耳其语。 1 2 3 4 5 6 7 | SELECT to_tsvector( 'english' , 'We are running' );
to_tsvector
'run' :3
(1 row) SELECT to_tsvector( 'french' , 'We are running' );
to_tsvector
'are' :2 'running' :3 'we' :1
(1 row)
|
基于我们最初的模型,列名可以用来创建tsvector。 假设post表中包含不同语言的内容,且它包含一列language。 1 | ALTER TABLE post ADD language text NOT NULL DEFAULT ( 'english' );
|
为了使用language列,现在我们重新编译文档。 1 2 3 4 | SELECT to_tsvector(post.language::regconfig, post.title) ||
to_tsvector(post.language::regconfig, post.content) ||
to_tsvector( 'simple' , author. name ) ||
to_tsvector( 'simple' , coalesce ((string_agg(tag. name , ' ' )), '' )) as documentFROM postJOIN author ON author.id = post.author_idJOIN posts_tags ON posts_tags.post_id = posts_tags.tag_idJOIN tag ON tag.id = posts_tags.tag_idGROUP BY post.id, author.id;
|
如果缺少显示的转化符::regconfig,查询时会产生一个错误: 1 | ERROR: function to_tsvector(text, text) does not exist
|
regconfig是对象标识符类型,它表示Postgres文本搜索配置项。:http://www.postgresql.org/docs/9.3/static/datatype-oid.html 现在,文档的语义会使用post.language中正确的语言进行编译。 我们也使用simple,它也是Postgres提供的一个文本搜索配置项。simple并不忽略禁用词表,它也不会试着去查找单词的词根。使用simple时,空格分割的每一组字符都是一个语义;对于数据来说,simple文本搜索配置项很实用,就像一个人的名字,我们也许不想查找名字的词根。 1 2 3 | SELECT to_tsvector( 'simple' , 'We are running' );
to_tsvector
|
重音字符
当你建立一个搜索引擎支持多种语言时你也需要考虑重音问题。在许多语言中重音非常重要,可以改变这个词的含义。Postgres附带一个unaccent扩展去调用 unaccentuate内容是有用处的。
1 2 3 4 | CREATE EXTENSION unaccent; SELECT unaccent( 'èéêë' );
unaccent
eeee
(1 row)
|
让我们添加一些重音的你内容到我们的post表中。
1 2 | INSERT INTO post (id, title, content, author_id, language)
VALUES (4, 'il était une fois' , 'il était une fois un hôtel ...' , 2, 'french' )
|
如果我们想要忽略重音在我们建立文档时,之后我们可以简单做到以下几点:
1 2 3 4 | SELECT to_tsvector(post.language, unaccent(post.title)) ||
to_tsvector(post.language, unaccent(post.content)) ||
to_tsvector( 'simple' , unaccent(author. name )) ||
to_tsvector( 'simple' , unaccent( coalesce (string_agg(tag. name , ' ' )))) JOIN author ON author.id = post.author_idJOIN posts_tags ON posts_tags.post_id = posts_tags.tag_idJOIN tag ON author.id = post.author_idGROUP BY p.id
|
这样工作的话,如果有更多错误的空间它就有点麻烦。 我们还可以建立一个新的文本搜索配置支持无重音的字符。
1 | CREATE TEXT SEARCH CONFIGURATION fr ( COPY = french ); ALTER TEXT SEARCH CONFIGURATION fr ALTER MAPPINGFOR hword, hword_part, word WITH unaccent, french_stem;
|
当我们使用这个新的文本搜索配置,我们可以看到词位
1 2 3 4 5 6 7 | SELECT to_tsvector( 'french' , 'il était une fois' );
to_tsvector
'fois' :4
(1 row) SELECT to_tsvector( 'fr' , 'il était une fois' );
to_tsvector
'etait' :2 'fois' :4
(1 row)
|
这给了我们相同的结果,第一作为应用unaccent并且从结果建立tsvector。
1 2 3 4 | SELECT to_tsvector( 'french' , unaccent( 'il était une fois' ));
to_tsvector
'etait' :2 'fois' :4
(1 row)
|
词位的数量是不同的,因为il était une在法国是一个无用词。这是一个问题让这些词停止在我们的文件吗?我不这么认为etait不是一个真正的无用词而是拼写错误。
1 2 3 4 | SELECT to_tsvector( 'fr' , 'Hôtel' ) @@ to_tsquery( 'hotels' ) as result;
result
t
(1 row)
|
如果我们为每种语言创建一个无重音的搜索配置,这样我们的post可以写入并且我们保持这个值在post.language的中,然后我们可以保持以前的文档查询。
1 2 3 4 | SELECT to_tsvector(post.language, post.title) ||
to_tsvector(post.language, post.content) ||
to_tsvector( 'simple' , author. name ) ||
to_tsvector( 'simple' , coalesce (string_agg(tag. name , ' ' ))) JOIN author ON author.id = post.author_idJOIN posts_tags ON posts_tags.post_id = posts_tags.tag_idJOIN tag ON author.id = post.author_idGROUP BY p.id
|
如果你需要为每种语言创建无重音的文本搜索配置由Postgres支持,然后你可以使用gist
我们当前的文档大小可能会增加,因为它可以包括无重音的无用词但是我们并没有关注重音字符查询。这可能是有用的如有人用英语键盘搜索法语内容。
|