Tutorial MongoDB para iniciantes em NoSQL – Parte 2

MongoDB Logo
MongoDB Logo

Na parte anterior deste tutorial sobre MongoDB para iniciantes em NoSQL, falei sobre os conceitos mais elementares deste banco de dados, sobre quando usar e quando não usar esta tecnologia, quais as principais diferenças dele para outros bancos de dados e deixamos o servidor e o cliente prontos para receber comandos, sendo que inclusive executamos alguns para testar tudo.

Se não fez a primeira parte do tutorial, faça, no mínimo a seção final onde configuramos o ambiente. Continuaremos exatamente de onde a última parte parou.

Nesta segunda parte, falaremos dos comandos elementares (CRUD) do MongoDB.

Insert “Avançado”

Na seção anterior aprendemos a fazer um find() que retorna todos os documentos de uma coleção e um insert que insere um novo documento em uma coleção, além de outros comandos menores. Agora vamos adicionar mais alguns registros no seu terminal cliente  mongo:

Atenção: para o nome dos campos dos seus documentos e até mesmo para o nome das coleções do seu banco, use o padrão de nomes de variáveis JS (camel-case, sem acentos, sem espaços, etc).

Nota: no exemplo acima a variável custArray passa a existir durante toda a sessão do terminal a partir do comando seguinte.

Nesse exemplo passei um array com vários documentos para nossa função insert inserir na coleção customers e isso nos trás algumas coisas interessantes para serem debatidas. Primeiro, sim, você pode passar um array de documentos por parâmetro para o insert. Segundo, você notou que o segundo documento não possui “idade”? E que ele possui um campo “uf”?

Find “avançado”

Para se certificar que todos documentos foram realmente inseridos na coleção, use o seguinte comando:

É o mesmo comando find() que usamos anteriormente, mas com a função pretty() no final para identar o resultado da função no terminal, ficando mais fácil de ler. Use e você vai notar a diferença, principalmente em consultas com vários resultados.

Mas voltando à questão do “uf”, ao contrário dos bancos relacionais, o MongoDB possui schema variável, ou seja, se somente um customer tiver “uf”, somente ele terá esse campo, não existe um schema pré-definido compartilhado entre todos os documentos, cada um é independente. Obviamente considerando que eles compartilham a mesma coleção, é interessante que eles possuam coisas em comum, caso contrário não faz sentido guardar eles em uma mesma coleção.

Mas como fica isso nas consultas? E se eu quiser filtrar por “uf”? Não tem problema!

Essa é uma boa deixa para eu mostrar como filtrar um find() por um campo do documento:

Note que a função find pode receber um documento por parâmetro representando o filtro a ser aplicado sobre a coleção para retornar documentos. Nesse caso, disse ao find que retornasse todos os documentos que possuam o campo uf definido como “RS”. O resultado no seu terminal deve ser somente o customer de nome “Teste” (não vou falar do _id dele aqui pois o valor muda completamente de um servidor MongoDB para outro).

Atenção: MongoDB é case-sensitive ao contrário dos bancos relacionais, então cuidado!

Experimente digitar outros valores ao invés de “RS” e verá que eles não retornam nada, afinal, não basta ter o campo uf, ele deve ser exatamente igual a “RS”.

Além de campos com valores específicos, esse parâmetro do find permite usar uma infinidade de operadores como por exemplo, trazer todos documentos que possuam a letra ‘a’ no nome:

Se você já mexeu com expressões regulares (regex) em JS antes, sabe exatamente como usar e o poder desse recurso junto a um banco de dados, sendo um equivalente muito mais poderoso ao LIKE dos bancos relacionais.

Mas e se eu quiser trazer todos os customers maiores de idade?

O operador $gte (Greater Than or Equal) retorna todos os documentos que possuam o campo idade e que o valor do mesmo seja igual ou superior à 18. E podemos facilmente combinar filtros usando vírgulas dentro do documento passado por parâmetro, assim como fazemos quando queremos inserir campos em um documento:

O que a expressão acima irá retornar?

