Tudo o que você precisa saber

Christiano Anderson
christiano@christiano.me
http://christiano.me
Twitter: @dump
Mongo?
●

Sim, em muitos idiomas pode ser um termo
pejorativo, mas a origem vem de:

Humongous
“Gigantesco”
História
●

Foi criado pelos fundadores da Doubleclick;

●

10gen foi fundada em 2007;

●

A ideia inicial era fazer um produto semelhante ao
Google App Engine;
Alta curva de crescimento

Contribuições ao core do MongoDB
Inúmeras contribuições
●

Em pouco tempo, muitos projetos novos:
–

MongoEngine;

–

Mongoose;

–

Pymongo;

–

MongoKit;

–

MongoMapper ….
Grandes players utilizando
●

Foursquare;

●

Github;

●

EA Games;

●

Entre diversos outros
–

No Brasil:
●
●
●

EasyTaxi;
Globo.com;
IG;
Um pouco de conceitos...
●

●

●

NoSQL: O termo foi criado por Carlo Strozzi e Eric
Evans como referência a um tipo de armazenamento
de dados;
Nunca, mas nunca está relacionado a ódio ao
modelo SQL, pelo contrário, podem até trabalhar em
conjunto;
O termo NoREL e Não Relacional também é
bastante utilizado
Por que usar NoSQL?
●

Novos paradigmas (nem tão novos assim);

●

Funcionalidades;

●

Escalabilidade;

●

Performance;

●

Não ficar preso a modelagem;
Volume de dados
●

Grande volume é relativo, o que você considera
grande?
–

Dados que crescem exponencialmente;

–

Agregam muitos valores dinamicamente;

–

Não precisam de modelagem;
Considere uso de MongoDB se...
●

Está usando muito cache em sua aplicação;

●

Os dados estão crescendo de forma exponencial;

●

Precisa de processamento em tempo real;

●

Gosta de desenvolvimento ágil;

●

Sua aplicação é “beta perpétua”;

●

Tem dificuldade para trabalhar com modelo relacional;

●

Usa muito “join” na sua aplicação relacional;
Iniciando com MongoDB
●

●

●

Sua distribuição GNU/Linux deve possuir pacotes
prontos;
No site da MongoDB, possível baixar binários para
outros sistemas operacionais;
A instalação é bem simples, a configuração padrão
do MongoDB já atende quase todos os cenários;
Pode substituir o banco relacional?
●

●

●

Até pode, mas é uma questão de arquitetura e
escolhas;
Uma aplicação pode usar MongoDB e banco
relacional;
Tudo vai depender da sua arquitetura;
Suporte a linguagens de programação
●

●

Praticamente todas as linguagens de programação
possuem suporte (driver) para MongoDB;
A lógica é bem parecida, o MongoDB tenta manter o
máximo de padrão;
Recursos animais!
●

Busca textual (Full Text Search);

●

Aggregation framework;

●

Índices espaciais (geográficos);

●

Sharding;

●

Replica Set;
Busca textual
●

Possui suporte a português do Brasil;

●

Stemming;

●

Stopwords;
Stemming
●

Se a frase abaixo estiver indexada como FTS:
“Enquanto houver vontade de lutar, haverá
esperança de vencer”

●

Se houver uma busca pela palavra “vencendo”, a
mesma será exibida no resultado de busca.
Interface em JavaScript
●

O MongoShell é baseado em JavaScript, oferece
toda flexibilidade para gerenciar o banco de dados e
executar operações administrativas
Nomenclaturas
Banco Relacional

MongoDB

Base de dados

-->

Base de Dados

Tabela

-->

Coleção

Registro

-->

Documento

Índice

-->

Índice

Join

-->

Documento embarcado

Foreign key

-->

Referência
Modelo de documento
{'nome':'Christiano',
'sobrenome':'Anderson',
'email':'anderson@nodeware.com.br',
'nota': 10}
Realizando operações via MongoShell
●

●

O MongoDB é implícito, não existe necessidade de
criar toda estrutura do banco de dados antes;
O MongoShell é uma ótima forma de aprendizado!
MongoShell
anderson@endor:~$ mongo
MongoDB shell version: 2.4.6
connecting to: test
> a = 10
10
> b = 30
30
>a<b
true
>b<a
false
Vamos lá...
anderson@endor:~$ mongo
MongoDB shell version: 2.4.6
connecting to: test
> use latinoware
switched to db latinoware
>

