agosto 17th, 2011 § § permalink
This is the first time that I blog in english, so please correct me if I write something wrong. Suggestions are welcome too =)
CouchDB automatically generates UUIDs if none are provided. Usually, it’s recommended because some things like replication. However, we can do better for users. UUIDs are ugly, then we can create friendly urls.
CouchRest Model Slug is a simple gem to generate better urls using CouchRest Model in an easy way. I created this gem based on Mongoid Slug.
Getting Started
Add to Gemfile
gem "couchrest_model_slug", "~> 0.0.2"
A simple example
class Post < CouchRest::Model::Base
include CouchRest::Model::Slug
property :title
property :text
slug :title
end
Querying
p = Post.create(:title => "CouchDB Time To Relax")
p.to_param # => "couchdb-time-to-relax"
Post.find("couchdb-time-to-relax") # =>#<Post slug: "couchdb-time-to-relax", title: "CouchDB...
CouchRest Model Slug was made to work with or without slugged value, then it uses the id to keep things running with no problems.
Post.create(:text => "post without slug") # => #<Post slug: "", title: nil, text: "post with no slug", _id: "9fdfdd090897680de59091c8c98ff064"...
Post.find("9fdfdd090897680de59091c8c98ff064") # => #<Post slug: "", title: nil, text: "post with no slug", _id: "9fdfdd090897680de59091c8c98ff064"...
See the github page for more information https://github.com/lucasrenan/couchrest-model-slug
outubro 12th, 2010 § § permalink
Faz um certo tempo que estou sem postar por aqui, ando um tanto quanto atarefado últimamente. Mas fazendo um trabalho para a faculdade nesse fim de semana, traduzi (meio porcamente para variar hehe) um conteúdo legal do livro CouchDB the Definite Guide sobre lock. No meu ponto de vista essa questão é uma das grandes vantagens do CouchDB sobre os bancos relacionais.
No Locking
Uma tabela em um banco de dados relacional é uma estrutura de dados única. Se você quer modificar uma tabela – digamos, atualizar um registro – o sistema de banco de dados deve garantir que ninguém mais esteja tentando atualizar aquela linha e que ninguém possa ler aquele registro enquanto ele esteja sendo atualizado. A forma mais comum para lidar com isso é o que conhecemos como lock. Se multiplos clientes quiserem acessar uma tabela, o primeiro cliente seta o lock, fazendo todos os outros clientes esperarem. Quando a requisição do primeiro cliente for processada, o próximo cliente terá acesso enquanto todos os outros clientes esperam e assim por diante. Essa execução de requisições seriais, mesmo quando chegam em paralelo, disperdiçam uma quantidade significativa de poder de processamento do seu servidor. Sob carga alta, um banco de dados relacional pode gastar mais tempo tentando descobrir quem é permitido fazer o que, e em que ordem, do que fazer qualquer trabalho efetivo.
Ao invés de locks, o CouchDB utiliza MVCC – Multi-Version Concurrency Control (controle de concorrência de multi versão) para gerenciar acessos ao banco. MVCC significa que o CouchDB pode ser executado a toda velocidade, todo o tempo, mesmo sob alta carga. As solicitações são executados em paralelo, fazendo um excelente uso de cada última gota do poder de processamento que seu servidor tem para oferecer.

