Руководство по анализаторам 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.