Nesse ponto, o banco
ainda está vazio.
Inserindo um registro
anderson@endor:~$ mongo
MongoDB shell version: 2.4.6
connecting to: test
> use latinoware
switched to db latinoware
> db.alunos.insert({
... 'nome':'Pedro',
... 'turma':'Python',
... 'nota': 10})
>

Nesse ponto, o banco foi criado
e o documento foi inserido, já
está persistido em disco
Verificando o registro
ObjectId é único para
cada documento

> db.alunos.findOne()
{
"_id" : ObjectId("525ecd6585512f4130afd2c4"),
"nome" : "Pedro",
"turma" : "Python",
"nota" : 10
}
Inserindo outro registro
> db.alunos.insert({
... nome: 'Carolina',
... sobrenome: 'Ferreira',
... sexo: 'feminino',
... idade: 29,
... email: 'carol@yahoo.com',
... materias: ['MongoDB','Riak','Java'],
... notas: {'MongoDB': 10, 'Riak': 8, 'Java': 9}
... })
>
Listando apenas o registro da Carolina
> db.alunos.find({'nome':'Carolina'}).pretty()
{
"_id" : ObjectId("522f25248434c181910716ec"),
"nome" : "Carolina",
"sobrenome" : "Ferreira",
"sexo" : "feminino",
"idade" : 29,
"email" : "carol@yahoo.com",
"materias" : [
"MongoDB",
"Riak",
"Java"
],
"notas" : {
"MongoDB" : 10,
"Riak" : 8,
"Java" : 9
}
}
>
Só mais um registro...
> db.alunos.insert({
... nome: 'Juliana',
... sobrenome: 'Silva',
... sexo: 'feminino',
... idade: 21,
... materias: ['Riak','Python']
... })

> db.alunos.count()
3
Listando apenas quem é do sexo
feminino
> db.alunos.find({sexo:'feminino'})
{ "_id" : ObjectId("5230ee7ec3141857756a81a8"), "nome" : "Carolina",
"sobrenome" : "Ferreira", "sexo" : "feminino", "idade" : 29, "email" :
"carol@yahoo.com", "materias" : [ "MongoDB", "Riak", "Java" ],
"notas" : { "MongoDB" : 10, "Riak" : 8, "Java" : 9 } }
{ "_id" : ObjectId("5230eec6c3141857756a81a9"), "nome" : "Juliana",
"sobrenome" : "Silva", "sexo" : "feminino", "idade" : 21, "materias" :
[ "Riak", "Python" ] }
Listando apenas quem tem MongoDB
na matéria
> db.alunos.find({materias:'MongoDB'})
{ "_id" : ObjectId("522f25248434c181910716ec"), "nome" : "Carolina",
"sobrenome" : "Ferreira", "sexo" : "feminino", "idade" : 29, "email" :
"carol@yahoo.com", "materias" : [ "MongoDB", "Riak", "Java" ],
"notas" : { "MongoDB" : 10, "Riak" : 8, "Java" : 9 } }
>
Listando quem tem menos de 30 anos
>

db.alunos.find({idade: {$lt: 30} })

{ "_id" : ObjectId("522f25248434c181910716ec"), "nome" : "Carolina", "sobrenome" : "Ferreira", "sexo" :
"feminino", "idade" : 29, "email" : "carol@yahoo.com", "materias" : [ "MongoDB", "Riak", "Java" ],
"notas" : { "MongoDB" : 10, "Riak" : 8, "Java" : 9 } }
{ "_id" : ObjectId("522f264c8434c181910716ed"), "nome" : "Juliana", "sobrenome" : "Silva", "sexo" :
"feminino", "idade" : 21, "materias" : [ "Riak", "Python" ] }
>
Alguns operadores de consulta
Operador

$gt
$gte
$lt
$lte

Descrição

Maior que

Maior ou igual que

Menor que

Menor ou igual que
Atualização de registro
Padrão de pesquisa

> db.alunos.update({'nome':'Carolina'},
{$set: {'sobrenome':'Ferreira Martins'}})

