sábado, 27 de fevereiro de 2010

MongoDB

Logo Há atualmente na Computação uma onda de adoção de bancos de dados não-relacionais, comumente chamados NoSQL.

O foco principal dos bancos NoSQL atuais é a orientação a documentos, uma variação hash da orientação a objetos e alternativa aos RDBMSs, que dominaram a orientação dos gerenciadores de banco de dados por décadas.

O principal banco NoSQL é o CouchDB, DBMS do Projeto Apache baseado em Erlang, porém há uma alternativa bem mais simples e aparentemente tão poderosa quanto: MongoDB.

MongoDB combina os melhores funcionalidades de orientação a documentos, hashes e RDBMSs. É um banco de dados orientado a documentos, escalável, livre de esquema, de alto desempenho e código aberto escrito em C++.

Este artigo dá continuidade ao artigo nas Reflexões de Monte Gasppa e Giulia C. sobre a instalação do MongoDB. Aqui você poderá ver um pouquinho do uso de um banco de dados orientado a documentos.

Este artigo é baseado na documentação oficial e entende que você instalou o MongoDB segundo descrito nas Reflexões de Monte Gasppa e Giulia C..

Conexão na base de dados


Para conectar na base basta usar o comando mongo:
bash$ mongo
MongoDB shell version: 1.2.2
url: test
connecting to: test
type "help" for help
>


Caso você tenha configurado uma porta alternativa em /srv/mongodb/etc/mongodb.conf, você pode especificar isso na linha de comando do cliente:
bash$ mongo --port 27018


Você também pode se conectar a um servidor remoto, caso ele esteja habilitado para conexão externa, usando a flag --host.

Para escolher qual base de dados usar, o comando é use:
> use mydb


Autorização


Você pode se autorizar na linha de comando com as opções -u (usuário) e -p (senha), ou já do prompt do interpretador:
> use admin
> db.auth('admin', 'ra35EG/dz');


Comandos básicos


A linguagem de interação com MongoDB é Javascript, mas há alguns comandos úteis no interpretador.

Para listar comandos úteis:
> help


Listar recursos uteis da base atual:
> db.help();


Listar recursos uteis de uma coleção (levemente equivalente a tabela de um banco relacional), no exemplo foo:
> db.foo.help();


Para listar as bases instaladas:
> show dbs


Para listar as coleções da base atual:
> show collections


Listar usuários da base atual:
> show users


Criar usuário na base atual:
> db.addUser('admin', 'ra35EG/dz');


Autenticar-se:
> db.auth('admin', 'ra35EG/dz');


Listar o conteúdo de uma coleção:
> db.foo.find();


Apagar uma coleção:
> db.foo.drop();


Apagar a base de dados atual:
> db.dropDatabase();


Armazenamento de dados


Repare que o MongoDB é livre de esquema, portanto uma coleção pode conter estruturas de dados radicalmente diferentes, sempre no formato JSON.

Por exemplo, vamos inserir dois dados distintos na coleção foo da base mydb:
> use mydb
> var j = { name: 'mongo' };
> var t = { x: 3 };
> db.foo.save(j);
> db.foo.save(t);
> db.foo.find();
{ "_id" : ObjectId("4b8949d9cd50237b8573833b"), "name" : "mongo" }
{ "_id" : ObjectId("4b8949dbcd50237b8573833c"), "x" : 3 }


Você não precisa predefinir a coleção, que é criada automaticamente no salvamento da primeira estrutura. O campo _id é criado automaticamente e é a chave primária.

Para apagar o objeto j:
> j = db.foo.findOne({ name: 'mongo' });
> db.foo.remove(j);


Para apagar todos os objetos basta remover a coleção:
> db.foo.drop();


Vamos usar um pouco mais de lógica:
> for (var i=0; i<100; ++i) db.foo.save({ x: i, y: i*i });


