Understanding Relationships in MongoDB and Mongoose

understanding-relationships-in-mongodb-and-mongoose

Understanding Relationships in MongoDB & Mongoose

Introduction

MongoDB, being a NoSQL database, handles relationships differently than relational databases like MySQL. Instead of foreign keys, it uses Embedded Documents (Denormalization) and References (Normalization) to establish connections between data.

In this guide, we’ll explore:

  • One-to-One (1:1) Relationships
  • One-to-Many (1:M) Relationships
  • Many-to-Many (M:N) Relationships
  • How to define them in MongoDB and Mongoose
  • How to query them and what the results look like

Let’s dive in! 🚀

1. Types of Relationships in MongoDB

In MongoDB, relationships can be represented in two ways:

  1. Embedded Documents (Denormalization)
  2. References (Normalization)

Just like MySQL, MongoDB supports:

  • One-to-One (1:1)
  • One-to-Many (1:M)
  • Many-to-Many (M:N)

For each type, I’ll cover:

  • MongoDB Schema (Embedded & Referenced)
  • Querying in MongoDB
  • How to Define it in Mongoose
  • Querying with Mongoose
  • Example Output

2. One-to-One (1:1) Relationship

Example Scenario

A User has one Profile, and a Profile belongs to only one User.

Method 1: Using Embedded Document (Denormalization)

MongoDB Schema

{
  "_id": ObjectId("user_id"),
  "name": "John",
  "profile": {
    "bio": "Loves coding"
  }
}

Mongoose Schema

const userSchema = new mongoose.Schema({
  name: String,
  profile: {
    bio: String
  }
});
const User = mongoose.model("User", userSchema);

Querying in Mongoose

const user = await User.findOne();
console.log(user);

Example Output

{
  "_id": "user_id",
  "name": "John",
  "profile": {
    "bio": "Loves coding"
  }
}

Method 2: Using References (Normalization)

MongoDB Schema (Separate Documents)

{
  "_id": ObjectId("user_id"),
  "name": "John",
  "profile": ObjectId("profile_id")
}
{
  "_id": ObjectId("profile_id"),
  "bio": "Loves coding",
  "user": ObjectId("user_id")
}

Mongoose Schema

const profileSchema = new mongoose.Schema({
  bio: String,
  user: { type: mongoose.Schema.Types.ObjectId, ref: "User" }
});

const userSchema = new mongoose.Schema({
  name: String,
  profile: { type: mongoose.Schema.Types.ObjectId, ref: "Profile" }
});

const Profile = mongoose.model("Profile", profileSchema);
const User = mongoose.model("User", userSchema);

Querying in Mongoose (Using .populate())

const user = await User.findOne().populate("profile");
console.log(user);

Example Output

{
  "_id": "user_id",
  "name": "John",
  "profile": {
    "_id": "profile_id",
    "bio": "Loves coding"
  }
}

3. One-to-Many (1:M) Relationship

Example Scenario

A User has many Posts, but each Post belongs to only one User.

Method 1: Using Embedded Documents (Denormalization)

MongoDB Schema

{
  "_id": ObjectId("user_id"),
  "name": "John",
  "posts": [
    { "title": "MongoDB Guide", "content": "MongoDB is great!" },
    { "title": "Mongoose Intro", "content": "Mongoose is helpful!" }
  ]
}

Mongoose Schema

const userSchema = new mongoose.Schema({
  name: String,
  posts: [
    {
      title: String,
      content: String
    }
  ]
});
const User = mongoose.model("User", userSchema);

Querying in Mongoose

const user = await User.findOne();
console.log(user);

Example Output

{
  "_id": "user_id",
  "name": "John",
  "posts": [
    { "title": "MongoDB Guide", "content": "MongoDB is great!" },
    { "title": "Mongoose Intro", "content": "Mongoose is helpful!" }
  ]
}

Method 2: Using References (Normalization)

MongoDB Schema

{
  "_id": ObjectId("user_id"),
  "name": "John",
  "posts": [ObjectId("post1_id"), ObjectId("post2_id")]
}
{
  "_id": ObjectId("post1_id"),
  "title": "MongoDB Guide",
  "content": "MongoDB is great!",
  "user": ObjectId("user_id")
}

Mongoose Schema

const postSchema = new mongoose.Schema({
  title: String,
  content: String,
  user: { type: mongoose.Schema.Types.ObjectId, ref: "User" }
});

const userSchema = new mongoose.Schema({
  name: String,
  posts: [{ type: mongoose.Schema.Types.ObjectId, ref: "Post" }]
});

const Post = mongoose.model("Post", postSchema);
const User = mongoose.model("User", userSchema);

Querying in Mongoose

const user = await User.findOne().populate("posts");
console.log(user);

Example Output

{
  "_id": "user_id",
  "name": "John",
  "posts": [
    { "_id": "post1_id", "title": "MongoDB Guide", "content": "MongoDB is great!" },
    { "_id": "post2_id", "title": "Mongoose Intro", "content": "Mongoose is helpful!" }
  ]
}

4. Many-to-Many (M:N) Relationship

Example Scenario

A Student can enroll in many Courses, and a Course can have many Students.

Using References with a Junction Collection

MongoDB Schema

{
  "_id": ObjectId("student_id"),
  "name": "John",
  "courses": [ObjectId("course1_id"), ObjectId("course2_id")]
}
{
  "_id": ObjectId("course1_id"),
  "title": "Math 101",
  "students": [ObjectId("student_id")]
}

Mongoose Schema

const studentSchema = new mongoose.Schema({
  name: String,
  courses: [{ type: mongoose.Schema.Types.ObjectId, ref: "Course" }]
});

const courseSchema = new mongoose.Schema({
  title: String,
  students: [{ type: mongoose.Schema.Types.ObjectId, ref: "Student" }]
});

const Student = mongoose.model("Student", studentSchema);
const Course = mongoose.model("Course", courseSchema);

Querying in Mongoose

const student = await Student.findOne().populate("courses");
console.log(student);

Example Output

{
  "name": "John",
  "courses": [
    { "_id": "course1_id", "title": "Math 101" },
    { "_id": "course2_id", "title": "Physics 201" }
  ]
}

5. Summary Table

Relationship Embedded Documents References with .populate()
One-to-One { profile: { bio: "text" } } { profile: ObjectId("profile_id") }
One-to-Many { posts: [ {title, content} ] } { posts: [ObjectId("post_id")] }
Many-to-Many ❌ (not ideal) { courses: [ObjectId("course_id")] }

Conclusion

  • Embedded documents are good for fast reads but increase duplication.
  • References with .populate() keep data normalized but require joins.
  • Mongoose makes relationships easy using Schema.Types.ObjectId.
Total
0
Shares
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post
playwright:-a-guide-to-browser-automation-&-testing

Playwright: A Guide to Browser Automation & Testing

Next Post
-testing-strategies:-essential-tips-and-tricks-for-developers

🚀 Testing Strategies: Essential Tips and Tricks for Developers

Related Posts