Operador de alteração
Resultado
> db.alunos.find({'nome':'Carolina'}).pretty()
{
"_id" : ObjectId("522f2b998434c181910716ee"),
"email" : "carol@yahoo.com",
"idade" : 29,
"materias" : [
"MongoDB",
"Riak",
"Java"
],
"nome" : "Carolina",
"notas" : {
"MongoDB" : 10,
"Riak" : 8,
"Java" : 9
},
"sexo" : "feminino",
"sobrenome" : "Ferreira Martins"
}
>
O que acontece se fizer isso?

> db.alunos.update({'nome':'Carolina'}, {'sobrenome':'Ferreira
Martins'})
Removendo registros

db.alunos.remove({'sobrenome':'Ferreira Martins'})
Listando só o nome dos alunos
> db.alunos.find({},{'nome':true,
{ "nome" : "Pedro" }
{ "nome" : "Juliana" }
{ "nome" : "Carolina" }

'_id': false})
Ordenação
> db.alunos.find({}, {'nome': true, '_id': false}).sort({nome: 1})
{ "nome" : "Carolina" }
{ "nome" : "Juliana" }
{ "nome" : "Pedro" }
Comparativo SQL
SQL

MongoDB

INSERT INTO USERS VALUES(1,1)

db.users.insert({a:1, b:1})

SELECT a,b FROM users

db.users.find({}, {a: 1, b: 1})

SELECT * FROM users

db.users.find()

SELECT * FROM users WHERE age=33

db.users.find({age: 33})

SELECT * FROm users WHERE name =

db.users.find({name:”pedro”})

“pedro”
Comparativo SQL
SQL
SELECT * FROM users WHERE age=33
ORDER BY name

MongoDB
db.users.find({‘age’:33}).sort({na
me:1})

SELECT * FROM users WHERE age < 33

db.users.find({‘age’:{$lt:33}})})

CREATE INDEX myindexname ON

db.users.ensureIndex({name:1})

user(name)
SELECT * FROM users WHERE a = 1
AND b = ‘q’
SELECT * FROM users LIMIT 10 SKIP 20

db.users.find({a:1, b:’q’})
db.users.find().limit(10).skip(20)
Como descobrir documentos que não
possuem determinada chave
> db.alunos.find({'email': {$exists: false} })
Adicionando chave em todos os
documentos
Query

> db.alunos.update({ },
O que adicionar

{ $set: { 'aprovado': true } },
{ multi: true })
Grava a alteração em todos
Os registros que atendem
Ao critério
Criando apontamentos
(relacionamentos)
> db.alunos.find().pretty()
{ "_id" : "pedro", "nome" : "Pedro", "email" : "pedro@teste.com" }
{ "_id" : "carol", "nome" : "Carol", "email" : "carol@teste.com" }
{ "_id" : "july", "nome" : "Juliana", "email" : "july@teste.com" }

> db.biblioteca.find().pretty()
{ "_id" : "0001", "titulo" : "Aprenda MongoDB" }
{ "_id" : "0002", "titulo" : "Aprenda Python" }
{ "_id" : "0003", "titulo" : "Aprenda Shell" }
Primeiro cenário
> db.alunos.update({'_id':'pedro'},{$set:{'biblioteca_id':
['0001','0002']}})
> db.alunos.find({'_id':'pedro'}).pretty()
{
"_id" : "pedro",
"biblioteca_id" : [
"0001",
"0002"
],
"email" : "pedro@teste.com",
"nome" : "Pedro"
}
Segundo cenário
> db.biblioteca.update({'_id':'0001'},
{$set:{'aluguel':
{'aluno_id':'pedro','data':'2013-09-09'}}})
> db.biblioteca.find({'_id':'0001'}).pretty()
{
"_id" : "0001",
"aluguel" : {
"aluno_id" : "pedro",
"data" : "2013-09-09"
},
"titulo" : "Aprenda MongoDB"
}
Terceiro cenário
●

Criar uma collection de referência, exemplo:
“aluguel_livros” e relacionar o _id do aluno com _id
do livro, assim como qualquer outra informação
adicional
Conclusão
●

●

É um novo paradigma, evite pensar de forma
relacional, senão o projeto ficará engessado;
Pode parecer estranho no começo, mas a prática
mostra que esse modelo funciona muito bem e é
muito produtivo;
Perguntas!
●

Obrigado!!! Se não deu tempo de responder sua
pergunta, me chame nas redes sociais ou pelos
corredores da Latinoware! :-)
http://christiano.me
Twitter: @dump
Email: christiano@christiano.me

