Value not incrementing in mongoose when creating a new object - node.js

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.

Related

How to fetch all data only from mongoose with Nodejs

I am not able to fetch all the data from mongoose. When I tried to fetch data it create new collection name(signins) with empty, but singin collection already exists.
I don't understand what I am doing wrong here
Index.js File
const express = require("express");
const app = express();
const mongoose = require("mongoose");
mongoose
.connect("mongodb://0.0.0.0:27017/signin")
.then(() => console.log("MongoDB Connected"))
.catch((err) => console.log(err));
const User = require("./models/signin");
app.use("/", (req, res) => {
User.find({}, (err, data) => {
if (err) throw new err();
return res.json(data);
});
});
app.listen(5500, () => console.log("Port Started on 5500"));
signin
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const loginSign = new Schema({
email: { type: String, required: true },
password: { type: String, required: true },
date: { type: Date, default: Date.now },
});
module.exports = Users = mongoose.model("signin", loginSign);
Mongoose will automatically "pluralize" the name of your collection from your model name.
So mongoose.model("signin", loginSign) is creating a collection named "signins".
From the documentation: https://mongoosejs.com/docs/models.html#compiling
The first argument is the singular name of the collection your model is for. Mongoose automatically looks for the plural, lowercased version of your model name.
Providing a third argument will use a collection name you specify, instead of the one mongoose creates. So in your case you could:
mongoose.model("signin", loginSign, "signin");
That said, having plural collection names is the standard, and encouraged.

Query was already executed

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

Is there a way to edit mongo db sorting?

I have a mongoose collection that sorts by first added, and I want it to sort by last edited
The model
var mongoose = require("mongoose");
var user = require("./user");
var questionSchema = new mongoose.Schema({
text: String,
asked: String,
answer: String
})
module.exports = mongoose.model("question", questionSchema);
The put request code:
router.put("/:user/:id", checkOwner, function(req, res){
question.findByIdAndUpdate(req.params.id, req.body.question, function(err,
updatedQuestion){
if(err) {
console.log(err);
} else {
res.redirect("/");
}
});
});
I want that updatedQuestion to be on the top my collection
Here is one simple approach:
First you have to add timestamps in your mongoose model, in order to have access on createdAt and updatedAt proerties.
You can see more about timestamps here
var mongoose = require("mongoose");
var user = require("./user");
var questionSchema = new mongoose.Schema({
text: String,
asked: String,
answer: String
},
{ timestamps: true}
)
module.exports = mongoose.model("question", questionSchema);
Then you can query your collections, applying sorting by updatedAt
question.find({}, {}, {sort: { 'updatedAt' : -1 }})
Thus the most recent updated document will be shown first.

Populating the cart doesnt work (mongoose)

I have been trying to populate cart products in user's cart. Below is my code.
const express = require('express')
const mongoose = require('mongoose')
const bodyParser = require('body-parser')
const app = express()
app.use(bodyParser.urlencoded({extended:false}))
const Schema = mongoose.Schema;
var userSchema = new Schema({
userName : {
type : String,
default : 'UserName'
},
cart : [ { type : mongoose.Schema.Types.ObjectId , ref : 'Cart' } ]
})
var cartSchema = new Schema({
productNameofUser : {
type : String,
default : 'Product'
}
})
var UserModel = mongoose.model('User' , userSchema)
var CartModel = mongoose.model('Cart' , cartSchema)
app.get('/users',(req,res)=>{
UserModel.find().populate('cart').exec((err,result)=>{
res.send(result)
})
})
app.post('/createUser',(req,res)=>{
let newUser = new UserModel()
newUser.save((err,result)=>{
console.log(result)
res.send(result)
})
})
app.get('/products',(req,res)=>{
CartModel.find((err,result)=>{
res.send(result)
})
})
app.post('/createProduct',(req,res)=>{
let newProduct = new CartModel()
newProduct.save((err,result)=>{
res.send(result)
})
})
app.listen(3000,()=>{
console.log('running')
mongoose.connect('mongodb://127.0.0.1:27017/populate' , { useNewUrlParser : true })
})
what I am trying to do is, There is a carts collection and there is another collection of users. Every user has a cart which i am trying to populate from the cart collections.
If you want to test the code, first create the user using the post link 'http://localhost:3000/createUser/' via postman and the create cart Products using 'http://localhost:3000/createProduct/' via postman. Then whenever i tried to fetch the users 'http://localhost:3000/users/' , the cart array stays empty no matter what i try.
Please figure where i am going wrong
Update your Schema defination as:
const Schema = mongoose.Schema;
var userSchema = new Schema({
userName : {
type : String,
default : 'UserName'
},
cart : [ { type : Schema .ObjectId , ref : 'Cart' } ]
})
And try this again:
app.get('/users',(req,res)=>{
UserModel.find().populate('cart').exec((err,result)=>{
res.send(result)
})
})
Let me know, if this works now.

Filter moongose results by reference field using express

I need filter the products of a collection by category id which is a reference field.
product.js
const restful = require('node-restful')
const mongoose = restful.mongoose
const productSchema = new mongoose.Schema({
name: { type: String, required: true },
category: {type: mongoose.Schema.Types.ObjectId, ref: 'CategoryProduct'}
})
productSchema.pre('find', function () {
this.find().populate('category')
})
module.exports = restful.model('product', productSchema)
routes.js
const express = require('express')
const auth = require('./auth')
module.exports = function (server) {
const protectedApi = express.Router()
server.use('/api', protectedApi)
const Product = require('../api/product/productService')
Product.register(protectedApi, '/products')
}
If I run this on Postman, http://localhost:3003/api/products/?name__regex=/test/i, I can get all products which contains 'test' on name.
So I try get all products by a specific category doing this, http://localhost:3003/api/products/?category=5af3ac4372edc6000468d766.
But as the category is an objectID, I receive this error:
{
"message": "Cast to ObjectId failed for value \"5\" at path \"category\" for model \"SimpleProduct\"",
"name": "CastError",
"stringValue": "\"5\"",
"kind": "ObjectId",
"value": 5,
"path": "category"
}
How do I filter the products by category? I do not know how to treat this parameter correctly and pass to mongoose
Here is my CategoryProduct.js file
const restful = require('node-restful')
const mongoose = restful.mongoose
const categorySchema = new mongoose.Schema({
name: {type: String, required: 'O campo Categoria é obrigatório'},
status: {type: Number, required: true},
updated_at: {type: Date}
})
module.exports = restful.model('CategoryProduct', categorySchema)
you would have to do the following in your route:
const mongoose = require('mongoose');
router.get('/', (req, res, next) => {
const category = req.query.category; // assign the query param for use
// mongoose query now
Product.find({category: mongoose.Types.ObjectId(category)}, (err, result) => {
if (err) {console.log(err)};
if (result) {
res.json(result);
}
});
});
This is just a basic example. Since you are using node-restful you might have to adjust the code but the main idea here is that you require the mongoose module using
const mongoose = require('mongoose')
then pass your category query param to it to convert the string to objectID using:
mongoose.Types.ObjectID(YOUR_CATEGORY_PARAM_HERE)
You can do this anywhere, in the your routes.js or in your express.js, just have to have the string you want to convert :)
hope that helps :).

Resources