SyntaxStudy
Sign Up
MongoDB Mongoose Queries and Populate
MongoDB Beginner 1 min read

Mongoose Queries and Populate

Mongoose provides a chainable query API that closely mirrors the MongoDB driver but adds Mongoose-specific features. The populate() method resolves ObjectId references to full documents from other collections, acting as an application-level join. You define which field to populate by referencing the model name in the schema ref option. Populate can be nested (populate within populate), and you can specify which fields to include or exclude from the populated document using a projection string. Mongoose also supports lean queries (using .lean()) which return plain JavaScript objects instead of Mongoose documents — these are significantly faster and use less memory when you do not need Mongoose features on the result.
Example
const mongoose = require('mongoose')

// Schema with a reference
const postSchema = new mongoose.Schema({
  title:    { type: String, required: true },
  body:     String,
  author:   { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
  tags:     [String],
  published: { type: Boolean, default: false }
}, { timestamps: true })

const Post = mongoose.model('Post', postSchema)

// --- Querying ---

// Find with populate — resolves authorId to full User document
const posts = await Post.find({ published: true })
  .populate('author', 'name email -_id')  // only name and email
  .sort({ createdAt: -1 })
  .limit(10)
  .lean()   // returns plain objects (faster)

// findById
const post = await Post.findById('64a1f2b3c4d5e6f7a8b9c0d1').populate('author')

// findOne with conditions
const latest = await Post.findOne({ tags: 'mongodb' }).sort({ createdAt: -1 })

// Update and return new document
const updated = await Post.findByIdAndUpdate(
  id,
  { $set: { published: true } },
  { new: true, runValidators: true }
)

// Delete
await Post.findByIdAndDelete(id)