MongoDB - Tudo o que você precisa saber

  • 1.
    Tudo o quevocê precisa saber Christiano Anderson christiano@christiano.me http://christiano.me Twitter: @dump
  • 2.
    Mongo? ● Sim, em muitosidiomas pode ser um termo pejorativo, mas a origem vem de: Humongous “Gigantesco”
  • 3.
    História ● Foi criado pelosfundadores da Doubleclick; ● 10gen foi fundada em 2007; ● A ideia inicial era fazer um produto semelhante ao Google App Engine;
  • 4.
    Alta curva decrescimento Contribuições ao core do MongoDB
  • 5.
    Inúmeras contribuições ● Em poucotempo, muitos projetos novos: – MongoEngine; – Mongoose; – Pymongo; – MongoKit; – MongoMapper ….
  • 6.
    Grandes players utilizando ● Foursquare; ● Github; ● EAGames; ● Entre diversos outros – No Brasil: ● ● ● EasyTaxi; Globo.com; IG;
  • 7.
    Um pouco deconceitos... ● ● ● NoSQL: O termo foi criado por Carlo Strozzi e Eric Evans como referência a um tipo de armazenamento de dados; Nunca, mas nunca está relacionado a ódio ao modelo SQL, pelo contrário, podem até trabalhar em conjunto; O termo NoREL e Não Relacional também é bastante utilizado
  • 8.
    Por que usarNoSQL? ● Novos paradigmas (nem tão novos assim); ● Funcionalidades; ● Escalabilidade; ● Performance; ● Não ficar preso a modelagem;
  • 9.
    Volume de dados ● Grandevolume é relativo, o que você considera grande? – Dados que crescem exponencialmente; – Agregam muitos valores dinamicamente; – Não precisam de modelagem;
  • 10.
    Considere uso deMongoDB se... ● Está usando muito cache em sua aplicação; ● Os dados estão crescendo de forma exponencial; ● Precisa de processamento em tempo real; ● Gosta de desenvolvimento ágil; ● Sua aplicação é “beta perpétua”; ● Tem dificuldade para trabalhar com modelo relacional; ● Usa muito “join” na sua aplicação relacional;
  • 11.
    Iniciando com MongoDB ● ● ● Suadistribuição GNU/Linux deve possuir pacotes prontos; No site da MongoDB, possível baixar binários para outros sistemas operacionais; A instalação é bem simples, a configuração padrão do MongoDB já atende quase todos os cenários;
  • 12.
    Pode substituir obanco relacional? ● ● ● Até pode, mas é uma questão de arquitetura e escolhas; Uma aplicação pode usar MongoDB e banco relacional; Tudo vai depender da sua arquitetura;
  • 13.
    Suporte a linguagensde programação ● ● Praticamente todas as linguagens de programação possuem suporte (driver) para MongoDB; A lógica é bem parecida, o MongoDB tenta manter o máximo de padrão;
  • 14.
    Recursos animais! ● Busca textual(Full Text Search); ● Aggregation framework; ● Índices espaciais (geográficos); ● Sharding; ● Replica Set;
  • 15.
    Busca textual ● Possui suportea português do Brasil; ● Stemming; ● Stopwords;
  • 16.
    Stemming ● Se a fraseabaixo estiver indexada como FTS: “Enquanto houver vontade de lutar, haverá esperança de vencer” ● Se houver uma busca pela palavra “vencendo”, a mesma será exibida no resultado de busca.
  • 17.
    Interface em JavaScript ● OMongoShell é baseado em JavaScript, oferece toda flexibilidade para gerenciar o banco de dados e executar operações administrativas
  • 18.
    Nomenclaturas Banco Relacional MongoDB Base dedados --> Base de Dados Tabela --> Coleção Registro --> Documento Índice --> Índice Join --> Documento embarcado Foreign key --> Referência
  • 19.
  • 20.
    Realizando operações viaMongoShell ● ● O MongoDB é implícito, não existe necessidade de criar toda estrutura do banco de dados antes; O MongoShell é uma ótima forma de aprendizado!
  • 21.
    MongoShell anderson@endor:~$ mongo MongoDB shellversion: 2.4.6 connecting to: test > a = 10 10 > b = 30 30 >a<b true >b<a false
  • 22.
    Vamos lá... anderson@endor:~$ mongo MongoDBshell version: 2.4.6 connecting to: test > use latinoware switched to db latinoware > Nesse ponto, o banco ainda está vazio.
  • 23.
    Inserindo um registro anderson@endor:~$mongo MongoDB shell version: 2.4.6 connecting to: test > use latinoware switched to db latinoware > db.alunos.insert({ ... 'nome':'Pedro', ... 'turma':'Python', ... 'nota': 10}) > Nesse ponto, o banco foi criado e o documento foi inserido, já está persistido em disco
  • 24.
    Verificando o registro ObjectIdé único para cada documento > db.alunos.findOne() { "_id" : ObjectId("525ecd6585512f4130afd2c4"), "nome" : "Pedro", "turma" : "Python", "nota" : 10 }
  • 25.
    Inserindo outro registro >db.alunos.insert({ ... nome: 'Carolina', ... sobrenome: 'Ferreira', ... sexo: 'feminino', ... idade: 29, ... email: 'carol@yahoo.com', ... materias: ['MongoDB','Riak','Java'], ... notas: {'MongoDB': 10, 'Riak': 8, 'Java': 9} ... }) >
  • 26.
    Listando apenas oregistro da Carolina > db.alunos.find({'nome':'Carolina'}).pretty() { "_id" : ObjectId("522f25248434c181910716ec"), "nome" : "Carolina", "sobrenome" : "Ferreira", "sexo" : "feminino", "idade" : 29, "email" : "carol@yahoo.com", "materias" : [ "MongoDB", "Riak", "Java" ], "notas" : { "MongoDB" : 10, "Riak" : 8, "Java" : 9 } } >
  • 27.
    Só mais umregistro... > db.alunos.insert({ ... nome: 'Juliana', ... sobrenome: 'Silva', ... sexo: 'feminino', ... idade: 21, ... materias: ['Riak','Python'] ... }) > db.alunos.count() 3
  • 28.
    Listando apenas quemé do sexo feminino > db.alunos.find({sexo:'feminino'}) { "_id" : ObjectId("5230ee7ec3141857756a81a8"), "nome" : "Carolina", "sobrenome" : "Ferreira", "sexo" : "feminino", "idade" : 29, "email" : "carol@yahoo.com", "materias" : [ "MongoDB", "Riak", "Java" ], "notas" : { "MongoDB" : 10, "Riak" : 8, "Java" : 9 } } { "_id" : ObjectId("5230eec6c3141857756a81a9"), "nome" : "Juliana", "sobrenome" : "Silva", "sexo" : "feminino", "idade" : 21, "materias" : [ "Riak", "Python" ] }
  • 29.
    Listando apenas quemtem MongoDB na matéria > db.alunos.find({materias:'MongoDB'}) { "_id" : ObjectId("522f25248434c181910716ec"), "nome" : "Carolina", "sobrenome" : "Ferreira", "sexo" : "feminino", "idade" : 29, "email" : "carol@yahoo.com", "materias" : [ "MongoDB", "Riak", "Java" ], "notas" : { "MongoDB" : 10, "Riak" : 8, "Java" : 9 } } >
  • 30.
    Listando quem temmenos de 30 anos > db.alunos.find({idade: {$lt: 30} }) { "_id" : ObjectId("522f25248434c181910716ec"), "nome" : "Carolina", "sobrenome" : "Ferreira", "sexo" : "feminino", "idade" : 29, "email" : "carol@yahoo.com", "materias" : [ "MongoDB", "Riak", "Java" ], "notas" : { "MongoDB" : 10, "Riak" : 8, "Java" : 9 } } { "_id" : ObjectId("522f264c8434c181910716ed"), "nome" : "Juliana", "sobrenome" : "Silva", "sexo" : "feminino", "idade" : 21, "materias" : [ "Riak", "Python" ] } >
  • 31.
    Alguns operadores deconsulta Operador $gt $gte $lt $lte Descrição Maior que Maior ou igual que Menor que Menor ou igual que
  • 32.
    Atualização de registro Padrãode pesquisa > db.alunos.update({'nome':'Carolina'}, {$set: {'sobrenome':'Ferreira Martins'}}) Operador de alteração
  • 33.
    Resultado > db.alunos.find({'nome':'Carolina'}).pretty() { "_id" :ObjectId("522f2b998434c181910716ee"), "email" : "carol@yahoo.com", "idade" : 29, "materias" : [ "MongoDB", "Riak", "Java" ], "nome" : "Carolina", "notas" : { "MongoDB" : 10, "Riak" : 8, "Java" : 9 }, "sexo" : "feminino", "sobrenome" : "Ferreira Martins" } >
  • 34.
    O que acontecese fizer isso? > db.alunos.update({'nome':'Carolina'}, {'sobrenome':'Ferreira Martins'})
  • 35.
  • 36.
    Listando só onome dos alunos > db.alunos.find({},{'nome':true, { "nome" : "Pedro" } { "nome" : "Juliana" } { "nome" : "Carolina" } '_id': false})
  • 37.
    Ordenação > db.alunos.find({}, {'nome':true, '_id': false}).sort({nome: 1}) { "nome" : "Carolina" } { "nome" : "Juliana" } { "nome" : "Pedro" }
  • 38.
    Comparativo SQL SQL MongoDB INSERT INTOUSERS VALUES(1,1) db.users.insert({a:1, b:1}) SELECT a,b FROM users db.users.find({}, {a: 1, b: 1}) SELECT * FROM users db.users.find() SELECT * FROM users WHERE age=33 db.users.find({age: 33}) SELECT * FROm users WHERE name = db.users.find({name:”pedro”}) “pedro”
  • 39.
    Comparativo SQL SQL SELECT *FROM users WHERE age=33 ORDER BY name MongoDB db.users.find({‘age’:33}).sort({na me:1}) SELECT * FROM users WHERE age < 33 db.users.find({‘age’:{$lt:33}})}) CREATE INDEX myindexname ON db.users.ensureIndex({name:1}) user(name) SELECT * FROM users WHERE a = 1 AND b = ‘q’ SELECT * FROM users LIMIT 10 SKIP 20 db.users.find({a:1, b:’q’}) db.users.find().limit(10).skip(20)
  • 40.
    Como descobrir documentosque não possuem determinada chave > db.alunos.find({'email': {$exists: false} })
  • 41.
    Adicionando chave emtodos os documentos Query > db.alunos.update({ }, O que adicionar { $set: { 'aprovado': true } }, { multi: true }) Grava a alteração em todos Os registros que atendem Ao critério
  • 42.
    Criando apontamentos (relacionamentos) > db.alunos.find().pretty() {"_id" : "pedro", "nome" : "Pedro", "email" : "pedro@teste.com" } { "_id" : "carol", "nome" : "Carol", "email" : "carol@teste.com" } { "_id" : "july", "nome" : "Juliana", "email" : "july@teste.com" } > db.biblioteca.find().pretty() { "_id" : "0001", "titulo" : "Aprenda MongoDB" } { "_id" : "0002", "titulo" : "Aprenda Python" } { "_id" : "0003", "titulo" : "Aprenda Shell" }
  • 43.
    Primeiro cenário > db.alunos.update({'_id':'pedro'},{$set:{'biblioteca_id': ['0001','0002']}}) >db.alunos.find({'_id':'pedro'}).pretty() { "_id" : "pedro", "biblioteca_id" : [ "0001", "0002" ], "email" : "pedro@teste.com", "nome" : "Pedro" }
  • 44.
    Segundo cenário > db.biblioteca.update({'_id':'0001'}, {$set:{'aluguel': {'aluno_id':'pedro','data':'2013-09-09'}}}) >db.biblioteca.find({'_id':'0001'}).pretty() { "_id" : "0001", "aluguel" : { "aluno_id" : "pedro", "data" : "2013-09-09" }, "titulo" : "Aprenda MongoDB" }
  • 45.
    Terceiro cenário ● Criar umacollection de referência, exemplo: “aluguel_livros” e relacionar o _id do aluno com _id do livro, assim como qualquer outra informação adicional
  • 46.
    Conclusão ● ● É um novoparadigma, evite pensar de forma relacional, senão o projeto ficará engessado; Pode parecer estranho no começo, mas a prática mostra que esse modelo funciona muito bem e é muito produtivo;
  • 47.
    Perguntas! ● Obrigado!!! Se nãodeu tempo de responder sua pergunta, me chame nas redes sociais ou pelos corredores da Latinoware! :-) http://christiano.me Twitter: @dump Email: christiano@christiano.me

Notas do Editor