<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Lucas Teixeira &#187; Lucene</title>
	<atom:link href="http://lucastex.com.br/category/lucene/feed/" rel="self" type="application/rss+xml" />
	<link>http://lucastex.com.br</link>
	<description>@lucastex</description>
	<lastBuildDate>Mon, 19 Jul 2010 11:39:24 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Criando um Transformer customizado para o Solr</title>
		<link>http://lucastex.com.br/2009/10/26/criando-um-transformer-customizado-para-o-solr/</link>
		<comments>http://lucastex.com.br/2009/10/26/criando-um-transformer-customizado-para-o-solr/#comments</comments>
		<pubDate>Mon, 26 Oct 2009 20:59:35 +0000</pubDate>
		<dc:creator>Lucas Teixeira</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Lucene]]></category>
		<category><![CDATA[Solr]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[dataimporthandler]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[indexação]]></category>

		<guid isPermaLink="false">http://lucastex.com.br/?p=78</guid>
		<description><![CDATA[

Solr é um framework uma ferramenta para a construção de servidores de indexação e busca on top of índices lucene.
Possui todas as funcionalidades que existem em um sistema moderno de busca, como paginação, highlight de campos, flexão das palavras e etc. Na minha opinião, de tudo que trabalhei nos últimos anos, sem dúvida, formam o [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_blue" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Flucastex.com.br%252F2009%252F10%252F26%252Fcriando-um-transformer-customizado-para-o-solr%252F%22%2C%20%22shorturl%22%3A%20%22http%3A%2F%2Fbit.ly%2F6Mg05M%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Criando%20um%20Transformer%20customizado%20para%20o%20Solr%22%20%7D);"></div>
<p><a href="http://lucene.apache.org/solr" target="_blank">Solr</a> é <span style="text-decoration: line-through;">um framework</span> uma ferramenta para a construção de servidores de indexação e busca <em>on top of</em> índices <a href="http://lucene.apache.org" target="_blank">lucene</a>.</p>
<p>Possui todas as funcionalidades que existem em um sistema moderno de busca, como paginação, highlight de campos, flexão das palavras e etc. Na minha opinião, de tudo que trabalhei nos últimos anos, sem dúvida, formam o conjunto mais poderoso de frameworks.</p>
<p>O Solr abstrai a camada Java do Lucene e disponibiliza uma interface http para a execução da consulta, fazendo com que fique <strong>muito</strong> fácil a integração com sistemas não-java.</p>
<p>Um dos recursos avançados de indexação com Solr são os <a href="http://wiki.apache.org/solr/DataImportHandler#Transformer" target="_blank">Transformers</a> a serem usados juntamente com o <a href="http://wiki.apache.org/solr/DataImportHandler">DataImportHandler</a>. Com eles, você pode processar o texto antes de ser indexado. (Um outro post sobre configuração básica de Solr e de DIH deve vir em breve).</p>
<p>Semana passada, precisei de um transformer que pudesse retirar todas as tags HTML do texto a ser indexado (malditos editores rich text em javascript). Como o projeto aqui está usando Solr 1.3 não tive a possibilidade de usar o <a href="http://wiki.apache.org/solr/DataImportHandler#HTMLStripTransformer" target="_blank">HTMLStripTransformer</a> que virá no Solr 1.4 (e está enroscado pra sair). Então acabei tendo que criar um semelhante para a funcionalidade.</p>
<p>A criação de transformers é muito simples (imho, simplista até demais, fazendo com que as vezes, o desenvolvedor se perca durante a implementação). Deve ser desenvolvida uma classe simples, que implementa um método com a seguinte assinatura:</p>
<pre class="brush: java;">public Map&lt;String, Object&gt; transformRow(Map&lt;String, Object&gt; aRow, Context context)</pre>
<p>Quando disse que é simplista demais, é pelo fato de que, (imho novamente), condições restritivas como esta deveriam estar documentadas em interfaces, mas não, quando necessário, o método é chamado via reflection, <a href="http://lucastex.com.br/wp-content/uploads/2009/10/1058unc.jpg" target="_blank">tendo certeza que ele está implementado</a>.</p>
<p>No meu caso, eu precisaria além de implementar o Transformer, deixar explícito no arquivo de configuração do DIH (<em>data-config.xml</em>) quais os campos que deveriam receber o tratamento desta tag, fiz isso adicionando a tag <strong>removeHtml</strong> no nó de cada campo (não, este xml não passa por validação).</p>
<pre class="brush: xml;">&lt;field name=&quot;txt&quot; column=&quot;texto&quot; removeHtml=&quot;true&quot; /&gt;</pre>
<p>Com isto, estamos deixando claro para o DIH que o valor que retornar da query na coluna <strong>texto</strong> será repassado ao Solr para indexação no field <strong>txt</strong> (definido no <em>schema.xml</em>) e terá processamento do meu HtmlTransformer. Ah, não podemos esquecer da declaração do transformer para a entidade:</p>
<pre class="brush: xml;">&lt;entity name=&quot;posts&quot; transformer=&quot;br.com.lucastex.dih.transformer.HtmlTransformer&quot; /&gt;</pre>
<p>Pronto, a classe está agora carregada e pronta para ser chamada quando um campo necessitar. Agora, vamos a implementação da classse. No meu caso, ela ficou bem trivial, pois o campo em questão nunca será multivalorado (ou seja, não preciso tratar a possibilidade do argumento ser um List, mas apenas uma String.</p>
<pre class="brush: java; gutter: true;">package br.com.lucastex.dih.transformer;

import java.util.Map;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.solr.handler.dataimport.Context;
import org.apache.solr.handler.dataimport.DataImporter;
import org.apache.solr.handler.dataimport.RegexTransformer;
import org.apache.solr.handler.dataimport.Transformer;

public class HtmlTransformer extends Transformer {

  public static final String TAG = &quot;removeHtml&quot;;

  public Map&lt;String, Object&gt; transformRow(Map&lt;String, Object&gt; aRow, Context context) {

    for (Map&lt;String, String&gt; map : context.getAllEntityFields()) {
      if (!Boolean.TRUE.toString().equals(map.get(TAG)))
        continue;
      String columnName = map.get(DataImporter.COLUMN);
      String sourceColumnName = map.get(RegexTransformer.SRC_COL_NAME);
      if (sourceColumnName == null)
        sourceColumnName = columnName;
      Object value = aRow.get(sourceColumnName);
      if (value instanceof String) {
        String result = stripHtml((String) value);
        aRow.put(columnName, result);
      }
    }
    return aRow;
  }

  private String stripHtml(String text) {
    try {
      String cleanText = StringEscapeUtils.unescapeHtml(text.replaceAll(&quot;\\&lt;.*?\\&gt;&quot;, &quot; &quot;));
      cleanText = cleanText.trim().replaceAll(&quot;\n&quot;,&quot; &quot;);
      cleanText = cleanText.replaceAll(&quot;\t&quot;,&quot; &quot;);
      cleanText = cleanText.replaceAll(&quot;[\\s]+&quot;, &quot; &quot;);
      return cleanText;
    } catch (Exception e) {
      //trata exception de acordo com seu contexto solr
    }
    return text;
  }
}</pre>
<p>Bom, o código é bem auto-explicativo. Basicamente recebe o além do contexto em execução do Solr/DIH, um Map de String e Object, que contém todos os campos e valores da linha que está sendo analisada/indexada no momento. Vale lembrar que este modelo de passagem de parâmetro é feito <strong>exatamente</strong> como descrito no <a href="http://desciclo.pedia.ws/wiki/Gambi_Design_Patterns#BCDR_Pattern">Pattern BCDR</a>.</p>
<p>Enfim, as linhas 18 e 19 garantem que o código só será executado para campos que tenham declarado a tag <strong>removeHtml=true</strong> como descrito anteriormente.</p>
<p>A linha 24 recupera o valor original do campo, ou seja, o que foi retornado pela query no banco de dados, e nas linhas 26 e 27 o resultado é tratado (através da chamada para o método stripHtml) e devolvida ao Map.</p>
<p>Ou seja, este é o momento onde você tem acesso a todos os campos que estão vindo do banco antes de irem ao índice. É nesta hora que você duplica seus campos, faz tratamentos, aplica padrões e templates, enfim trabalha a informação bruta original do banco de dados.</p>
<p>Já o método de remoção de tags HTML, não é nada além de um conjunto de regexes encadeadas  com uma ajudinha do StringEscapeUtils (<a href="http://lucastex.com.br/2009/10/25/maravilhas-do-groovy-a-propriedade-metaclass/" target="_blank">que saudade deste post</a>) do Commons-lang.</p>
<p>Simples como</p>

]]></content:encoded>
			<wfw:commentRss>http://lucastex.com.br/2009/10/26/criando-um-transformer-customizado-para-o-solr/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
	</channel>
</rss>
