how to populate() a mongoose .findOneAndUpdate object - javascript

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});
});

Related

edit the last inserted record in mongodb

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 });

Iterating upon Variable From Database Entry using Mongoose

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);
}
});

How do I $cond a $push/$pull in a MongoDB update with upsert:true

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();
}
)

mongoose remove does not remove all documents

I have 2 collections (User and Offer) and I use async to do a series of operations, but the last document is not removed.
async.series([
function (callback) {
User.findOne({_id: req.user._id}, function (err, result) {
if (err) {
return callback(err);
}
user = result;
callback();
});
},
function (callback) {
Offer.find({_owner: user._id}, function (err, offers) {
if (err) {
return callback(err);
}
var i = offers.length;
while (i--) {
var offer = offers[i];
Offer.remove(offer, function (err) {
return callback(err);
});
}
callback();
});
},
function (callback) {
User.remove(user, function (err) {
if (err) {
return callback(err);
}
});
callback();
}
], function (err) {
if (err) {
return res.status(503).json({message: err});
}
res.status(200).json({message: 'User and offers successfully deleted'});
});
I turned mongoose debugging on and I see that 8 documents for this user are removed (as expected). Example message:
Mongoose: users.remove({ active: true, role: 'user', favoredOffers: [],
salt: 'qIADYcMS3SiAuF3M007E9g==', hashedPassword: '**', emailVerified: true,
name: 'Test', email: 'test1#test.com', emailVerificationToken: '***',
updatedAt: new Date("Mon, 21 Sep 2015 00:52:58 GMT"),
createdAt: new Date("Mon, 21 Sep 2015 00:52:58 GMT"),
_id: ObjectId("55ff54ea1d6201ff42ea0045") }) {
safe: { w: 'majority', wtimeout: 10000 } }
When I run a test the result is, that this one document remains. At first I thought my test runs too fast (async.waterfall) but then I switched to async.series and the behaviour remains and when I look into the test-db the document is still there.
Test code:
it.only('should allow deleting my account and all my offers if my password was right', function (done) {
async.series([function (callback) {
login(server, server.delete('*url*'), 'test#test.com', 'testpwd1', function (server) {
server
.send({password: 'testpwd1'})
.expect(200)
.end(callback);
});
}, function (callback) {
User.findOne({email: 'test1#test.com'}, function (err, user) {
callback(user ? new Error('user was still found in mongo') : null); //misuse not found user as 'error'-object
});
}, function (callback) {
Offer.find({_owner: user1._id}, function (err, offers) {
expect(offers).to.have.length(0);
callback();
});
}], done);
});
Result: Uncaught AssertionError: expected [ Array(1) ] to have a length of 0 but got 1
Wether I am missing something or I don't understand the behaviour of the mongdb.
Whilst the operations are being called in series per those that are split in the async.series the problem here is the while loops are not asynchronously controlled, and therefore the callbacks to move to the next stage are being called before all removal is complete.
You could use async.whilst to control the callbacks from the .remove() operations, but that would be overkill since you really should just be issuing ther "query" directly to .remove() rather than trying to work with a list returned by .find():
async.series(
[
function (callback) {
Offer.remove({ "_owner": req.user._id}, callback);
},
function (callback) {
User.remove({ "_id": req.user._id },callback);
}
],
function (err,results) {
if (err) {
res.status(503).json({message: err});
} else {
if ( Array.sum(results) == 0 ) {
res.status(404).json({message: 'User not found'});
} else {
res.status(200).json({message: 'User and offers successfully deleted'});
}
}
}
);
Of course if the result of the remove operations did not remove anything, then you know the input did not match the user.
The async library allows callback passing so you don't need to control each stage. Any errors immediately go to the end block, along with the results of each stage. Also look at parallel instead for this case as one stage is not dependent on the other to complete first.

How to confirm if update succeeds using mongoose and bluebird promise

I'm using bluebird and mongoose for a node page.
I want to check if the update is successful before sending data back to clients via socket.js.Here's the part of the code that I can't figure out:
.then(function(a) {
var g = collection3.update({
_id: a.one[0]._id
}, {
$set: {
avg: a.one[0].avg
}
}).function(err, d) {
if (!err) {
return 1; // Here's the problem
}
}) return {
updated: g,
info: a
};
}).then(function(c) {
console.log(c.updated); // I can't get the `1` value
if (c == 1) {
io.sockets.in('index|1').emit("estimate", c.three);
}
})
Does mongoose return a success message after update? I can't return 1 from the update query and pass it to the next then function, instead, I'm getting this object:
{ _mongooseOptions: {},
mongooseCollection:
{ collection:
{ db: [Object],
collectionName: 'table',
internalHint: null,
opts: {},
slaveOk: false,
serializeFunctions: false,
raw: false,
pkFactory: [Object],
serverCapabilities: undefined },
opts: { bufferCommands: true, capped: false },
name: 'table',
conn:....
Here's the full code:
socket.on("input",function(d){
Promise.props({
one: collection2.aggregate([
{
$match:{post_id:mongoose.Types.ObjectId(d.id)}
},
{
$group:{
_id:"$post_id",
avg:{$avg:"$rating"}
}
}
]).exec();
}).then(function(a){
var g = collection3.update({_id:a.one[0]._id},{$set:{avg:a.one[0].avg}}).function(err,d){
if(!err){
return 1; // Here's the problem
}
})
return {updated:g,info:a};
}).then(function(c){
console.log(c.updated); // I can't get the `1` value
if(c.updated == 1){
io.sockets.in('index|1').emit("estimate",c.three);
}
}).catch(function (error) {
console.log(error);
})
I'm assuming you're using Mongoose here, update() is an asynchronous function, your code is written in a synchronous style.
Try:
socket.on("input",function(d){
Promise.props({
one: collection2.aggregate([
{
$match:{post_id:mongoose.Types.ObjectId(d.id)}
},
{
$group:{
_id:"$post_id",
avg:{$avg:"$rating"}
}
}
]).exec()
}).then(function(a){
return collection3.update({_id:a.one[0]._id},{$set:{avg:a.one[0].avg}})
.then(function(updatedDoc){
// if update is successful, this function will execute
}, function(err){
// if an error occured, this function will execute
})
}).catch(function (error) {
console.log(error);
})
Mongoose docs says
Mongoose async operations, like .save() and queries, return
Promises/A+ conformant promises. This means that you can do things
like MyModel.findOne({}).then() and yield MyModel.findOne({}).exec()
(if you're using co).
Also
Mongoose Update returns the updated document.
So this should look something like this.
function runBarryRun(d) {
Promise.props({
one: aggregateCollection2(d)
})
.then(updateCollection3)
.then(updatedDoc => {
// if update is successful, do some magic here
io.sockets.in('index|1').emit("estimate", updatedDoc.something);
}, err => {
// if update is unsuccessful, find out why, throw an error maybe
}).catch(function(error) {
// do something here
console.log(error);
});
}
function aggregateCollection2(d) {
return collection2.aggregate([{
$match: { post_id: mongoose.Types.ObjectId(d.id) }
}, {
$group: {
_id: "$post_id",
avg: { $avg: "$rating" }
}
}]).exec();
}
function updateCollection3(a) {
return collection3.update({ _id: a.one[0]._id }, { $set: { avg: a.one[0].avg } }).exec();
}
socket.on("input", runBarryRun);

Categories

Resources