DEV Community

Chiranjib
Chiranjib

Posted on

Working with Node.js Entities and Mongoose Models - I

Previous: Integrate Node.js Backend with MongoDB

Let's reiterate the idea of our example application

Our application maintains a database of movies, and their ratings by people. So, we are dealing with three entities: User (you or me), Movie (the things that require our time, money and attention) and Rating (link between User & Movie).

Step 1 - define Schemas and Models

As can be deduced from above, let us create three (data-access) models:

./data-access/models/User.js
const { Schema } = require('mongoose');
const { Types } = Schema;
const { getDBConnection } = require('_data-access/ConnectionFactory');

const UserSchema = new Schema(
    {
        email: { type: Types.String, required: true, unique: true },
        firstName: { type: Types.String },
        lastName: { type: Types.String },
    },
    {
        timestamps: true,
    }
);

module.exports = getDBConnection().model('user', UserSchema, null, {});

Enter fullscreen mode Exit fullscreen mode
./data-access/models/Movie.js
const { Schema } = require('mongoose');
const { Types } = Schema;
const { getDBConnection } = require('_data-access/ConnectionFactory');

const Movie = new Schema(
    {
        name: { type: Types.String, required: true, unique: true },
        imdbLink: { type: Types.String, required: true, unique: true },
        rottenTomatoesLink: { type: Types.String, required: true, unique: true },
    },
    {
        timestamps: true,
    }
);

module.exports = getDBConnection().model('movie', Movie, null, {});

Enter fullscreen mode Exit fullscreen mode
./data-access/models/MovieRating.js
const { Schema } = require('mongoose');
const { Types } = Schema;
const { getDBConnection } = require('_data-access/ConnectionFactory');

const MovieRating = new Schema(
    {
        movie: { type: Types.ObjectId, ref: require('./Movie'), required: true },
        user: { type: Types.ObjectId, ref: require('./User'), required: true },
        rating: { type: Types.Number, enum: [1, 2, 3, 4, 5] },
    },
    {
        timestamps: true,
    }
);

module.exports = getDBConnection().model('movierating', MovieRating, null, {});


module.exports = getDBConnection().model('movierating', MovieRating, null, {});
Enter fullscreen mode Exit fullscreen mode

Step 2 - define indices for faster data access

This step is easy to miss, and it seems unnecessary, but it is something essential that can come back to bite people in the middle of the night when a customer data can't be loaded or the database starts sending high severity alerts because it is experiencing high I/O or is running out of memory.

Now we're walking a slippery slope. Database indices are a two-edged sword. As the name suggests, think of indices as an index to a book. So, if there are edits to any page of a book or a new page gets added to the book, the index would have to be revised and recreated. That's why, while they enable faster data access, one too many indices will have I/O or memory implications of their own. Potentially, every document write will trigger refresh of the indices.

That's why, it is important to imagine and define the data-access patterns in the application. With our example, here are the potential data queries:

  • find user by email
  • find movie by name
  • find how many users have rated a movie
  • find how many movies a user has rated

Revisiting the Models and Schemas we have defined, let us define indices:

data-access/models/User.js
UserSchema.index({ firstName: 1 }); // enable searching user by first name
Enter fullscreen mode Exit fullscreen mode
data-access/models/Movie.js
Movie.index({ name: 1 }); // enable searching a movie by name
Enter fullscreen mode Exit fullscreen mode
data-access/models/MovieRating.js
MovieRating.index({ movie: 1, user: 1 }); // enable searching movie-ratings by movie first, and then by specific user if required
MovieRating.index({ user: 1, movie: 1 }); // enable searching movie-ratings by users first, and then by specific movie if required
Enter fullscreen mode Exit fullscreen mode

At this point, we are well-equipped to take the next step and achieve full sufficiency in our backend.

Next: Working with Node.js Entities and Mongoose Models - II

Top comments (0)