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

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

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/