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, {});
./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, {});
./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, {});
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
data-access/models/Movie.js
Movie.index({ name: 1 }); // enable searching a movie by name
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
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)