So I have a model in my DB called “Review.” I’m including the code for the model below. As you can see, there’s a field in each document called “reviewLikes.” My code is structured so that one someone clicks on a “like” button for a review, a PATCH request is sent to a controller, which triggers an update the “reviewLikes” field using findByIdAndUpdate. When this happens, the new number for “reviewLikes” is the only thing being passed in (along with the ID for the review, which is used to make sure the right document gets the new “like” number).
The problem: When this all happens, there is aggregation middleware in the model that runs. The part that is causing me a headache is “reviewSchema.statics.calcAverageRatings” that is included below. As a result, the “reviewLikes” is correctly updated, however, the averages for the ship and the review count all get reset back to 3 and 0 (respectively).
So, my question is, how can I prevent this block of code from executing if I am ONLY updating the “reviewLikes” field in a document? I have tried, unsuccessfully, to put a simple “if” statement outside of the block of code. It was something like if (this.rating) { the middleware } … but that did not work. Then I was going to try passing the entire review into the function instead of just reviewLikes, but then I realized that this wouldn’t stop the middleware from running… it would probably just generate funky changes to the counts each time the “like” button is clicked.
Anyway, if you’re still with me, I appreciate you reading this. I’ve been stuck on this all weekend. Any help would be amazing. Thanks.
const mongoose = require('mongoose');
const Ship = require('./shipModel');
const dateFormat = require('dateformat');
const reviewSchema = new mongoose.Schema(
{
review: {
type: String,
required: [true, 'Review cannot be empty!'],
},
rating: {
type: Number,
min: 1,
max: 5,
},
ratingDining: {
type: Number,
min: 1,
max: 5,
},
ratingCabin: {
type: Number,
min: 1,
max: 5,
},
ratingKids: {
type: Number,
min: 1,
max: 5,
},
ratingValue: {
type: Number,
min: 1,
max: 5,
},
ratingEntertainment: {
type: Number,
min: 1,
max: 5,
},
ratingValue: {
type: Number,
min: 1,
max: 5,
},
cabinType: {
type: String,
},
cabinNumber: {
type: String,
},
sailDate: {
type: String,
},
createdAt: {
type: Date,
default: Date.now,
},
displayDate: {
type: String,
},
reviewLikes: {
type: Number,
default: 0,
},
ship: {
type: mongoose.Schema.ObjectId,
ref: 'Ship',
required: [true, 'Review must belong to a ship.'],
},
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: [true, 'Review must belong to a user.'],
},
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true },
}
);
reviewSchema.index({ ship: 1, user: 1 }, { unique: true });
//MIDDLEWARE
reviewSchema.pre(/^find/, function (next) {
// this.populate({
// path: 'ship',
// select: 'name',
// }).populate({
// path: 'user',
// select: 'name photo',
// });
this.populate({
path: 'user',
select: 'photo name',
});
next();
});
reviewSchema.pre(/^find/, function (next) {
this.populate({
path: 'ship',
select: 'shipName',
});
next();
});
reviewSchema.pre('save', async function () {
const newDate = dateFormat(this.createdAt, 'mmmm dS, yyyy');
this.displayDate = newDate;
});
reviewSchema.pre('save', async function () {
const newDate = dateFormat(this.sailDate, 'mmmm, yyyy');
this.sailDate = newDate;
});
reviewSchema.statics.calcAverageRatings = async function (shipId) {
const stats = await this.aggregate([
{
$match: { ship: shipId },
},
{
$group: {
_id: '$ship',
nRating: { $sum: 1 },
avgRating: { $avg: '$rating' },
avgRatingDining: { $avg: '$ratingDining' },
avgRatingCabin: { $avg: '$ratingCabin' },
avgRatingKids: { $avg: '$ratingKids' },
avgRatingValue: { $avg: '$ratingValue' },
avgRatingEnt: { $avg: '$ratingEntertainment' },
},
},
]);
if (stats.length > 0) {
await Ship.findByIdAndUpdate(shipId, {
ratingsQuantity: stats[0].nRating,
ratingsAverage: stats[0].avgRating.toFixed(1),
ratingsAverageDining: stats[0].avgRatingCabin.toFixed(1),
ratingsAverageCabin: stats[0].avgRatingCabin.toFixed(1),
ratingsAverageKids: stats[0].avgRatingKids.toFixed(1),
ratingsAverageValue: stats[0].avgRatingValue.toFixed(1),
ratingsAverageEnt: stats[0].avgRatingEnt.toFixed(1),
});
} else {
await Ship.findByIdAndUpdate(shipId, {
ratingsQuantity: 0,
ratingsAverage: 3,
ratingsAverageDining: 3,
ratingsAverageCabin: 3,
ratingsAverageKids: 3,
ratingsAverageValue: 3,
ratingsAverageEnt: 3,
});
}
};
reviewSchema.post('save', function () {
//"this" points to current review
this.constructor.calcAverageRatings(this.ship);
});
// findByIdAndUpdate
// findByIdAndDelete
reviewSchema.pre(/^findOneAnd/, async function (next) {
this.r = await this.findOne();
//console.log(this.r);
next();
});
reviewSchema.post(/^findOneAnd/, async function (next) {
await this.r.constructor.calcAverageRatings(this.r.ship);
});
const Review = mongoose.model('Review', reviewSchema);
module.exports = Review;