Python Iluminado

MongoDB

Como já sabemos, a linguagem Python pode ser usada em conjunto com banco de dados, anteriormente utilizamos os bancos de dados relacionais SQL, agora vamos dar um passo a frente e trabalhar com NoSQL.

Um banco de dados NoSQL (originalmente referindo-se a "não SQL" ou "não relacional") fornece um mecanismo para armazenamento e recuperação de dados que são modelados em meios diferentes das relações tabulares usadas em bancos de dados relacionais.

MongoDB

MongoGB é um dos mais populares banco de dados NoSQL, ele permite guardarmos nossos dados em um modelo semelhante ao JSON, o que torna o nosso banco de dados flexível e escalável.

Um registro no MongoDB é um documento, que é uma estrutura de dados composta de pares de campo e valor. Os valores dos campos podem incluir outros documentos, arrays e arrays de documentos.

O MongoDB armazena documentos em coleções. Coleções são análogas às tabelas em bancos de dados relacionais e documentos às linhas.

Para que você possa continuar conosco nesse capítulo é necessário que você faça o download do MongoDB que está disponível gratuitamente em https://www.mongodb.com.

Supondo que você tenha baixado e instalado o MongoDB, você pode iniciar uma instância do MongoDB no host e na porta padrão com o seguinte comando:

mongod

Se todos os procedimentos foram seguidos corretamente, será apresentando a informação de que o MongoDB foi inicializado, bem como informações da porta que ele está rodando e o host. Neste tutorial vamos utilizar a porta padrão 27017.

Vamos então ao Python!

PyMongo

Para que possamos trabalhar com o MongoDB em Python precisamos instalar o driver PyMongo, para isso utilizaremos o PIP. Vamos então executar o seguinte comando:

pip install pymongo

Aguarde o download e instalação. Para confirmarmos se está tudo certo, vamos digitar no nosso interpretador

import pymongo

Caso nenhum erro tenha sido disparado, signifca que estamos com o pymongo instalado e pronto para ser usado.

Criando um Banco de Dados

Para criarmos um banco de dados no MongoDB é necessário criarmos um objeto MongoClient, em seguida devemos especificar uma URL de conexão com o endereço IP correto e o nome do banco de dados, caso o banco de dados não exista, MongoDB criará para nós e fará uma conexão.

Vejamos então como podemos instanciar o cliente e nos conectar ao MongoDB, criaremos um banco de dados chamado de banco_de_dados:

import pymongo
cliente = pymongo.MongoClient("mongodb://localhost:27017/")
meu_banco = cliente['banco_de_dados']
print(meu_banco)
# Database(MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True), 'base_de_dados')

Importante lembrarmos que no MongoDB um banco de dados não é criado até que ele receba conteúdo. MongoDB esperará até que criemos a coleção (similar a tabela SQL), com pelo menos um documento (similar ao registro SQL) antes de criar o banco de dados.

Verificando se um Banco de Dados existe

Podemos checar se um determinado banco de dados existe executando o método list_database_names()

print(cliente.list_database_names()) # ['local', 'admin']

Observe que nosso banco anterior ainda não está presente, precisamos populá-lo!

Criando uma Coleção

No MongoDB chamamos de coleção o que no SQL seriam as tabelas, são os compartimentos que guardamos nossos dados. Para criarmos uma coleção no MongoDB vamos usar o objeto do banco de dados que definimos anteriormente e especificar o nome da coleção que queremos criar.

import pymongo
cliente = pymongo.MongoClient("mongodb://localhost:27017/")
meu_banco = cliente['banco_de_dados']
colecao = meu_banco['cientistas']

Lembrando novamente que a coleção não será criada até que receba conteúdo!

Checando se uma coleção existe

print(meu_banco.list_collection_names()) # []

Veja que nos retornou uma lista vazia, isso porque nossa coleção só será criada de fato quando inserirmos dados nela, aguarde que logo teremos nossa tão esperada coleção!

Inserindo Documentos no MongoDB

Um documento no MongoDB é como um registro nos bancos de dados SQL. Para que possamos inserir um documento numa coleção do MongoDB usaremos o método insert_one().

O primeiro parâmetro do método insert_one() é um dicionário contendo nomes e valores de cada campo do documento que queremos inserir. Vejamos um exemplo:

