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).