Django 和 PostgreSQL, 从 SQL 的 LIKE 到全文搜索(Full-Text-Search) (1)

在我们的博客中, 记录了我们在开发过程中所使用的技术和遇到的问题, 希望作为其他开发和设计者的一个学习交流平台.

Django 和 PostgreSQL, 从 SQL 的 LIKE 到全文搜索(Full-Text-Search) (1)


一般我们是这样从超多记录中过滤出相应的query的:

    >>> Entry.objects.filter(title__icontains='Man bites dog')

这一语句在PostgreSQL中则被转化为:

    SELECT ... WHERE title ILIKE '%Man bites dog%';

但对于"Man Bites Dogs Tails"这样的记录还是无法过滤出.

PostgreSQL的全文搜索

我们可以使用PostgreSQL的全文搜索功能, 而不是正则表达式来解决这一问题. 因为这则表达式:

  • 没有语言支持, 不支持派生词, 衍生词, 相近词等
  • 不能根据相近性排序
  • 运行慢, 因为没有index支持

当使用全文搜索时, 则可以做到返回衍生词:

    >>> Entry.objects.search('man is biting a tail')
    [>Entry: Man Bites Dogs Tails<]

Document 和 Query

document指的是全文搜索的一个单位, document可以是任何东西, 在这里, 我们定义为title和body栏为一个单一的document.

为了搜索的速度和效率考虑, 数据库使用document的compact representation (tsvetor). compact representation是经过特殊处理的原始内容.

为了使用查询(query) tsvector, 我们需要使用tsquery. tsquery是经过普通化的query.

通过tsvector和tsquery的匹配, 才能完成搜索.

在query中使用他们, 则需要to_tsvector和to_query的帮助, 以下是解决本篇开头问题的一个语句:

    SELECT ... WHERE to_tsvector(COALESCE(title, '') || ' ' || COALESCE(body, '')) @@ to_tsquery('man & bites & dog');

关于 Stem

为了将搜索项和document文字整合起来, PostgreSQL使用了stemming词典. 显示PostgreSQL中已安装的词典:

    => \dFd
                                 List of text search dictionaries
       Schema   |      Name       |                        Description
    ------------+-----------------+-------------------------------
     pg_catalog | danish_stem     | snowball stemmer for danish language
     pg_catalog | dutch_stem      | snowball stemmer for dutch language
     pg_catalog | english_stem    | snowball stemmer for english language
     pg_catalog | finnish_stem    | snowball stemmer for finnish language
     pg_catalog | french_stem     | snowball stemmer for french language
     pg_catalog | german_stem     | snowball stemmer for german language
     pg_catalog | hungarian_stem  | snowball stemmer for hungarian language
     pg_catalog | italian_stem    | snowball stemmer for italian language
     pg_catalog | norwegian_stem  | snowball stemmer for norwegian language
     pg_catalog | portuguese_stem | snowball stemmer for portuguese language
     pg_catalog | romanian_stem   | snowball stemmer for romanian language
     pg_catalog | russian_stem    | snowball stemmer for russian language
     pg_catalog | simple          | simple dictionary: just lower case and check for stopword
     pg_catalog | spanish_stem    | snowball stemmer for spanish language
     pg_catalog | swedish_stem    | snowball stemmer for swedish language
     pg_catalog | turkish_stem    | snowball stemmer for turkish language

不幸的是, PostgreSQL没有自带中文的全文搜索stem词典, 但我们还是可以通过社区找到.

使用:

    => SELECT ts_lexize('english_stem', 'programming');
     ts_lexize
    -----------
     {program}
    (1 row)

    => SELECT ts_lexize('spanish_stem', 'programming');
       ts_lexize
    ---------------
     {programming}
    (1 row)

注意, 使用错误的stem会带来错误的结果.


原文链接: http://weiguda.com/blog/54/