Перейти к основному содержимому

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

· 5 мин. чтения

1. Обзор

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

Мы кратко упомянули об анализаторах в нашем вводном туториале .

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

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

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

<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>7.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>7.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>7.4.0</version>
</dependency>

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

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

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

Анализаторы в основном состоят из токенизаторов и фильтров. Разные анализаторы состоят из разных комбинаций токенизаторов и фильтров.

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

public List<String> analyze(String text, Analyzer analyzer) throws IOException{
List<String> result = new ArrayList<String>();
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

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

4.1. Стандартный анализатор

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

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

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

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

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

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

4.2. StopAnalyzer

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

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

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

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

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

4.3. SimpleAnalyzer

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

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

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

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

4.4. Анализатор пробелов

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

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

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

4.5. Анализатор ключевых слов

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

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

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

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

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

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

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

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

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

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

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

В первом примере мы будем использовать построитель CustomAnalyzer для создания нашего анализатора из предопределенных токенизаторов и фильтров :

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

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

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

Во втором примере мы создадим тот же анализатор, расширив абстрактный класс Analyzer и переопределив метод createComponents() :

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<Document> documents = luceneIndex.searchIndex(query);
assertEquals(1, documents.size());
}

6. PerFieldAnalyzerWrapper

Наконец, мы можем назначить разные анализаторы разным полям с помощью PerFieldAnalyzerWrapper .

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

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

Мы сопоставили «заголовок» с нашим пользовательским анализатором, а «тело» — с 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<Document> 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, как создать собственный анализатор и как использовать разные анализаторы для каждого поля.

Полный исходный код можно найти на GitHub .