I'm making a program that counts different types of "accepts" and "denies". I'm using the findOneAndUpdate from mongoose. I can't get it to do what I want to do. I have 6 values, userID, accept, deny, nsfw, copyright, and invalid_format.
Here is my code:
if (interaction.customId === 'accept') {
leaderboard.findOneAndUpdate(
{
userID: interaction.user.id
},
{
userID: interaction.user.id,
$inc: { accepts: 1 }, // Increment accepts by 1
},
{
upsert: true, new: true
}, (err: any, doc: any) => {
if (err) console.log(err)
console.log(`Updated ${interaction.user.username}'s accepts to ${doc.accepts} `)
})
}
if (interaction.customId === 'deny') {
leaderboard.findOneAndUpdate(
{
userID: interaction.user.id
},
{
userID: interaction.user.id,
$inc: { denies: 1 }, //increment the denies by 1
},
{
upsert: true, new: true
}, (err: any, doc: any) => {
if (err) console.log(err)
console.log(`Updated ${interaction.user.username}'s denies to ${doc.denies} `)
})
}
}
if (interaction.isStringSelectMenu()) {
if (interaction.customId === 'reason') {
if (reason === 'nsfw') {
// Find the user in the database and increment the nsfw field by 1
leaderboard.findOneAndUpdate({ userID: interaction.user.id }, { userID: interaction.user.ID, $inc: { nsfw: 1 } }, { upsert: true, new: true }, (err: any, doc: any) => {
if (err) console.log(err)
console.log(`Updated ${interaction.user.username}'s ${reason}'s to ${doc.nsfw} `)
})
}
if (reason === 'copyright') {
// Find the user in the database and increment the copyright field by 1
leaderboard.findOneAndUpdate({ userID: interaction.user.id }, { userID: interaction.user.ID, $inc: { copyright: 1 } }, { upsert: true, new: true }, (err: any, doc: any) => {
if (err) console.log(err)
console.log(`Updated ${interaction.user.username}'s ${reason}'s to ${doc.copyright} `)
})
}
if (reason === 'invalid_format') {
// Find the user in the database and increment the invalid_format field by 1
leaderboard.findOneAndUpdate({ userID: interaction.user.id }, { userID: interaction.user.ID, $inc: { invalid_format: 1 } }, { upsert: true, new: true }, (err: any, doc: any) => {
if (err) console.log(err)
console.log(`Updated ${interaction.user.username}'s ${reason}'s to ${doc.invalid_format} `)
})
}
}
This keeps making new entries instead of updating the one with the same userID How can I fix this?
Increment and Upsert don't work together. You have to make sure it exists first before the increment.
Something like this should work:
//make sure the user's record exists
await leaderboard.updateOne(
{ userID: interaction.user.id },
{ $setOnInsert: { accepts: 0, denies: 0 } }, //defaults for insert
{ upsert: true }
)
if (interaction.customId === 'accept') {
leaderboard.findOneAndUpdate(
{
userID: interaction.user.id
},
{
$inc: { accepts: 1 }, // Increment accepts by 1
},
(err: any, doc: any) => {
if (err) console.log(err)
console.log(`Updated ${interaction.user.username}'s accepts to ${doc.accepts} `)
}
)
} else if (interaction.customId === 'deny') {
leaderboard.findOneAndUpdate(
{
userID: interaction.user.id
},
{
$inc: { denies: 1 }, //increment the denies by 1
},
(err: any, doc: any) => {
if (err) console.log(err)
console.log(`Updated ${interaction.user.username}'s denies to ${doc.denies} `)
}
)
}
Related
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 });
This is my schema and update function, here it update all of record expect fullname.
var employeeSchema = new Schema({
fullname: {
type: String,
},
email: {
type: String,
require: true,
},
mobile: {
type: String,
require: true,
},
});
function UpdateEmployee(req, res) {
Employee.findOneAndUpdate(
{ _id: req.body._id },
req.body,
{ new: true },
(err, docs) => {
if (!err) {
res.json(docs);
//res.redirect('employee/list');
} else {
res.json("Updation Error " + err);
}
}
);
}
Delete the key value pair from the update object
function UpdateEmployee(req, res) {
let data = {...req.body};
delete data['fullname'];
Employee.findOneAndUpdate(
{ _id: data._id },
data,
{ new: true },
(err, docs) => {
if (!err) {
res.json(docs);
//res.redirect('employee/list');
} else {
res.json("Updation Error " + err);
}
}
);
}
OR with spread the fullname key
function UpdateEmployee(req, res) {
const {fullname,...data}= req.body;
Employee.findOneAndUpdate(
{ _id: data._id },
data,
{ new: true },
(err, docs) => {
if (!err) {
res.json(docs);
//res.redirect('employee/list');
} else {
res.json("Updation Error " + err);
}
}
);
}
I have a scenario in which if you like a post, it will change
liked:false to liked:true
This liked is based if the current user liked the post. The problem is when a new user signs up, it will still show liked being true despite the new user NOT liking the post.
How would i be able to check if the current user liked the post ? I don't think my logic is somewhat right as far as checking if the current user liked the post.
I want to keep the findAll functionality, i should get all posts, not just by the current user.
Sorta like instagram, or facebook.
this is posts array
and this is how im liking a post
likePost
likePost: async (req: any, res: Response) => {
const created = await models.Likes.findOne({
where: {
userId: req.session.user.id,
resourceId: req.params.id
}
});
console.log(created);
const post = await models.Post.findOne({ where: { id: req.params.id } });
// if like not created then do this
if (!created && post) {
await models.Likes.create({
userId: req.session.user.id,
resourceId: req.params.id
}).then(() => {
post.increment("likeCounts", { by: 1 });
post.update({ liked: req.session.user.id ? true : false });
res.status(200).send({
message: "You liked this post"
});
});
// else if post does not exist
} else if (!post) {
res.status(200).send({
message: "there is not post to be liked"
});
} else {
// else if a like does exist destroy like
await models.Likes.destroy({
where: {
userId: req.session.user.id
}
}).then(() => {
post.decrement("likeCounts", { by: 1 });
res.status(200).send({
message: "You unliked this post"
});
});
}
this is how im getting the posts.
getPosts
getPosts: async (req: any, res: Response) => {
await models.Post.findAll({
include: [
{ model: models.User, as: "author", attributes: ["username"] },
{ model: models.Likes }
],
order: [["createdAt", "DESC"]],
limit: 6
}).then(posts => {
res.json(posts);
});
},
Post.js(model)
"use strict";
module.exports = (sequelize, DataTypes) => {
var Post = sequelize.define("Post", {
title: DataTypes.STRING,
postContent: DataTypes.STRING,
liked: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
},
likeCounts: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
validate: {
min: 0,
}
},
authorId: DataTypes.INTEGER
}, {});
Post.associate = function (models) {
Post.belongsTo(models.User, {
as: "author",
foreignKey: "authorId",
onDelete: "CASCADE"
});
Post.hasMany(models.Likes, {
foreignKey: "resourceId",
timestamps: false,
targetKey: "id",
onDelete: "CASCADE"
});
};
return Post;
};
I believe the bug you are seeing is because you are not resolving the promises that are returned by:
post.increment("likeCounts", { by: 1 });
post.update({ liked: req.session.user.id ? true : false });
This means that the response will be send before those queries execute. The post.liked value will be set to true any time there is a user.id on the session. You may want to consider using transactions to roll back some of the earlier queries if later ones fail. I would also recommend using Promise.all() to make concurrent queries (it will be faster) and use async/await exclusively without mixing in thenables.
likePost: async (req: any, res: Response) => {
// fetch created and post at the same time
const [ created, post ] = await Promise.all([
models.Likes.findOne({
where: {
userId: req.session.user.id,
resourceId: req.params.id
}
}),
models.Post.findOne({
where: {
id: req.params.id
}
}),
]);
// no post, no updates
if (!post) {
return res.status(200).send({
message: "there is no post to be liked"
});
}
// we are going to make updates, so use a transaction, you will need to reference sequelize
let transaction;
try {
transaction = await sequelize.transaction();
if (!created && post) {
// use Promise.all() for concurrency
await Promise.all([
models.Likes.create({
userId: req.session.user.id,
resourceId: req.params.id
}, { transaction }),
post.increment("likeCounts", { by: 1, transaction }),
post.update({ liked: req.session.user.id ? true : false }, { transaction }),
]);
await transaction.commit();
return res.status(200).send({
message: "You liked this post"
});
}
await Promise.all([
models.Likes.destroy({
where: {
userId: req.session.user.id
}
}, { transaction }),
post.decrement("likeCounts", { by: 1, transaction }),
]);
await transaction.commit();
return res.status(200).send({
message: "You unliked this post"
});
} catch (err) {
if (transaction) {
await transaction.rollback();
}
console.log('There was an error', err);
return res.status(500);
}
}
To only return Likes for the current user on the getPost()
getPosts: async (req: any, res: Response) => {
const posts = await models.Post.findAll({
include: [
{ model: models.User, as: "author", attributes: ["username"] },
// limit the likes based on the logged in user
{ model: models.Likes, required: false,
where: { userId: req.session.user.id },
},
],
order: [["createdAt", "DESC"]],
limit: 6
});
return res.json(posts);
},
So by following #doublesharp help, i was able to determine if the current user liked the post or not, by using a sequelize data type VIRTUAL, along with using getDataValue
updated code
Post(model)
"use strict";
module.exports = (sequelize, DataTypes) => {
var Post = sequelize.define("Post", {
title: DataTypes.STRING,
postContent: DataTypes.STRING,
liked: {
type: DataTypes.VIRTUAL,
allowNull: false,
defaultValue: false,
get: function () {
return this.getDataValue('Likes').length ? true : false;
}
},
likeCounts: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
validate: {
min: 0,
}
},
authorId: DataTypes.INTEGER
}, {});
Post.associate = function (models) {
Post.belongsTo(models.User, {
as: "author",
foreignKey: "authorId",
onDelete: "CASCADE"
});
Post.hasMany(models.Likes, {
foreignKey: "resourceId",
timestamps: false,
targetKey: "id",
onDelete: "CASCADE"
});
};
return Post;
};
//# sourceMappingURL=post.js.map
I'm trying to do a push or pull based on a condition, along with an upsert
myCollection.update(
{'id': location},
{
$set: { count },
$setOnInsert: {
id: location,
users: []
},
},
{
$cond: {
if: (increment==1),
then: {$push: { users: userToken }},
else: {$pull: { users: userToken }}
}
},
{'upsert':true},
(err, data) => {
...
I'm trying to DRY this up (which works):
mongo.connect(dbUrl, (err, db) => {
if (err) throw err
let myCollection = db.collection('myCollection')
if(increment==1){
myCollection.update(
{'id': location},
{
$set: { count },
$push: { users: userToken },
$setOnInsert: {
id: location
}
},
{'upsert':true},
(err, data) => {
if (err) throw err
console.log(data);
callback()
db.close()
}
)
}
else{
myCollection.update(
...
$pull: { users: userToken },
...
)
}
})
It's not adding anything to the DB when I have $cond. Where should the $cond be?
$cond is not applicable here but in the aggregation framework. What you need is a pure old native JS conditional statement where you create the update document prior to using it in the update operation, and this of course should be set in a condition block. Consider the following example:
let queryObj = { 'id': location },
usersObj = { 'users': userToken },
updateObj = {
'$set': { count },
'$setOnInsert': queryObj
},
options = { 'upsert': true },
updateOperator = '$pull';
if (increment == 1) updateOperator = '$push';
updateObj[updateOperator] = usersObj;
myCollection.update(queryObj, updateObj, options,
(err, data) => {
if (err) throw err
console.log(data);
callback();
db.close();
}
)
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});
});