MongoDB One-to-One relationship tutorial with Mongoose example
Model One-to-One Relationships
Assume that we have 2 entity: Identifier and Customer. For example:
We want to map Identifier and Customer relationship in that: – One Customer has only one Identifier. – One Identifier belongs to only one Customer.
This is called One-to-One Relationship. Now let’s take a look at 2 ways to design schema for this kind of model.
Reference Data Models (Normalization)
In this model, an object A connects to the object B by reference to object B id or a unique identification field.
For example, Identifier has a customer_id
field which value is equal to Customer object’s unique _id
value.
Embedded Data Models (Denormalization)
It’s easy to understand with ‘Embedded’ word. Instead of using a reference, Object A contains the whole object B, or object B is embedded inside object A.
You can see the example below, Identifier will have a nested field customer
.
You’ve known 2 ways to make One-to-One Relationships. Let’s implement each of them in a Node.js app using Mongoose. After that, I will tell you which model should be used for implementing One-to-One relationship between collections in MongoDb Database.
Mongoose One-to-One relationship example
Setup Nodejs App
First we need to install mongoose, so run the command:
Next, we create project structure like this:
src
models
Customer.js
Identifier.js
server.js
package.json
Open server.js, we import mongoose
to our app and connect to MongoDB database.
The first step is done, we’re gonna create appropriate models and use mongoose
to interact with MongoDB database in two ways:
Referencing
Embedding
Mongoose One-to-One relationship: Referencing
1. Define One-to-One models using Mongoose
Let’s create 2 main model with mongoose.Schema()
construtor function.
In models/Customer.js, define Customer
with 3 fields: name
, age
, gender
.
Identifier object will have cardCode
field and a reference customer
.
So open models/Identifier.js, define Identifier
like this:
In the code above, we add customer
field, set its type
to ObjectId
and ref
to Customer
. What does it help?
Now if we save an Identifier to MongoDB database, a document will be added like this:
Let’s test it, and I will show you how to get an Identifier object with full-fields Customer in this approach.
2. Test with MongoDB database
Open server.js, add the code below:
Console shows the result.
We can check collections in the database.
As I’ve said before, the Identifier has only ObjectId
in customer
field.
The question is: How to get full items of customer
?
Oh yeah, we can reference documents in other collections using populate().
Let me show you how to do this.
The result looks like this.
Mongoose Hide _id & __v in result
If you don’t want to get customer._id
& customer.__v
in the result, just add second parameters to populate()
function like this:
Check the result:
How about __v
in parent object?
We use select()
function to remove it.
The result will be:
Mongoose One-to-One relationship: Embedding
1. Define One-to-One models using Mongoose
The way we define models for Embedded Documents will be different from Referenced Documents.
In models/Customer.js, we also define Customer
like code above, but also export CustomerSchema
.
Identifier object will still have cardCode
field & customer
field. But instead using a reference, we assign import and CustomerSchema
directly.
models/Identifier.js
2. Test with MongoDB database
Open server.js, the definition of createCustomer
& createIdentifier
functions will be the same as Referencing.
It’s a little change in how we call createIdentifier()
. Instead passing customerId
, we use customer
object.
Run the code, then check result in the console.
Now look at our MongoDB database. Full Customer object is embedded in Identifier object now.
To get the items in identifiers
collections, just use find()
function.
And you can see all things you want in the result:
Mongoose Hide _id & __v in result
With embedded documents, to hide _id
& __v
is easy with only one select()
function.
For example, if I want to remove Identifier __v
, also embedded document (Customer) fields such as customer._id
& customer.__v
, just write select()
with parameter like this.
Now the result is pretty.
Referencing or Embedding for One-to-One Relationships
To make One-to-One Relationship between documents, we can reference or embed a document in the other. Which should be used in general?
Now remind what we’ve done and you can see that we have to use an additional function called populate()
after find()
function for Referenced Data Model.
Embedding stores related information directly inside the objects. So we only use find()
function to get everything. It gives us better performance with a single query. This Model also helps us update the embedded data in just one query using Dot Notation.
So the answer is that modeling One-to-One relationships with Embedded documents is the better choice.
Last updated