📖
Maissen's Grimoire
  • Maissen's Grimoire
  • Html and css grimoire
    • HTML5 Periodical Table
    • HTML Cheat Sheet
    • CSS Cheatsheets
  • Javascript Grimoire
    • JavaScript Cheat Sheet
      • Javascript Array in depth
      • Tagged Template Literals
      • Guard Clauses - The Best Way To Write Complex Conditional Logic
      • JavaScript Optional Chaining
      • JavaScript Null Coalesce
      • What Are Magic Numbers And Why Are They Bad
      • ES6/ECMAScript2015 Cheatsheet
      • First-class and Higher Order Functions: Effective Functional JavaScript
    • Useful JavaScript Tips, Tricks and Best Practices
    • Bits of code
    • Useful JavaScript libraries
      • Components
      • Animation
      • Maps
      • Helpers
      • Presentations
      • Charts
      • Games
      • Audio
      • Images
      • Video
    • Js the right way
  • Angular Grimoire
    • Angular doc
    • Getting Started
    • Angular clean architecture
    • Angular Cheat Sheet
    • TypeScript Cheat Sheet
    • My Favorite Tips and Tricks in Angular
    • NgRx: tips & tricks
    • Bits of code
      • Execute Multiple HTTP Requests in Angular
      • Authentification
        • Angular 8 JWT Authentication with HttpInterceptor and Router
      • Integrations
        • Spring Boot
          • Rest Example
            • Angular,Spring Boot,Spring Data and Rest Example(CRUD)
          • Authentification
            • Angular, Spring Boot: JWT Authentication with Spring Security example
            • Angular Spring Boot Security Oauth2
              • Spring Boot OAUTH2 Role-Based Authorization
              • Spring Boot Security Google Oauth
              • Spring Security OAuth2 User Registration
    • Most used dependency
  • Node Grimoire
    • Express.js 4 Cheatsheet
    • Useful Dependencies
    • How To Use And Write Express Middleware
    • Node.js with SQL databases
      • Node.js Token Based Authentication & Authorization example
      • Node.js Rest APIs example with Express, Sequelize & MySQL
      • Node.js Express & PostgreSQL: CRUD Rest APIs example with Sequelize
      • Sequelize
        • Sequelize Many-to-Many Association example – Node.js & MySQL
        • Sequelize One-to-Many Association example with Node.js & MySQL
    • Node.js with NOSQL databases
      • Node.js + MongoDB: User Authentication & Authorization with JWT
      • Node.js, Express & MongoDb: Build a CRUD Rest Api example
      • MongoDB One-to-One relationship tutorial with Mongoose example
      • MongoDB One-to-Many Relationship tutorial with Mongoose examples
      • MongoDB Many-to-Many Relationship with Mongoose examples
  • Upload files
    • How to upload multiple files in Node.js
    • Upload & resize multiple images in Node.js using Express, Multer, Sharp
    • Upload/store images in MySQL using Node.js, Express & Multer
    • How to upload/store images in MongoDB using Node.js, Express & Multer
  • React Grimoire
    • React Doc
    • React Grimoire
    • React Cheat Sheet
  • spring boot Grimoire
    • Getting started
    • Spring Boot, Spring Data JPA – Rest CRUD API example
    • Spring Boot Token based Authentication with Spring Security & JWT
  • Mongo Grimoire
    • MongoDb-Mongoose Cheat Sheet
  • Development tools
    • Design Patterns
  • maissen_grimoire
Powered by GitBook
On this page
  • Model One-to-One Relationships
  • Mongoose One-to-One relationship example
  • Referencing or Embedding for One-to-One Relationships

Was this helpful?

  1. Node Grimoire
  2. Node.js with NOSQL databases

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:

// Identifier
{
   _id: "12345xyz",
   cardCode: "BKD2019",
}

// Customer
{
   _id: "cus123",
   name: "bezkoder",
   age: 29,
   gender: "male"
}

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.

// Identifier
{
   _id: "12345xyz",
   cardCode: "BKD2019",
   customer_id: "cus123",
}

// Customer
{
   _id: "cus123",  // equal Identifier[customer_id]
   name: "bezkoder",
   age: 29,
   gender: "male"
}

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.

// Identifier
{
   _id: "12345xyz",
   cardCode: "BKD2019",
   customer: {
                _id: "cus123", // Identifier[customer_id]
                name: "bezkoder",
                age: 29,
                gender: "male"
             }
}

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:

npm install mongoose

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.

const mongoose = require("mongoose");

mongoose
  .connect("mongodb://localhost/bezkoder_db", {
    useNewUrlParser: true,
    useUnifiedTopology: true
  })
  .then(() => console.log("Successfully connect to MongoDB."))
  .catch(err => console.error("Connection error", err));

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.

const mongoose = require("mongoose");

const Customer = mongoose.model(
  "Customer",
  new mongoose.Schema({
    name: String,
    age: Number,
    gender: String
  })
);

module.exports = Customer;

Identifier object will have cardCode field and a reference customer. So open models/Identifier.js, define Identifier like this:

const mongoose = require("mongoose");

const Identifier = mongoose.model(
  "Identifier",
  new mongoose.Schema({
    cardCode: String,
    customer: {
      type: mongoose.Schema.Types.ObjectId,
      ref: "Customer"
    }
  })
);

module.exports = Identifier;

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:

{
   _id : ObjectId("5da000be062dc522eccaedeb"),
   cardCode : "5DA000BC06",
   customer : ObjectId("5da000bc062dc522eccaedea"),
   __v : 0
}

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:

const Customer = require("./models/Customer");
const Identifier = require("./models/Identifier");

const createCustomer = function(name, age, gender) {
  const customer = new Customer({
    name,
    age,
    gender
  });

  return customer.save();
};

