Introduction
When building complex applications with MongoDB and Mongoose, you’ll often find yourself needing to create relationships between different collections. One commonly used technique is referencing documents using MongoDB’s ObjectID
. In this article, we’ll explore how to use ObjectID
as a reference to other documents in a Mongoose schema and touch upon the use of subdocuments.
ObjectID as a Reference
In MongoDB, each document is automatically assigned a unique identifier known as _id
, represented as an ObjectID
. You can use this ObjectID
to establish relationships between different documents across collections.
Example Schema with ObjectID Reference
Here’s a simple example using Mongoose to demonstrate how to reference another document using ObjectID
. Let’s say we have a User
model and a Post
model, and we want to associate each post with a user.
import mongoose, { Schema, Document } from "mongoose";
// User Schema
interface IUser extends Document {
username: string;
email: string;
}
const UserSchema: Schema = new Schema({
username: { type: String, required: true },
email: { type: String, required: true },
});
const User = mongoose.model<IUser>("User", UserSchema);
// Post Schema with ObjectID reference to User
interface IPost extends Document {
title: string;
content: string;
_user: IUser["_id"];
}
const PostSchema: Schema = new Schema({
title: { type: String, required: true },
content: { type: String, required: true },
_user: { type: Schema.Types.ObjectId, ref: "User" },
});
const Post = mongoose.model<IPost>("Post", PostSchema);
In the PostSchema
, notice the _user
field. The _user
field is set to be of type Schema.Types.ObjectId
and references the User
model. The underscore _
is a naming convention to indicate that this field is a reference to another document.
Subdocuments
Subdocuments in Mongoose allow you to nest documents within other documents. However, it’s crucial to be mindful of MongoDB’s 16MB size limit for a single document when using nested subdocuments.
Example Schema with Subdocuments
Here, we introduce a recipientSchema
and use it as a subdocument in the Post
schema.
// Recipient Schema
const recipientSchema: Schema = new Schema({
email: { type: String, required: true },
responded: { type: Boolean, default: false },
});
// Update Post Schema to include recipients field
interface IPost extends Document {
title: string;
content: string;
_user: IUser["_id"];
recipients: typeof recipientSchema[];
}
const PostSchema: Schema = new Schema({
title: { type: String, required: true },
content: { type: String, required: true },
_user: { type: Schema.Types.ObjectId, ref: "User" },
recipients: [recipientSchema],
});
const Post = mongoose.model<IPost>("Post", PostSchema);
In the PostSchema
, the recipients
field is an array of recipientSchema
records. This feature is particularly useful for embedding related data, but it comes with the 4MB size limitation caveat.
When to Use Subdocuments?
Use nested subdocuments sparingly and only when the data in the subdocument has no standalone meaning without the parent document. Otherwise, opt for ObjectID
references.
Conclusion
Understanding how to use MongoDB’s ObjectID
for referencing documents and the limitations of subdocuments helps in designing a well-structured database schema. The flexibility of MongoDB allows various ways to represent relationships between collections, but being mindful of best practices and limitations ensures that your application is both scalable and maintainable.
,