CouchDB Lucene – Buscas Full-text no CouchDB

O CouchDB Lucene provê a funcionalidade de realizar buscas full-text no CouchDB utilizando Lucene.

Antes de começar precisamos instalar as dependências, só lembrando que esse tutorial foi testado nas versões 10.04 e 9.10 do Ubuntu Linux, mas a princípio o procedimento de instalação para Mac deve ser parecido.
No Ubuntu 10.04 os pacotes sun-java6 foram removidos da seção Multiverse do Ubuntu archive, então você precisa adicionar o repositório Partner da Canonical.

sudo add-apt-repository "deb http://archive.canonical.com/ lucid partner"

Então você pode instalar o JDK e o JRE:

sudo apt-get install sun-java6-bin sun-java6-jre sun-java6-jdk

Para instalar o CouchDB Lucene é preciso que você tenha o Git e o Maven instalados:

sudo apt-get install git-core maven2

Finalmente para instalar o CouchDB Lucene é necessário clonar o repositório:

git clone git://github.com/rnewson/couchdb-lucene.git

No caso, hoje a versão estável é a 0.5.3, então vamos dar o build:

cd couchdb-lucene
git checkout v0.5.3
mvn

Configurando o CouchDB
Agora vamos configurar o CouchDB editando o arquivo de configuração que geralmente está localizado em /usr/local/etc/couchdb/local.ini

[couchdb]
os_process_timeout=60000 ; aumenta o timeout para 5 segundos

[external]
fti=/path/do/python /path/para/couchdb-lucene/tools/couchdb-external-hook.py ; geralmente /usr/bin/python

[httpd_db_handlers]
_fti = {couch_httpd_external, handle_external_req, <<"fti">>}

Agora vamos criar uma view de exemplo, supondo que temos um design document de Posts

{
    "_id": "_design/Post",
    "fulltext": {
        "by_title": {
            "index": "
            function(doc) { 
                if(doc['couchrest-type'] == 'Post') {
                    var ret=new Document(); 
                    ret.add(doc.title); 
                    return ret; 
                }
            }"
        }
    }
}

Pronto, você pode realizar buscas full-text no CouchDB utilizando Lucene:

curl http://localhost:5984/nome_banco/_fti/_design/Post/by_title?q=titulo_do_post

Group by no Couchdb

No post anterior sobre Order by no CouchDB eu mencionei que o próximo post seria sobre Group by, como prometido vou tentar abordar essa questão apresentando um exemplo simples de como agrupar resultados de views.

Para facilitar (como sempre) estou utilizando Ruby on Rails + CouchRest, porém vou postar aqui só a parte relevante do código para entendermos o exemplo. Então vamos considerar nosso model Post:

class Post < CouchRest::ExtendedDocument
  use_database CouchRest.database("http://127.0.0.1:5984/group_couchdb")

  property :titulo
  property :conteudo
  property :autor_id

  timestamps!

  view_by :data,
  :map => "
    function(doc) {
      if ((doc['couchrest-type'] == 'Post')) {
        data = doc.created_at;
        ano = parseInt(data.substr(0, 4));
        mes = parseInt(data.substr(5, 2), 10);
        dia = parseInt(data.substr(8, 2), 10);

        emit([ano, mes, dia], 1);
      }
    }",
  :reduce => "_count"

end

Detalhe para a view data que emite como chave um array contendo o ano, mes e dia do post e para cada valor (dependendo do agrupamento) emite o valor 1 para a função reduce (que conta quantos valores foram emitidos para cada chave em questão).

Não vou postar o código de inserção dos posts, você pode inserir alguns posts na sua base para realizar os testes.
Primeiro quero contar o número de posts realizados por dia (isso é possível pois a função map gera o array com dia, mês e ano, assim a função reduce soma os valores para as chaves).

curl http://127.0.0.1:5984/group_couchdb/_design/Post/_view/by_data?group=true

Acessando a url acima você vai ver um resultado parecido com esse:

{"rows":[
  {"key":[2010,6,10],"value":1},
  {"key":[2010,7,17],"value":1},
  {"key":[2010,7,18],"value":2}
]}

Ou seja, no dia 10/06/2010 foi cadastrado um post, assim como no dia 17/07/2010, já no dia 18/07/2010 foram cadastrados dois posts (esses são os posts que eu tenho cadastrado na minha base).

Agora vamos supor que queremos contar quantos posts foram cadastrados por ano. Isso é muito simples utilizando a view que já temos, o CouchDB aceita um parâmetro chamado group_level. Cada posição do array que foi emitida é um “level”, então podemos agrupar os valores por cada posição do array. Se passarmos group_level=2 serão emitidos como chave o ano e o mês, group_level=1 apenas o mês, etc.

Post.by_data(:reduce => true, :group_level=>1)

A url seria parecida com essa:


http://127.0.0.1:5984/group_couchdb/_design/Post/_view/by_data?group=true&group_level=1

E o resultado seria quatro posts para o ano de 2010

{"rows":[
  {"key":[2010],"value":4}
]}

Podemos concluir que agrupar valores no CouchDB não é tão difícil quanto parece, acredito que é uma questão da modelagem dos dados e da forma como utilizamos as views.