Se você disse customers cujo nome sejam Luiz e que sejam maiores de idade, você acertou!

E a expressão abaixo:

Customers cujo nome contenham a letra ‘a’ e que sejam maiores de idade, é claro!

Outros operadores que você pode usar junto ao filtro do find são:

  • $e: exatamente igual (=)
  • $ne: diferente (<> ou !=)
  • $gt: maior do que (>)
  • $lt: menor do que (<)
  • $lte: menor ou igual a (<=)
  • $in: o valor está contido em um array de possibilidades, como em um OU. Ex: {idade: {$in: [10,12] }}
  • $all: MongoDB permite campos com arrays. Ex: { tags: [“NodeJS”, “MongoDB”] }. Com esse operador, você compara se seu campo multivalorado possui todos os valores de um array específico. Ex: {tags: {$all: [“NodeJS”, “Android”]}}

Entre outros!

Você também pode usar findOne ao invés de find para retornar apenas o primeiro documento, ou ainda as funções limit e skip para limitar o número de documentos retornados e para ignorar alguns documentos, especificamente, da seguinte maneira:

No exemplo acima retornaremos 10 customers ignorando o primeiro existente na coleção.

E para ordenar? Usamos a função sort no final de todas as outras, com um documento indicando quais campos e se a ordenação por aquele campo é crescente (1) ou descrescente (-1), como abaixo em que retorno todos os customers ordenados pela idade:

Nota: assim como nos bancos relacionais, os métodos de consulta retornam em ordem de chave primária por padrão, o que neste caso é o _id.

Ok, vimos como usar o find de maneiras bem interessantes e úteis, mas e os demais comandos de manipulação do banco?

Update

Além do insert que vimos antes, também podemos atualizar documentos já existentes, por exemplo usando o comando update e derivados. O jeito mais simples (e mais burro) de atualizar um documento é chamando a função update na coleção com 2 parâmetros:

  • documento de filtro para saber qual(is) documento(s) será(ão) atualizado(s);
  • novo documento que substituirá o antigo;

Como em:

Como resultado você deve ter um nModified maior do que 1, mostrando quantos documentos foram atualizados.

Por que essa é a maneira mais burra de fazer um update? Porque além de perigosa ela exige que você passe o documento completo a ser atualizado no segundo parâmetro, pois ele substituirá o original!

Primeira regra do update inteligente: se você quer atualizar um documento apenas, comece usando updateOne ao invés de update. O updateOne vai te obrigar a usar operadores ao invés de um documento inteiro para a atualização, o que é muito mais seguro.

Segunda regra do update inteligente: sempre que possível, use a chave primária (_id) como filtro da atualização, pois ela é sempre única dentro da coleção.

Terceira regra do update inteligente: sempre use operadores ao invés de documentos inteiros no segundo parâmetro, independente do número de documentos que serão atualizados.

Mas que operadores são esses?

Assim como o find possui operadores de filtro, o update possui operadores de atualização. Se eu quero, por exemplo, mudar apenas o nome de um customer, eu não preciso enviar todo o documento do respectivo customer com o nome alterado, mas sim apenas a expressão de alteração do nome, como abaixo (já usando o _id como filtro, que é mais seguro):

Nota: para saber o _id correto do seu update, faça um find primeiro e não tente copiar o meu pois não vai repetir.

Esta função vai alterar (operador $set) a idade para o valor 28 do documento cujo _id seja “59ab46e433959e2724be2cbd” (note que usei uma função ObjectId para converter, pois esse valor não é uma string).

Nota: você pode usar null se quiser “limpar” um campo.

O operador $set recebe um documento contendo todos os campos que devem ser alterados e seus respectivos novos valores. Qualquer campo do documento original que não seja indicado no set continuará com os valores originais.

Atenção: o operador $set não adiciona campos novos em um documento, somente altera valores de campos já existentes.

