Published on

Optimize mongodb query

Authors
  • avatar
    Name
    Loc Truong
    Twitter

Use Indexes Effectively

// In your schema
ExampleSchema.index({ user_id: 1, updated_at: -1 }) // Compound index for sorting
ExampleSchema.index({ user_id: 1, show_id: 1 })     // Compound index for grouping

// In your query
.hint({ user_id: 1 }) // Force using index

Project Only Needed Fields

{
  $lookup: {
    from: 'shows',
    let: { show_id: '$_id' },
    pipeline: [
      {
        $match: {
          $expr: { $eq: ['$_id', '$$show_id'] }
        }
      },
      {
        $project: {
          // Only get fields you need
          title: 1,
          coverImage: 1,
          slug: 1
        }
      }
    ],
    as: 'show'
  }
}

Use $facet for Multiple Operations

{
  $facet: {
    // Get count in parallel with results
    total: [
      { $group: { _id: '$show_id' } },
      { $count: 'count' }
    ],
    result: [
      // Your main query pipeline
    ]
  }
}

Early Filtering

// Do this first
{
  $match: { user_id: ObjectId(user_id) }
}
// Before expensive operations like
{ $sort: ... }
{ $group: ... }

Efficient Data Processing

// Instead of multiple $lookup stages
{
  $project: {
    show: {
      $mergeObjects: [
        { $arrayElemAt: ['$show', 0] },
        { url: { $arrayElemAt: ['$episode.url', 0] } }
      ]
    }
  }
}

Use Aggregation Stages in Right Order

[
  { $match: ... },     // Filter first
  { $sort: ... },      // Sort early if possible
  { $group: ... },     // Reduce document count
  { $skip: ... },      // Pagination after reducing
  { $limit: ... },     // Limit results
  { $lookup: ... },    // Join with other collections
  { $project: ... }    // Final shape
]

Monitor Query Performance

// Add explain() to see query plan
const explain = await Example.aggregate([...]).explain('executionStats')
console.log(explain.executionStats.executionTimeMillis)

Use Pagination

const perPage = 6
const page = parseInt(req.query.page) || 1
const offset = (page - 1) * perPage

// In pipeline
{ $skip: offset }
{ $limit: perPage }

Error Handling and Defaults

try {
  const result = await query
  return {
    total: total || 0,      // Default if null
    result: result || []    // Default if null
  }
} catch (error) {
  console.error('Query error:', error)
  return rep.internalServerError()
}

Query Analysis Tools

// In MongoDB Compass or Shell
db.example.aggregate([...]).explain("executionStats")

// Check indexes
db.example.getIndexes()

// Check collection stats
db.example.stats()
  • Always test with real data volumes
  • Monitor query performance in production
  • Use appropriate indexes for your query patterns
  • Keep your queries as simple as possible
  • Filter early, project late -> These principles will help you write efficient MongoDB queries!