Руководство по анализаторам Lucene

Руководство по анализаторам Lucene

1. обзор

Анализаторы Lucene используются для анализа текста при индексации и поиске документов.

Мы кратко упомянули анализаторы в нашихintroductory tutorial.

В этом руководствеwe’ll discuss commonly used Analyzers, how to construct our custom analyzer and how to assign different analyzers for different document fields.

2. Maven Зависимости

Во-первых, нам нужно добавить эти зависимости в нашpom.xml:


    org.apache.lucene
    lucene-core
    7.4.0


    org.apache.lucene
    lucene-queryparser
    7.4.0


    org.apache.lucene
    lucene-analyzers-common
    7.4.0

Последнюю версию Lucene можно найтиhere.

3. Анализатор Lucene

Анализаторы Lucene разбили текст на токены.

Analyzers mainly consist of tokenizers and filters. Различные анализаторы состоят из разных комбинаций токенизаторов и фильтров.

Чтобы продемонстрировать разницу между часто используемыми анализаторами, мы воспользуемся следующим методом:

public List analyze(String text, Analyzer analyzer) throws IOException{
    List result = new ArrayList();
    TokenStream tokenStream = analyzer.tokenStream(FIELD_NAME, text);
    CharTermAttribute attr = tokenStream.addAttribute(CharTermAttribute.class);
    tokenStream.reset();
    while(tokenStream.incrementToken()) {
       result.add(attr.toString());
    }
    return result;
}

Этот метод преобразует данный текст в список токенов, используя данный анализатор.

4. Общие анализаторы люцена

Теперь давайте посмотрим на некоторые часто используемые анализаторы Lucene.

4.1. StandardAnalyzerс

Мы начнем сStandardAnalyzer, который является наиболее часто используемым анализатором:

private static final String SAMPLE_TEXT
  = "This is example.com Lucene Analyzers test";

@Test
public void whenUseStandardAnalyzer_thenAnalyzed() throws IOException {
    List result = analyze(SAMPLE_TEXT, new StandardAnalyzer());

    assertThat(result,
      contains("example.com", "lucene", "analyzers","test"));
}

Обратите внимание, чтоStandardAnalyzer может распознавать URL-адреса и электронные письма.

Кроме того, он удаляет стоп-слова и строчные буквы сгенерированных токенов.

4.2. StopAnalyzerс

StopAnalyzer состоит изLetterTokenizer, LowerCaseFilter, аStopFilter:

@Test
public void whenUseStopAnalyzer_thenAnalyzed() throws IOException {
    List result = analyze(SAMPLE_TEXT, new StopAnalyzer());

    assertThat(result,
      contains("example", "com", "lucene", "analyzers", "test"));
}

В этом примереLetterTokenizer  разделяет текст небуквенными символами, аStopFilter удаляет стоп-слова из списка токенов.

Однако, в отличие отStandardAnalyzer,StopAnalyzer не может распознавать URL-адреса.

4.3. SimpleAnalyzerс

SimpleAnalyzer состоит изLetterTokenizer и aLowerCaseFilter:

@Test
public void whenUseSimpleAnalyzer_thenAnalyzed() throws IOException {
    List result = analyze(SAMPLE_TEXT, new SimpleAnalyzer());

    assertThat(result,
      contains("this", "is", "example", "com", "lucene", "analyzers", "test"));
}

ЗдесьSimpleAnalyzer не удалял стоп-слова. Он также не распознает URL-адреса.

4.4. WhitespaceAnalyzerс

WhitespaceAnalyzer использует толькоWhitespaceTokenizer, который разбивает текст на символы пробела:

@Test
public void whenUseWhiteSpaceAnalyzer_thenAnalyzed() throws IOException {
    List result = analyze(SAMPLE_TEXT, new WhitespaceAnalyzer());

    assertThat(result,
      contains("This", "is", "example.com", "Lucene", "Analyzers", "test"));
}

4.5. KeywordAnalyzerс

KeywordAnalyzer токенизирует ввод в один токен:

@Test
public void whenUseKeywordAnalyzer_thenAnalyzed() throws IOException {
    List result = analyze(SAMPLE_TEXT, new KeywordAnalyzer());

    assertThat(result, contains("This is example.com Lucene Analyzers test"));
}

KeywordAnalyzer  полезен для таких полей, как идентификаторы и почтовые индексы.

4.6. Анализаторы языка

Также существуют специальные анализаторы для разных языков, напримерEnglishAnalyzer,FrenchAnalyzer иSpanishAnalyzer:

