1. Información general
Los analizadores Lucene se utilizan para analizar texto al indexar y buscar documentos.
Mencionamos brevemente los analizadores en nuestro tutorial introductorio.
En este tutorial, discutiremos los analizadores de uso común, cómo construir nuestro analizador personalizado y cómo asignar diferentes analizadores para diferentes campos de documentos .
2. Dependencias de Maven
Primero, necesitamos agregar estas dependencias a nuestro 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
La última versión de Lucene se puede encontrar aquí.
3. Analizador de Lucene
Los analizadores de Lucene dividen el texto en tokens.
Los analizadores consisten principalmente en tokenizadores y filtros. Los diferentes analizadores constan de diferentes combinaciones de tokenizadores y filtros.
Para demostrar la diferencia entre los analizadores de uso común, usaremos este método siguiente:
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; }
Este método convierte un texto dado en una lista de tokens usando el analizador dado.
4. Analizadores de Lucene comunes
Ahora, echemos un vistazo a algunos analizadores Lucene de uso común.
4.1. Analizador estándar
Comenzaremos con StandardAnalyzer, que es el analizador más utilizado:
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")); }
Tenga en cuenta que StandardAnalyzer puede reconocer URL y correos electrónicos.
Además, elimina las palabras vacías y pone en minúsculas los tokens generados.
4.2. StopAnalyzer
El StopAnalyzer consiste en LetterTokenizer, LowerCaseFilter y StopFilter:
@Test public void whenUseStopAnalyzer_thenAnalyzed() throws IOException { List result = analyze(SAMPLE_TEXT, new StopAnalyzer()); assertThat(result, contains("baeldung", "com", "lucene", "analyzers", "test")); }
En este ejemplo, LetterTokenizer divide el texto por caracteres que no son letras, mientras que StopFilter elimina las palabras vacías de la lista de símbolos .
Sin embargo, a diferencia de StandardAnalyzer , StopAnalyzer no puede reconocer las URL.
4.3. SimpleAnalyzer
SimpleAnalyzer consta de LetterTokenizer y 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")); }
Aquí, SimpleAnalyzer no eliminó las palabras vacías . Tampoco reconoce las URL.
4.4. Analizador de espacios en blanco
El WhitespaceAnalyzer usa solo un WhitespaceTokenizer que divide el texto por caracteres de espacio en blanco:
@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
El KeywordAnalyzer tokenizes de entrada en un único 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")); }
El KeywordAnalyzer es útil para campos como los identificadores y códigos postales.
4.6. Analizadores de idiomas
También hay analizadores especiales para diferentes idiomas como EnglishAnalyzer , FrenchAnalyzer y SpanishAnalyzer :
@Test public void whenUseEnglishAnalyzer_thenAnalyzed() throws IOException { List result = analyze(SAMPLE_TEXT, new EnglishAnalyzer()); assertThat(result, contains("baeldung.com", "lucen", "analyz", "test")); }
En este caso, estamos usando la EnglishAnalyzer que consiste en StandardTokenizer , StandardFilter , EnglishPossessiveFilter , LowerCaseFilter , StopFilter y PorterStemFilter .
5. Analizador personalizado
A continuación, veamos cómo construir nuestro analizador personalizado. Construiremos el mismo analizador personalizado de dos formas diferentes.
En el primer ejemplo, usaremos el constructor CustomAnalyzer para construir nuestro analizador a partir de tokenizadores y filtros predefinidos :
@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")); }
Nuestro analizador es muy similar a EnglishAnalyzer , pero en su lugar, capitaliza los tokens.
En el segundo ejemplo, crearemos el mismo analizador ampliando la clase abstracta Analyzer y anulando el método 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); } }
También podemos crear nuestro tokenizador o filtro personalizado y agregarlo a nuestro analizador personalizado si es necesario.
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.
El código fuente completo se puede encontrar en GitHub.