Não importa o valor que ela tenha antes, o operador $set vai sobrescrevê-lo. Agora, se o valor anterior importa, como quando queremos incrementar o valor de um campo, não se usa o operador $set, mas sim outros operadores. A lista dos mais úteis operadores de update estão abaixo:

  • $unset: remove o respectivo campo do documento;
  • $inc: incrementa o valor original do campo com o valor especificado;
  • $mul: multiplica o valor original do campo com o valor especificado;
  • $rename: muda o nome do campo para o nome especificado;

Além disso, existe um terceiro parâmetro oculto no update que são as opções de update. Dentre elas, existe uma muito interessante do MongoDB: upsert, como abaixo:

Um upsert é um update como qualquer outro, ou seja, vai atualizar o documento que atender ao filtro passado como primeiro parâmetro, porém, se não existir nenhum documento com o respectivo filtro, ele será inserido, como se fosse um insert.

upsert = update + insert

Eu já falei como amo esse banco de dados? 😀

Delete

Pra encerrar o nosso conjunto de comandos mais elementares do MongoDB falta o delete.

Existe uma função delete e uma deleteOne, o que a essa altura do campeonato você já deve saber a diferença. Além disso, assim como o find e o update, o primeiro parâmetro do delete é o filtro que vai definir quais documentos serão deletados e todos os operadores normais do find são aplicáveis.

Sendo assim, de maneira bem direta:

Vai excluir todos os clientes cujo nome seja igual a “Luiz”.

Simples, não?!

Obviamente existem coisas muito mais avançadas do que esse rápido tópico de MongoDB. Lhe encorajo a dar uma olhada no site oficial do banco de dados onde há a seção de documentação, vários tutoriais e até mesmo a possibilidade de tirar certificações online para garantir que você realmente entendeu a tecnologia.

Na terceira e última parte deste tutorial de MongoDB para iniciantes, falarei de modelagem de bancos de dados orientados à documentos. Aguarde!

Curtiu o post? Que tal aprender a usar MongoDB com Node.js? Então clica no banner abaixo e dá uma conferida no meu livro sobre programação web com Node.js!

Tutorial MongoDB para iniciantes em NoSQL

MongoDB Logo
MongoDB Logo

MongoDB é um banco de dados de código aberto, gratuito, de alta performance, sem esquemas e orientado a documentos lançado em fevereiro de 2009 pela empresa 10gen. Foi escrito na linguagem de programação C++ (o que o torna portável para diferentes sistemas operacionais) e seu desenvolvimento durou quase 2 anos, tendo iniciado em 2007.

Já falei de MongoDB na prática aqui no blog em outras três oportunidades juntamente com a tecnologia Node.js. Confira em:

No entanto, em ambas ocasiões, MongoDB foi o coadjuvante e pouco falei de suas características, suas vantagens, desvantagens e como modelar bases reais com ele. Pois bem, esse é o intuito do post de hoje, compartilhar os meus pouco mais de 2 anos (na data que escrevo este post) de experiência com este fantástico banco de dados.

Orientado à Documentos

Por ser orientado a documentos JSON (armazenados em modo binário, apelidado de JSON), muitas aplicações podem modelar informações de modo muito mais natural, pois os dados podem ser aninhados em hierarquias complexas e continuar a ser indexáveis e fáceis de buscar, igual ao que já é feito em JavaScript.

Existem vários bancos NOSQL atualmente no mercado porque existem dezenas de problemas de persistência de dados que o SQL tradicional não resolve. Bancos não-relacionais document-based (que armazenam seus dados em documentos) são os mais comuns e mais proeminentes de todos, sendo o seu maior expoente o banco MongoDB como o gráfico abaixo da pesquisa mais recente de bancos de dados utilizados pela audiência do StackOverflow em 2017 mostra.

Bancos de dados mais usados
Bancos de dados mais usados

Dentre todos os bancos não relacionais o MongoDB é o mais utilizado com ⅕ de todos os respondentes alegarem utilizar ele em seus projetos, o que é mais do que até mesmo o Oracle, um banco muito mais tradicional.

Basicamente neste tipo de banco (document-based ou document-oriented) temos coleções de documentos, nas quais cada documento é autossuficiente, contém todos os dados que possa precisar, ao invés do conceito de não repetição + chaves estrangeiras do modelo relacional.

