Há algum tempo atrás começou uma onda de NOSQL e eu a principio só me mantive observando a distancia. A questão é que como toda coisa nova acaba sendo sobreutilizada ou utilizada em funções erradas.
No caso existem na internet diversos textos sobre o Mongo, usos e etc. Nesse artigo vou abordar um uso bem simples mas que por conta das caracteristicas do mongodb o tornaram quase que uma luva.
Essas caracteristicas são schema free e documented oriented. No caso estou desenvolvendo um leitor de rss/atom/xml e que eu preciso escrever pequenos "pontos de dados" e que roda em cluster. Também ele tem diversos crawlers internos dos quais cada um tem sua configuração.
É possivel fazer utilizando mysql ou postgres ou qualquer outro, mas a simplicidade de se erguer um servidor mongo sem esquema definido e que possibilita salvar documentos json direto facilita muito o desenvolvimento. Especialmente para empresas que tem seus negocios apenas baseado em aplicações internet que não tem de fato a arquitetura final definida.
Claro que Perl, uma instancia de MongoDB e o modulo do MongoDB do cpan.
*http://search.cpan.org/~kristina/MongoDB-0.45/lib/MongoDB.pm.
Para começar estou assumindo que o programa perl está na mesma maquina em que o mongo está rodando, mas é trivial configurar para um servidor diferente.
use strict;
use warnings;
use MongoDB;
my $connection = MongoDB::Connection->new( host => 'localhost', port => 27017);
my $database = $connection->get_database('sppm');
my $collection = $database->get_collection('equinocio');
my $data = $collection->find_one({ type => "artigo" });
Além do header classico, temos a declaração do modulo do MongoDB e as strings de conexão e de escolha de database e collection. Collection é um nome bonito para o que seria uma tabela num banco de dados classico.
Note que se o database não existir no MongoDB será autovivificado, assim como a collection. Se você rodar esse exemplo $data estara vazio por motivos obvios :).
Vamos assumir que esse script roda eventualmente pegando a tabela de artigos da sppm e avisa se há novos. A Tabela de artigos tem esse schema:
artigo, data, link
Obvio que eu não gostaria de varrer todos os registros toda vez, assim como que por algum motivo valido eu não quero ou não posso alterar o banco de dados. Então eu gostaria que esse script, salvasse a data em que ele leu da ultima vez a tabela para que na proxima continuasse de onde parou.
Então depois de varrer a tabela eu gostaria de salvar somente o ultimo timestamp em que o script o fez.
my $timestamp_id = $collection->insert({
type => 'last_verified',
last_verified => '1999/12/31 23:59:59'
});
Isso insere o valor "last_verified" na collection equinocio e retorna uma id, no caso, "4f6367450be985cd5c000000". Note que ao inserir uma nova chave é gerado um novo id. Esse id é o id do conjunto de chave valor. E voce pode ter duas chaves valores iguais. Note que criamos um tipo, indicado por type. Isso é apenas para diferenciar eventuais outras entradas nessa collection.
Se executar duas vezes e consultar no console do mongo db:
> db.equinocio.find()
{
"_id" : ObjectId("4f63a04a367cf6c772000000"),
"last_verified" : "1999/12/31 23:59:59",
"type" : "last_verified"
}
{
"_id" : ObjectId("4f63a05b097b8f1b73000000"),
"last_verified" : "1999/12/31 23:59:59",
"type" : "last_verified"
}
Agora temos duas entradas para "last_verified".
No nosso caso queremos apenas salvar a data que nós varremos o banco de dados pela ultima vez. Então vamos apagar as entradas e vamos criar só uma. Quando o script for executado ele tentara primeiro recuperar last_verified e depois escrevera o resultado.
A classe que manipula Collections no Mongo é MongoDB::Collection:
*http://search.cpan.org/~kristina/MongoDB-0.45/lib/MongoDB/Collection.pm
Vamos apagar todos os type "last_verified" para começar de novo, primeiro vamos achar todos objetos "last_verified".
my $data = $collection->find({ type => 'last_verified' } );
$data é um objeto MongoDB::Cursor, um iterador para varios items numa lista de resultados. Como eu executei varias vezes temos uma coleção de timestamps repetidos...
jeferson@ps80344:~/Reader/bin$ perl mongo.pl
$VAR1 = {
'_id' => bless( {
'value' => '4f6367450be985cd5c000000'
}, 'MongoDB::OID' ),
'last_verified' => '1999/12/31 23:59:59'
};
$VAR1 = {
'_id' => bless( {
'value' => '4f6368b7279b784b6b000000'
}, 'MongoDB::OID' ),
'last_verified' => '1999/12/31 23:59:59'
};
$VAR1 = {
'_id' => bless( {
'value' => '4f636a8cd8947c5c7e000000'
}, 'MongoDB::OID' ),
'last_verified' => '1999/12/31 23:59:59'
};
Vamos apaga-los e manter somente um. A classe Cursor permite que se apague (drop) todo o collection de uma vez:
$collection->drop;
Mas queremos apagar todos menos um. De quebra aprendemos sobre o OID.
Cada documento armazenado contem um id unico, Object Id, ( vulgo OID ). Ele é um hash key para cada set de key values e é unico. Para apagar vamos iterar sobre o resultado ( Cursor ) e apagar todos menos um. Para apagar devemos passar uma query para o metodo remove. Isso significa que se para apagar sub-sets de dados semelhantes temos que recolher os ids e apaga-los individualmente.
Então temos:
my $data = $collection->find();
my @all_copies = $data->all;
pop @all_copies;
for my $object ( @all_copies ){
$collection->remove( { _id => $object->{ _id } } ) ;
}
Primeiro recuperamos todos os registros com um find() sem parametros. Salvamos todos os OID num array e removemos um com pop. Esse removido sera salvo. Depois iteramos os objetos e os removemos.
$object->{ _id } # Mongo::OID dentro do resultado do Cursor.
O cursor retorna o OID mais um hash com os keys values. Não é muito sabio usar '_id' como chave quando estiver utilizando-o.
Agora que temos apenas um documento com a ultima data, podemos utilizar para os devidos fins.
use Data::Dumper;
use DateTime;
use MongoDB;
my $connection = MongoDB::Connection->new(host => 'localhost', port => 27017 );
my $database = $connection->get_database('sppm');
my $collection = $database->get_collection('equinocio');
my $timestamp_id = $collection->find_one( { type => 'last_verified' } );
if ( $timestamp_id ) {
print $timestamp_id->{ last_verified } , " \n" ;
} else {
$timestamp_id = $collection->insert({ type => 'last_verified' , last_verified => '1999/12/31 23:59:59' });
}
O codigo vai ler o timestamp no MongoDB, e caso exista ele utilizara, ou caso contrario ele criara um hardcoded para o bug do milenio. Esse mecanismo é similar a outro muito comum, cache. Você procura se existe no cache e caso não encontre você partira para a proxima camada. No caso um select max(timestamp) na tabela de artigos.
No caso o uso mais comum para mim é criar documentos ( renderizações de paginas ) em json e salva-las no MongoDB. Mantenho uma lista indexada dos documentos e faço o retorno facil. Em geral para gerar esses documentos do banco de dados RDBMS são feitas diversas queries, e mesmo quentes na memoria ainda iriam gerar o documento final. Tenho utilizado o mongo como cache desses documentos com sucesso. Obvio que depende de varios fatores e isso não é uma bala de prata mas é meu caso de uso atual.
Outro uso é simplesmente salvar documentos, como os resultados de um web crawler para o banco de dados. Cada pagina tem seu tamanho e suas caracteristicas e ele funciona bem para salvar esse tipo de dados.
Frederico Recsky <cartas@frederico.me>