Tutorial MongoDB para iniciantes em NoSQL – Parte 2

MongoDB Logo
MongoDB Logo

Atualizado em 23/11/2017!

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:

  • $eq: 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.

Gravei recentemente um vídeo pra Umbler onde falo bastante de consultas em MongoDB, que você pode conferir abaixo:

Já os slides do vídeo podem ser conferidos abaixo:

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: se o campo a ser alterado não existir no documento, ele será criado.

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, ops, deleteOne e deleteMany na verdade. Tem também o remove, que possui mais opções, mas é pouco usado.

Existe uma função deleteOne e uma deleteMany, 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 remove é 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 parte deste tutorial, você verá como fazer backup e restaurar bases de dados MongoDB!

E o vídeo? Já viu o vídeo com Introdução ao MongoDB que gravei pra Umbler?

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!

O que achou desse artigo?
[Total: 7 Média: 3]

  • Giovanni Pires da Silva

    Boa Luiz, bom post novamente. Aprendendo um pouco de Mongo por aqui. Seguinte, duas notas:

    1. No exemplo do upsert, corrija update no lugar de updateOne, aparentemente como você mesmo mencionou, o upsert é só pro update – pelo menos pra mim gera Error: the update operation document must contain atomic operators caso tento usar com updateOne;
    2. Não sei se a documentação mudou recentemente ou o que, mas o comando delete não rolou, e testando no chute vi que é o remove. Otherwise, gera TypeError: db.customers.delete is not a function.

    Eras isso, valeu pelo post! Abraços.

    • 1. É updateOne mesmo (embora funcione com o update também), mas por algum motivo bizarro me esqueci de colocar o $set. Já resolvi.
      2. é remove, eu sempre cometo este mesmo erro de escrever ‘delete’…

      Obrigado pelos apontamentos!

      • Giovanni Pires da Silva

        Boa, valeu! É nóis. =]