MongoDB: Padrões avançados do Mongoose

Última atualização: 2026-08-26

Padrões avançados do Mongoose — Domine recursos avançados como populate, discriminator, lean e aggregate.

1. O que você vai aprender


100%
graph TB
    A[mongoose Advanced Features] --> B[populate<br/>Application Layer JOIN]
    A --> C[discriminator<br/>Single Set, Multiple Schema]
    A --> D[lean<br/>Performance Optimization]
    A --> E[aggregate<br/>Database-Level Aggregation]
    A --> F[Schema Index<br/>Declarative]

    B --> B1[Multiple queries<br/>N+1 Risks]
    C --> C1[role Field<br/>Classification by Type]
    D --> D1[Skip hydrate<br/>↑5x Performance]
    E --> E1[$facet/$lookup<br/>Return Multiple Dimensions in a Single Operation]

    style D fill:#d4edda

2. Preencher consultas com junções

O que é populate? populate é a implementação do Mongoose de um “JOIN na camada de aplicação” — quando um esquema contém um campo ref do tipo ObjectId, o populate emite automaticamente uma consulta adicional para substituir o ObjectId referenciado pelo documento completo. Essencialmente, isso envolve duas consultas: primeiro, recuperar o ObjectId do documento primário e, em seguida, recuperar os dados completos do documento associado.

Os princípios fundamentais do populate:

100%
sequenceDiagram
    participant App as Node.js Applications
    participant Mongo as MongoDB

    App->>Mongo: 1st query: Order.find()
    Mongo-->>App: [order1, order2, ...]
    App->>Mongo: 2nd query: User.find({_id: {$in: [ObjectId1, ObjectId2, ...]}})
    Mongo-->>App: Back users
    App->>App: Merge: order.userId → user Object

Comparação entre populate e $lookup:

Dimensão preencher $lookup
Camada de execução Camada de aplicação (2 ou mais consultas) Camada de banco de dados (1 agregação)
Número de consultas Risco N+1 1
Flexibilidade Média (suporta apenas referências a ObjectId) Alta (associações condicionais arbitrárias)
Desempenho Adequado para conjuntos de dados pequenos Recomendado para conjuntos de dados grandes
Concisão do código Alta (uma linha de .populate()) Baixa (sintaxe do pipeline de agregação)
Tipo de retorno Documento Mongoose objeto simples

O Problema N+1: Ao consultar 100 pedidos e usar populate em cada order.userId, isso gera 1 (consulta de pedidos) + 100 (consultas de usuários) = 101 consultas. O Mongoose otimiza isso automaticamente para uma consulta em lote $in (1+1=2 consultas), mas operações populate aninhadas ainda podem gerar consultas adicionais.

Casos de uso: Para relações um-para-um (por exemplo, recuperar o usuário associado a um pedido), use populate; para relações em massa com condições complexas, use $lookup; para fins exclusivamente de exibição, use lean combinado com populate.

(1) Preenchimento básico

JAVASCRIPT
// === Basic populate ===
const user = await User.findById(userId).populate('addresses');
// SELECT u.*, a.* FROM users u LEFT JOIN addresses a ON u._id = a.userId

// === Nested populate ===
const order = await Order.findById(orderId)
  .populate('userId')
  .populate({
    path: 'items.productId',
    select: 'sku title price'
  });

// === Conditions populate ===
const orders = await Order.find()
  .populate({
    path: 'userId',
    match: { isActive: true },
    select: 'username avatar'
  });
populate vs $lookup populate $lookup
Camada de execução Camada de aplicação Camada de banco de dados
Número de consultas N+1 1
Flexibilidade Média Alta
Desempenho Adequado para conjuntos de dados pequenos Recomendado para conjuntos de dados grandes

3. Discriminador

O que é um discriminador? Um discriminador é o mecanismo de “herança de coleção única” do Mongoose — vários modelos compartilham a mesma coleção do MongoDB, com os tipos de documentos diferenciados por uma chave discriminadora. Isso é semelhante à herança na programação orientada a objetos: a classe base define campos comuns, as subclasses a estendem com campos específicos e todas as instâncias residem na mesma tabela.

Mecanismo subjacente do discriminador:

100%
graph TB
    subgraph "users Gathering(Single Set)"
        D1["{role: 'Customer', loyaltyPoints: 100, email: 'alice@...'}"]
        D2["{role: 'Customer', loyaltyPoints: 50, email: 'bob@...'}"]
        D3["{role: 'Admin', permissions: ['manage'], email: 'admin@...'}"]
    end

    subgraph "mongoose Model Layer"
        User[User Model<br/>email + username + passwordHash]
        Customer[Customer Model<br/>+ loyaltyPoints + preferredCategories]
        Admin[Admin Model<br/>+ permissions + lastLoginAt]
    end

    User -->|discriminator| Customer
    User -->|discriminator| Admin
    Customer -->|Search: role='Customer'| D1
    Customer -->|Search: role='Customer'| D2
    Admin -->|Search: role='Admin'| D3

    style User fill:#fff3cd
    style Customer fill:#d4edda
    style Admin fill:#cce5ff

Discriminador x Conjunto Independente:

Dimensão Discriminador (conjunto único) Conjunto independente
Método de consulta Filtro automático por chave de identificação Consulta entre conjuntos
Eficiência de armazenamento Alta (índices compartilhados) Baixa (índices duplicados em campos comuns)
Consistência dos dados Naturalmente consistente (dentro do mesmo conjunto) Requer manutenção (atualizações entre conjuntos)
Tamanho do índice Pequeno (um índice para campos comuns) Grande (criar um índice separado para cada coleção)
Desempenho da consulta Um pouco mais lento (requer filtragem por função) Rápido (conjunto menor)
Escalabilidade Ruim (expansão de conjuntos) Boa (escalonamento independente)
Casos de uso Diferenças mínimas entre campos, consultas com junções frequentes Diferenças significativas entre campos, consultas principalmente independentes

Casos de uso: Funções de usuário (Cliente/Administrador/Moderador compartilham e-mail e senha, cada uma com seus próprios campos específicos); formas de pagamento (Cartão de crédito/PayPal/Transferência bancária compartilham valor e status, cada uma com campos específicos do canal); tipos de notificação (E-mail/SMS/Notificação push compartilham assunto e conteúdo, cada um com configurações específicas do canal).

JAVASCRIPT
// === Basics User Model ===
const UserSchema = new mongoose.Schema({
  email: String,
  username: String,
  passwordHash: String
}, { discriminatorKey: 'role' });

const User = mongoose.model('User', UserSchema);

// === Customer Discriminator ===
const Customer = User.discriminator('Customer', new mongoose.Schema({
  loyaltyPoints: { type: Number, default: 0 },
  preferredCategories: [String]
}));

// === Admin Discriminator ===
const Admin = User.discriminator('Admin', new mongoose.Schema({
  permissions: [String],
  lastLoginAt: Date
}));

// === Create Different Roles ===
const customer = await Customer.create({
  email: 'alice@example.com',
  username: 'alice',
  passwordHash: '...',
  loyaltyPoints: 100,
  preferredCategories: ['Electronics']
});

const admin = await Admin.create({
  email: 'admin@example.com',
  username: 'admin',
  passwordHash: '...',
  permissions: ['manage_products']
});

// === When querying, based on role Category ===
const customers = await Customer.find();
const admins = await Admin.find();
// All data is in the same collection(users),Through discriminatorKey Category

Caso de uso: Coleção única com vários esquemas (campos diferentes para funções diferentes).


4. Otimização de desempenho da função lean()

O que é lean()? lean() é um método de otimização de desempenho no Mongoose — ele ignora o processo de hidratação do documento e retorna diretamente um objeto JavaScript simples. Enquanto as consultas comuns retornam um documento Mongoose (com métodos como save() e validate(), além do rastreamento de alterações), lean() retorna um objeto simples (contendo apenas dados, sem nenhum método).

Princípios por trás das diferenças de desempenho no lean():

100%
graph LR
    subgraph "General Query (without lean)"
        T1[Product.find] --> D1[mongoose Document<br/>with save/validate etc.<br/>~150ms / 100 docs]
    end

    subgraph "lean Search"
        Q2[MongoDB Original BSON] --> H2[JSON.parse Direct Conversion]
        H2 --> D2[plain object<br/>Raw Data<br/>~30ms / 100 docs]
    end

    style D1 fill:#f8d7da
    style D2 fill:#d4edda

Dados de comparação de desempenho (100 documentos, categoria Eletrônicos):

Operação Sem Lean Com Lean Multiplicador de desempenho
Tempo de consulta ~150 ms ~30 ms ↑5x
Uso de memória ~5 MB ~1 MB ↓5x
JSON.stringify ~8 ms ~2 ms ↑4x
Suporta save()
Suporta preenchimento ✅ (chamadas encadeadas)
Compatível com Controle de Alterações

