Esses dias trabalhei firme no meu mapeador para MongoDB, o MongoParadigm. O código dele, como sempre, está disponível no GitHub. Atualmente estou me esforçando para integrar ele com Rails, e depois disso tudo pretendo finalmente implementar o “has :many” e o “belongs_to”. Pensei como seriam essas associações, e percebi que elas era a parte menos importante do MongoDB.

Isso porque eu acho que estou entendendo o que exatamente é uma base de dados orientada a “documentos”, finalmente – trabalhando na UFABC, é fácil de ver esse tipo de coisa: O registro de um aluno é um documento (no caso, de uma pessoa). O histórico do aluno é outro documento – e essa é a parte interessante, o histórico é um documento que pertence a um aluno, e não o contrário. Isso deve ficar bem documentado no MongoDB, porque apesar de não parecer, as bases de dados relacionais levam a gente a pensar de forma não-natural. Por exemplo, quando você vai armazenar um histórico de notas de um aluno em uma base relacional, normalmente você não armazena UM histórico, e sim um monte de registros que estão ligadas a um aluno por uma chave. Agora, é perfeitamente simples de entender porque o MongoDB não implementa trasações – afinal, no caso dele, se fosse necessária uma alteração se está mexendo em UM documento, e não em 20 registros, por exemplo.

Ainda tenho algumas dúvidas sobre como implementar tudo isso (e também, como ligar isso ao Rails – formulários deveriam ser revistos, porque todos sabemos que o Rails nunca pensou, por motivos óbvios, em atributos multi-valorados). Por exemplo, não há saída fácil para cadastrar uma estrutura como a que segue, no Rails:

{
  nome_disciplina: "Linguagem de Programação",
  horarios: [
    { dia_semana: "segunda", inicio: "10:00", fim: "12:00" },
    { dia_semana: "terça", inicio: "08:00", fim: "09:00" },
  ]
}

Isso, porque o Rails não prevê uma maneira de fazer os horários, por exemplo. Nomear os “input fields” como horarios[][dia_semana] às vezes dá bagunça, e não vejo uma maneira fácil de integrar com o “form_for”, por exemplo. Além disso, tem as validações: eu preciso de alguma forma dizer que a validação é da seguinte maneira: se o Array “horários” for vazio, dá erro. Agora, se ele tiver elementos, deve iterar nesses elementos e ver se o primeiro elemento possui “dia_semana” válido, “início” válido… e também não vejo uma maneira fácil de fazer isso.

Mas acho que esse é o menor dos problemas. O que eu preciso fazer é dar uma maneira de integrar o MongoParadigm com o Rails – Fixtures, Migrations (embora ele seja Schemaless, não significa que não seja possível fazer Migrations – bem ou mal, acredito que existirão momentos não muito raros em que precisaremos transformar um Documento que foi pensado de um jeito em um outro formato), e até mesmo um rake db:fixtures:load ajudaria bastante. Depois disso, associações – isso porque, é tão simples esse modelo que na verdade não vejo muito uso para associações. Por exemplo:

Hoje temos: Aluno pertence a Pessoa, que tem muitos Perfis através de PerfisPessoa, que tem muitas Diretivas de Acesso através do DiretivasPerfil. Só nesse caso, é uma associação 1 para 1, e duas associações Muitos para Muitos. Se eu precisar de uma nova associação, provavelmente vou fazer da seguinte forma: Uma pessoa (que contém aluno) tem muitos Perfis (a foreign-key, digamos assim, fica guardada no próprio documento de Pessoas) e o Perfil possui muitas Diretivas (de novo, o Foreign-key fica em Perfil). Eu elimino 3 tabelas nesse caso, e ainda de graça ganho algumas vantagens que eu não tenho no meu modelo atual: eu posso buscar quais Perfis possuem duas Diretivas específicas, por exemplo. A busca é simples, portanto dá pra viver sem ela por um tempo.

E principalmente – o MongoParadigm funciona como se fosse um Hash do Ruby. Na verdade, é mais ou menos uma mistura entre o Hash e o OpenStruct, mas menos flexível que o segundo e mais flexível que o primeiro. Para todos os efeitos, é um Hash normal, mas tem o “save” e o “update_attributes” que todos os railers amam. Também é bom tomar um cuidado especial com o “update_attributes!”, porque ao contrário do Rails, ele não é tão inofensivo. Por exemplo:

class Pessoa < MongoParadigm::Document
end

eu = Pessoa.create :nome => 'Fulano', :idade => 30
eu.update_attributes :idade => 10
p eu #Retorna Pessoa: { :nome => 'Fulano', :idade => 10 }

eu.update_attributes! :idade => 10
p eu #Retorna Pessoa: { :idade => 10 }

Ou seja, o segundo caso basicamente apaga todos os outros atributos que você já tinha cadastrado. Acho importante esse caso porque acredito que será útil em alguns registros, mas basta lembrar que não existe (ainda – não sei se definitivamente, mas para falar a verdade não vejo porque incluir) algo que funcione como o “save!” ou o “create!”, ou mesmo o “update_attributes!” do Rails. O único que retorna uma exceção é o “get”, que seria um equivalente ao “find” do Rails (por ID). Mas mesmo o “find” é diferente porque ele busca de uma maneira mais “MongoDB” e menos “SQL”.

Agora, porque essas diferenças? Simples – porque MongoDB não é SQL. Se o MongoDB trabalha bem com Hashes, e o Ruby trabalha bem com Hashes, porque transformar isso em um “registro”, com atributos, foreign-keys, “embedded documents” e outras terminologias quando o documento na verdade é um Hash? Para mim isso não fazia o menor sentido, e por isso comecei esse mapeador. Ele ainda não foi transformado em Gem, mas o código já está bem estável e já pode ser usado em muitos exemplos – embora ainda tenha algumas “arestas” que eu pretendo aparar com a integração com Rails.

Independente de usarem este ou outro mapeador, vale dizer que o MongoDB é um projeto interessante que merece mais atenção do que está tendo – a idéia de MapReduce do CouchDB é bem interessante, e eles pegaram carona legal com o Google (que é um grande defensor deste algoritmo) mas as buscas do Mongo ainda são insuperáveis. Isto, claro, na minha opinião.