Непоследовательное поведение анализатора запросов Lucene

#lucene #standardanalyzer

#lucene #standardanalyzer

Вопрос:

Следующая программа:

 import java.util.Arrays;
import java.util.List;

import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.util.Version;

public class LuceneTest {

  static final List<Character> SPECIAL_CHARS =
      Arrays.asList('\', ' ', '-', '!', '(', ')', ':', '^', '[', ']', '"', '{', '}', '~', '*', '?', '|', 'amp;');

  public static void main(String[] args) throws ParseException {
    QueryParser query = 
        new QueryParser(Version.LUCENE_31, "", new StandardAnalyzer(Version.LUCENE_31));


    for (char c : SPECIAL_CHARS) {
      System.out.println(c   " -> "   query.parse("__catch_all:foo\"   c   "bar").toString());
    }
  }

}
  

Выдает такой результат:

  -> __catch_all:foo __catch_all:bar
  -> __catch_all:foo __catch_all:bar
- -> __catch_all:foo __catch_all:bar
! -> __catch_all:foo __catch_all:bar
( -> __catch_all:foo __catch_all:bar
) -> __catch_all:foo __catch_all:bar
: -> __catch_all:foo:bar
^ -> __catch_all:foo __catch_all:bar
[ -> __catch_all:foo __catch_all:bar
] -> __catch_all:foo __catch_all:bar
" -> __catch_all:foo __catch_all:bar
{ -> __catch_all:foo __catch_all:bar
} -> __catch_all:foo __catch_all:bar
~ -> __catch_all:foo __catch_all:bar
* -> __catch_all:foo __catch_all:bar
? -> __catch_all:foo __catch_all:bar
| -> __catch_all:foo __catch_all:bar
amp; -> __catch_all:foo __catch_all:bar
  

Обратите внимание на очевидную несогласованность с : и также обратите внимание, что я экранирую специальный символ (делая точно так же, как это делает QueryParser.escape). Я ожидаю, что StandardAnalyzer удалит специальные знаки препинания из терминов запроса, и это происходит почти во всех случаях.

Причина, по которой это кажется особенно непоследовательным, заключается в том, что написание документа со StandardAnalyzer и текстом поля «foo: bar» даст мне поле с двумя терминами, foo и bar!

Второй раунд экранирования дает правильный результат, т.Е. эффективно «foo \:bar»; но почему это необходимо только для двоеточий? Почему мне нужно выполнять QueryParser.escape (QueryParser.escape(mystring)), чтобы избежать такого поведения?

Ответ №1:

Различная обработка ‘:’ является ошибкой не QueryParser, а StandardAnalyzer. На самом деле, ‘:’ — это единственный символ из вашего списка, который StandardAnalyzer не считает разделителем. Как следствие, анализ «a: b» выдал бы один токен «a: b», тогда как анализ «a’b» выдал бы два токена «a» и «b».

Вот что происходит:

Original String -> unescaped string -> tokens -> query

"foo:bar" -> "foo:bar" -> [ "foo:bar" ] -> TermQuery(__catch_all, "foo:bar")

"foo bar" -> "foo bar" -> [ "foo", "bar" ] -> TermQuery(__catch_all, "foo") OR TermQuery(__catch_all, "bar")

Комментарии:

1. Это отличная информация, спасибо. На стороне записи кажется, что StandardAnalyzer помечает строку как «foo bar», что кажется непоследовательным.

2. Что заставляет вас так думать? Можете ли вы предоставить фрагмент кода, который показывает эту проблему?