I'm trying to set it up so that every time the API query entry in the database it iterates 1 to a value named Popularity, contained inside of that entry.
I have set it up so that it finds the entry then gets ready to edit the Popularity value. Is this the right approach?
router.get("/:ItemID", async (req, res) => {
try {
const updateditem = await Items.findOneAndUpdate(
{ ItemID: req.params.ItemID },
{
$set: {
Popularity: //Previous Value of POPULARITY + 1
}
}
);
res.json(updateditem);
} catch (err) {
console.log(err);
}
});
After Creating your Schema, you just have to update your model in every API hit by using mongoose inc Query
Items.findOneAndUpdate(
{ ItemID: req.params.ItemID },
{ $inc: { Popularity: 1 } },
{ new: true },
function(err, response) {
if (err) {
callback(err);
} else {
callback(response);
}
}
);
or in your code:
router.get("/:ItemID", async (req, res) => {
try {
const updateditem = await Items.findOneAndUpdate(
{ ItemID: req.params.ItemID },
{ $inc: { Popularity: 1 } },
{ new: true },
);
res.json(updateditem);
} catch (err) {
console.log(err);
}
});
Related
I am trying to update using this route.
router.put("/:id", async(req,res)=>{
try {
const updateUser = await User.findByIdAndUpdate(req.params.id, {
$push: {
clients:{
client_name: req.body.client_name,
client_Username: req.body.client_Username,
client_Password: req.body.client_Password,
documents : [
{
name : req.body.docName,
descritption : req.body.docDescription,
doc_upload : req.body.doc_upload,
}
]
}
}
},{new:true})
res.status(200).json(updateUser);
}
catch(err) {
res.status(500).json(err);
}
});
Once the function founds the id it updates client_name, client_Username and client_password without any issue.
My problem is when I try to update the nested array documents with a name/description and doc_upload. I am not able to do that.
What’s wrong ? How to do it please ?
One solution could be to separate the updates:
router.put('/:id', async (req, res) => {
try {
const { id } = req.params;
const { client_name, client_Username, client_Password } = req.body;
const updateUser = await User.findByIdAndUpdate(
id,
{
$push: {
clients: {
client_name,
client_Username,
client_Password,
},
},
},
{ new: true }
);
await User.findOneAndUpdate(
{
id,
'clients.client_name': client_name,
'clients.client_Username': client_Username,
},
{
$push: {
'clients.$.documents': {
name: req.body.docName,
descritption: req.body.docDescription,
doc_upload: req.body.doc_upload,
},
},
}
);
res.status(200).json(updateUser);
} catch (err) {
res.status(500).json(err);
}
});
I am inserting two different objects into the db, i am doing this according to a certain criteria.
After that i am editing this record and setting the status to verified or not verified according to an amazon reply.
The problem is , i want to update the record that has been just inserted , since i am using findOneAndUpdate, only one record is being edited and it is not the last one it is the first.
Since the user can do as many purchases as he wants , he can have as many records as he want but only the first object found in the db having the userId sent as a param is edited.
what shall i use? the date and time when the object is inserted or what ?
async createAndSendToAmazon(data) {
try {
const records = new this.model(data);
const purchaseFromAppObjectRecord = await records.save();
let userId = purchaseFromAppObjectRecord.UserData[0].userId;
let receiptId = purchaseFromAppObjectRecord.receiptId;
await sendToAmazon(userId, receiptId);
await changeStatusToVerified(userId);
return purchaseFromAppObjectRecord;
} catch (error) {
return error;
}
}
}
async function sendToAmazon(userId, receiptId) {
const requestUrl = `https://appstore-sdk.amazon.com/version/1.0/verifyReceiptId/developer/2:smXBjZkWCxDMSBvQ8HBGsUS1PK3jvVc8tuTjLNfPHfYAga6WaDzXJPoWpfemXaHg:iEzHzPjJ-XwRdZ4b4e7Hxw==/user/${userId}/receiptId/${receiptId}`;
console.log(requestUrl);
fetch(requestUrl).then(function (response) {
if (response.status === 200) {
console.log(response.status);
response.json().then(async function (data) {
AmazonResolver.create(data);
});
} else {
try {
changeStatusToNotVerified(userId);
console.log(response.status);
response.json();
console.log("err will not add amazon verification object");
} catch (err) {
console.log(err);
}
}
});
}
async function changeStatusToVerified(userId) {
try {
await purchaseFromAppObjectModel.findOneAndUpdate(
{
UserData: { $elemMatch: { userId: userId } },
},
{ $set: { status: "verified" } }
);
} catch (err) {
console.log(err);
}
}
I want to write down my question as a minimal one but i want you to see my functions.
// you can use sort aggregate function to sort users in desc order and update the last element first
async function changeStatusToVerified(userId) {
try {
await purchaseFromAppObjectModel.findOneAndUpdate(
{
UserData: { $elemMatch: { userId: userId } },
},
{ $set: { status: "verified" } },
{ sort: { userId: -1 }, upsert: true, returnNewDocument: true }
);
} catch (err) {
console.log(err);
}
}
OR
async function changeStatusToVerified(userId) {
try {
await purchaseFromAppObjectModel.findOneAndUpdate(
{
UserData: { $elemMatch: { userId: userId } },
},
{ $set: { status: "verified" } },
{ sort: { userId: -1 } }
);
} catch (err) {
console.log(err);
}
}
if any one passes by here later on , this worked for me :
.findOneAndUpdate(
{
UserData: { $elemMatch: { userId: userId } },
},
{ $set: { status: "verified" }, limit: 1 }
)
.sort({ $natural: -1 });
I am trying to extend the amount of fields that our API is returning. Right now the API is returning the student info by using find, as well as adding some information of the projects by getting the student info and using findOne to get the info about the project that the student is currently registered to.
I am trying to add some information about the course by using the same logic that I used to get the project information.
So I used the same findOne function that I was using for Projects and my logic is the following.
I created a variable where I can save the courseID and then I will put the contents of that variable in the temp object that sending in a json file.
If I comment out the what I added, the code works perfectly and it returns all the students that I require. However, when I make the additional findOne to get information about the course, it stops returning anything but "{}"
I am going to put a comment on the lines of code that I added, to make it easier to find.
Any sort of help will be highly appreciated!
User.find({
isEnrolled: true,
course: {
$ne: null
}
},
'email pantherID firstName lastName project course',
function(err, users) {
console.log("err, users", err, users);
if (err) {
return res.send(err);
} else if (users) {
var userPromises = [];
users.map(function(user) {
userPromises.push(new Promise(function(resolve, reject) {
///////// Added Code START///////
var courseID;
Course.findOne({
fullName: user.course
}, function(err, course) {
console.log("err, course", err, course);
if (err) {
reject('')
}
courseID = course ? course._id : null
//console.log(tempObj)
resolve(tempObj)
}),
///// ADDED CODE END //////
Project.findOne({
title: user.project
}, function(err, proj) {
console.log("err, proj", err, proj);
if (err) {
reject('')
}
//Course ID, Semester, Semester ID
//map to custom object for MJ
var tempObj = {
email: user.email,
id: user.pantherID,
firstName: user.firstName,
lastName: user.lastName,
middle: null,
valid: true,
projectTitle: user.project,
projectId: proj ? proj._id : null,
course: user.course,
courseId: courseID
}
//console.log(tempObj)
resolve(tempObj)
})
}))
})
//async wait and set
Promise.all(userPromises).then(function(results) {
res.json(results)
}).catch(function(err) {
res.send(err)
})
}
})
using promise could be bit tedious, try using async, this is how i would have done it.
// Make sure User, Course & Project models are required.
const async = require('async');
let getUsers = (cb) => {
Users.find({
isEnrolled: true,
course: {
$ne: null
}
}, 'email pantherID firstName lastName project course', (err, users) => {
if (!err) {
cb(null, users);
} else {
cb(err);
}
});
};
let findCourse = (users, cb) => {
async.each(users, (user, ecb) => {
Project.findOne({title: user.project})
.exec((err, project) => {
if (!err) {
users[users.indexOf(user)].projectId = project._id;
ecb();
} else {
ecb(err);
}
});
}, (err) => {
if (!err) {
cb(null, users);
} else {
cb(err);
}
});
};
let findProject = (users, cb) => {
async.each(users, (user, ecb) => {
Course.findOne({fullName: user.course})
.exec((err, course) => {
if (!err) {
users[users.indexOf(user)].courseId = course._id;
ecb();
} else {
ecb(err);
}
});
}, (err) => {
if (!err) {
cb(null, users);
} else {
cb(err);
}
});
};
// This part of the code belongs at the route scope
async.waterfall([
getUsers,
findCourse,
findProject
], (err, result) => {
if (!err) {
res.send(result);
} else {
return res.send(err);
}
});
Hope this gives better insight on how you could go about with multiple IO transactions on the same request.
req.body.courses has multiples id's of courses that I want to add to a specific categorie, the problem is that when my code runs it save a course more that one time, sometimes four or five times, depending on the number of loops it does.
The function:
router.post('/categories/:cat_id/', function (req, res) {
Categorie.findById(req.params.cat_id, function(err, categorie){
if(err){
console.log(err);
} else {
var courses = req.body.courses;
courses.forEach(function (course){
Course.findOne({ _id: course }, function(err, foundCourse) {
if(err){
console.log(err);
} else {
categorie.courses.push(foundCourse._id);
categorie.save();
}
});
});
}
});
return res.redirect('/dash');
});
The CategorieSchema:
var categorieSchema = mongoose.Schema({
name: String,
courses: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Course"
}
]
});
Here is an example of trying to add 4 courses to the categorie:
{ "_id" : ObjectId("5a871964a6b4820ecf7abaa7"), "courses" : [ ObjectId("5a870a7374486e0b0d69f710"), ObjectId("5a870a7a74486e0b0d69f711"), ObjectId("5a870a6974486e0b0d69f70f"),
ObjectId("5a870a7374486e0b0d69f710"), ObjectId("5a870a7a74486e0b0d69f711"), ObjectId("5a870a6974486e0b0d69f70f"),
ObjectId("5a870a7374486e0b0d69f710"), ObjectId("5a870a7a74486e0b0d69f711"), ObjectId("5a870a6974486e0b0d69f70f") ], "name" : "test2", "__v" : 3 }
Node.js Is async, It does not wait for the loop to execute completely and each time you are adding _id in existing array because of that adds 2-3 times.
Try this once I have not tested this.
const findOne = (course) => {
return new Promise((resolve, reject) => {
Course.findOne({
_id: course
}, (err, foundCourse) => {
if (err)
return reject(err);
return resolve(foundCourse._id);
});
});
}
router.post('/categories/:cat_id/', function (req, res) {
Categorie.findById(req.params.cat_id, function (err, categorie) {
if (err) {
console.log(err);
res.status(400).json(err);
} else {
var courses = req.body.courses;
Promise.all(courses.map((course) => {
return findOne(course);
})).then((data) => {
// check if course id already there skip
data = data.filter((course) => {
return !categorie.courses.includes(course);
});
categorie.courses = categorie.courses.concat(data);
categorie.save();
return res.redirect('/dash');
}).catch((err) => {
console.log(err);
res.status(400).json(err);
});
}
});
});
An alternative would involve a first query returning the courses using $in operator with Course.find() and then update the courses array in the Categorie model with Categorie.findByIdAndUpdate():
router.post('/categories/:cat_id/', function (req, res) {
Course.find({ '_id': { '$in': req.body.courses }}).exec((err, courses) => {
Categorie.findByIdAndUpdate(
req.params.cat_id,
{ '$addToSet': { 'courses': courses } },
{ 'new': true },
(err, categorie) => {
if (err){
console.log(err);
} else {
return res.redirect('/dash');
}
}
});
});
});
The code below works, it updates a record or creates one if it doesn't exist yet. However, I'd like to combine this findOneAndUpdate() statement with the populate() method in order to populate the "user" of my object. What would be the right way to add the populate("user") statement to this logic?
I tried adding the populate() method after the findOneAndUpdate finishes but that returns an error saying that this method doesn't exist. I'm running the latest version of mongoose.
LoyaltyCard.findOneAndUpdate({ business: businessid}, { $set: newCard, $inc: { stamps: +1 } }, { upsert: true}, function(err, card){
if(err)
{
}
else
{
}
res.json(result);
});
Use exec() instead of a callback parameter:
LoyaltyCard.findOneAndUpdate(
{business: businessid},
{$set: newCard, $inc: {stamps: +1}},
{upsert: true}
)
.populate('user')
.exec(function(err, card) {
if (err) {
// ...
} else {
res.json(result);
}
});
With async/await I removed the exec
const getLoyaltyCard = async () => {
const results = await LoyaltyCard.findOneAndUpdate(
{ business: businessid },
{ $set: newCard, $inc: { stamps: + 1 } },
{ upsert: true }
)
.populate('user')
return results
}
You can also add a populate object in the 3rd parameter of .findOneAndUpdate() as one of the option, like this:
LoyaltyCard.findOneAndUpdate(
{ business: businessid },
{ $set: newCard, $inc: { stamps: +1 } },
{ upsert: true, populate: { path: 'user' } }
)
.exec(function(err, card) {
if (err) {
// ...
} else {
res.json(result);
}
});
Just enhancing #rahulchouhan's answer:
You can add the populate as one of the options which is the third parameter of findOneAndUpdate function and it works just like any other promise (then, catch)
LoyaltyCard.findOneAndUpdate(
{ business: businessid },
{ $set: newCard, $inc: { stamps: +1 } },
{ upsert: true, populate: { path: 'user' } }
)
.then(card => {
res.status(200).json(card);
}).catch(err => {
res.status(500).json({ message: err.message});
});