const createIdentifier = function(cardCode, customer) {
  const identifier = new Identifier({
    cardCode,
    customer
  });

  return identifier.save();
};

createCustomer("bezkoder", 29, "male")
  .then(customer => {
    console.log("> Created new Customer\n", customer);
    
    const customerId = customer._id.toString();
    return createIdentifier(customerId.substring(0, 10).toUpperCase(), customerId);
  })
  .then(identifier => {
    console.log("> Created new Identifier\n", identifier);
  })
  .catch(err => console.log(err));

Console shows the result.

> Created new Customer
 { _id: 5da135bd61a1dd3e9c2a6e81,
  name: 'bezkoder',
  age: 29,
  gender: 'male',
  __v: 0 }

> Created new Identifier
 { _id: 5da135bf61a1dd3e9c2a6e82,    
  cardCode: '5DA135BD61',
  customer: 5da135bd61a1dd3e9c2a6e81,
  __v: 0 }

We can check collections in the database.

As I’ve said before, the Identifier has only ObjectId in customer field.

Let me show you how to do this.

const showAllIdentifier = async function() {
  const identifiers = await Identifier.find().populate("customer");

  console.log("> All Identifiers\n", identifiers);
};

The result looks like this.

> All Identifiers
 [ { _id: 5da135bf61a1dd3e9c2a6e82,
    cardCode: '5DA135BD61',
    customer:
     { _id: 5da135bd61a1dd3e9c2a6e81,
       name: 'bezkoder',
       age: 29,
       gender: 'male',
       __v: 0 },
    __v: 0 } ]

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:

const identifiers = await Identifier.find()
    .populate("customer", "-_id -__v");

Check the result:

> All Identifiers
 [ { _id: 5da135bf61a1dd3e9c2a6e82,
    cardCode: '5DA135BD61',
    customer: { name: 'bezkoder', age: 29, gender: 'male' },
    __v: 0 } ]
const identifiers = await Identifier.find()
    .populate("customer", "-_id -__v");
    .select("-__v");

The result will be:

> All Identifiers
 [ { _id: 5da135bf61a1dd3e9c2a6e82,
    cardCode: '5DA135BD61',
    customer: { name: 'bezkoder', age: 29, gender: 'male' } } ]

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.

const mongoose = require("mongoose");

const CustomerSchema = new mongoose.Schema({
  name: String,
  age: Number,
  gender: String
});

const Customer = mongoose.model("Customer", CustomerSchema);

module.exports = { Customer, CustomerSchema };

Identifier object will still have cardCode field & customer field. But instead using a reference, we assign import and CustomerSchema directly.

models/Identifier.js

const mongoose = require("mongoose");
const CustomerSchema = require("./Customer").CustomerSchema;

const Identifier = mongoose.model(
  "Identifier",
  new mongoose.Schema({
    cardCode: String,
    customer: CustomerSchema
  })
);

module.exports = Identifier;

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.

const Customer = require("./models/Customer").Customer;
const Identifier = require("./models/Identifier");

const createCustomer = function(name, age, gender) {
  const customer = new Customer({
    name,
    age,
    gender
  });

  return customer.save();
};

const createIdentifier = function(cardCode, customer) {
  const identifier = new Identifier({
    cardCode,
    customer
  });

  return identifier.save();
};

createCustomer("bezkoder", 29, "male")
  .then(customer => {
    console.log("> Created new Customer\n", customer);

    return createIdentifier(
      customer._id.toString().substring(0, 10).toUpperCase(),
      customer
    );
  })
  .then(identifier => {
    console.log("> Created new Identifier\n", identifier);
  })
  .catch(err => console.log(err));

Run the code, then check result in the console.

> Created new Customer
 { _id: 5da1406de666c118c89bba28,
  name: 'bezkoder',
  age: 29,
  gender: 'male',
  __v: 0 }

> Created new Identifier
 { _id: 5da1406fe666c118c89bba29,  
  cardCode: '5DA1406DE6',
  customer:
   { _id: 5da1406de666c118c89bba28,
     name: 'bezkoder',
     age: 29,
     gender: 'male',
     __v: 0 },
  __v: 0 }

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.

const showAllIdentifier = async function() {
  const identifiers = await Identifier.find();

  console.log("> All Identifiers\n", identifiers);
};

And you can see all things you want in the result:

> All Identifiers
 [ { _id: 5da1406fe666c118c89bba29,  
    cardCode: '5DA1406DE6',
    customer:
     { _id: 5da1406de666c118c89bba28,
       name: 'bezkoder',
       age: 29,
       gender: 'male',
       __v: 0 },
    __v: 0 } ]

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.

const showAllIdentifier = async function() {
  const identifiers = await Identifier.find()
    .select("-__v -customer.__v -customer._id");

  console.log("> All Identifiers\n", identifiers);
};

Now the result is pretty.

> All Identifiers
 [ { _id: 5da1406fe666c118c89bba29,
    cardCode: '5DA1406DE6',
    customer: { name: 'bezkoder', age: 29, gender: 'male' } } ]

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.

So the answer is that modeling One-to-One relationships with Embedded documents is the better choice.

PreviousNode.js, Express & MongoDb: Build a CRUD Rest Api exampleNextMongoDB One-to-Many Relationship tutorial with Mongoose examples

Last updated 5 years ago

Was this helpful?

mongoose-one-to-one-relationship-example-nodejs-mongodb-reference

The question is: How to get full items of customer? Oh yeah, we can reference documents in other collections using .

How about __v in parent object? We use function to remove it.

mongoose-one-to-one-relationship-example-nodejs-mongodb-embedded

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 .

populate()
select()
Dot Notation