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


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:

JAVASCRIPT
// ❌ 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

JAVASCRIPT
// ✅ 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

100%
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.

100%
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

JAVASCRIPT
// === 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.

100%
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

JAVASCRIPT
// === 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

JAVASCRIPT
// === 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

JAVASCRIPT
// === 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.

100%
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

JAVASCRIPT
// === 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

JAVASCRIPT
// === 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.

100%
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

JAVASCRIPT
// === 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

JAVASCRIPT
// === 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 $

JAVASCRIPT
// === 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).

100%
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

JAVASCRIPT
// 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

JAVASCRIPT
// 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; arrayFilters realiza uma atualização em lote com base em condições; $pull exclui os comentários que atendem às condições.

JAVASCRIPT
// === 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

JAVASCRIPT
// 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 >= 4 e 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; $elemMatch deve 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


📝 Exercícios

  1. Pergunta básica (⭐): Pesquisar produtos cujas tags incluam “5g” ou “amoled”.
  2. Problema básico (⭐): Use $pull para excluir todos os comentários com uma avaliação inferior a 2.
  3. Problema avançado (⭐⭐): Use $elemMatch para encontrar produtos nos comentários que tenham uma “avaliação >= 4 e contenham a palavra-chave ‘bom’”.
  4. Exercício avançado (⭐⭐): Use o placeholder $ para atualizar o campo helpful nos comentários de um usuário específico.
  5. 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.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%