Esse loop cria cem registros, com x variando de zero a noventa e nove e y igual a seu quadrado:
> db.foo.find();
{ "_id" : ObjectId("4b894c05cd50237b8573833e"), "x" : 0, "y" : 0 }
{ "_id" : ObjectId("4b894c05cd50237b8573833f"), "x" : 1, "y" : 1 }
{ "_id" : ObjectId("4b894c05cd50237b85738340"), "x" : 2, "y" : 4 }
{ "_id" : ObjectId("4b894c05cd50237b85738341"), "x" : 3, "y" : 9 }
{ "_id" : ObjectId("4b894c05cd50237b85738342"), "x" : 4, "y" : 16 }
{ "_id" : ObjectId("4b894c05cd50237b85738343"), "x" : 5, "y" : 25 }
{ "_id" : ObjectId("4b894c05cd50237b85738344"), "x" : 6, "y" : 36 }
{ "_id" : ObjectId("4b894c05cd50237b85738345"), "x" : 7, "y" : 49 }
{ "_id" : ObjectId("4b894c05cd50237b85738346"), "x" : 8, "y" : 64 }
{ "_id" : ObjectId("4b894c05cd50237b85738347"), "x" : 9, "y" : 81 }
{ "_id" : ObjectId("4b894c05cd50237b85738348"), "x" : 10, "y" : 100 }
{ "_id" : ObjectId("4b894c05cd50237b85738349"), "x" : 11, "y" : 121 }
{ "_id" : ObjectId("4b894c05cd50237b8573834a"), "x" : 12, "y" : 144 }
{ "_id" : ObjectId("4b894c05cd50237b8573834b"), "x" : 13, "y" : 169 }
{ "_id" : ObjectId("4b894c05cd50237b8573834c"), "x" : 14, "y" : 196 }
{ "_id" : ObjectId("4b894c05cd50237b8573834d"), "x" : 15, "y" : 225 }
{ "_id" : ObjectId("4b894c05cd50237b8573834e"), "x" : 16, "y" : 256 }
{ "_id" : ObjectId("4b894c05cd50237b8573834f"), "x" : 17, "y" : 289 }
{ "_id" : ObjectId("4b894c05cd50237b85738350"), "x" : 18, "y" : 324 }
{ "_id" : ObjectId("4b894c05cd50237b85738351"), "x" : 19, "y" : 361 }
has more


Consultas (queries)


É possível fazer consultas simples ou bem complexas assim como em modelos relacionais usando MongoDB.

Vamos obter uma contagem de objetos:
> db.foo.find().count();
100


Só os cinco primeiros objetos:
> db.foo.find().limit(5);
{ "_id" : ObjectId("4b894c05cd50237b8573833e"), "x" : 0, "y" : 0 }
{ "_id" : ObjectId("4b894c05cd50237b8573833f"), "x" : 1, "y" : 1 }
{ "_id" : ObjectId("4b894c05cd50237b85738340"), "x" : 2, "y" : 4 }
{ "_id" : ObjectId("4b894c05cd50237b85738341"), "x" : 3, "y" : 9 }


Agora os cinco seguintes:
> db.foo.find().skip(5).limit(5);
{ "_id" : ObjectId("4b894c05cd50237b85738343"), "x" : 5, "y" : 25 }
{ "_id" : ObjectId("4b894c05cd50237b85738344"), "x" : 6, "y" : 36 }
{ "_id" : ObjectId("4b894c05cd50237b85738345"), "x" : 7, "y" : 49 }
{ "_id" : ObjectId("4b894c05cd50237b85738346"), "x" : 8, "y" : 64 }
{ "_id" : ObjectId("4b894c05cd50237b85738347"), "x" : 9, "y" : 81 }


Para obter o elemento com valor de x vinte e quatro:
> var e = db.foo.findOne({ x: 24 });
> print(e.y);
576
> print(tojson(e));
{ "_id" : ObjectId("4b894c05cd50237b85738356"), "x" : 24, "y" : 576 }


Consultas avançadas


Vamos complicar um pouco…