CouchDB Lock Free MVCC
Os documentos no CouchDB são versionados, bem como estariam em um sistema regular de controle de versões como Subversion. Se você quiser alterar um valor em um documento, você cria uma versão totalmente nova daquele documento e salva sobre o antigo.
Como isso oferece uma melhoria em relação aos locks? Considere um conjunto de requisições que querem acessar um documento. A primeira requisição lê o documento. Enquanto isso está sendo processado, a segunda requisição atualiza o documento. Desde que a segunda requisição inclua uma nova versão completa do documento, o CouchDB pode simplesmente adiciona-la ao banco sem ter que esperar pela primeira requisição de leitura terminar.
Quando uma terceira requisição quiser ler o mesmo documento, o CouchDB irá apontar para a nova versão do documento que acabou de ser escrita. Durante todo esse processo, a primeira requisição pode continuar lendo a versão original.
Uma requisição de leitura sempre verá a versão mais atualizada do seu banco no começo da requisição.
julho 19th, 2010 § § permalink
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
julho 18th, 2010 § § permalink
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.
maio 25th, 2010 § § permalink
Uma das cláusulas mais utilizadas em SQL nos bancos relacionais é a ORDER BY. Sempre que queremos ordenar o resultado de uma query por determinada coluna de uma tabela, utilizamos ORDER BY nome_da_coluna e recomenda-se que seja criado um índice para essa coluna, afim de otimizar a ordenação dos dados. O problema começa quando precisamos ordernar os resultados por vários campos da tabela, sendo que pode ficar inviável criar uma série de índices, já que essa tabela pode também sofrer um grande número de atualizações.
Uma das grandes vantagens do CouchDB (noSQL) é o poder das views (eu sempre gosto de reforçar isso). Você pode criar várias views (que resultem em várias formas) para os seus dados, sem comprometer a inserção para aquele tipo de dado (documento). Ou seja, supondo que você precise ordenar alguns documentos por 3 “campos” diferentes, basta que você crie 3 views, sendo que cada uma emita como chave o determinado “campo” em questão. Podemos exemplificar em Ruby utilizando a gem CouchRest, vamos supor um modelo de cidades:
class City < CouchRest::ExtendedDocument
use_database CouchRest.database("http://127.0.0.1:5984/order_by_couchdb")
unique_id :slug
property :slug, :read_only => true
property :name
property :state
property :population
timestamps!
#o couchrest criara as views no banco
view_by :name
view_by :state
view_by :population
#antes de salvar cria o slug que sera o id do documento
set_callback :save, :before, :generate_slug_from_name
def generate_slug_from_name
self['slug'] = name.downcase.gsub(/[^a-z0-9]/,'-').squeeze('-').gsub(/^\-|\-$/,'') if new?
end
end
Vamos inserir algumas cidades para exemplificar (eu estou utilizando Rails também)
c = City.new(:name => "Sorocaba", :state => "SP", :population => 700000)
c.save
c2 = City.new(:name => "Sao Paulo", :state => "SP", :population => 11000000)
c2.save
c3 = City.new(:name => "Rio de Janeiro", :state => "RJ", :population => 6000000)
c3.save
O CouchRest vai criar 3 views (by_name, by_state, by_population). Ordenando as cidades pelo nome
City.by_name
Repare que no banco será criada a view (função map)
function(doc) {
if ((doc['couchrest-type'] == 'City') && doc['name']) {
emit(doc['name'], null);
}
}
E o resultado será (ordenado alfabeticamente)
{"total_rows":3,"offset":0,"rows":[
{"id":"rio-de-janeiro","key":"Rio de Janeiro","value":null},
{"id":"sao-paulo","key":"Sao Paulo","value":null},
{"id":"sorocaba","key":"Sorocaba","value":null}
]}
Ordenando as cidades pelo número da população, da maior para a menor
City.by_population(:descending => true)
{"total_rows":3,"offset":0,"rows":[
{"id":"sao-paulo","key":11000000,"value":null},
{"id":"rio-de-janeiro","key":6000000,"value":null},
{"id":"sorocaba","key":600000,"value":null}
]}
Até aí tudo muito simples e fácil, podemos ordenar nossos dados de forma bem flexível. Então, vamos complicar um pouco. Vamos supor que queremos filtrar os resultados passando como parâmetro o estado, porém que as cidades sejam ordenadas pela data de criação, de forma que as mais velhas sejam mostradas primeiro. A princípio isso parece estranho, mas é muito útil por exemplo se quisermos ordernar posts, buscando por determinada categoria (queremos que sejam listados os últimos cadastrados) e também em outras situações que se precisa ordenar os resultados de forma crescente ou decrescente.
Se utilizarmos a view que emite apenas o estado como chave (by_state), o CouchDB vai ordenar as cidades pelo id (no caso o id é o nome da cidade) então teríamos as cidades do determinado estado, ordenadas alfabeticamente. Então, precisamos criar uma view que emita um array (estado e data de criação) como chave.
Acrescente na classe
view_by :state, :created_at
Será criada a seguinte view
function(doc) {
if ((doc['couchrest-type'] == 'City') && doc['state'] && doc['created_at']) {
emit([doc['state'], doc['created_at']], null);
}
}
Buscando as cidades (cadastradas primeiro) do estado SP
City.by_state_and_created_at(:startkey => ['SP'], :endkey => ['SP', {}])
Repare que a cidade de Sorocaba vem primeiro (pois foi cadastrada primeiro), se tivéssemos utilizado a view que emite como chave apenas o estado, São Paulo viria primeiro
{"total_rows":3,"offset":1,"rows":[
{"id":"sorocaba","key":["SP","2010/05/25 01:25:53 +0000"],"value":null},
{"id":"sao-paulo","key":["SP","2010/05/25 01:25:54 +0000"],"value":null}
]}
Podemos concluir que as views do CouchDB são extremamente poderosas e simples, de início parece complicado, já que estamos habituados ao SQL, porém quando se entende bem o conceito do Map/Reduce as coisas ficam bem mais fáceis. No próximo post pretendo escrever sobre agrupamentos (GROUP BY).
abril 30th, 2010 § § permalink
Sábado 15 de maio de 2010 vai rolar em São Paulo o primeiro encontro de noSQL do Brasil.
O noSQL Brasil é o primeiro encontro brasileiro que visa apresentar, promover e discutir as tecnologias “noSQL”. Para isso, serão realizadas palestras sobre as diversas abordagens noSQL com exemplos práticos e demonstrações, bem como um painel onde será discutido como e quando utilizar noSQL.
Com certeza estarei presente e já quero parabenizar os organizadores do evento por essa excelente iniciativa que vai ajudar a fortalecer a comunidade noSQL brasileira.
http://nosqlbr.com/
março 31st, 2010 § § permalink
Em ruby temos algumas gems para serem utilizadas com o CouchDB. Particularmente eu prefiro a gem CouchRest, construída pelos commiters do CouchDB (Chris Anderson é um deles). E um dos commiters do CouchRest (que vem fazendo um excelente trabalho) é o Marcos Tapajos da Improve IT e RedeParede.
Para saber mais como instalar e utilizar o CouchRest acesse a wiki no Github.
Vamos considerar a seguinte classe (Post) que herda ExtendedDocument da gem CouchRest
class Post < CouchRest::ExtendedDocument
use_database DB #constante com as infos do seu banco
property :titulo
property :conteudo
#será criado no banco uma view pelo título
view_by :titulo
end
Carregando todos os documentos com o “couchrest-type” Post
@posts = Post.all
puts @posts.inspect
Carregando um documento pelo ID
#Sem excessões caso o documento não seja encontrado
@post = Post.get("id_do_documento")
puts @post.inspect
#Será gerado uma excessão se o documento não for encontrado
@post = Post.get!("id_do_documento")
puts @post.inspect
Utilizando Views
A view criada pelo CouchRest
function(doc) {
if ((doc['couchrest-type'] == 'Post') && doc['titulo']) {
emit(doc['titulo'], null);
}
}
#Carregando todos do documentos com "couchrest-type" Post e que tenham um título
@posts = Post.by_titulo
puts "O número de posts encontrados foi #{@posts.count}"
puts
puts @posts.inspect
#Carregando todos do documentos com "couchrest-type" Post e um título específico
@posts = Post.by_titulo(:key => "Um título em específico")
puts @posts.inspect
março 30th, 2010 § § permalink
Ontem foi lançada a versão 0.11 do CouchDB que é a base antes da versão 1.0.
Algumas das melhorias foram:
Adicionado suporte a instalador Windows
Melhora nas views
Correção de bugs na construção de views
Melhorias nos cálculos de estatísticas
Melhorias de velocidade e concorrência em pesquisas
Corrigido timeout de HTTP para replicação
Melhorias no Futon UI
Melhorias nos anexos (attachments)
Melhorias na autenticação
Adicionado um manipular de reescrita de url
Adicionado um manipulador de atenticação de proxy
Adicionado habilidade de replicar documentos pelo ID
Adicionado a criação de virtual hosts
São muitas as melhorias que a equipe do CouchDB vem realizando, com certeza o projeto está bem próximo da maturidade para a versão 1.0
março 4th, 2010 § § permalink
O CouchDB assim como alguns bancos relacionais (MySQL, Postgre, Oracle etc) utiliza o modelo MVCC (Multiversion concurrency control), então quando um documento recebe uma atualização, é criada uma nova versão do documento.
Devido ao fato da criação de novas versões, seu banco de dados pode começar a crescer exponencialmente. Então você pode compactar o banco, para economizar certo espaço em disco. A compactação comprimi o arquivo de banco de dados removendo seções não utilizadas que foram criadas durante as atualizações.
A compactação pode ser feita enviando uma requisição HTTP Post para o “sub recurso” _compact do seu banco. Em caso de sucesso um status HTTP 202 é retornado.
curl -X POST http://localhost:5984/meu_banco/_compact
#=> {"ok":true}
Você pode verificar informações sobre sua base (inclusive se uma compactação está ocorrendo) enviando uma requisição HTTP Get ao banco.
curl -X GET http://localhost:5984/meu_banco
#=> {"db_name":"meu_banco", "doc_count":334, "doc_del_count":0, "update_seq":5329, "purge_seq":0, "compact_running":false, "disk_size":614498, "instance_start_time":"1267717198892433", "disk_format_version":4}
É recomendado que a compactação seja feita em um horário em que o CouchDB não receba muitas escritas.
fevereiro 11th, 2010 § § permalink
Por padrão o CouchDB não exige que seja configurado uma conta com usuário e senha, porém criar uma conta é simples.
Para criar uma conta edite o arquivo local.ini
sudo vim /usr/local/etc/couchdb/local.ini
Altere require_valid_user para true na sessão couch_httpd_auth
[couch_httpd_auth]
require_valid_user = true
Acrescente o usuário e senha na sessão admins
[admins]
admin = senha_admin
Reinicie o CouchDB
sudo /usr/local/etc/init.d/couchdb restart
O CouchDB irá criar um hash com a senha:
[admins]
admin = -hashed-c3574aef8a2969b53a8c33edf28cb538d997397c,51e3cce80f8e 809804cfcbe481263bf5
Se você estiver programando em ruby e utilizando a gem couchrest, você pode fazer a conexão assim:
CouchRest.database!("http://admin:senha_admin@127.0.0.1:5984/base_de_dados")