A ideia é que você não tenha de fazer JOINs pois eles prejudicam muito a performance em suas queries (são um mal necessário no modelo relacional, infelizmente). Você modela a sua base de forma que a cada query você vai uma vez no banco e com apenas uma chave primária pega tudo que precisa.

Obviamente isto tem um custo: armazenamento em disco. Não é raro bancos MongoDB consumirem muitas vezes mais disco do que suas contrapartes relacionais.

Quando devo usar MongoDB?

MongoDB foi criada com Big Data em mente. Ele suporta tanto escalonamento horizontal quanto vertical usando replica sets (instâncias espelhadas) e sharding (dados distribuídos), tornando-o uma opção muito interessante para grandes volumes de dados, especialmente os desestruturados.

Dados desestruturados são um problema para a imensa maioria dos bancos de dados relacionais, mas não tanto para o MongoDB. Quando o seu schema é variável, é livre, usar MongoDB vem muito bem a calhar. Os documentos BSON (JSON binário) do Mongo são schemaless e aceitam quase qualquer coisa que você quiser armazenar, sendo um mecanismo de persistência perfeito para uso com tecnologias que trabalham com JSON nativamente, como JavaScript (e consequentemente Node.js).

BSON Document
BSON Document

Cenários altamente recomendados e utilizados atualmente são em catálogos de produtos de e-commerces. Telas de detalhes de produto em ecommerces são extremamente complicadas devido à diversidade de informações aliada às milhares de variações de características entre os produtos que acabam resultando em dezenas de tabelas se aplicado sobre o modelo relacional. Em MongoDB essa problemática é tratada de uma maneira muito mais simples, que explicarei mais adiante.

Além do formato de documentos utilizado pelo MongoDB ser perfeitamente intercambiável com o JSON serializado do JS, MongoDB opera basicamente de maneira assíncrona em suas operações, assim como o próprio Node.js, o que nos permite ter uma persistência extremamente veloz aliado a uma plataforma de programação igualmente rápida.

Embora o uso de Node.js com bancos de dados relacionais não seja incomum, é com os bancos não-relacionais como MongoDB e Redis que ele mostra todo o seu poder de tecnologia para aplicações real-time e volumes absurdos de requisições na casa de 500 mil/s, com as configurações de servidor adequadas.

Além disso, do ponto de vista do desenvolvedor, usar MongoDB permite criar uma stack completa apenas usando JS uma vez que temos JS no lado do cliente, do servidor (com Node) e do banco de dados (com Mongo), pois todas as queries são criadas usando JS também, como você verá mais à frente.

Quando não devo usar MongoDB?

Nem tudo são flores e o MongoDB não é uma “bala de prata”, ele não resolve todos os tipos de problemas de persistência existentes.

Você não deve utilizar MongoDB quando relacionamentos entre diversas entidades são importantes para o seu sistema. Se for ter de usar muitas “chaves estrangeiras” e “JOINs”, você está usando do jeito errado, ou, ao menos, não do jeito mais indicado.

Além disso, diversas entidades de pagamento (como bandeiras de cartão de crédito) não homologam sistemas cujos dados financeiros dos clientes não estejam em bancos de dados relacionais tradicionais. Obviamente isso não impede completamente o uso de MongoDB em sistemas financeiros, mas o restringe apenas a certas partes (como dados públicos).

Em meu post sobre Persistência Poliglota eu falo bastante dos conceitos por trás de diversos bancos não-relacionais e incluo concorrentes do MongoDB e onde eles devem ser utilizados ao invés deste mecanismo de persistência, dê uma olhada para mais informações neste sentido.

Instalação e Testes

Diversos players de cloud computing fornecem versões de Mongo hospedadas e prontas para uso como Umbler e mLab, no entanto é muito importante um conhecimento básico de administração local de MongoDB para entender melhor como tudo funciona. Não focaremos aqui em nenhum aspecto de segurança, de alta disponibilidade, de escala ou sequer de administração avançada de MongoDB. Deixo todas estas questões para você ver junto à documentação oficial no site oficial, onde inclusive você pode estudar e tirar as certificações.

