/Banco de dados

voltar
/Banco de dados

MongoDB para iniciantes em NoSQL – Parte 02

PorLuiz Fernando Duarte Junior em

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:

> custArray = [{ nome : "Fernando", idade : 29 }, { nome : "Teste", "uf" : "RS" }]
> db.customers.insert(custArray)

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 seçã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 de que todos os documentos foram realmente inseridos na coleção, use o seguinte comando:

> db.customers.find().pretty()

É 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 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:

> db.customers.find({uf: "RS"})

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:

> db.customers.find({nome: { $regex: /a/ }})

Se você já mexeu com expressões regulares (regex) em JS antes, sabe exatamente como usar e conhece 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?

> db.customers.find({idade: {$gte: 18}})

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:

> db.customers.find({nome: "Luiz", idade: {$gte: 18}})

O que a expressão acima irá retornar?

Se você disse customers, cujo os nomes sejam “Luiz” e que sejam maiores de idade, você acertou!

E a expressão abaixo:

> db.customers.find({nome: { $regex: /a/ }, idade: {$gte: 18}})

Customers cujo os nomes 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:

> db.customers.find().skip(1).limit(10)

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 decrescente (-1), como abaixo, em que retorno todos os customers ordenados pela idade:

> db.customers.find().sort({idade: 1})

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 dois parâmetros:

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

Como em:

> db.customers.update({nome: "Luiz"}, {nome: "Luiz", idade: 29, uf: "RS"})

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 apenas um documento, 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 somente a expressão de alteração do nome, como abaixo (já usando o _id como filtro, que é mais seguro):

> db.customers.updateOne({_id: ObjectId("59ab46e433959e2724be2cbd")}, {$set: {idade: 28}})

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.

Essa 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. Entre elas, existe uma muito interessante do MongoDB: upsert, como abaixo:

> db.customers.updateOne({nome: "LuizTools"}, {nome: "LuizTools", uf: "RS"}, {upsert: true})

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:

> db.customers.delete({nome: "Luiz"})

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!

Deixe um comentário! 0

0 comentário

Comentários

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Comentando como Anônimo

leia mais
Este projeto é mantido e patrocinado pelas empresas: