Guida agli analizzatori di Lucene

1. Panoramica

Gli analizzatori Lucene vengono utilizzati per analizzare il testo durante l'indicizzazione e la ricerca di documenti.

Abbiamo menzionato brevemente gli analizzatori nel nostro tutorial introduttivo.

In questo tutorial, discuteremo degli analizzatori di uso comune, come costruire il nostro analizzatore personalizzato e come assegnare diversi analizzatori per diversi campi del documento .

2. Dipendenze di Maven

Innanzitutto, dobbiamo aggiungere queste dipendenze al nostro 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 

L'ultima versione di Lucene può essere trovata qui.

3. Lucene Analyzer

Lucene Analyzers divide il testo in token.

Gli analizzatori sono costituiti principalmente da tokenizzatori e filtri. Diversi analizzatori sono costituiti da diverse combinazioni di tokenizzatori e filtri.

Per dimostrare la differenza tra gli analizzatori di uso comune, utilizzeremo il seguente metodo:

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; }

Questo metodo converte un dato testo in un elenco di token usando l'analizzatore dato.

4. Analizzatori comuni di Lucene

Diamo ora uno sguardo ad alcuni analizzatori Lucene comunemente usati.

4.1. StandardAnalyzer

Inizieremo con lo StandardAnalyzer che è l'analizzatore più comunemente usato:

private static final String SAMPLE_TEXT = "This is baeldung.com Lucene Analyzers test"; @Test public void whenUseStandardAnalyzer_thenAnalyzed() throws IOException { List result = analyze(SAMPLE_TEXT, new StandardAnalyzer()); assertThat(result, contains("baeldung.com", "lucene", "analyzers","test")); }

Tieni presente che StandardAnalyzer può riconoscere URL ed e-mail.

Inoltre, rimuove le parole di arresto e mette in minuscolo i gettoni generati.

4.2. StopAnalyzer

La StopAnalyzer è costituito da LetterTokenizer, LowerCaseFilter , e StopFilter:

@Test public void whenUseStopAnalyzer_thenAnalyzed() throws IOException { List result = analyze(SAMPLE_TEXT, new StopAnalyzer()); assertThat(result, contains("baeldung", "com", "lucene", "analyzers", "test")); }

In questo esempio, LetterTokenizer divide il testo in caratteri non alfabetici , mentre StopFilter rimuove le parole di interruzione dall'elenco dei token.

Tuttavia, a differenza di StandardAnalyzer , StopAnalyzer non è in grado di riconoscere gli URL.

4.3. SimpleAnalyzer

SimpleAnalyzer è composto da LetterTokenizer e LowerCaseFilter :

@Test public void whenUseSimpleAnalyzer_thenAnalyzed() throws IOException { List result = analyze(SAMPLE_TEXT, new SimpleAnalyzer()); assertThat(result, contains("this", "is", "baeldung", "com", "lucene", "analyzers", "test")); }

Qui, il SimpleAnalyzer non ha rimosso le parole di arresto. Inoltre non riconosce gli URL.

4.4. WhitespaceAnalyzer

I WhitespaceAnalyzer utilizza solo una WhitespaceTokenizer che divide il testo da caratteri di spaziatura:

@Test public void whenUseWhiteSpaceAnalyzer_thenAnalyzed() throws IOException { List result = analyze(SAMPLE_TEXT, new WhitespaceAnalyzer()); assertThat(result, contains("This", "is", "baeldung.com", "Lucene", "Analyzers", "test")); }

4.5. KeywordAnalyzer

Il KeywordAnalyzer tokenizza input in un unico token:

@Test public void whenUseKeywordAnalyzer_thenAnalyzed() throws IOException { List result = analyze(SAMPLE_TEXT, new KeywordAnalyzer()); assertThat(result, contains("This is baeldung.com Lucene Analyzers test")); }

Il KeywordAnalyzer è utile per campi come ID e zipcodes.

4.6. Analizzatori di linguaggio

Esistono anche analizzatori speciali per diverse lingue come EnglishAnalyzer , FrenchAnalyzer e SpanishAnalyzer :

@Test public void whenUseEnglishAnalyzer_thenAnalyzed() throws IOException { List result = analyze(SAMPLE_TEXT, new EnglishAnalyzer()); assertThat(result, contains("baeldung.com", "lucen", "analyz", "test")); }

Qui stiamo usando EnglishAnalyzer che consiste in StandardTokenizer , StandardFilter , EnglishPossessiveFilter , LowerCaseFilter , StopFilter e PorterStemFilter .

5. Analizzatore personalizzato

Successivamente, vediamo come costruire il nostro analizzatore personalizzato. Costruiremo lo stesso analizzatore personalizzato in due modi diversi.

Nel primo esempio, utilizzeremo il builder CustomAnalyzer per costruire il nostro analizzatore da tokenizzatori e filtri predefiniti :

@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("Baeldung.com", "Lucen", "Analyz", "Test")); }

Il nostro analizzatore è molto simile a EnglishAnalyzer , ma capitalizza invece i token.

Nel secondo esempio, costruiremo lo stesso analizzatore estendendo la classe astratta Analyzer e sovrascrivendo il metodo 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); } }

Possiamo anche creare il nostro tokenizer o filtro personalizzato e aggiungerlo al nostro analizzatore personalizzato, se necessario.

Now, let's see our custom analyzer in action – we'll use InMemoryLuceneIndex in this example:

@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

Finally, we can assign different analyzers to different fields using PerFieldAnalyzerWrapper.

First, we need to define our analyzerMap to map each analyzer to a specific field:

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

We mapped the “title” to our custom analyzer and the “body” to the EnglishAnalyzer.

Next, let's create our PerFieldAnalyzerWrapper by providing the analyzerMap and a default Analyzer:

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

Now, let's test it:

@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. Conclusion

We discussed popular Lucene Analyzers, how to build a custom analyzer and how to use a different analyzer per field.

Il codice sorgente completo può essere trovato su GitHub.