Campos customizáveis com Rails, MongoDB e Mongoid

Em algumas situações queremos ter um modelo onde alguns campos podem ser customizáveis pelo cliente, vou dar um exemplo de como implementar isso com Rails, MongoDB e Mongoid

Onde guardar os campos customizáveis

A primeira idéia seria se aproveitar do fato do Mongo não ter uma estrutura definida para as collections (tabelas) e criar os campos customizáveis dinamicamente no modelo. No mongoid existe a configuração allow_dynamic_fields que permite que sejam inseridos campos dinamicos no modelo. Mas nessa solução temos alguns problemas, entre eles:
  1. perdemos o controle dos campos customizados que foram criados, pois teriamos que varrer cada documento e ver quais campos existem
  2. nao temos o controle do tipo de campo customizado que foi criado, por exemplo não saberíamos se ele deve se comportar como uma string, data ou inteiro
  3. temos que nos preocupar em proteger os campos default, por exemplo state, id, created_at, etc.. e não permitir que o usuário consiga apagar ou editar esses campos
  4. teríamos que implementar getters e setters no modelo para esses campos, pois não conseguiríamos definir os fields que é o jeito padrão do mongoid
A sugestão é criar um campo no modelo chamado custom_fields (ou algo parecido) e externamente um cadastro de campos customizáveis, desse jeito temos algumas vantagens:
  1. temos a lista dos campos customizáveis que existem e seus tipos (data, string, inteiro, etc..)
  2. só precisamos liberar o campo custom_fields para alteração e os campos default podem ficar protegidos no modelo
  3. podemos fazer buscas tipadas, por exemplo se um campo for do tipo data, buscar por um range de datas
Utilizando mongoid, a implementação ficaria desse jeito:

field :custom_fields, type: Hash   , default: {}

Desse jeito, no mongodb, os campos customizáveis ficam armazenados do mesmo jeito que se fossem embed documents

Criando índice de campos customizáveis

Porém quando implementamos essa solução, tivemos o receio de que não conseguiriamos criar um índice no mongo para otimizar as buscas por esses campos customizáveis, aí veio a grande surpresa do Mongo!

O Mongo permite que sejam criados índices desses campos customizáveis mesmo que alguns documentos dessa collection não tenham esse campo, e melhor ainda, mesmo que nenhum documento tenha esse campo customizável. Desse jeito podemos por exemplo, criar um índice em "custom_fields.empresa" mesmo que esse campo ainda não exista, mas quando ele for criado já terá um índice!

No exemplo abaixo temos uma busca feita no mongo sem o índice e logo após a criação do índice e a nova busca. Podemos ver que na primeira, foram percorridos os 1002 documentos e na 2a, somente os 482 do sexo "fem"



*Créditos também ao @marciotrindade e Claudio Bruno Martins por esse conteúdo


0 comments: