I want to calculate averageRating from my reviews collection. So, firstly I make an aggregation pipeline to find the avgRating and ratingQuantity by matching with item ID.
Then I make an post middleware(document middleware) and when any one create a new review then the averageRating and ratingQuantity fields are get updated, but the problem is that this only works on save not on update or delete. So, i make a query middleware and then for getting the document I execute the query but got error Query was already executed Please Help!!!
My reviewModel.js code
const mongoose = require('mongoose');
const movieModel = require('./movieModel');
const reviewSchema =new mongoose.Schema({
review:{
type:String,
required:[true,"review can't be blank"],
maxlength:100,
minlength:10
},
rating:{
type:Number,
required:[true,"review must have a rating"],
max:10,
min:1
},
movie:{
type:mongoose.Schema.ObjectId,
ref:'movies',
required:[true,'review must belong to a movie']
},
user:{
type:mongoose.Schema.ObjectId,
ref:'users',
required:[true,'review must belong to a user']
}
},
{
toJSON:{virtuals:true},
toObject:{virtuals:true}
});
reviewSchema.pre(/^find/,function(next){
this.populate({
path:'movie',
select:'name'
}).populate({
path:'user',
select:'name'
});
next();
})
reviewSchema.index({movie:1,user:1},{unique:true});
reviewSchema.statics.calcAvgRating = async function(movieId){
console.log(movieId);
const stats = await this.aggregate([
{
$match:{movie:movieId}
},
{
$group:{
_id:'$movie',
nRating:{$sum:1},
avgRating:{$avg:'$rating'}
}
}
])
console.log(stats);
const movie = await movieModel.findByIdAndUpdate(movieId,{
ratingsQuantity:stats[0].nRating,
avgRating:stats[0].avgRating
});
}
reviewSchema.post('save',function(){
this.constructor.calcAvgRating(this.movie);
})
reviewSchema.pre(/^findOneAnd/,async function(next){
const r = await this.findOne();
console.log(r);
next();
})
const reviewModel = mongoose.model('reviews',reviewSchema);
module.exports = reviewModel;
My updateOne controller
exports.updateOne = Model=> catchAsync(async(req,res,next)=>{
console.log("handler");
const doc = await Model.findByIdAndUpdate(req.params.id,req.body,{
new:true,
runValidators:true
});
if(!doc)
return next(new appError('Ooops! doc not found',404));
sendResponse(res,200,'success',doc);
})
Try this
reviewSchema.post(/^findOneAnd/,async function(doc){
const model=doc.constructor;
})
Here doc is actually the current executed document and by doing doc.constructor you got its model. On that model you can use the calcAvgRating
Related
exports.addToCart = async(req,res)=>{
const cart = await schema.cart.findOne({username:req.body.username})
if(cart){
return res.status(404).json({
message:"User's cart is already available, append to the same cart"
})
}
else{
const cart = new schema.cart({
cartId : getValueForNextSequence("item_id"),
username : req.body.username,
productsInCart :req.body.productsInCart
});
console.log(cart.cartId);
await cart.save();
res.status(200).json(cart)
}
}
async function getValueForNextSequence(sequenceOfName){
const sequenceDoc = await schema.counter.findOneAndUpdate(
{"_id": sequenceOfName },
{"$inc":{"sequence_value":1}},
);
return sequenceDoc.sequence_value;
}
THis is the schema for counter I added a document with _id as item_id and sequence_value as 0
const counterSch = new mongoose.Schema({
_id :{
type : String
},
sequence_value:{
type : Number
}
})
getValueForNextSequence method is not returning any value I dont know why please help with this issue.Here I have to increment the cartId automatically but its not happening
Mongoose newbe here. I got the following function to update the references (deleting them) in the document Post when a Tag is deleted. When I call my GraphQl API this is what I got:
message": "posts.save is not a function"
The function in my gql resolver:
async deleteTag(root, { id }, context) {
const posts = await Post.find();
const tag = await Tag.findById(id);
if(!tag){
const error = new Error('Tag not found!');
error.code = 404;
throw error;
}
posts?.forEach(async (post) => {
await post.tags.pull(id);
})
await posts.save()
await Tag.findByIdAndRemove(id);
return true;
}
This is the Post model:
const PostSchema = new Schema({
body: {
type: String,
required: true
},
tags: {
type: [Schema.Types.ObjectId],
ref: 'Tag',
required: false
},
});
and this is the Tag model:
const TagSchema = new Schema(
{
name: {
type: String,
required: true
},
},
{ timestamps: true }
);
Looks like I can't call the method save() on the array of objects returned by Exercise.find()
I used the same pattern in other functions, the difference is that there I used .findById()
Any solution? Advice and best practice advide are super welcome.
You have to save the posts individually:
posts?.forEach(async (post) => {
await post.tags.pull(id);
await post.save();
})
Or use Model.updateMany() combined with the $pull operator.
FWIW, you should probably limit the number of matching Post documents by selecting only documents that have the specific tag listed:
await Post.find({ 'tags._id' : id });
These are my Schemas
const dataSchema = new mongoose.Schema({
email:String,
date:String,
amount:Number
})
const userSchema = new mongoose.Schema({
email: String,
password: String,
data:[{
type: mongoose.Schema.Types.ObjectId,
ref: "UserData",
}],
})
const User = new mongoose.model("User", userSchema);
const UserData = new mongoose.model("UserData", dataSchema);
I wish to update Data whenever a user post it. If the Userdata on the particular date already exists i wish to update it by adding the prev amount and new amount
app.post("/insert",(req,res)=>{
const username = req.cookies.username;
const Date = now.toLocaleDateString("en-Uk");
const Amount = req.body.value;
function createNewData(){
const userData = new UserData({
email:username,
date:Date,
amount: Amount
});
userData.save((err)=>{
if(err){
console.log(err);
}else{
console.log('newdatasaved');
res.redirect("/")
}
});
User.findOneAndUpdate({email:username},{$push:{data:userData._id}},(err)=>{
if(err){
console.log('cant push');
}else{
console.log('pushed data');
}
});
}
UserData.findOne({email:username,date:Date},(err,found)=>{
if(err){
createNewData();
console.log('cant find on particular date new created');
}else{
if(found){
let a = Number(found.amount);
let b = Number(Amount)+a;
UserData.findOneAndUpdate({email:username,date:Date},{$set:{amount:b}});
console.log('updated in existing');
res.redirect("/");
}
}
})
})
But it seems the data is always zero in database
See the amount section it is still zero.
Can anyone tell me what am i doing wrong. I have used set method but new data is unable to be posted.
You have to add callback to your update function.
UserData.findOneAndUpdate(
{email:username,date:Date},
{$set:{amount:b}},
function (error, success){}
)
If you don't want to use callback, then you have to use .exec()
UserData.findOneAndUpdate(
{email:username,date:Date},
{$set:{amount:b}},
).exec()
did you check the value of amount? Check the amount value in console.
Here is my code, I want to increment the total no of counts as soon as any user gives the rating. But the $inc command is not running and result is showing the default value which I set zero.
The given is my Schema.
const mongoose = require('mongoose');
const schema = mongoose.Schema;
let Rating = new schema({
user_id:{
type:mongoose.Types.ObjectId
},
stars:{
type:Number
},
ratingCount:{
type:Number,
default:0
}
})
const rating = mongoose.model('Rating', Rating);
module.exports = rating;
This is the function where I want to increment the value.
const express = require('express');
const Router = express.Router();
let Rating = require('../model/rating');
Router.route('/add/:userid').post((req,res)=>{
new Rating({
user_id: req.params.userid,
$inc: {ratingCount:1},
stars: req.body.stars
})
.save()
.then(rating=>res.send(rating))
.catch(err=>console.log(err));
});
module.exports = Router;
Result showing default value of ratingCount.
You need not to use .save() instead you can simply use .findOneAndUpdate() with option { new: true } to return updated document, if it doesn't find any matching document .findOneAndUpdate() will return null.
Code :
const express = require("express");
const Router = express.Router();
let Rating = require("../model/rating");
Router.route("/add/:userid").post((req, res) => {
Rating.findOneAndUpdate(
{ user_id: req.params.userid },
{ $inc: { ratingCount: 1 }, stars: req.body.stars },
{ new: true }
)
.then((rating) => res.send(rating))
.catch((err) => console.log(err));
});
module.exports = Router;
Usually .save() will track changes to document which is returned from find call. Otherwise if it's not the mongoose document returned from .find() call if it's the mongoose object which you're forming like what you're doing now then if it finds _id in object it will update the matching doc else if no matching doc exists with _id or no _id present in request it will insert the new document.
i want to add additional properties to the result document of a mongoose query. i have a Post Model, inside the post model i have added favourites which contains reference to the users who favourited the post, i want to get whether the user has favourited the post and the total number of favourites the post has
Post Model
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const mongoosePaginate = require('mongoose-paginate-v2');
var aggregatePaginate = require('mongoose-aggregate-paginate-v2');
const postSchema = Schema({
title: String,
favourites: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}],
description: String
});
var Post = mongoose.model('Post', postSchema.plugin(mongoosePaginate));
Post.prototype.hasLiked = function (uid) {
return this.favourites.indexOf(uid) > -1
}
Post.prototype.totalLikes = function () {
return this.favourites.length;
}
module.exports = Post;
Controller
Post.paginate(query,
options,
function (err, result) {
if (err) {
console.log(err)
res.json({
error: err,
status: 501,
message: "Unable to get data"
});
} else {
let isFavourite = result.hasLiked(res.locals.user.uid)
let favouriteLength = result.totalLikes()
console.log(isFavourite)
console.log(favouriteLength)
res.json({
status: 200,
data: result
});
}
}
);
});
Im facing the following error while running the above code
TypeError: result.hasLiked is not a function
Is this an efficient solution, if not please suggest any alternate solution for this scenario.
Post.paginate doesn't return a promise fulfilled with an instance of Post.
Following the documentation ( https://www.npmjs.com/package/mongoose-paginate-v2 ), you will receive your post in result.docs. Loop on it and you can use your getters.