Diretrizes de uso: Use lean() para APIs somente leitura (listas, detalhes); não use lean() se precisar chamar save() ou modificar o rastreamento; adicione lean() se não forem necessárias outras modificações após populate.

JAVASCRIPT
// === General Inquiry:Back mongoose Document ===
const products = await Product.find();
// Each product is a Mongoose Document (with save() and other methods)

// === lean():Return to Pure JS Object ===
const products = await Product.find().lean();
// Each product is a plain object, Performance up 3-5x

// === Comparison Test ===
console.time('without lean');
const a = await Product.find({ category: 'Electronics' }).limit(100);
console.timeEnd('without lean');  // ~150ms

console.time('with lean');
const b = await Product.find({ category: 'Electronics' }).lean().limit(100);
console.timeEnd('with lean');  // ~30ms

5. Pipeline de agregação Model.aggregate()

Canais de agregação no Mongoose: Model.aggregate() aciona diretamente o mecanismo de agregação do MongoDB para realizar agrupamentos, junções e cálculos na camada do banco de dados — ao contrário do populate, que lida com essas operações na camada de aplicação; os dados nos canais de agregação não precisam ser transferidos para o lado do Node.js para processamento, resultando em melhor desempenho.

Guia para escolher entre aggregate e populate:

Cenário Solução recomendada Motivo
Consultar pedido + nome de usuário preencher Relação simples, código limpo
Calcular o preço médio para cada categoria agregar Os cálculos devem ser agrupados
Juntar + Agrupar + Ordenar agregar + $lookup Tudo em uma única etapa
Junções aninhadas em vários níveis agregação + várias chamadas $lookup Evitar N+1
Retornar resultados multidimensionais aggregate + $facet Retornar várias visualizações de uma só vez

Processo de execução do pipeline de agregação:

100%
graph LR
    Input[1,000,000 Documents] -->|"$match"| Filter[After filtering: 500,000]
    Filter -->|"$group"| Group[Group by category<br/>5 groups]
    Group -->|"$sort"| Sorted[Sort by count]
    Sorted -->|"$limit"| Output[Top 5]
    
    style Input fill:#f8d7da
    style Output fill:#d4edda
JAVASCRIPT
// === mongoose Using Aggregation in ===
const stats = await Product.aggregate([
  { $match: { isActive: true } },
  { $group: { _id: '$category', count: { $sum: 1 }, avgPrice: { $avg: '$price' } } },
  { $sort: { count: -1 } }
]);

// === aggregate + populate(mongoose 6+)===
const results = await Order.aggregate([
  { $match: { status: 'paid' } },
  {
    $lookup: {
      from: 'users',
      localField: 'userId',
      foreignField: '_id',
      as: 'customer'
    }
  },
  { $unwind: '$customer' }
]);

6. Declaração de índice de esquema

Métodos de declaração de índices no Mongoose: O Mongoose oferece suporte à criação declarativa de índices dentro das definições de esquema — índices no nível do campo (index: true), índices compostos (Schema.index()) e índices especiais (índices de texto, índices TTL e índices parciais). A vantagem dos índices declarativos é que eles são definidos junto com o esquema, tornando-os fáceis de entender à primeira vista; eles são criados automaticamente na inicialização (autoIndex=true).

Tipos de índices e casos de uso:

Tipo de índice Método de declaração Casos de uso Parâmetros especiais
Índice de campo único { sku: { index: true } } Consultas de igualdade, ordenação único
Índice Composto Schema.index({a:1, b:-1}) Consulta com múltiplas condições Regra ESR
Índice de texto { title: { text: true } } Pesquisa de texto completo Pesos
Índice TTL Schema.index({at:1}, {expireAfterSeconds:86400}) Expiração automática Tempo de expiração
Índice parcial partialFilterExpression Índice de condições Filtrar condições
Índice geográfico { loc: { type: '2dsphere' } } Pesquisa geográfica

Regra ESR (Equality-Sort-Range): A ordem dos campos em um índice composto deve seguir a seguinte sequência: condições de igualdade → condições de classificação → condições de intervalo. Por exemplo, { category: 1, price: -1 } suporta find({category:'E'}) + sort({price:-1}), mas não suporta a classificação apenas por preço.

JAVASCRIPT
const ProductSchema = new mongoose.Schema({
  sku: { type: String, index: true, unique: true },
  title: { type: String, text: true },  // Text Index
  price: { type: Number, index: true },
  category: { type: String, index: true }
});

