【问题标题】:Elasticsearch how to configure language analyzer (German) or build a custom normalizerElasticsearch 如何配置语言分析器(德语)或构建自定义规范器
【发布时间】:2016-11-09 23:55:00
【问题描述】:

我正在使用德语分析器来标记一些内容。我知道它基本上是“lowercase”、“german_stop”、“german_keywords”、“german_normalization”、“german_stemmer”的宏过滤器。

我的问题与规范化过滤器有关。这是过滤器的Elasticsearch DocumentationLucene Implementation。问题在于 ae ue 和 oe 被视为德语字母 ä,ö 和 ü,因此转换为 a,o,u。

第二个转变很好,但第一个导致的问题比它解决的要多。德语文本中通常没有真正代表ä、ü、ö的ae、ue、oe。大多数情况下,它们实际出现在外来词中,源自拉丁语或英语,例如“Aearodynamik”(空气动力学)。然后过滤器将“Ae”解释为“Ä”,然后将其转换为“A”。这会产生“arodynamik”作为令牌。通常这不是问题,因为搜索词也使用该过滤器进行了标准化。但是,如果与通配符搜索结合使用,这确实会成为一个问题:

想象一个像“FooEdit”这样的词,它会被标记为“foodit”。搜索“edit OR *edit*”(这是我在用户搜索“edit”时的正常搜索)不会产生结果,因为“edit”的“e”丢失了。因为我的内容有很多这样的词,而且人们正在搜索部分词,所以它不像看起来那么极端。

所以我的问题是有什么办法可以摆脱 'ae -> a' 转换?我的理解是这是German2 snowball algorithm 的一部分,因此可能无法更改。这是否意味着我必须摆脱整个标准化步骤,或者我可以提供我自己版本的雪球算法,我只是去掉我不喜欢的部分(没有找到任何关于如何使用自定义的文档用于标准化的雪球算法)?

干杯

汤姆

【问题讨论】:

    标签: elasticsearch lucene snowball


    【解决方案1】:

    正如您所说,德语分析器是一个结合了您列出的步骤的管道。 (Documentation)

    理论上,您可以像上面一样指定自己的分析器,然后将 German_normalization 过滤器替换为另一个。例如Pattern Replace Token Filter。我从未使用过它,但我猜它的语法等于 Char Replace Token Filter (link)。

    【讨论】:

    • 我认为问题在于通配符、模糊和正则表达式查询子句没有得到分析,因此aerody* 与“arodynamik”不匹配
    【解决方案2】:

    此转换由GermanNormalizationFilter 处理,而不是词干分析器。一个类真的不难理解(不像许多词干分析器),如果我理解正确,看起来单行改变就会得到你想要的:

    public final class CustomGermanNormalizationFilter extends TokenFilter {
      // FSM with 3 states:
      private static final int N = 0; /* ordinary state */
      private static final int V = 1; /* stops 'u' from entering umlaut state */
      private static final int U = 2; /* umlaut state, allows e-deletion */
    
      private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class);
    
      public CustomGermanNormalizationFilter(TokenStream input) {
        super(input);
      }
    
      @Override
      public boolean incrementToken() throws IOException {
        if (input.incrementToken()) {
          int state = N;
          char buffer[] = termAtt.buffer();
          int length = termAtt.length();
          for (int i = 0; i < length; i++) {
            final char c = buffer[i];
            switch(c) {
    //Removing this case should prevent e-deletion for "ae"
    //        case 'a':
              case 'o':
                state = U;
                break;
              case 'u':
                state = (state == N) ? U : V;
                break;
              case 'e':
                if (state == U)
                  length = StemmerUtil.delete(buffer, i--, length);
                state = V;
                break;
              case 'i':
              case 'q':
              case 'y':
                state = V;
                break;
              case 'ä':
                buffer[i] = 'a';
                state = V;
                break;
              case 'ö':
                buffer[i] = 'o';
                state = V;
                break;
              case 'ü': 
                buffer[i] = 'u';
                state = V;
                break;
              case 'ß':
                buffer[i++] = 's';
                buffer = termAtt.resizeBuffer(1+length);
                if (i < length)
                  System.arraycopy(buffer, i, buffer, i+1, (length-i));
                buffer[i] = 's';
                length++;
                state = N;
                break;
              default:
                state = N;
            }
          }
          termAtt.setLength(length);
          return true;
        } else {
          return false;
        }
      }
    }
    

    使用它代替german_normalization 应该可以解决问题。

    【讨论】:

    • 你说得对,在代码中改变它应该如何表现的细节并不是什么大问题。但是,即使我会对算法本身进行更改,我也看不出如何在 Elasticsearch 中使用它(我最初问题的最后一句话)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-04
    • 2018-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多