import pymongo
cliente = pymongo.MongoClient("mongodb://localhost:27017/")
meu_banco = cliente['banco_de_dados']
colecao = meu_banco['cientistas']
cientista = {"nome": "Donald Knuth", "país": "USA"}
c = colecao.insert_one(cientista)
print(c) # <pymongo.results.InsertOneResult object at 0x7fce96f8f320>

Veja que nos foi retornado um objeto InsertOneResult, que tem a propriedade inserted_id que guarda o id do documento inserido. Agora vamos inserir outro documento na coleção usuarios e retornaremos o valor do campo _id.

novo_cientista = {"nome": "Charles Babbage", "país": "England"}
nc = colecao.insert_one(novo_cientista)
print(nc.inserted_id) # 5fb2505216fa7564711462fc

Observe que em nenhum momento especificamos um _id, MongoDB o fez automaticamente para nós e ele o fará sempre que não o especificarmos.

Inserindo Múltiplos Documentos

PyMongo nos permite inserir diversos documentos em uma coleção, para isso utilizamos o método insert_many(). O primeiro parâmetro de insert_many() é uma lista contendo dicionários dos dados que queremos inserir.

import pymongo
cliente = pymongo.MongoClient("mongodb://localhost:27017/")
meu_banco = cliente['banco_de_dados']
colecao = meu_banco['cientistas']
lista = [
{ "_id": 1, "nome": "Marvin Minsky", "país": "USA"},
{ "_id": 2, "nome": "Dennis Ritchie", "país": "USA"},
{ "_id": 3, "nome": "Edsger Dijkstra", "país": "Netherlands"},
{ "_id": 4, "nome": "Grace Hopper", "país": "USA"},
{ "_id": 5, "nome": "John McCarthy", "país": "USA"}
]
l = colecao.insert_many(lista)
print(l.inserted_ids) # [1, 2, 3, 4, 5]

Observe que dessa vez inserimos manualmente os _ids, porém você pode omití-los se quiser!

Buscando Dados no MongoDB

Para selecionarmos dados de uma coleção no MongoDB podemos usar o método find_one() que nos trará a primeira ocorrência na seleção.

import pymongo
cliente = pymongo.MongoClient("mongodb://localhost:27017/")
meu_banco = cliente['banco_de_dados']
colecao = meu_banco['cientistas']
busca = colecao.find_one()
print(busca) # {'_id': ObjectId('5fb2500b16fa7564711462fb'), 'nome': 'Donald Knuth', 'país': 'USA'}

Buscando Todos os Dados

O método find() nos permite buscarmos por todas as ocorrências, o primeiro parâmetro do método find() é um objeto de query, nesse caso específico vamos deixá-lo vazio para retornarmos todos os documentos da coleção.

import pymongo
cliente = pymongo.MongoClient("mongodb://localhost:27017/")
meu_banco = cliente['banco_de_dados']
colecao = meu_banco['cientistas']
for itens in colecao.find():
print(itens)
# {'_id': ObjectId('5fb252ddbbf4a01d238808d8'), 'nome': 'Donald Knuth', 'país': 'USA'}
# {'_id': ObjectId('5fb252e3bbf4a01d238808d9'), 'nome': 'Charles Babbage', 'país': 'England'}
# {'_id': 1, 'nome': 'Marvin Minsky', 'país': 'USA'}
# {'_id': 2, 'nome': 'Dennis Ritchie', 'país': 'USA'}
# {'_id': 3, 'nome': 'Edsger Dijkstra', 'país': 'Netherlands'}
# {'_id': 4, 'nome': 'Grace Hopper', 'país': 'USA'}
# {'_id': 5, 'nome': 'John McCarthy', 'país': 'USA'}

Buscando Dados Específicos

Se desejarmos que a busca nos retorne apenas campos específicos, o segundo parâmetro do método find() é um objeto descrevendo qual campo incluir no resultado.

O parâmetro é opcional, e se for omitido, todos os campos serão incluídos no resultado.

import pymongo
cliente = pymongo.MongoClient("mongodb://localhost:27017/")
meu_banco = cliente['banco_de_dados']
colecao = meu_banco['cientistas']
for item in colecao.find({'_id': 1}):
print(item) # {'_id': 1, 'nome': 'Marvin Minsky', 'país': 'USA'}

