Lucene Search
Para quem precisa disponibilizar um mecanismo de busca para um site, que permita indexação de conteúdos em diversos formatos (PDF, ODT, DOC, registros de banco de dados, etc.), a solução Lucene Search, da Apache Foundation (http://lucene.apache.org/) supre eficientemente essa demanda.
Nesse post, pretendo mostrar, de forma sucinta, como o recurso de indexação e busca pode ser implementado com Lucene Search, usando um framework JEE, no caso, o JBoss Seam (http://seamframework.org), ou outro qualquer de interesse do desenvolvedor.
Primeiro, é preciso permitir que as suas entidades (JPA ou Hibernate) tenham a capacidade de gerar informações de índice (metadados), que serão gravados em disco, a cada vez que um dado for alterado no banco. Haverá, é lógico, uma preocupação sobre quais campos da entidade (digamos, o tipo Pessoa será indexado unicamente pelo campo str_nome da tabela pessoa) serão indexados. Para configurar os eventos do Lucene Search, de forma que eventos de escrita no banco atualizem os "listenes" do Lucene, é preciso configurar o arquivo "ejb-jar.xml" ou o "hibernate.cfg.xml", com os seguintes itens de configuração:
<property name="hibernate.search.default.indexBase"> value="./lucene_indexes/"/>
<property name="hibernate.ejb.event.post-insert" value="org.hibernate.search.event.FullTextIndexEventListener"/>
<property name="hibernate.ejb.event.post-update" value="org.hibernate.search.event.FullTextIndexEventListener"/>
<property name="hibernate.ejb.event.post-delete" value="org.hibernate.search.event.FullTextIndexEventListener"/>
No caso, a propriedade "hibernate.search.default.indexBase" configura o diretório onde serão gravados os índices. É permitido usar diretórios relativos, como no caso acima, que configura o diretório de índices para o caminho lucene_indexes, que será referenciado dentro do path atual configurado para esse contexto. Dependendo do servidor de aplicação, o diretório "." pode referenciar o diretório server, do diretório padrão de instalação do JBoss, ou o server/default/deploy. Caso não exista o diretório acima, será automaticamente criado (criará o diretório server/default/deploy/lucene_indexes).
Outro passo importante é copiar as bibliotecas do Lucene e do Hibernate Search (que serão adicionados no arquivo MANIFEST.MF, no diretório META-INF):
lucene-core.jar
hibernate-search.jar
hibernate-commons-annotations.jar
hibernate-annotations.jar
lucene-highlighter-2.1.0.jar
Após isso, é necessário inserir as anotações específicas do Lucene e do Hibernate Search. A primeira indicará, na declaração da sua classe POJO (JPA/Hibernate) que ela será indexada:
@EntityÉ preciso marcar o campo da entidade que representa o índice. Na verdade, qualquer campo único pode ser usado. A anotação DocumentId apenas ajudará o Lucene a garantir a unicidade dos registros:
@Indexed
@Table(name = "relator", schema = "juris")
public class Relator implements java.io.Serializable {
...
@Id
@DocumentId
@Column(name = "id_relator", unique = true, nullable = false)
public short getIdRelator() {
return this.idRelator;
}
Dentro da definição dessa entidade, agora é necessário especificar quais campos são indexados, e como eles serão indexados:
@Column(name = "str_nome_relator", length = 500)Nesse ponto, cabem algumas explicações: o parâmetro index permite dizer se o campo será quebrado em tokens (trechos de caracteres), ou será tratado como uma sequência única. No caso, por ser um campo String composto por nome e sobrenome do relator, é aconselhável usar a constante Index.TOKENIZED. O parâmetro store diz se o campo será armazenado em arquivo, e pode ser visualizado através da ferramenta Luke (http://code.google.com/p/luke/). Essa ferramenta, por sinal, é muito útil para estudar a forma com que o Lucene gera os índices em arquivo, permitindo simular buscas usando diferentes analizadores semânticos de linguagens.
@Length(max = 500)
@Field(index=Index.TOKENIZED,store=Store.YES,
analyzer= @Analyzer(impl = BrazilianAnalyzer.class))
public String getStrNomeRelator() {
return this.strNomeRelator;
}
O outro parâmetro (analyzer) permite definir um analizador semântico diferente do Inglês. No caso, existe um BrazilianAnalyzer, e isso é muito importante, pois o tratamento sobre caracteres acentuados, por exemplo, é viabilizado através dos analizadores. Caso um analizador adequado não seja usado, internamente o Lucene pode ignorar caracteres acentuados, por exemplo, como se fossem símbolos inválidos. Ademais, o analizador específico para a linguagem que você utiliza para armazenar dados permite que uma busca por "Abraão" ou "Abraao" retorne o mesmo resultado. O recurso de internacionalização dos Analyzers permite ignorar detalhes de acentuação, melhorando a qualidade da busca.
Agora, já é possível fazer uma busca, usando o trecho a seguir:
try
{
QueryParser parser =
new MultiFieldQueryParser( new String[] {
"strNomeRelator"
},
new BrazilianAnalyzer() );
Query luceneQuery = parser.parse(nome_relator);
FullTextQuery ftq = entityManager.createFullTextQuery(luceneQuery
,Relator.class);
} catch (ParseException exc) {
}
Na chamado a classe MultiFieldQueryParser, nós passaremos como parâmetro: o campo que foi indexado, e que será buscado agora; e o analizador utilizado (BrazilianAnalyzer). Normalmente, pode-se passar null para o analyzer, pois ele já foi configurado na entidade Relator. Dessa forma, nós ainda não criamos a Query Lucene, apenas criamos um "criador de Queries Lucene". A seguir, nós chamamos o método parse, herdado da classe ancestral QueryParser, passando como parâmetro o valor que se quer buscar (por exemplo, a variável String nome_relator poderia conter "José", pois queríamos buscar por José na base). Com a query em mão, criaremos a FullTextQuery, essa sim que fará a busca propriamente dita na base de índices.
Para retornar a lista de registros encontrados, devemos chamar:
List<relator> resultPrimary = ftq.getResultList();
No próximo post, veremos como executar outras funções, como: marcar os hits encontrados, procurar em múltiplas tabelas, etc.
Marcadores: depth-first search, full text search engine, hibernate, java, java enterprise, jboss seam, jee, lucene