Olá, dev! Seja bem-vindo ao Dia 04 do seu plano de preparação para a certificação MongoDB Associate Developer. O tema de hoje é um dos mais cobrados na prova: como consultar dados aninhados (Embedded Documents) e coleções de elementos (Arrays), dominando a Notação de Ponto e o uso preciso do operador $elemMatch.
1. Consultando Documentos Embutidos (Embedded Documents)
Documentos embutidos permitem armazenar dados relacionados diretamente no mesmo documento pai. Existem duas maneiras de consultar subdocumentos, com comportamentos drasticamente diferentes:
A. Correspondência Exata de Subdocumento (Exact Embedded Match)
// Exige que o subdocumento 'endereco' seja EXATAMENTE igual, na mesma ordem de campos
db.usuarios.find({
endereco: { cidade: "Uberaba", estado: "MG" }
})
Armadilha do Exame: Se o documento no banco for { endereco: { estado: "MG", cidade: "Uberaba" } } (ordem invertida) ou possuir um campo extra como pais: "Brasil", a consulta acima não retornará nada.
B. Notação de Ponto (Dot Notation)
Para consultar subcampos de forma flexível e independente da ordem ou de outros campos presentes, envolva o caminho do campo entre aspas:
// Busca qualquer usuario que tenha cidade = "Uberaba", independentemente da ordem ou outros campos
db.usuarios.find({
"endereco.cidade": "Uberaba"
})
2. Consultas a Arrays Simples
A. Correspondência de Elemento Único e Correspondência Exata
// Retorna documentos onde o array 'habilidades' CONTÉM o elemento "MongoDB"
db.developers.find({ habilidades: "MongoDB" })
// Retorna documentos onde o array 'habilidades' é EXATAMENTE igual a ["MongoDB", "Kotlin"] (mesma ordem e tamanho)
db.developers.find({ habilidades: ["MongoDB", "Kotlin"] })
B. Operadores `$all` e `$size`
$all: Seleciona documentos cujo array contém todos os elementos especificados, independentemente da ordem.$size: Filtra documentos pelo número exato de elementos no array.
// Busca desenvolvedores que possuem TANTO "MongoDB" QUANTO "Kotlin" no array habilidades
db.developers.find({ habilidades: { $all: ["MongoDB", "Kotlin"] } })
// Busca desenvolvedores com exatamente 3 habilidades cadastradas
db.developers.find({ habilidades: { $size: 3 } })
C. Acesso por Índice do Array
Utilize a Notação de Ponto com a posição numérica do elemento:
// Busca documentos onde a PRIMEIRA habilidade (índice 0) é "Kotlin"
db.developers.find({ "habilidades.0": "Kotlin" })
3. O Poderoso Operador `$elemMatch` em Arrays de Objetos
Quando um array armazena objetos/documentos embutidos, surge uma armadilha comum. Suponha a coleção de pedidos:
{
_id: 101,
cliente: "Ada Lovelace",
itens: [
{ produto: "Teclado Mecânico", qtd: 2, preco: 350 },
{ produto: "Mousepad XL", qtd: 10, preco: 80 }
]
}
O Problema sem `$elemMatch`:
// Consulta SEM $elemMatch:
db.pedidos.find({
"itens.qtd": { $gt: 5 },
"itens.preco": { $lt: 100 }
})
Esta consulta retornará o documento acima! Por quê? O MongoDB verifica se algum item tem qtd > 5 (o Mousepad tem) e se algum item tem preco < 100 (o Mousepad tem). As condições são satisfeitas no array, mas não necessariamente no mesmo elemento.
A Solução com `$elemMatch`:
O operador $elemMatch obriga o MongoDB a encontrar pelo menos um único elemento do array que satisfaça TODAS as condições simultaneamente:
db.pedidos.find({
itens: {
$elemMatch: {
qtd: { $gt: 5 },
preco: { $lt: 100 }
}
}
})
4. Projeções com Arrays
Além de filtrar documentos, é possível controlar quais elementos de um array são retornados na resposta:
A. Projeção com `$elemMatch`
Retorna no resultado apenas o primeiro elemento do array que atende à condição especificada:
db.pedidos.find(
{ cliente: "Ada Lovelace" },
{ cliente: 1, itens: { $elemMatch: { preco: { $gt: 300 } } } }
)
B. Operador Posicional de Projeção `$`
Retorna o primeiro elemento do array que coincidiu com o filtro da consulta principal:
db.pedidos.find(
{ "itens.produto": "Mousepad XL" },
{ cliente: 1, "itens.$": 1 }
)
C. Operador `$slice`
Limita a quantidade de elementos retornados de um array:
// Retorna apenas os últimos 2 itens do array
db.pedidos.find({}, { cliente: 1, itens: { $slice: -2 } })
// Pula 1 elemento e retorna os próximos 2
db.pedidos.find({}, { cliente: 1, itens: { $slice: [1, 2] } })
5. Questões no Estilo Exame de Certificação
Questão 1: Considere o documento: { _id: 1, alunos: [{ nome: "Ana", nota: 8 }, { nome: "Bruno", nota: 5 }] }. Qual das consultas a seguir retornará este documento?
A) db.turmas.find({ "alunos.nome": "Ana", "alunos.nota": 5 })
B) db.turmas.find({ alunos: { $elemMatch: { nome: "Ana", nota: 5 } } })
C) db.turmas.find({ alunos: { $size: { $gte: 2 } } })
D) db.turmas.find({ "alunos.0.nota": 5 })
Resposta Correta: A. Sem o $elemMatch, a consulta A verifica se existe algum elemento com nome "Ana" (existe) e algum elemento com nota 5 (Bruno tem). As condições são satisfeitas separadamente e o documento é retornado. A opção B não retorna pois nenhum aluno individual possui nome "Ana" AND nota 5. C lança erro pois $size não aceita operadores de comparação.
Questão 2: Como consultar uma coleção de cursos para encontrar documentos onde o array topicos contenha os valores "BSON" e "Indexação", independentemente de outros tópicos e da ordem?
A) db.cursos.find({ topicos: ["BSON", "Indexação"] })
B) db.cursos.find({ topicos: { $all: ["BSON", "Indexação"] } })
C) db.cursos.find({ "topicos.$": ["BSON", "Indexação"] })
D) db.cursos.find({ topicos: { $elemMatch: ["BSON", "Indexação"] } })
Resposta Correta: B) `topicos: { $all: ["BSON", "Indexação"] }`. O operador $all garante a presença de todos os itens informados no array sem impor ordem ou exclusividade.