MongoDB: Consultas a matrizes e documentos aninhados
Última atualização: 2026-08-26
Documentos aninhados e matrizes são recursos essenciais do MongoDB — é preciso dominá-los para aproveitar ao máximo o modelo de documentos.
Este curso oferece uma análise aprofundada sobre consultas em matrizes, notação de ponto em documentos aninhados, correspondência exata com $elemMatch e operações de atualização de matrizes.
1. O que você vai aprender
- Consulta de documentos aninhados usando a notação de ponto
- Consultas a campos de matriz (elemento único, vários elementos)
- $elemMatch: Correspondência exata com um elemento do mesmo array
$Atualização do marcador de posição de localização- Índice de matriz (índice com várias chaves)
- Compromissos de projeto entre matrizes e referências
2. A história real de um engenheiro de dados de comércio eletrônico
(1) Problema: consultas em documentos aninhados retornam resultados incorretos
Alice estava fazendo a manutenção do sistema de avaliações de produtos quando se deparou com um erro na consulta:
// ❌ Counterexample:Search"Rating >= 4 and includes 'good' Keywords"Comments on
db.products.find({
"reviews.rating": { $gte: 4 },
"reviews.content": /good/i
});
// ⚠️ Question:May match different comments(One comment, high rating,Another one containing good)
// Expectations:A comment meets both conditions at the same time
(2) Solução para uma correspondência exata usando $elemMatch
// ✅ Correct Example:Use $elemMatch
db.products.find({
reviews: {
$elemMatch: {
rating: { $gte: 4 },
content: /good/i
}
}
});
// ✅ Correct:A single comment that meets all of the following criteria
graph TB
subgraph "Embedded Documentation"
A1[products Gathering] --> A2[Document 1<br/>address: {<br/> city: Tokyo<br/> country: Japan<br/>}]
end
subgraph "Citation-Style Documentation"
B1[products Gathering] --> B2[Document 1<br/>addressId: ObjectId]
B3[addresses Gathering] --> B4[Document 1<br/>city: Tokyo]
B2 -.->|Search| B3
end
style A2 fill:#d4edda
3. Notação para nós de documentos aninhados
Explicação do conceito: Um documento aninhado é aquele que contém outro documento como valor de um campo. O MongoDB suporta até 100 níveis de aninhamento; no entanto, em ambientes de produção, recomenda-se não ultrapassar 3 níveis. A notação de ponto é um método de acessar campos aninhados usando a sintaxe "field.subfield.subsubfield" e constitui o mecanismo central para consultar e atualizar dados aninhados no MongoDB.
Como funciona: Quando o MongoDB analisa a notação de pontos, ele localiza os caminhos dos campos camada por camada, do nível mais externo ao mais interno. O mecanismo de consulta divide "specs.screen.size" em doc.specs.screen.size e percorre a árvore do documento BSON nível por nível. Os índices também suportam campos na notação de pontos; por exemplo, { "specs.screen.size": 1 } pode ser usado para criar índices eficientes.
graph TD
A[BSON Document] --> B[specs: Object]
B --> C[screen: Object]
C --> D["size: '6.5'"]
C --> E["type: 'OLED'"]
B --> F[battery: Object]
F --> G["capacity: '4500mAh'"]
H["specs.screen.size"] -->|Analysis of Dot Notation| D
I["specs.battery.capacity"] -->|Analysis of Dot Notation| G
style D fill:#d4edda
style G fill:#d4edda
| Dimensão | Descrição |
|---|---|
| Cenários aplicáveis | Dados com uma hierarquia fixa que são lidos e gravados em conjunto (por exemplo, endereços, especificações) |
| Cenários não aplicáveis | Dados com hierarquia incerta ou que são frequentemente atualizados de forma independente (use referências) |
| Impacto no desempenho | Um único nível ≈ campo normal; vários níveis exigem percorrimento — recomenda-se ≤ 3 níveis |
| Suporte a índices | Totalmente compatível; { "a.b.c": 1 } é equivalente a um índice comum |
(1) Consulta e atualização de documentos aninhados
// === Nested Document Structure ===
db.products.insertOne({
sku: 'PHONE-001',
title: 'Smartphone X',
specs: {
screen: { size: '6.5', type: 'OLED' },
battery: { capacity: '4500mAh', type: 'Li-Po' }
}
});
// === Dot Notation Query ===
db.products.find({ "specs.screen.size": '6.5' });
// === Multi-level nesting ===
db.products.find({ "specs.battery.capacity": '4500mAh' });
// === Updating Nested Fields ===
db.products.updateOne(
{ sku: 'PHONE-001' },
{ $set: { "specs.battery.capacity": "5000mAh" } }
);
4. Consultas a campos de matriz
Explicação do conceito: As matrizes são uma das estruturas de dados mais flexíveis no modelo de documentos do MongoDB. Um único campo pode armazenar vários valores, e as consultas do MongoDB em campos de matriz seguem o princípio da “correspondência qualquer” — desde que um elemento da matriz atenda à condição, o documento é selecionado. Isso torna as consultas em matrizes ao mesmo tempo poderosas e propensas a resultados inesperados.
Como funciona: O mecanismo de consulta do MongoDB utiliza uma estratégia de “correspondência por expansão implícita” para campos de matriz. Ao consultar { tags: '5g' }, o mecanismo verifica os elementos da matriz um por um; se algum elemento for igual a '5g', é considerado uma correspondência. Para consultas com múltiplas condições (como {"reviews.rating": {$gte:4}, "reviews.content": /good/}), cada condição compara os elementos da matriz de forma independente, podendo corresponder a elementos diferentes — é por isso que $elemMatch existe.
graph LR
A[Query Criteria] --> B{Array Matching Strategies}
B -->|Single condition| C[Any element matches<br/>tags: '5g']
B -->|Multi-condition point notation| D[Independent matching of each condition<br/>May hit different elements]
B -->|Multiple conditions $elemMatch| E[The same element must<br/>Meets all conditions at the same time]
C --> F[✅ Simple and Efficient]
D --> G[⚠️ May not be accurate]
E --> H[✅ Exact Match]
style F fill:#d4edda
style G fill:#fff3cd
style H fill:#d4edda
| Método de pesquisa | Regras de correspondência | Cenários aplicáveis |
|---|---|---|
{ tags: '5g' } |
Qualquer elemento igual a '5g' | Correspondência de valor único |
{ tags: { $all: ['5g','amoled'] } } |
Deve incluir todos os valores | Vários valores |
{ tags: { $in: ['5g','fast'] } } |
Contém qualquer valor | Vários valores ou correspondências |
{ "tags.0": '5g' } |
Especificar posição do índice | Correspondência de posição |
{ tags: { $size: 3 } } |
Correspondência exata para o comprimento da matriz | Filtro de comprimento |
(1) Correspondência de um único elemento
// === An array contains the specified elements ===
db.products.find({ tags: '5g' });
// tags The array contains '5g' All Products
// === The array contains all the specified elements($all)===
db.products.find({ tags: { $all: ['5g', 'amoled'] } });
// Must include both '5g' and 'amoled'
// === The array contains any element($in)===
db.products.find({ tags: { $in: ['5g', 'fast-charging'] } });
// Includes '5g' or 'fast-charging'
(2) Consulta de elementos de uma matriz por índice
// === Get the first element of an array ===
db.products.find({ "tags.0": '5g' });
// tags[0] = '5g'
// === Retrieve the second element of the array ===
db.products.find({ "tags.1": 'amoled' });
// tags[1] = 'amoled'
// === Querying the Range of Array Elements ===
db.products.find({ "tags.0": { $in: ['new', 'sale'] } });
(3) Verificando o comprimento de uma matriz
// === Exact Match for Array Length ===
db.products.find({ tags: { $size: 3 } });
// Number of tags = 3
// === Query on Array Length Range ===
db.products.find({
$expr: { $gt: [{ $size: '$tags' }, 3] }
});
// Number of tags > 3
5. $elemMatch: Correspondência exata
Explicação do conceito: $elemMatch é um operador de correspondência exata projetado especificamente para campos de matriz no MongoDB. Ele garante que o mesmo elemento da matriz satisfaça todas as condições da consulta simultaneamente, em vez de elementos diferentes satisfazerem apenas algumas das condições. Quando os elementos da matriz são objetos (como comentários ou notas) e é necessária uma consulta de junção com vários campos, $elemMatch é a única escolha correta.
Como funciona: $elemMatch aplica todas as condições a cada elemento da matriz, um por um. Um elemento só é considerado compatível se passar em todas as verificações de condição simultaneamente. Principal diferença em relação à notação de ponto: na notação de ponto, várias condições percorrem a matriz e podem corresponder a elementos diferentes; $elemMatch exige que um único elemento passe em todos os testes.
sequenceDiagram
participant Query as Search Engine
participant Doc as Document<br/>reviews: [{rating:5,content:"bad"},<br/>{rating:3,content:"good"}]
Note over Query,Doc: Dot Notation Query: reviews.rating>=4 AND reviews.content=/good/
Query->>Doc: Conditions1: rating>=4 → Hit Element0
Query->>Doc: Conditions2: /good/ → Hit Element1
Doc-->>Query: ⚠️ Different elements each meet some of the conditions → Return to this document
Note over Query,Doc: $elemMatch Search
Query->>Doc: Element0: rating>=4 ✅, /good/ ❌ → Mismatch
Query->>Doc: Element1: rating>=4 ❌ → Mismatch
Doc-->>Query: ✅ No elements satisfy all of the following conditions → Does not return
| Dimensão de comparação | Notação de pontos | $elemMatch |
|---|---|---|
| Granularidade da correspondência | Cada condição é avaliada de forma independente | Todas as condições para o mesmo elemento devem ser atendidas |
| Sintaxe | { "a.b": x, "a.c": y } |
{ a: { $elemMatch: { b: x, c: y } } } |
| Precisão | ⚠️ Pode selecionar elementos diferentes | ✅ Preciso até um único elemento |
| Desempenho | Um pouco mais rápido (é possível usar índices separadamente) | Um pouco mais lento (é necessário verificar cada elemento) |
| Suporte a índices | Várias condições podem ser processadas separadamente por meio de índices | É necessário um índice composto |
(1) A questão central
// === Counterexample:Matching Elements in Different Arrays ===
db.products.find({
"reviews.rating": { $gte: 4 },
"reviews.content": /good/i
});
// Possible matches:review1.rating=5, review2.content="good"
// That is, different comments each satisfy the condition
// ✅ Correct Example:$elemMatch Match the same element
db.products.find({
reviews: {
$elemMatch: {
rating: { $gte: 4 },
content: /good/i
}
}
});
// A single comment must meet all of the following criteria at the same time
(2) Condições complexas
// === Multiple conditions elemMatch ===
db.products.find({
reviews: {
$elemMatch: {
rating: { $gte: 4 },
helpful: { $gt: 10 },
createdAt: { $gte: new Date('2026-01-01') }
}
}
});
// === Nested elemMatch ===
db.students.find({
courses: {
$elemMatch: {
name: 'Math',
score: { $gte: 90 },
assignments: {
$elemMatch: {
submitted: true,
grade: { $gte: 80 }
}
}
}
}
});
6. Modificadores de atualização de matrizes
Explicação do conceito: O MongoDB oferece um conjunto de modificadores de atualização específicos para a manipulação de campos de matriz: $push (acrescentar), $pull (excluir), $addToSet (acrescentar sem duplicatas), $pop (remover o primeiro e o último elementos). Quando usados em conjunto com o placeholder de posição $ e arrayFilters, esses operadores permitem que você atualize com precisão elementos específicos em um array sem substituir o array inteiro.
Como funciona: Os modificadores de atualização de array são executados de forma atômica no servidor MongoDB. $push Acrescenta um elemento ao final do array; $addToSet Verifica se o elemento já existe antes de acrescentá-lo; $pull Acrescenta um elemento ao final do array; $addToSet verifica se o elemento existe antes de adicioná-lo; $pull exclui um elemento com base em uma condição; $ o placeholder aponta para a posição do primeiro elemento da matriz que atende à condição da consulta; $[] aponta para todos os elementos; $[filter] funciona em conjunto com arrayFilters para atualizações condicionais.
graph TB
A[Array Update Modifiers] --> B[$push<br/>Additional Elements]
A --> C[$pull<br/>Conditional Deletion]
A --> D[$addToSet<br/>Remove Duplicates and Append]
A --> E[$pop<br/>Remove the first and last characters]
F[Position Operators] --> G[$<br/>Update the first match]
F --> H[$[]<br/>Update All Elements]
F --> I[$[filter]<br/>Conditional Batch Update<br/>In conjunction witharrayFilters]
style B fill:#d4edda
style C fill:#f8d7da
style D fill:#cce5ff
style I fill:#fff3cd
| Modificador | Função | Exemplo |
|---|---|---|
$push |
Adicionar um elemento (repetível) | { $push: { tags: 'new' } } |
$each |
Use $push para acrescentar vários | { $push: { tags: { $each: ['a','b'] } } } |
$addToSet |
Remover duplicatas e adicionar | { $addToSet: { tags: 'new' } } |
$pull |
Excluir por critérios | { $pull: { tags: 'old' } } |
$pop |
Excluir o primeiro/último | { $pop: { tags: 1 } } (Excluir o último) |
$ |
Atualizar o primeiro elemento correspondente | { $set: { "reviews.$.flagged": true } } |
$[] |
Atualizar todos os elementos | { $set: { "reviews.$[].status": "ok" } } |
$[f] |
Atualização em lote condicional | { arrayFilters: [{ "f.rating": {$lt:2} }] } |
(1) $push: Adiciona um elemento
// === Add a single item ===
db.products.updateOne(
{ sku: 'PHONE-001' },
{ $push: { tags: 'bestseller' } }
);
// === Add multiple($each)===
db.products.updateOne(
{ sku: 'PHONE-001' },
{ $push: { tags: { $each: ['5g', 'amoled', 'fast-charging'] } } }
);
(2) $pull: Exclui um elemento
// === Delete a Specified Value ===
db.products.updateOne(
{ sku: 'PHONE-001' },
{ $pull: { tags: 'old-tag' } }
);
// === Delete Multiple ===
db.products.updateOne(
{ sku: 'PHONE-001' },
{ $pull: { tags: { $in: ['outdated1', 'outdated2'] } } }
);
// === Delete elements that meet the criteria ===
db.products.updateOne(
{ sku: 'PHONE-001' },
{ $pull: { reviews: { rating: { $lt: 2 } } } }
);
// Delete Rating < 2 Comments on
(3) Espaço reservado para a posição em $
// === Update the first matching array element ===
db.products.updateOne(
{ sku: 'PHONE-001', "reviews.userId": 'user_001' },
{ $set: { "reviews.$.helpful": 10 } }
);
// Update user_001 Comments on
// === Update all matching array elements ===
db.products.updateOne(
{ sku: 'PHONE-001' },
{ $set: { "reviews.$[].status": "approved" } }
);
// === Conditional Update(arrayFilters)===
db.products.updateOne(
{ sku: 'PHONE-001' },
{ $set: { "reviews.$[lowRating].flagged": true } },
{ arrayFilters: [{ "lowRating.rating": { $lt: 2 } }] }
);
7. Índice com várias chaves: indexação de matrizes
Explicação do conceito: Quando um campo de índice é uma matriz, o MongoDB cria automaticamente um índice multichave. Sua principal característica é que cada elemento da matriz gera uma chave de índice separada. Por exemplo, tags: ['5g', 'amoled'] criaria duas entradas no índice, cada uma apontando para o mesmo documento. Isso garante que o desempenho das consultas para campos de matriz seja equivalente ao dos índices de campos comuns.
Como funciona: Ao inserir um documento, o MongoDB verifica se o campo indexado é uma matriz. Se for o caso, ele cria uma entrada de índice para cada elemento. Durante uma consulta, o MongoDB usa índices com múltiplas chaves para localizar rapidamente o documento que contém o elemento alvo. Limitação: Um índice composto pode conter, no máximo, um campo de matriz; caso contrário, o número de entradas de índice aumentará exponencialmente (produto cartesiano).
graph LR
A[Document<br/>tags: 5g, amoled, fast] --> B[multikey index]
B --> C["Index Entries: '5g' → docId"]
B --> D["Index Entries: 'amoled' → docId"]
B --> E["Index Entries: 'fast' → docId"]
F["Search {tags: '5g'}"] -->|Index Lookup| C
C --> G[✅ Document in Chinese]
style B fill:#d4edda
style G fill:#d4edda
| restrições de chaves múltiplas | descrição |
|---|---|
| Cada elemento da matriz possui um índice | O comprimento da matriz influencia o tamanho do índice |
| Índices compostos podem ter, no máximo, um campo de matriz | Evitar a explosão de índices |
| Alto desempenho para consultas com índice de matriz | Equivalente aos índices de campo comuns |
| Não é possível indexar o array inteiro | Indexe apenas os elementos, não o próprio array |
▶ Exemplo 1: Guia prático para consultar documentos aninhados
// Scene:ShopHub Checking Product Specifications on E-commerce Platforms
db.products.insertOne({
sku: 'PHONE-002',
title: 'Smartphone Y',
specs: {
screen: { size: '6.7', type: 'AMOLED', refreshRate: '120Hz' },
battery: { capacity: '5000mAh', type: 'Li-Po', fastCharging: true },
storage: { ram: '12GB', internal: '256GB' }
}
});
// Search:AMOLED Products on the Screen
db.products.find({ "specs.screen.type": 'AMOLED' });
// Search:Supports fast charging and has a battery ≥ 5000mAh Products
db.products.find({
"specs.battery.fastCharging": true,
"specs.battery.capacity": { $in: ['5000mAh', '6000mAh'] }
});
// Update:Add Screen Protector Information
db.products.updateOne(
{ sku: 'PHONE-002' },
{ $set: { "specs.screen.protection": "Gorilla Glass 5" } }
);
Resultado: A notação por pontos identifica com precisão campos aninhados em vários níveis; as consultas e atualizações afetam apenas os campos-alvo e não sobrescrevem todo o objeto aninhado.
▶ Exemplo 2: Aplicação prática abrangente do $elemMatch e atualizações de matrizes
// Scene:ShopHub Product Review Management
db.products.insertOne({
sku: 'LAPTOP-001',
title: 'Laptop Pro',
reviews: [
{ userId: 'user_010', rating: 2, content: 'Poor quality', helpful: 0, createdAt: new Date('2026-05-01') },
{ userId: 'user_011', rating: 5, content: 'Excellent good value', helpful: 30, createdAt: new Date('2026-06-01') },
{ userId: 'user_012', rating: 4, content: 'Good performance', helpful: 12, createdAt: new Date('2026-07-01') }
]
});
// 1. Exact Search:The Same Comment rating>=4 and includes 'good'
db.products.find({
reviews: { $elemMatch: { rating: { $gte: 4 }, content: /good/i } }
});
// Hit only user_011 Comments on(rating=5 and includes "good")
// 2. Use $ to update the first top-rated review
db.products.updateOne(
{ sku: 'LAPTOP-001', "reviews.rating": { $gte: 5 } },
{ $set: { "reviews.$.featured": true } }
);
// 3. Use arrayFilters Mark all low-rated reviews
db.products.updateOne(
{ sku: 'LAPTOP-001' },
{ $set: { "reviews.$[low].flagged": true } },
{ arrayFilters: [{ "low.rating": { $lt: 3 } }] }
);
// 4. Delete all low-rated reviews
db.products.updateOne(
{ sku: 'LAPTOP-001' },
{ $pull: { reviews: { rating: { $lt: 3 } } } }
);
Resultado: $elemMatch corresponde exatamente ao mesmo comentário;
$atualiza a primeira correspondência;arrayFiltersrealiza uma atualização em lote com base em condições;$pullexclui os comentários que atendem às condições.
// === Creating an Array Field Index ===
db.products.createIndex({ tags: 1 });
// Automatically become multikey index
// === Composite multikey index ===
db.products.createIndex({ category: 1, tags: 1 });
// === View Index ===
db.products.getIndexes();
▶ Exemplo 3: Exercício prático abrangente que combina matrizes e documentos aninhados
// Complete Scene:Nested Queries for E-commerce Product Reviews
// 1. Preparing the Data:Products with nested reviews
db.products.insertOne({
sku: 'PHONE-001',
title: 'Smartphone X',
specs: {
screen: { size: '6.5', type: 'OLED' },
battery: { capacity: '4500mAh', type: 'Li-Po' }
},
reviews: [
{ userId: 'user_001', rating: 5, content: 'Excellent phone! Great battery', helpful: 15, createdAt: new Date('2026-06-01') },
{ userId: 'user_002', rating: 3, content: 'Good but expensive', helpful: 5, createdAt: new Date('2026-06-15') },
{ userId: 'user_003', rating: 5, content: 'Best phone ever, amazing good quality', helpful: 25, createdAt: new Date('2026-07-01') }
]
});
// 2. Search:Rating >= 4 and includes 'good' Comments on Keywords(The same comment meets the criteria)
db.products.find({
reviews: {
$elemMatch: {
rating: { $gte: 4 },
content: /good/i
}
}
}, { sku: 1, title: 1, reviews: 1 });
// 3. Update:Feature the top-rated comment
db.products.updateOne(
{ sku: 'PHONE-001', 'reviews.rating': { $gte: 5 } },
{ $set: { 'reviews.$.featured': true } }
);
// 4. Query Array Elements:tags Includes '5g' And the array length = 3
db.products.find({ tags: { $all: ['5g', 'amoled'], $size: 3 } });
// 5. Multi-level nested queries:Screen Size = 6.5
db.products.find({ 'specs.screen.size': '6.5' });
Saída: Retorna o produto PHONE-001; na matriz
reviews, apenas a terceira avaliação atende às duas condições —rating >= 4e conter “good” —, o que demonstra perfeitamente a capacidade de correspondência exata de$elemMatch.
❓ Perguntas Frequentes
P: O $elemMatch é obrigatório? R: É obrigatório quando um único array precisa atender a várias condições ao mesmo tempo. Se houver apenas uma condição (como
{ tags: '5g' }), o $elemMatch não é necessário.
P: Qual é a diferença entre a notação por pontos e
$elemMatch? R: A notação por pontos{ "a.b": 1, "a.c": 2 }pode corresponder a elementos do mesmo documento, mas de matrizes diferentes;$elemMatchdeve corresponder a elementos da mesma matriz.
P: Os campos de matriz podem ser indexados? R: Sim. O MongoDB cria automaticamente índices com várias chaves para campos de matriz, mas um índice composto pode conter, no máximo, um campo de matriz.
P: O desempenho das consultas a documentos aninhados é bom? R: O aninhamento de nível único apresenta desempenho semelhante ao dos campos comuns. O aninhamento de vários níveis requer a notação de ponto, mas os índices continuam funcionando. Recomendamos limitar o aninhamento a no máximo 3 níveis.
📖 Resumo
- Consulta de documentos aninhados usando notação de ponto:
field.subfield - Correspondência de matriz de elemento único:
{ tags: '5g' } - $all corresponde a tudo:
{ tags: { $all: ['a', 'b'] } } - $elemMatch: Correspondência exata com um elemento do mesmo array
- Atualização da matriz:
$push/$pull/$pop/$addToSet $Atualizar o marcador de posição com o primeiro elemento correspondente
📝 Exercícios
- Pergunta básica (⭐): Pesquisar produtos cujas tags incluam “5g” ou “amoled”.
- Problema básico (⭐): Use $pull para excluir todos os comentários com uma avaliação inferior a 2.
- Problema avançado (⭐⭐): Use $elemMatch para encontrar produtos nos comentários que tenham uma “avaliação >= 4 e contenham a palavra-chave ‘bom’”.
- Exercício avançado (⭐⭐): Use o placeholder $ para atualizar o campo
helpfulnos comentários de um usuário específico. - Desafio (⭐⭐⭐): Implementar o recurso de “curtir” (útil +1) no sistema de comentários, filtrar os comentários com baixa avaliação e marcar em lote os comentários como revisados.