【问题标题】:Full text search index on a multilingual column多语言列上的全文搜索索引
【发布时间】:2014-02-12 20:17:02
【问题描述】:

我有一个数据库,其中有一个表 shows 和一个多语言列 title。我想通过添加这样的索引来优化全文搜索:

CREATE INDEX title_idx ON shows USING gin(to_tsvector(title));

我收到此错误:

ERROR:  functions in index expression must be marked IMMUTABLE

它基本上要求我添加语言参数以使 to_tsvector 不可变。结果是:

 CREATE INDEX title_idx ON shows USING gin(to_tsvector(LANGUAGE, title));

LANGUAGE 将是我的目标语言之一。

是否可以创建适用于多种语言的索引?

【问题讨论】:

    标签: sql postgresql


    【解决方案1】:

    是否可以创建适用于多种语言的索引?

    是的,但您需要第二列来标识文本的语言。假设您在表格中添加了 doc_language 列;然后你可以写:

    CREATE INDEX title_idx ON shows USING gin(to_tsvector(doc_language, title));
    

    当然,这需要您了解主题文本的语言,这在实践中可能很难做到。如果您不需要词干等,您可以使用语言simple,但我猜如果可以选择的话,您已经这样做了。

    作为替代方案,如果您有一组固定且有限的语言,您可以连接不同语言的向量。例如:

    regress=> SELECT to_tsvector('english', 'cafés') || to_tsvector('french', 'cafés') || to_tsvector('simple', 'cafés');
              ?column?          
    ----------------------------
     'caf':2 'café':1 'cafés':3
    (1 row)
    

    这将匹配使用这三种语言中的任何一种的cafés 的 tsquery。

    作为索引:

    CREATE INDEX title_idx ON shows USING gin((
        to_tsvector('english', title) || 
        to_tsvector('french', title) || 
        to_tsvector('simple', title)
    ));
    

    但这在查询中使用起来很笨拙,因为规划器在匹配索引质量方面不是很聪明。所以我会把它包装在一个函数中:

    CREATE FUNCTION to_tsvector_multilang(text) RETURNS tsvector AS $$
    SELECT to_tsvector('english', $1) || 
           to_tsvector('french', $1) || 
           to_tsvector('simple', $1)
    $$ LANGUAGE sql IMMUTABLE;
    
    CREATE INDEX title_idx ON shows USING gin(to_tsvector_multilang(title));
    

    如果您愿意,您甚至可以花哨:将语言列表作为数组传递(但请记住,它必须完全与索引限定匹配的顺序相同)。使用setweight 的优先级,因此您更喜欢用英语匹配而不是用法语匹配。各种选择。

    【讨论】:

    • 谢谢,我不知道 tsvectors 能够被合并。那太棒了。给我点赞:)
    • 最后一行代码多了一个“(”。(不能编辑少于6个字符...)
    • 串联是唯一对我有效的策略。
    • 仅供参考:"index qual" = "index qualifier",这里有更多信息:postgresql.org/docs/current/static/index-cost-estimation.html - 这不足以让我理解为什么包装在一个函数中会有所帮助!
    • “当然,这需要您了解主题文本的语言,这在实践中可能很难做到”。我有一堆想要全文索引的不同语言的现有数据(支持请求)。我用它来检测语言。效果很好。 detectlanguage.com
    【解决方案2】:

    我刚刚制作了一个 Postgres 函数来测试文本语言。它并不完美,但它适用于长文本。

    CREATE OR REPLACE FUNCTION get_language(t text) RETURNS regconfig AS $$
    DECLARE
        ret regconfig;
        BEGIN
                WITH l as ( SELECT cfgname, to_tsvector(cfgname::regconfig, title) as vector, length(to_tsvector(cfgname::regconfig, title)) as len
            FROM pg_ts_config, (select t as title) as ti)
        SELECT cfgname::regconfig
        INTO ret 
        FROM l
        WHERE len=(SELECT MIN(len) FROM l)
        ORDER BY cfgname='simple' DESC, cfgname ASC
        LIMIT 1;
        RETURN ret;
        END;
    $$ LANGUAGE plpgsql;
    

    它只是为给定的文本寻找最短的 tsvector(所以它会尝试 postgres 的每个 ts 配置)。

    【讨论】:

      【解决方案3】:

      就我而言,我知道当前的语言。我有一个“语言”专栏。但如果我这样做:

      CREATE INDEX filmtraduction_tsindex ON filmtraduction USING GIN (
      to_tsvector((case when $1=''language.fr'' then ''french'' when $1=''language.es'' then ''spanish'' else ''english'' end)::regconfig, body));
      

      如果有“索引表达式中的函数必须标记为 IMMUTABLE”。

      所以我需要创建一个不可变的函数:

       CREATE FUNCTION toregconfig(text) RETURNS regconfig AS 'select (case
       when $1=''language.fr'' then ''french'' when $1=''language.es'' then
       ''spanish'' else ''english'' end)::regconfig;' LANGUAGE SQL IMMUTABLE
       RETURNS NULL ON NULL INPUT;
      

      然后我可以创建我的索引:

      CREATE INDEX filmtraduction_tsindex ON filmtraduction USING GIN (
      to_tsvector(toregconfig(language), body));
      

      我这样查询:

      select * from movietraduction where to_tsvector(toregconfig(language), body) @@ plainto_tsquery('foo');
      

      【讨论】:

        猜你喜欢
        • 2014-01-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多