// === Composite Index ===
ProductSchema.index({ category: 1, price: -1 });

// === Selected Indexes ===
ProductSchema.index(
  { category: 1 },
  { partialFilterExpression: { isActive: true } }
);

// === TTL Index ===
ProductSchema.index(
  { createdAt: 1 },
  { expireAfterSeconds: 30 * 24 * 60 * 60 }
);

7. Otimização de desempenho do Mongoose 7.x

Metodologia de otimização de desempenho do Mongoose: A otimização de desempenho não se resume simplesmente a “adicionar uma chamada lean() e dar o trabalho por encerrado”; envolve, na verdade, um ajuste sistemático desde a camada de conexão → camada de consulta → camada de aplicação → camada de implantação. Os princípios fundamentais são: reduzir o volume de transferência de dados, minimizar o número de consultas, reduzir a sobrecarga de serialização e aproveitar os recursos nativos do banco de dados.

Matriz de Estratégias de Otimização:

Camada de otimização Estratégia Eficácia Invasividade
Camada de conexão Ajuste do maxPoolSize Reduzir o tempo de espera pela conexão Baixo (Configuração)
Camada de consulta Seleção de projeção (select()) Reduz a transferência de dados em mais de 90% Baixa
Nível da consulta Índice + hint() Evitar varredura completa da tabela Médio
Camada de consulta lean() Reduz a sobrecarga do Hydrate em 5 vezes Baixa
Camada de aplicação Paralelismo com Promise.all Reduz o tempo de espera em série Baixo
Camada de aplicação Operações em lote com bulkWrite Reduz as idas e voltas na rede em 10 vezes ↑ Média
Camada de Aplicação Transmissão de cursor Como evitar o estouro de memória Médio
Nível de implantação autoIndex=false Inicialização mais rápida Baixo
Camada de implantação Separação de leitura e gravação Reduz a carga no nó primário Alta

Práticas de otimização de Charlie: A API da lista de produtos da TechCorp foi otimizada, passando de 3 segundos para 50 milissegundos — ① Adicionou-se índices compostos para evitar o COLLSCAN; ② Utilizou-se select() para consultar apenas 5 campos; ③ Utilizou-se lean() para pular hydrate; ④ Utilizou Promise.all para executar find e count em paralelo; ⑤ Definia um limite de 100 linhas.

JAVASCRIPT
// === Optimization 1:Disable autoIndex(Production)===
mongoose.connect(uri, { autoIndex: false });
// Manually create indexes at startup:await Product.syncIndexes();

// === Optimization 2:Bulk Operations ===
await Product.bulkWrite([
  { updateOne: { filter: { sku: 'A' }, update: { $inc: { stock: -1 } } } },
  { updateOne: { filter: { sku: 'B' }, update: { $inc: { stock: -1 } } } }
]);

// === Optimization 3:Projection Reduces Data Transmission ===
const products = await Product.find()
  .select('sku title price')  // Search only 3 field
  .lean();

// === Optimization 4:Usage cursor Streaming Processing of Big Data ===
const cursor = Product.find().cursor();
for await (const doc of cursor) {
  // Process each document
}

// === Optimization 5:Bulk Insert ===
await Product.insertMany(docs, { ordered: false });

8. Treinamento prático abrangente

JAVASCRIPT
// === Optimized List API ===
app.get('/api/products', async (req, res) => {
  const { page = 1, limit = 20, category, search } = req.query;

  // 1. Build a Query
  const query = { isActive: true };
  if (category) query.category = category;
  if (search) query.$text = { $search: search };

  // 2. Parallel Queries(find + count)
  const [products, total] = await Promise.all([
    Product.find(query)
      .select('sku title price thumbnail rating')  // Projection
      .sort({ createdAt: -1 })
      .limit(+limit)
      .skip((page - 1) * limit)
      .lean(),  // Performance Optimization
    Product.countDocuments(query)
  ]);

  res.json({
    success: true,
    data: products,
    meta: { page: +page, limit: +limit, total, pages: Math.ceil(total / limit) }
  });
});

▶ Exemplo 1: Preenchimento de relações multiníveis + otimização enxuta do desempenho

JAVASCRIPT
// === Scene:ShopHub Order Details API(3 Layer Association)===
const mongoose = require('mongoose');