Selecionando apenas cientistas de um país específico:

for item in colecao.find({'país': 'USA'}):
print(item)
# {'_id': ObjectId('5fb252ddbbf4a01d238808d8'), 'nome': 'Donald Knuth', 'país': 'USA'}
# {'_id': 1, 'nome': 'Marvin Minsky', 'país': 'USA'}
# {'_id': 2, 'nome': 'Dennis Ritchie', 'país': 'USA'}
# {'_id': 4, 'nome': 'Grace Hopper', 'país': 'USA'}
# {'_id': 5, 'nome': 'John McCarthy', 'país': 'USA'}

Podemos também usar expressões regulares como filtros de nossas consultas. Vejamos um exemplo de como podemos consultar apenas os cientistas que começam com a letra D:

query = { "nome": { "$regex": "^D" } }
for item in colecao.find(query):
print(item)
# {'_id': ObjectId('5fb252ddbbf4a01d238808d8'), 'nome': 'Donald Knuth', 'país': 'USA'}
# {'_id': 2, 'nome': 'Dennis Ritchie', 'país': 'USA'}

Ordenando o Resultado

O método sort() pode ser usado para ordenar os resultados em ordem ascendente ou descendente.

O método sort() recebe um parâmetro para "fieldname" (campo) e um parâmetro para "direção" (ascendente é a direção padrão).

doc = colecao.find().sort('nome')
for d in doc:
print(d)
# {'_id': ObjectId('5fb252e3bbf4a01d238808d9'), 'nome': 'Charles Babbage', 'país': 'England'}
# {'_id': 2, 'nome': 'Dennis Ritchie', 'país': 'USA'}
# {'_id': ObjectId('5fb252ddbbf4a01d238808d8'), 'nome': 'Donald Knuth', 'país': 'USA'}
# {'_id': 3, 'nome': 'Edsger Dijkstra', 'país': 'Netherlands'}
# {'_id': 4, 'nome': 'Grace Hopper', 'país': 'USA'}
# {'_id': 5, 'nome': 'John McCarthy', 'país': 'USA'}
# {'_id': 1, 'nome': 'Marvin Minsky', 'país': 'USA'}

Para ordenarmos na ordem inversa, usamos -1 como segundo parâmetro, nossa lista será então ordenada de forma descendente.

doc = colecao.find().sort('nome', -1)
for d in doc:
print(d)
# {'_id': 1, 'nome': 'Marvin Minsky', 'país': 'USA'}
# {'_id': 5, 'nome': 'John McCarthy', 'país': 'USA'}
# {'_id': 4, 'nome': 'Grace Hopper', 'país': 'USA'}
# {'_id': 3, 'nome': 'Edsger Dijkstra', 'país': 'Netherlands'}
# {'_id': ObjectId('5fb252ddbbf4a01d238808d8'), 'nome': 'Donald Knuth', 'país': 'USA'}
# {'_id': 2, 'nome': 'Dennis Ritchie', 'país': 'USA'}
# {'_id': ObjectId('5fb252e3bbf4a01d238808d9'), 'nome': 'Charles Babbage', 'país': 'England'}

Deletando Documentos

Para deletarmos documentos utilizamos o método delete_one(), o primeiro parâmetro do método delete_one() é um objeto query definindo o documento a ser deletado.

Importante: Se o query (consulta) encontrar mais de um documento, somente a primeira ocorrência será deletada.

import pymongo
cliente = pymongo.MongoClient("mongodb://localhost:27017/")
meu_banco = cliente['banco_de_dados']
colecao = meu_banco['cientistas']
query = {"nome": "Marvin Minsky"}
colecao.delete_one(query)

Observe que ao executarmos a remoção, nos será retornado um objeto DeleteResult.

Deletando vários Documentos

Caso queiramos deletar mais de um documento, temos o método delete_many() para nos auxiliar, que recebe como primeiro parâmetro um objeto query que definirá quais documentos deletar.

