Model: Document Store (MongoDB)
História
Open Source, free, multiplataforma, escrito em C++Introdução
O modelo orientado a documentos é o mais usado dentre todos os modelos de bancos NoSQL.MongoDB é o banco mais usado dentro de todos os modelos, e dentro do modelo orientado a Documentos.
MongoDB aceita comandos em JavaScript.
- Database é composta de collections.
- Collection é composta de documentos. Analoga a Table.
- Document é a menor unidade dentro do banco. É o que identifica o registro, é a referência de uma tupla. Dados e documentos devem ser autocontidos e auto descritivos.
Estruturação de dados: Document => Tupla/Registro.
BSON é a forma de armazenamento dos dados.
Recebe os valores no formato JSON e os armazena em um formato binário chamado BSON.
- Serialização codificada em binário de documentos semelhantes a JSON
- Contém extensões que permitem a representação de tipos de dados que não fazem parte da espeficação JSON JSON lida apenas com dados primitivos.
BSON lida com dados além dos primitivos, como ObjectID, Date, etc.
Usabilidade
Alta performance.Permitem redundâncias e incosistências.
Scheme Free, podendo usar JSON, XML, etc.
- Armazena os dados em BSON (Binary JSON) Que são pares de chaves e valores.
- Permite que dentro de uma collection pode-se ter documentos de estruturas diferentes Não que isso seja uma boa prática.
- Suporte a indices Consultas mais complexas ou para garantir algumas integridades.
- Auto-Sharding Nativamente feito para escalar horizontalmente. Mas permiti escalar verticalmente com réplica de leitura.
- Map-Reduce Ferramenta poderosa de consulta e agregação para grandes volumes de dados.
- GridFS Armazenamento de arquivos.
- Rica linguagem de consulta Permiti fazer quase tudo dentro do banco de dados.
MongoDB
- Grande volume de dados com alta performance
- Dados não necessariamente estruturados
Quando usar
- Necessidade de relacionamentos/joins
- Propriedades ACID e transações são importantes
- Curiosidade: Diversas entidades de pagamento não homologam Os dados financeiros dos clientes precisam estar em um banco de dados relacional tradicional.
Quando não usar
Schema Design
Atomicidade (Transação executada por completo ou não (rollback) ) somente a nível de documento.- Embedding Boa prática: SIM
- Consulta informações em uma única query
- Atualiza o registro em uma única operação
- Limite 16MB por documento
- Reference Boa prática: NÃO
- Documentos pequenos (Não armazena as references)
- Não duplica informações
- Usado quando os dados não são acessados em todas as consultas Para cada subdocumento é realizado uma nova query, a responsabilidade dos joins do banco é transferida para a app, aí entra toda uma questão de rede, de latência, e isso pode comprometer a performance.
- Perde Atomicidade
- Duas ou mais queries ou utilização do $lookup $lookup é o comando para executar queries com referências.
Relationships: Como os dados vão se relacionar entre si? Há duas formas de modelar
Documentos autocontidos, onde todas as informações necessárias vão estar dentro dele, sem a necessidade de fazer referência para outros documents ou collectins.
Prós
Contras
Documents com dependência de outros documentos ou collections.
Não existe o conceito de FK (Forein Key), então não há uma constraint para garantir esses relacionamentos.
As references serão realizadas através de atributos dentro dos documents ou collectins.
*Dados não devem depender de outros dados, pode ocasionar redundâncias.
Prós
Contras
Scheme design: Embedding vs Reference
Boas Práticas
- Evitar documentos muito grandes.
- Nomes significativos e curtos Porque documentos são compostos não somente com valores, mas também com os seus schemes.
- Analise as queries usando explain() MongoDB Compass mostra gráficamente uma análise das queries.
- Atualize apenas os campos alterados Comando update recebe JSON por completo, problema disso é que vai realizar toda a substituição binária da informação.
- Evite negações em queries MongoDB não consegue indexar condições e valores negativos, então nestes casos ele faz um escaneamento completo da collection.
- Listas/Arrays dentro de documentos não podem crescer sem limite Porque podem gerar problemas de performance.
Visando a questão de espaço de armazenamento e clareza de significados as informações.
MongoDB tem esse comando explain() para dar no final da querie, que mostra uma análise.
Recomendado, atualizar somente os campos necessários e não o JSON por completo.
One-to-one
Prefira atributos key-value no documento. Sem subdocumentos, listas ou referências.
{
"_id": ObjectId("5c8f8f8f8f8f8f8f8f8f8f8"),
"name": "Dio",
"street": "Rua das Flores",
"number": "123"
}
One-to-few
Prefira embedding, visando que o relacionamento do documento não seja tão grande.
{
"_id": ObjectId("5c8f8f8f8f8f8f8f8f8f8f8"),
"name": "Dio",
"addresses": [
{"street": "Rua das Flores", "number": "123"},
{"street": "Av das Perolas", "number": "100"}
]
}
Enquanto houver um número limitado de subdocumentos dentro desse array é preferível que utilize subdocumentos.Agora, supondo que tenhamos muitos relacionamentos, como em uma rede social, muitas informações como subdocumento não seria viável, pelo limite de tamanho do documento e pela dificuldade de recuperação dos dados.
Quanto maior o tamanho do documento, maior será o override da consulta.
One-to-many e Many-to-many
Prefira references.
{
"_id": ObjectId("5c8f8f8f8f8f8f8f8f8f8f8"),
"name": "Dio",
"addresses": [ ObjectId('123'), ObjectId('456') ]
}
// addresses
{
"_id": ObjectId("123"),
"street": "Rua das Flores",
"number": "123"
}
{
"_id": ObjectId("456"),
"street": "Av das Perolas",
"number": "100"
}
Patterns de acordo com casos de uso para escolha de Schemes design
Scheme design: Patterns according to use cases
Conceitos na prática
MongoDB - Client
Robo 3T é um cliente para MongoDB.Compass é um cliente para MongoDB. Permite conectar-se ao MongoDB - Cloud
MongoDB - Cloud
MongoDB cloud é uma ferramenta paga, mas existe opção gratuíta para teste.Comando nativo para conectar-se ao MongoDB
Este comando apresenta uma string de conexão (conectar com os clients) e o mongo shell (enviar comandos diretamente).-p refere-se a password.
-u refere-se a username.
mongo --host 127.0.0.1:27017 -p dio -u dio
Dentro de uma instância podem existir várias databases
// LISTA databases
show databases
show db_name
// SELECIONA se existir
// CRIA se não existir
use db_name
Operações de manipulação de dados
- Explicitamente Comando createCollection(), permite passar alguns validadores, por exemplo, tamanho máximo do documento ou da collection, entre inumeros outros.
Criar collections
db.createCollection("name_collection");
db.createCollection("name_collection", {capped: true, max: 2, size: 2});
db.name_collection.insertOne( {'name': 'teste 1'} );
- Todas as collactions
Consultar collactions
show collections;
- Todos os documentos da collaction especificada É necessário passar o parâmetro (1º parametro) de macth (query), que é um objeto contendo pares de key e value usados para realização dos filtros.
Consultar documentos
Quando a query for passada vazia, retorna todos os documentos.
db.name_collection.find({});
Limita o número de documentos retornados em 1 (Retorna o primeiro que encontrar).
db.name_collection.find({'age': 40}).limit(1);
db.name_collection.find({'gender': 'woman','age': 40});
Filtra todos os documentos que tenham a idade igual a 40 ou 41 ou 42 anos.
db.name_collection.find( {'age': {$in: [40, 41, 42]} } );
Filtra todos os documentos que sejam do sexo feminino OU que tenham a idade de 41 anos.
db.name_collection.find( { $in: [ {'gender': 'woman'}, {'age': 41}] } );
lte - menor ou igual.
db.name_collection.find( {'age': {$lt: 45} } );
db.name_collection.find( {'age': {$lte: 45} } );
Recomendação: Quando o filtro for aplicado para o mesmo atributo é preferivel usar o operador in.- Inserir um documento Retorna o ID do objeto criado.
Inserir documentos em collactions
MongoDB gera o ID e o indice do ID de forma automática.
db.name_collection.insertOne( {'name': 'teste 1'} );
db.name_collection.insert(
[
{'name': 'teste 1'},
{'name': 'teste 2'},
{'name': 'teste 3'}
]
);
- Comando save() Recebe documento ou ID, e realiza a atualização se existir ou a criação se não existir.
Atualização
Realiza atualização do documento por completo.
db.name_collection.save( {'name': 'teste 1'} );
- 1º - objeto com os campos para filtrar, condição (query)
- 2º - objeto com os campos a serem atualizados
- 3º - objeto com as configurações (há inumeras opções) multi = true - atualiza todos os documentos que atendem a condição.
Obs: A partir da versão 3.2, está disponivel o comando updateMany(), no qual não é necessário passar as configurações.
Se atributo existir, atualiza, caso contrário cria novo atributo.
db.name_collection.update(
{'name': 'teste 1'},
{'$set': {'age': 40} }
);
db.name_collection.update(
{'age': 40},
{'$set': {'age': 45} },
{ multi = true }
);
Parametros:
- 1º - objeto com os campos para filtrar, condição (query)
- 2º - objeto com os campos a serem atualizados
db.name_collection.updateMany(
{'age': 45},
{'$set': {'age': 50} }
);
- deleteOne() Exclui somente um documento. Se usado em uma query que retorna mais de um documento, exclui o primeiro.
Excluir documentos em collactions
// Return um count de 1
db.name_collection.deleteOne( {'age': 50} );
// Return um count de N (quantidade de documentos excluidos)
db.name_collection.deleteMany( {'age': 50} );
Performance e indices
- Escalonamento
- Disco
- Rede
- Indices Mesmo conceito dos bancos relacionais. Fornecer uma direção de onde estão os dados para acesso mais rápido.
Fatores Importantes
A única diferença é que nos bancos relacionais o objetivo é prevenir uma table scan, e no MongoDB é prevenir o scan collection.
- Exemplo Prático (Robo 3T)
- 1. Criar uma collection
- 2. Criar um script para inserir dados em massa
// Script em JavaScript para inserir dez mildocumentos
for(var i=0, i < 10000; i++){
db.name_collection.insert({name: 'teste ' + i, age: i});
}
// Consultar a quantidade de documentos inseridos
db.getCollection('teste').count();
// Listar todos os documentos
db.getCollection('teste').find({});
- 3. Realizar query com o comando explain()
// Consulta pelo ID (Não realiza um scan collection)
// Vai direto no ID através do indice, 1 documento examinado
db.getCollection('teste').find({_id: ObjectId('5c8f8f8f8f8f8f8f8f8f8f8')}).explain(true);
// Consulta (Realiza um scan collection), 10000 documentos examinados
db.getCollection('teste').find({name: 'teste0'}).explain(true);
- 4. Criar indice (recebe dois parâmetros)
4.2. Nome do indice.
// Indexar campo "name" de forma ordenada
db.getCollection('teste').createIndex({name: 1}, {'name':'idx_name'});
Agregações
Agregação é o procedimento de processar dados em uma ou mais etapas, onde o resultado de cada etapa é usado na etapa seguinte, de modo a retornar um resultado combinado.Exemplo: Importação de um banco de dados de teste no MongoDB Cloud.
Opção "Clusters", clicar nos tres pontinhos (...) e selecionar "Load Sample Dataset".
- count Retorna a quantidade de documentos.
Agregação de propósito único
Não permitem customizações.
db.getCollection('teste').count({});
db.getCollection('teste').distinct('name');
- $group Agrupar dados conforme um campo, e realizar diversas operações matemáticas, por exemplo: count, average, min, max, etc.
Agregação de pipeline
Pipeline is a technique for implementing instruction-level parallelism within a single processor. A segmentação de instruções (em inglês, pipeline) é uma técnica de hardware que permite que a CPU realize a busca de uma ou mais instruções além da próxima a ser executada.Permitem customizações. Recebem um array como parâmetro. As pipelines mais básicas fornecem "filtros" e "operadores".
Permite passar um número infinito de processamento.
Filtros são as condições que vão dar match para descobrir quais dados serão processados.
Operadores são as funções que vão manipular os dados e dar input para as próximas etapas.
db.getCollection('teste').aggregate([
{
$group: { _id: '$name', count: {$sum: 1} }
}
]);
db.getCollection('teste').aggregate([
{
$addFields: { 'new_field': {$sum: 1} }
}
- $sum
Funções
db.getCollection('teste').aggregate([
{
$group: { _id: '$name', count: {$sum: 1}, sum: {$sum: '$age'} }
}
]);
db.getCollection('teste').aggregate([
{
$group: { _id: '$name', count: {$sum: 1}, average: {$avg: '$age'} }
}
]);
db.getCollection('teste').aggregate([
{
$group: { _id: '$name', count: {$sum: 1}, max: {$max: '$age'} }
}
]);
db.getCollection('teste').aggregate([
{
$group: { _id: '$name', count: {$sum: 1}, min: {$min: '$age'} }
}
]);
- $and
Operadores Lógicos
Função match responsável por aplicar os filtros. Recebem um array como parametro.
db.getCollection('teste').aggregate([
{
$match: { $and: [ {gender: 'woman'}, {age: {$gt: 20}} ] }
}
]);
db.getCollection('teste').aggregate([
{
$match: { $or: [ {gender: 'woman'}, {age: {$gt: 20}} ] }
}
]);
- $gt Maior que
- $lt Menor que
- $nte Diferente de
- $eq Igual
- $lte Menor ou igual
- $gte Maior ou igual