// Schemas
const AddressSchema = new mongoose.Schema({ city: String, country: String, zipCode: String });
const UserSchema = new mongoose.Schema({
  email: String, username: String,
  addresses: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Address' }]
});
const ProductSchema = new mongoose.Schema({ sku: String, title: String, price: Number, thumbnail: String });
const OrderSchema = new mongoose.Schema({
  orderNumber: String,
  userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
  items: [{ productId: { type: mongoose.Schema.Types.ObjectId, ref: 'Product' }, qty: Number, price: Number }],
  status: String
}, { timestamps: true });

const Address = mongoose.model('Address', AddressSchema);
const User = mongoose.model('User', UserSchema);
const Product = mongoose.model('Product', ProductSchema);
const Order = mongoose.model('Order', OrderSchema);

// 3-level populate + lean
const order = await Order.findById('647f1f77bcf86cd799439001')
  .populate({ path: 'userId', select: 'username email',
    populate: { path: 'addresses', select: 'city country' }
  })
  .populate({ path: 'items.productId', select: 'sku title price' })
  .lean();

console.log({
  orderNumber: order.orderNumber,
  customer: order.userId.username,
  city: order.userId.addresses[0]?.city,
  items: order.items.map(i => `${i.productId.title} x${i.qty}`)
});
// Output:{ orderNumber: 'ORD-001', customer: 'alice', city: 'San Francisco',
//         items: ['Smartphone X x2', 'Laptop Pro x1'] }

Resultado: 3 camadas de populate (Pedido → Usuário → Endereço + Pedido → Produto) + otimização de desempenho lean().

▶ Exemplo: E-commerce Dashboard com populate, lean, e aggregate (Difficulty ⭐⭐)

JAVASCRIPT
// Scene: ShopHub admin dashboard showing orders with customer and product details
const mongoose = require('mongoose');

// Schema with refs
const OrderSchema = new mongoose.Schema({
  orderNumber: String,
  customerId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
  items: [{
    productId: { type: mongoose.Schema.Types.ObjectId, ref: 'Product' },
    qty: Number,
    price: Number
  }],
  status: { type: String, enum: ['pending', 'paid', 'shipped', 'delivered'] },
  total: Number,
  createdAt: { type: Date, default: Date.now }
});

const Order = mongoose.model('Order', OrderSchema);

// Method 1: populate (good for small datasets)
async function getOrdersWithPopulate() {
  return await Order.find({ status: 'paid' })
    .populate('customerId', 'username email')
    .populate('items.productId', 'sku title')
    .sort({ createdAt: -1 })
    .limit(20)
    .lean();  // Combine with lean for read-only
}

// Method 2: aggregate with $lookup (better for large dataset + filtering)
async function getOrdersWithAggregate() {
  return await Order.aggregate([
    { $match: { status: 'paid' } },
    { $sort: { createdAt: -1 } },
    { $limit: 20 },
    // Join customer
    {
      $lookup: {
        from: 'users',
        localField: 'customerId',
        foreignField: '_id',
        as: 'customer'
      }
    },
    { $unwind: '$customer' },
    // Join products
    {
      $lookup: {
        from: 'products',
        localField: 'items.productId',
        foreignField: '_id',
        as: 'productDetails'
      }
    },
    // Final shape
    {
      $project: {
        orderNumber: 1,
        total: 1,
        status: 1,
        customer: { username: '$customer.username', email: '$customer.email' },
        itemCount: { $size: '$items' }
      }
    }
  ]);
}

// Usage
const orders = await getOrdersWithPopulate();
console.log(`${orders.length} orders loaded with customer info`);

Saída:

TEXT 📖 Somente leitura
20 orders loaded with customer info
// Each order includes customer.username, customer.email, and items with product details

▶ Exemplo 2: Uma aplicação prática abrangente de Populate + Discriminator + Lean

JAVASCRIPT
// === 1. populate Multi-level relationships ===
// Order + User + Products(3 Nested Layers)
const order = await Order.findById(orderId)
  .populate({
    path: 'userId',
    select: 'username email avatar',
    populate: { path: 'addresses', select: 'city country' }  // Address under the user
  })
  .populate({
    path: 'items.productId',
    select: 'sku title price thumbnail'
  })
  .lean();  // Performance Optimization

console.log('Order:', {
  orderNumber: order.orderNumber,
  customer: {
    username: order.userId.username,
    address: order.userId.addresses[0]?.city
  },
  items: order.items.map(i => ({
    product: i.productId.title,
    qty: i.qty,
    price: i.price
  }))
});