@Test
public void whenUseEnglishAnalyzer_thenAnalyzed() throws IOException {
    List result = analyze(SAMPLE_TEXT, new EnglishAnalyzer());

    assertThat(result, contains("example.com", "lucen", "analyz", "test"));
}

Здесь мы используемEnglishAnalyzer, который состоит изStandardTokenizer,StandardFilter,EnglishPossessiveFilter,LowerCaseFilter,StopFilter иPorterStemFilterс.

5. Пользовательский анализатор

Теперь давайте посмотрим, как создать собственный анализатор. Мы построим один и тот же пользовательский анализатор двумя разными способами.

В первом примереwe’ll use the CustomAnalyzer builder to construct our analyzer from predefined tokenizers and filters:

@Test
public void whenUseCustomAnalyzerBuilder_thenAnalyzed() throws IOException {
    Analyzer analyzer = CustomAnalyzer.builder()
      .withTokenizer("standard")
      .addTokenFilter("lowercase")
      .addTokenFilter("stop")
      .addTokenFilter("porterstem")
      .addTokenFilter("capitalization")
      .build();
    List result = analyze(SAMPLE_TEXT, analyzer);

    assertThat(result, contains("example.com", "Lucen", "Analyz", "Test"));
}

Наш анализатор очень похож наEnglishAnalyzer, но вместо этого использует заглавные буквы.

Во втором примереwe’ll build the same analyzer by extending the Analyzer abstract class and overriding the createComponents() method:

public class MyCustomAnalyzer extends Analyzer {

    @Override
    protected TokenStreamComponents createComponents(String fieldName) {
        StandardTokenizer src = new StandardTokenizer();
        TokenStream result = new StandardFilter(src);
        result = new LowerCaseFilter(result);
        result = new StopFilter(result,  StandardAnalyzer.STOP_WORDS_SET);
        result = new PorterStemFilter(result);
        result = new CapitalizationFilter(result);
        return new TokenStreamComponents(src, result);
    }
}

Мы также можем создать наш собственный токенайзер или фильтр и добавить его в наш собственный анализатор, если это необходимо.

Теперь давайте посмотрим на наш собственный анализатор в действии - в этом примере мы будем использоватьInMemoryLuceneIndex:

@Test
public void givenTermQuery_whenUseCustomAnalyzer_thenCorrect() {
    InMemoryLuceneIndex luceneIndex = new InMemoryLuceneIndex(
      new RAMDirectory(), new MyCustomAnalyzer());
    luceneIndex.indexDocument("introduction", "introduction to lucene");
    luceneIndex.indexDocument("analyzers", "guide to lucene analyzers");
    Query query = new TermQuery(new Term("body", "Introduct"));

    List documents = luceneIndex.searchIndex(query);
    assertEquals(1, documents.size());
}

6. PerFieldAnalyzerWrapperс

Наконец,we can assign different analyzers to different fields using PerFieldAnalyzerWrapper.

Во-первых, нам нужно определить нашanalyzerMap для сопоставления каждого анализатора с конкретным полем:

Map analyzerMap = new HashMap<>();
analyzerMap.put("title", new MyCustomAnalyzer());
analyzerMap.put("body", new EnglishAnalyzer());

Мы сопоставили «заголовок» нашего пользовательского анализатора и «тело» английскому анализатору.

Затем давайте создадим нашPerFieldAnalyzerWrapper, указавanalyzerMap и значение по умолчаниюAnalyzer:

PerFieldAnalyzerWrapper wrapper = new PerFieldAnalyzerWrapper(
  new StandardAnalyzer(), analyzerMap);

Теперь давайте проверим это:

@Test
public void givenTermQuery_whenUsePerFieldAnalyzerWrapper_thenCorrect() {
    InMemoryLuceneIndex luceneIndex = new InMemoryLuceneIndex(new RAMDirectory(), wrapper);
    luceneIndex.indexDocument("introduction", "introduction to lucene");
    luceneIndex.indexDocument("analyzers", "guide to lucene analyzers");

    Query query = new TermQuery(new Term("body", "introduct"));
    List documents = luceneIndex.searchIndex(query);
    assertEquals(1, documents.size());

    query = new TermQuery(new Term("title", "Introduct"));
    documents = luceneIndex.searchIndex(query);
    assertEquals(1, documents.size());
}

7. Заключение

Мы обсудили популярные анализаторы Lucene, как создать собственный анализатор и как использовать другой анализатор для каждой области.

Полный исходный код может бытьfound on GitHub.