Caso ainda não tenha feito isso, acesse o site oficial do MongoDB e baixe gratuitamente a versão mais recente para o seu sistema operacional, que é a versão 3.4 na data em que escrevo este livro.

Baixe o arquivo compactado e, no caso do Windows, rode o executável que extrairá os arquivos na sua pasta de Arquivos de Programas (não há uma instalação de verdade, apenas extração de arquivos), seguido de uma pasta server/versão, o que é está ok para a maioria dos casos, mas que eu prefiro colocar em C:\Mongo ou dentro de Applications no caso do Mac.

Dentro dessa pasta do Mongo podem existir outras pastas, mas a que nos interessa é a pasta bin. Nessa pasta estão uma coleção de utilitários de linha de comando que são o coração do MongoDB (no caso do Windows, todos terminam com .exe):

  • mongod: inicializa o servidor de banco de dados;
  • mongo: inicializa o cliente de banco de dados;
  • mongodump: realiza dump do banco (backup binário);
  • mongorestore: restaura dumps do banco (restore binário);
  • mongoimport: importa documentos JSON ou CSV pro seu banco;
  • mongoexport: exporta documentos JSON ou CSV do seu banco;
  • entre outros.

Para subir um servidor de MongoDB na sua máquina é muito fácil: execute o utilitário mongod via linha de comando  como abaixo, onde dbpath é o caminho onde seus dados serão salvos (esta pasta já deve estar criada).

Isso irá iniciar o servidor do Mongo. Uma vez que apareça no prompt “[initandlisten] waiting for connections on port 27017”, está pronto, o servidor está executando corretamente e você já pode utilizá-lo, sem segurança alguma e na porta padrão 27017.

Nota: se já existir dados de um banco MongoDB na pasta data, o mesmo banco que está salvo lá ficará ativo novamente, o que é muito útil para os nossos testes.

Agora abra outro prompt de comando (o outro ficará executando o servidor) e novamente dentro da pasta bin do Mongo, digite:

Após a conexão funcionar, se você olhar no prompt onde o servidor do Mongo está rodando, verá que uma conexão foi estabelecida e um sinal de “>” indicará que você já pode digitar os seus comandos e queries para enviar à essa conexão.

Ao contrário dos bancos relacionais, no MongoDB você não precisa construir a estrutura do seu banco previamente antes de sair utilizando ele. Tudo é criado conforme você for usando, o que não impede, é claro, que você planeje um pouco o que pretende fazer com o Mongo.

O comando abaixo no terminal cliente mostra os bancos existentes nesse servidor:

Se é sua primeira execução ele deve listar as bases admin e local. Não usaremos nenhuma delas. Agora digite o seguinte comando para “usar” o banco de dados “workshop” (um banco que você sabe que não existe ainda):

O terminal vai lhe avisar que o contexto da variável “db” mudou para o banco workshop, que nem mesmo existe ainda (mas não se preocupe com isso!). Essa variável “db” representa agora o banco workshop e podemos verificar quais coleções existem atualmente neste banco usando o comando abaixo:

Isso também não deve listar nada, mas não se importe com isso também. Assim como fazemos com objetos JS que queremos chamar funções, usaremos o db para listar os documentos de uma coleção de customers (clientes) da seguinte forma:

find é a função para fazer consultas no MongoDB e, quando usada sem parâmetros, retorna todos os documentos da coleção. Obviamente não listará nada pois não inserimos nenhum documento ainda, o que vamos fazer agora com a função insert:

A função insert espera um documento JSON por parâmetro com as informações que queremos inserir, sendo que além dessas informações o MongoDB vai inserir um campo _id automático como chave primária desta coleção.

Como sabemos se funcionou? Além da resposta ao comando insert (nInserted indica quantos documentos foram inseridos com o comando), você pode executar o find novamente para ver que agora sim temos customers no nosso banco de dados. Além disso se executar o “show collections” e o “show databases” verá que agora sim possuímos uma coleção customers e uma base workshop nesse servidor.

