设为首页收藏本站

LUPA开源社区

 找回密码
 注册
文章 帖子 博客
LUPA开源社区 首页 业界资讯 技术文摘 查看内容

Postgres的全文搜索已经足够好了

2014-10-14 11:35| 发布者: joejoe0332| 查看: 3869| 评论: 0|原作者: DavidWTF, daxiang, 小猪猪0406, 无若, warrior_by|来自: oschina

摘要: 搜索是项非常重要的功能,所以像elasticsearch和SOLR这样的基于lucene的工具变得很流行。它们都很棒。但使用这些大规模“杀伤性”的搜索武器前,你可能需要来点轻量级的,但又足够好的搜索工具。 ...


 

查询

我们知道了如何构建一个文档,但我们的目标是搜索文档。我们对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
---------------------------- 'are':2 'running':3 'we':1(1 row)

重音字符

当你建立一个搜索引擎支持多种语言时你也需要考虑重音问题。在许多语言中重音非常重要,可以改变这个词的含义。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

我们当前的文档大小可能会增加,因为它可以包括无重音的无用词但是我们并没有关注重音字符查询。这可能是有用的如有人用英语键盘搜索法语内容。



酷毙

雷人

鲜花

鸡蛋

漂亮
  • 快毕业了,没工作经验,
    找份工作好难啊?
    赶紧去人才芯片公司磨练吧!!

最新评论

关于LUPA|人才芯片工程|人才招聘|LUPA认证|LUPA教育|LUPA开源社区 ( 浙B2-20090187 浙公网安备 33010602006705号   

返回顶部