- Mario Piccin RA: 1801551
- Isabela Silva RA:2500631
A empresa Amazonas do ramo de e-commerce deseja acompanhar os fluxos de cliques de seus clientes, bem como rastrear os produtos que eles compram.Neste momento, a empresa vende livros, CDs e pequenos eletrodomésticos de cozinha apenas, mas provavelmente irá expandir para outros produtos no futuro. Amazonas quer ser capaz de responder às seguintes perguntas:
- Qual é a média de produtos comprados por cliente?
- Quais são os 20 produtos mais populares por estado dos clientes?
- Qual é o valor médio das vendas por estado do cliente?
- Quantos de cada tipo de produto foram vendidos nos últimos 30 dias?
Vocês foram contratados para decidir qual a melhor maneira de organizar esses dados, seja em uma coleção única ou múltiplas, e devem definir quais campos específicos que cada produto deve ter e os porquês. Devem produzir um ou mais eventos de sistêmicos no formato JSON para exemplificar as decisões. Devem escolher uma das quatro perguntas de negócio e gerar um comando MongoDB para responder.
Optamos por duas coleções especializadas para atender diferentes necessidades de negócio:
Coleção transactions
:
- Propósito: Dados transacionais de compra para métricas de negócio
- Frequência: Baixa (apenas quando há compra)
- Volume: Moderado
- Consultas: Agregações complexas para relatórios
Coleção clickstream
:
- Propósito: Rastreamento comportamental e fluxo de navegação
- Frequência: Muito alta (cada clique/interação)
- Volume: Muito alto
- Consultas: Análises de comportamento, funis de conversão, heatmaps
Vantagens da Separação:
- Performance: Consultas de clickstream não impactam relatórios de vendas
- Escalabilidade: Cada coleção pode ser otimizada independentemente
- Índices: Estratégias de indexação específicas para cada caso de uso
Campos do Cliente:
customer_id
: Identificação única do clientecustomer_name
: Nome completo do cliente (para personalização e suporte)customer_email
: Email do cliente (comunicação e identificação secundária)customer_state
: Estado do cliente (essencial para 2 das 4 perguntas propostas)customer_city
: Cidade (para análises geográficas detalhadas)customer_segment
: Segmento do cliente (novo, recorrente, VIP - para análises de retenção)
Por que incluir dados do cliente nas transações? Vantagens:
- Performance: Consultas mais rápidas sem JOINs complexos
- Disponibilidade: Dados essenciais sempre acessíveis mesmo se sistema de clientes estiver indisponível
- Simplicidade: Queries diretas para todas as métricas de negócio
- Histórico: Preserva estado do cliente no momento da compra
Campos Essenciais Incluídos:
- Nome e Email: Para relatórios personalizados e suporte ao cliente
- Localização: Fundamental para as consultas com análises por região
- Segmento: Análises de retenção e valor do cliente
Dados NÃO Incluídos (mantidos em sistema separado):
- Senha e dados sensíveis de autenticação
- Histórico de endereços detalhado
- Preferências complexas de usuário
- Dados de pagamento (PCI compliance)
Consistência de Dados:
- Dados críticos (nome, email) podem ser atualizados via triggers ou batch jobs
- Dados analíticos (segmento) podem ter pequena latência aceitável
- Estado/cidade raramente mudam, baixo risco de inconsistência
Campos da Transação:
transaction_id
: ID único da transaçãotransaction_date
: Data/hora da compra (crucial para análise dos últimos 30 dias)total_amount
: Valor total da compra
Campos dos Produtos:
products
: Array de produtos comprados (um cliente pode comprar múltiplos itens)- Cada produto contém:
product_id
: ID único do produtoname
: Nome do produtocategory
: Categoria (livros, CDs, eletrodomésticos)price
: Preço unitárioquantity
: Quantidade comprada
Campos de Controle:
_id
: Identificador único do documento no MongoDB (ObjectId gerado automaticamente)created_at
: Timestamp de criação do registro (auditoria e troubleshooting)updated_at
: Timestamp da última modificação (controle de versão e sincronização)
Campos do Evento:
event_id
: ID único do eventosession_id
: ID da sessão do usuáriocustomer_id
: ID do cliente (se logado, pode ser null para usuários anônimos)anonymous_id
: ID para rastreamento de usuários não logadosevent_timestamp
: Timestamp preciso do eventoevent_type
: Tipo do evento (page_view, product_click, add_to_cart, search, purchase, etc.)
Campos de Contexto:
page_url
: URL da página atualpage_title
: Título da página (SEO e análise de conteúdo)previous_page
: URL da página anterioruser_agent
: Informações do navegadorip_address
: IP do usuário (para geolocalização)
Campos de Marketing:
utm_source
: Fonte do tráfego (google, facebook, email)utm_medium
: Meio de marketing (organic, cpc, social, email)utm_campaign
: Campanha específica (para ROI de marketing)referrer
: Site de origem do tráfego
Campos de Produto (quando aplicável):
product_id
: ID do produto clicado/visualizadoproduct_category
: Categoria do produtosearch_query
: Termo de busca (se aplicável)search_results_count
: Número de resultados encontrados (para análise de busca)
Campos de Performance:
session_duration
: Duração da sessão até o momento (em segundos)page_load_time
: Tempo de carregamento da página (em segundos)screen_resolution
: Resolução da tela (para análise de UX)
Campos Específicos por Evento:
transaction_id
: ID da transação (apenas para evento purchase)purchase_value
: Valor da compra (apenas para evento purchase)
Campos Técnicos:
device_type
: mobile, desktop, tablet
Campos de Controle:
_id
: Identificador único do documento no MongoDB (ObjectId gerado automaticamente)created_at
: Timestamp de criação do evento (essencial para ordenação temporal e debugging)
Observação sobre updated_at em clickstream:
- Não utilizamos
updated_at
na coleção clickstream pois eventos são imutáveis (write-once) - Uma vez registrado, um clique não deve ser modificado, mantendo integridade do tracking
- Caso seja necessário corrigir um evento, o padrão é criar um novo evento de correção
Usuários Logados:
customer_id
: Preenchido com ID do clienteanonymous_id
: null
Usuários Anônimos:
customer_id
: nullanonymous_id
: ID único gerado para rastreamento (baseado em device fingerprint, IP, etc.)
Conversão Anônimo → Autenticado:
- Quando usuário faz login, eventos subsequentes passam a ter
customer_id
anonymous_id
pode ser mantido para análise de jornada completa- Permite conectar comportamento pré e pós login
Usando coleção transactions
:
- P1. Qual é a média de produtos comprados por cliente?: Group usando
$unwind
nos produtos + group por customer_id - P2. Quais são os 20 produtos mais populares por estado dos clientes?: Group de estado + produto com count de vendas
- P3. Qual é o valor médio das vendas por estado do cliente?: Group de estado com average do total_amount
- P4. Quantos de cada tipo de produto foram vendidos nos últimos 30 dias?: Filter por data + group por categoria
Sugestões de análises usando a coleção clickstream
:
- Funil de conversão: page_view → product_click → add_to_cart → purchase
- Taxa de abandono: Análise de sessões que não resultaram em compra
- Produtos mais visualizados: Count de eventos product_view por produto
- Jornada do cliente: Sequência de eventos por session_id
Análises Cross-Collection:
- Conversion rate: Visualizações (clickstream) vs vendas (transactions)
- Produtos com baixa conversão: Muitas visualizações, baixa venda
- Efetividade de busca: Consultas realizadas vs compras
Para a coleção transactions
:
- Novos tipos de produto: Apenas adicionar novas categorias no campo
category
- Novos campos de produto: Estrutura flexível permite campos específicos por categoria
- Múltiplas moedas: Adicionar campo
currency
para expansão internacional
Para a coleção clickstream
:
- Novos tipos de evento: Fácil adição de event_types (wishlist, review, share)
- Personalização: Dados podem ser utilizados para enriquecimento de algoritmos de recomendação
- Machine Learning: Dados estruturados para modelos preditivos
A indexação é crucial no MongoDB pois acelera dramaticamente as consultas, evitando a varredura de cada documento de uma coleção, o que é altamente ineficiente. Sem índices, o MongoDB precisa percorrer todos os dados, enquanto um índice, uma estrutura de dados especial, permite encontrar documentos relevantes de forma rápida e direcionada. Embora melhore as consultas, um excesso de índices pode prejudicar as operações de escrita, sendo essencial um equilíbrio para otimizar o desempenho geral.
Índice | Pergunta(s) Atendida(s) | Justificativa | Tipo de Operação |
---|---|---|---|
{ 'customer_state': 1 } |
P2: Top 20 produtos por estado P3: Valor médio das vendas por estado |
Filtragem e agrupamento por estado em múltiplas análises geográficas | $match , $group |
{ 'transaction_date': -1 } |
P4: Produtos vendidos nos últimos 30 dias Análises temporais |
Filtros por período (últimos X dias) - ordenação descendente para queries de período recente | $match com range de datas |
{ 'customer_id': 1 } |
P1: Média de produtos por cliente Análises de comportamento |
Agrupamento por cliente para métricas individuais e análises de retenção | $group , $addToSet |
{ 'customer_segment': 1 } |
Segmentação de clientes Análises de valor |
Filtragem e agrupamento por segmento (novo, recorrente, VIP) | $match , $group |
{ 'customer_email': 1 } |
Lookup de clientes Suporte ao cliente |
Busca rápida por cliente específico via email | $match exato |
{ 'products.category': 1 } |
P2: Top produtos por estado P4: Produtos por categoria nos últimos 30 dias |
Filtragem e agrupamento por categoria de produto após $unwind |
$match , $group em arrays |
{ 'products.product_id': 1 } |
P2: Top produtos por estado Cross-collection: Conversion rate |
Identificação de produtos específicos após $unwind para análises detalhadas |
$match , $group em arrays |
Índice | Pergunta(s) Atendida(s) | Justificativa | Benefício |
---|---|---|---|
{ 'customer_state': 1, 'transaction_date': -1 } |
Análises geográficas temporais Ex: Vendas por estado no último trimestre |
Combinação de filtro geográfico + temporal em uma única operação | Evita multiple index scan |
{ 'customer_segment': 1, 'customer_state': 1 } |
Segmentação regional Ex: Clientes VIP por estado |
Análise cruzada de segmento de cliente com localização | Suporte a queries multidimensionais |
{ 'transaction_date': -1, 'products.category': 1 } |
P4 otimizada: Produtos por categoria em período específico | Filtro temporal seguido de agrupamento por categoria | Pipeline eficiente para queries temporais + categoria |
Índice | Pergunta(s) Atendida(s) | Justificativa | Tipo de Operação |
---|---|---|---|
{ 'event_timestamp': -1 } |
Análises temporais Ordenação cronológica |
Queries por período (últimos X dias/horas) e ordenação de eventos | $match range, $sort |
{ 'customer_id': 1, 'event_timestamp': -1 } |
Jornada do cliente Sequência de eventos por usuário |
Buscar todos eventos de um cliente específico ordenados por tempo | Compound query otimizada |
{ 'session_id': 1 } |
Análise de sessões Taxa de abandono |
Agrupamento de eventos por sessão para calcular funis e abandonos | $group , $match |
{ 'event_type': 1 } |
Funil de conversão Métricas por tipo de evento |
Filtragem por tipo de evento específico (page_view, purchase, etc.) | $match , $group |
{ 'product_id': 1 } |
Produtos mais visualizados Cross-collection: Conversion rate |
Análise de produtos específicos no clickstream | $match , $group |
{ 'product_category': 1 } |
Funil por categoria Interesse por categoria |
Agrupamento de eventos por categoria de produto | $group , $match |
Índices para Marketing e Analytics
Índice | Pergunta(s) Atendida(s) | Justificativa | Benefício Específico |
---|---|---|---|
{ 'utm_source': 1, 'utm_medium': 1 } |
Performance de marketing por canal UTM ROI por campanha |
Análise de efetividade de canais de marketing | Attribution modeling |
{ 'event_type': 1, 'product_category': 1 } |
Funil de conversão por categoria Eventos específicos por categoria |
Combinação de tipo de evento com categoria para análises detalhadas | Pipeline otimizado |
{ 'session_id': 1, 'event_timestamp': 1 } |
Jornada temporal da sessão Duração de sessão |
Ordenação de eventos dentro de cada sessão | Análise comportamental |
Índice TTL (Time To Live)
Índice TTL | Função | Configuração | Benefício |
---|---|---|---|
{ 'event_timestamp': 1, expireAfterSeconds: 7776000 } |
Limpeza automática de dados antigos | Remove documentos após 90 dias | Gestão automática de storage, compliance LGPD |
Antes de iniciar o desenvolvimento do caso de uso, é necessário preparar o ambiente para garantir que todas as dependências estejam corretamente instaladas e que a conexão com o MongoDB funcione de forma adequada.
- Para clonar o repositório com o container utilizado no caso de uso, execute o seguinte comando:
git clone https://github.com/stailer37/impacta-labs.git
- Para construir a imagem, navegue para a pasta impacta-labs/nosql-databases/mongodb e execute o seguinte comando:
docker build -t impacta_labs_mongodb .
- Para iniciar o container, execute o seguinte comando:
docker run -d -p 27017:27017 --name mongodb-standalone impacta_labs_mongodb
- Para acessar o shell do MongoDB, execute o seguinte comando:
docker exec -it mongodb-standalone mongosh
- Para navegar no banco de dados, execute o seguinte comando:
db
- Para exibir as coleções do banco de dados, execute o seguinte comando:
show collections
- Para sair do shell do MongoDB, execute o seguinte comando:
exit
- Artefatos do caso de uso.
caso_estudo_marisa/
├── command_mongo.sh
├── collection_transactions.json
├── collection_clickstream.json
└── README.md
- Para executar o arquivo command_mongo.sh, execute o seguinte comando:
chmod +x command_mongo.sh
./command_mongo.sh
- P1. Qual é a média de produtos comprados por cliente?
docker exec -i mongodb-standalone mongosh test --eval '
db.transactions.aggregate([
// 1) Explode o array de produtos
{ $unwind: "$products" },
// 2) Agrega por cliente e soma quantidade de produtos
{
$group: {
_id: "$customer_id",
total_products: { $sum: "$products.quantity" }
}
},
// 3) Calcula a média de produtos por cliente
{
$group: {
_id: null,
avg_products_per_customer: { $avg: "$total_products" }
}
},
// 4) Projeta a saída final com arredondamento
{
$project: {
_id: 0,
media_produtos_comprados_por_cliente: { $round: ["$avg_products_per_customer", 2] }
}
}
]).forEach(printjson);
'