Tudo foi criado a partir do primeiro insert e isso mostra que está tudo funcionando bem no seu servidor MongoDB!

E assim encerra a primeira parte do nosso tutorial de MongoDB para iniciantes. Na parte 2, que você confere neste link, falo dos comandos elementares do MongoDB (CRUD) e de modelagem orientada à documentos.

Espero que tenha gostado e até a próxima!

Curtiu o post? Que tal aprender a usar MongoDB com Node.js? Então clica no banner abaixo e dá uma conferida no meu livro sobre programação web com Node.js!

Lançamento do meu livro de Node.js

Faz tempo desde meus primeiros estudos com Node.js, quase concomitantemente às minhas primeiras postagens sobre o assunto aqui no blog.

Assim como fiz em 2012, conforme ia iniciando meus estudos de Android, fui documentando tudo o que aprendia e testava sobre Node.js e todo o ecossistema de tecnologias ao redor dele.

Obviamente não comecei meus estudos partindo do zero, uma vez que já trabalho com desenvolvimento web há cerca de 10 anos. Todo o meu conhecimento de front-end (que se resume a JQuery e Bootstrap) e de banco de dados (que se resume a SQL Server e MongoDB), aliado às boas práticas de Engenharia de Software, Gestão de Projetos, Testes de Software, etc formaram o profissional Node.js que sou hoje.

Puramente com Node eu tenho experiência de quase um ano na data que escrevo este post e uma meia dúzia de projetos entregues e funcionando.

Pois então que há alguns meses decidi escrever um novo livro. O primeiro de 2017, justamente sobre todo esse conhecimento de programação web que possuo e que já me renderam o cargo de docente de disciplinas como Programação para Internet e Serviço para Web em faculdades do RS durante alguns anos.

Esse livro chama-se Programação Web com Node.js, e está à venda desde essa semana na Amazon.

É um livro completo. Mesmo. São 355 páginas, o maior livro que já escrevi na minha curta carreira de escritor. Uma monstruosidade que pode assustar pelo tamanho, mas que possui uma didática clara e objetiva, como todos livros de minha autoria (quem já leu os demais sabe do que estou falando), e que pega o leitor pela mão e ensina desde os conceitos mais básicos até tudo que é necessário para ter uma aplicação rodando.

Do front-end ao back-end, passando por banco de dados e com muito Node.js, Programação Web com Node.js é para quem está começando a trabalhar com web e está completamente confuso com a quantidade de frameworks, bibliotecas e tecnologias disponíveis. Nele, ensino a usar tudo o que deu certo em meus projetos nos últimos anos, não necessariamente as stacks mais badaladas da atualidade, mas o que é bom, confiável e que possui grande mercado.

Neste livro você vai aprender:

  • o básico de algoritmos e programação com JavaScript;
  • tecnologias básicas da web: HTML+CSS+JS;
  • construção de back-end de aplicações e serviços com Node.js;
  • como usar o web framework ExpressJS e diversos outros módulos famosos do Node;
  • como sistemas web funcionam e, principalmente, o protocolo HTTP;
  • como criar interfaces web atraentes com Bootstrap;
  • como criar scripts poderosos e interações real-time com Ajax em JQuery;
  • como usar o banco de dados MongoDB;
  • dezenas de boas práticas de programação com Node.js;

Novamente, devido às atualizações constantes e ao tamanho do livro, optei apenas por ter versão digital, que você pode ler no Kindle, no PC usando o ler.amazon.com e no smartphone/tablet usando o app Kindle Cloud Reader. Um livro desse tamanho impresso não custaria menos de R$100 e rapidamente ficaria desatualizado, coisa que eu detesto.

Além disso, caso assine o Kindle Unlimited (primeiro mês grátis e depois R$20/mês), você ler o meu livro e milhares de outros títulos gratuitamente.

Se ainda não é cliente da Amazon, esta é uma excelente oportunidade de começar com o pé direito. Modéstia à parte. 😉