import pymongo
cliente = pymongo.MongoClient("mongodb://localhost:27017/")
meu_banco = cliente['banco_de_dados']
colecao = meu_banco['cientistas']
minha_query = { "nome" : {"$regex": "^J"} }
d = colecao.delete_many(minha_query)

Veja que utilizamos uma expressão regular, nesse caso todos os nomes que começarem com J vão ser deletados. Caso seja necessário deletar todos os documentos, utilizamos o método delete_many() passando um objeto vazio, veja:

d = colecao.delete_many({})

Para vermos quantos documentos foram removidos, podemos acessar o atributo deleted_count:

print(f'Documentos removidos: {d.deleted_count}')
# Documentos removidos: 5

Deletando uma Coleção

Podemos deletar uma coleção de forma muito simples com o método drop(), nesse caso vamos deletar nossa coleção de cientistas:

import pymongo
cliente = pymongo.MongoClient("mongodb://localhost:27017/")
meu_banco = cliente['banco_de_dados']
colecao = meu_banco['cientistas']
colecao.drop()

O método drop() irá retornar True se a coleção for deletada com sucesso, caso contrário retornará False (se ela não existir).

Atualizando Coleções

Podemos atualizar nossos documentos com o método update_one(), que recebe como primeiro parâmetro um objeto query definindo o documento a ser atualizado, se o query encontrar mais de um elemento, somente a primeira ocorrência será atualizada, o segundo parâmetro é um objeto definindo os novos valores do documento.

import pymongo
cliente = pymongo.MongoClient("mongodb://localhost:27017/")
meu_banco = cliente['banco_de_dados']
colecao = meu_banco['cientistas']
query = { "nome": "Marvin Minsky" }
novos_valores = { "$set": { "país": "China"} }
colecao.update_one(query, novos_valores)
for x in colecao.find():
print(x)
# {'_id': 1, 'nome': 'Marvin Minsky', 'país': 'China'}
# {'_id': 2, 'nome': 'Dennis Ritchie', 'país': 'USA'}
# {'_id': 3, 'nome': 'Edsger Dijkstra', 'país': 'Netherlands'}
# {'_id': 4, 'nome': 'Grace Hopper', 'país': 'USA'}
# {'_id': 5, 'nome': 'John McCarthy', 'país': 'USA'}

Atualizando Diversas Coleções

Para atualizarmos todos os documentos que atendam ao critério de nosso query, utilizamos o método update_many(). Nesse caso vamos atualizar todos os endereços que começam com a letra R.

import pymongo
cliente = pymongo.MongoClient("mongodb://localhost:27017/")
meu_banco = cliente['banco_de_dados']
colecao = meu_banco['cientistas']
minha_query = { "país": { "$regex": "^U"} }
valores_novos = { "$set": { "país": "Índia" } }
x = colecao.update_many(minha_query, valores_novos)
for itens in colecao.find():
print(itens)
# {'_id': 1, 'nome': 'Marvin Minsky', 'país': 'China'}
# {'_id': 2, 'nome': 'Dennis Ritchie', 'país': 'Índia'}
# {'_id': 3, 'nome': 'Edsger Dijkstra', 'país': 'Netherlands'}
# {'_id': 4, 'nome': 'Grace Hopper', 'país': 'Índia'}
# {'_id': 5, 'nome': 'John McCarthy', 'país': 'Índia'}

O atributo modified_count nos informa quantos documentos foram atualizados:

print(f'Documentos atualizados: {x.modified_count}') # 3

Limitando o Resultado

Se desejarmos limitar nosso resultado apenas a uma certa quantidade, podemos usar o método limit(). O método limit() recebe um parâmetro: um número que define quantos documentos retornar.

Para usá-lo é muito simples:

limite = colecao.find().limit(3)
for l in limite:
print(l)
# {'_id': 1, 'nome': 'Marvin Minsky', 'país': 'China'}
# {'_id': 2, 'nome': 'Dennis Ritchie', 'país': 'Índia'}
# {'_id': 3, 'nome': 'Edsger Dijkstra', 'país': 'Netherlands'}

Esse pequeno capítulo teve como objetivo nos mostrar como podemos trabalhar com MongoDB no Python, embora seja uma tecnologia "recente", os bancos de dados NoSQL estão crescendo muito e nos prometem muita flexibilidade e escalabilidade.