Para obter todos os registros com x maior que cinquenta:
> db.foo.find('this.x > 50');
{ "_id" : ObjectId("4b894c05cd50237b85738371"), "x" : 51, "y" : 2601 }
{ "_id" : ObjectId("4b894c05cd50237b85738372"), "x" : 52, "y" : 2704 }
{ "_id" : ObjectId("4b894c05cd50237b85738373"), "x" : 53, "y" : 2809 }
{ "_id" : ObjectId("4b894c05cd50237b85738374"), "x" : 54, "y" : 2916 }
{ "_id" : ObjectId("4b894c05cd50237b85738375"), "x" : 55, "y" : 3025 }
{ "_id" : ObjectId("4b894c05cd50237b85738376"), "x" : 56, "y" : 3136 }
{ "_id" : ObjectId("4b894c05cd50237b85738377"), "x" : 57, "y" : 3249 }
{ "_id" : ObjectId("4b894c05cd50237b85738378"), "x" : 58, "y" : 3364 }
{ "_id" : ObjectId("4b894c05cd50237b85738379"), "x" : 59, "y" : 3481 }
{ "_id" : ObjectId("4b894c05cd50237b8573837a"), "x" : 60, "y" : 3600 }
{ "_id" : ObjectId("4b894c05cd50237b8573837b"), "x" : 61, "y" : 3721 }
{ "_id" : ObjectId("4b894c05cd50237b8573837c"), "x" : 62, "y" : 3844 }
{ "_id" : ObjectId("4b894c05cd50237b8573837d"), "x" : 63, "y" : 3969 }
{ "_id" : ObjectId("4b894c05cd50237b8573837e"), "x" : 64, "y" : 4096 }
{ "_id" : ObjectId("4b894c05cd50237b8573837f"), "x" : 65, "y" : 4225 }
{ "_id" : ObjectId("4b894c05cd50237b85738380"), "x" : 66, "y" : 4356 }
{ "_id" : ObjectId("4b894c05cd50237b85738381"), "x" : 67, "y" : 4489 }
{ "_id" : ObjectId("4b894c05cd50237b85738382"), "x" : 68, "y" : 4624 }
{ "_id" : ObjectId("4b894c05cd50237b85738383"), "x" : 69, "y" : 4761 }
{ "_id" : ObjectId("4b894c05cd50237b85738384"), "x" : 70, "y" : 4900 }
has more


Outra opção é usar o operador condicional $gt:
> db.foo.find({ x: { $gt: 50 } });


Da mesma forma outros operadores. Por exemplo, todos os elementos com x entre vinte e três e vinte e seis, inclusive:
> db.foo.find('23 <= this.x && this.x <= 26');


Ou:
> db.foo.find({ x: { $gte: 23, $lte: 26 } });


Os principais operadores são:
  • $lt – menor que
  • $gt – maior que
  • $lte – menor ou igual a
  • $gte – maior ou igual a
  • $ne – diferente de
  • $in – está em (recebe uma lista)
  • $nin – não está em
  • $mod – resto igual a (recebe uma lista onde o primeiro valor é o divisor e o segundo o resto)
  • $exists – contém ou não o atributo
  • $not – negação de uma condição


Ordenação


A ordenação é feita usando o método sort. Por exemplo, para ordenar de modo descrescente pelo atributo y:
> db.foo.find().sort({ y: -1 });


Expressões regulares

Quando há campos com valor string, é possível usar expressões regulares:
> db.foo.find({ name: /^mon.o$/i });


Índices

Quando você faz muitas consultas baseadas em um determinado atributo, é interessante criar um índice para ele. Por exemplo, se quisermos criar um índice para nosso atributo x:
> db.foo.ensureIndex({ x: 1 });


Obs.¹: o atributo _id possui índice por padrão.

Obs.²: é possível criar índices compostos.

Agregação


Não vou entrar nesse mérito, mas é possível criar agregações, como GROUP_BY de SQL. Para tanto veja a documentação oficial.

Módulos de conexão com linguagens de programação


Há módulos para várias linguagens, que podem ser encontrados na lista de APIs.

O módulo que pude e me fascinou foi para Python. Sua instalação usando easy_install foi muito simples:
bash$ sudo easy_install -U pymongo


E seu uso extremamente fácil devido à similiaridade entre dicionários de Python e JSON. A única reclamação que faço é o fato do método db.auth() do MongoDB ter sido traduzido como db.authenticate() em Python, o que me fez perder alguns segundos tentando entender por que não funcionava.

**


Espero que este artigo seja mais do que útil e incite o uso dessa intrigante ferramenta.

[]’s
Cacilhas, La Batalema
blog comments powered by Disqus