// === 2. discriminator Single Set, Multiple Schema ===
// User Base Class
const UserSchema = new mongoose.Schema({
  email: { type: String, required: true, unique: true },
  username: String,
  passwordHash: String,
  createdAt: { type: Date, default: Date.now }
}, { discriminatorKey: 'role' });

const User = mongoose.model('User', UserSchema);

// Customer Discriminator(Inheritance User + Extended Fields)
const Customer = User.discriminator('Customer', new mongoose.Schema({
  loyaltyPoints: { type: Number, default: 0 },
  preferredCategories: [String],
  totalSpent: mongoose.Schema.Types.Decimal128
}));

// Admin Discriminator
const Admin = User.discriminator('Admin', new mongoose.Schema({
  permissions: [String],
  lastLoginAt: Date
}));

// Create Different Roles(All exist users Gathering,Through role Field Delimiter)
const customer = await Customer.create({
  email: 'alice@example.com',
  username: 'alice',
  passwordHash: '...',
  loyaltyPoints: 100,
  preferredCategories: ['Electronics']
});

const admin = await Admin.create({
  email: 'admin@example.com',
  username: 'admin',
  passwordHash: '...',
  permissions: ['manage_products', 'manage_users']
});

// When querying, based on role Automatic Filtering
const customers = await Customer.find({ loyaltyPoints: { $gt: 50 } });
// Actual Query:{ role: 'Customer', loyaltyPoints: { $gt: 50 } }

// === 3. lean() Performance Optimization Comparison ===
console.time('without lean');
const a = await Product.find({ category: 'Electronics' }).limit(100);
console.timeEnd('without lean');  // ~150ms

console.time('with lean');
const b = await Product.find({ category: 'Electronics' }).lean().limit(100);
console.timeEnd('with lean');  // ~30ms(5x Performance Improvements)

// === 4. Model.aggregate() Database-Level Aggregation ===
const stats = await Product.aggregate([
  { $match: { isActive: true } },
  {
    $facet: {
      totalCount: [{ $count: 'count' }],
      byCategory: [
        { $group: { _id: '$category', count: { $sum: 1 }, avgPrice: { $avg: '$price' } } },
        { $sort: { count: -1 } }
      ],
      topRated: [
        { $sort: { rating: -1 } },
        { $limit: 5 },
        { $project: { sku: 1, title: 1, rating: 1 } }
      ]
    }
  }
]);

// Output:
// {
//   totalCount: [{ count: 1250 }],
//   byCategory: [
//     { _id: 'Electronics', count: 450, avgPrice: 599 },
//     { _id: 'Books', count: 380, avgPrice: 29 },
//     ...
//   ],
//   topRated: [
//     { sku: 'PHONE-001', title: 'Smartphone X', rating: 4.9 },
//     ...
//   ]
// }

Resultado: populate implementa associações em vários níveis; discriminator suporta múltiplas funções dentro de um único conjunto; lean() proporciona um aumento de 5 vezes no desempenho; e aggregate realiza uma única agregação na camada do banco de dados para retornar resultados multidimensionais.

❓ Perguntas Frequentes

P: Quando se deve usar populate em vez de $lookup? R: Use populate para conjuntos de dados pequenos (flexibilidade na camada de aplicação) e $lookup para conjuntos de dados grandes (eficiência na camada de banco de dados).

P: Existe alguma diferença entre um discriminador e uma coleção? R: Os discriminadores compartilham a mesma coleção (distinguida pelo campo “role”), enquanto as coleções independentes são fisicamente isoladas.

P: Posso chamar save() depois de lean()? R: Não. lean() retorna um objeto simples que não possui métodos do Mongoose. Se você precisar chamar save(), consulte o documento novamente ou use os métodos do documento.


📖 Resumo


📝 Exercícios

  1. Problema básico (⭐): Implemente uma consulta de junção populate (Pedido + Usuário + Produto).
  2. Problema básico (⭐): Use lean() para otimizar a API da lista de produtos e comparar as diferenças de desempenho.
  3. Problema avançado (⭐⭐): Use um discriminador para implementar as três funções: Usuário, Cliente e Administrador.
  4. Exercício avançado (⭐⭐): Use bulkWrite para atualizar o estoque de produtos em massa (lidar com situações de falta de estoque).
  5. Desafio (3 estrelas): Conclua as APIs avançadas do Mongoose (populate + lean + aggregate + discriminator).
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%