Mongoose update only the values that have changed - mongodb

I have a PUT route to update value. I am hitting this route from two places. One is sending information about details and one about completed. The problem is that mongoose is updating booth even though it gets value from only one.
So if I send information about completed that it is true and latter I hit this route with new details (that dont have completed value) it will update completed also to false. How do I update just the value that was changed?
router.put('/:id', (req, res) => {
Todo.findOne({_id:req.body.id}, (err, foundObject) => {
foundObject.details = req.body.details
foundObject.completed = req.body.completed
foundObject.save((e, updatedTodo) => {
if(err) {
res.status(400).send(e)
} else {
res.send(updatedTodo)
}
})
})
})
EDIT:
Thanks to Jackson hint I was managed to do it like this.
router.put('/:id', (req, res) => {
Todo.findOne({_id:req.body.id}, (err, foundObject) => {
if(req.body.details !== undefined) {
foundObject.details = req.body.details
}
if(req.body.completed !== undefined) {
foundObject.completed = req.body.completed
}
foundObject.save((e, updatedTodo) => {
if(err) {
res.status(400).send(e)
} else {
res.send(updatedTodo)
}
})
})
})

const updateQuery = {};
if (req.body.details) {
updateQuery.details = req.body.details
}
if (req.body.completed) {
updateQuery.completed = req.body.completed
}
//or
Todo.findOneAndUpdate({id: req.body.id}, updateQuery, {new: true}, (err, res) => {
if (err) {
} else {
}
})
//or
Todo.findOneAndUpdate({id: req.body.id}, {$set: updateQuery}, {new: true}, (err, res) => {
if (err) {
} else {
}
})

Had a function similar to this my approach was this
const _ = require('lodash');
router.put('/update/:id',(req,res, next)=>{
todo.findById({
_id: req.params.id
}).then(user => {
const obj = {
new: true
}
user = _.extend(user, obj);
user.save((error, result) => {
if (error) {
console.log("Status not Changed")
} else {
res.redirect('/')
}
})
}).catch(error => {
res.status(500);
})
};
Taking new : true as the value you updating

It gets kinda ugly as the fields to be updated get increased. Say 100 fields.
I would suggest using the following approach:
try {
const schemaProperties = Object.keys(Todo.schema.paths)
const requestKeys = Object.keys(req.body)
const requestValues = Object.values(req.body)
const updateQuery = {}
// constructing dynamic query
for (let i = 0; i < requestKeys.length; i++) {
// Only update valid fields according to Todo Schema
if ( schemaProperties.includes(requestKeys[i]) ){
updateQuery[requestKeys[i]] = requestValues[i]
}
}
const updatedObject = await TOdo.updateOne(
{ _id:req.params.idd},
{ $set: updateQuery }
);
res.json(updatedObject)
} catch (error) {
res.status(400).send({ message: error });
}

Related

React-Query: useInfiniteQuery

So, I have looked through the docs and answers on here and I'm still needing some help:
index.tsx
const getInfiniteArticles = ({ pageParams = 0 }) => {
const res = await axios.get('/api/articles', { params: { page: pageParams } });
return res.data;
}
api/articles.ts
const getArticles = async (req: NextApiRequest, res: NextApiResponse) => {
try {
const { page } = req.query;
const pageNum = Number(page);
const data = await NewsService.getArticles(getRange(pageNum));
return res.status(200).json({
data,
previousPage: pageNum > 0 ? (pageNum - 1) : null,
nextPage: pageNum + 1,
});
} catch (err) {
res.json(err);
res.status(405).end();
}
};
export default getArticles;
index.tsx
const { data: articlePages, fetchNextPage } = useInfiniteQuery(
'infinite-articles',
getInfiniteArticles,
{
getNextPageParam: (lastPage, allGroups) => {
console.log('lastPage: ', lastPage);
console.log('allGroups: ', allGroups);
return lastPage.nextPage;
}
});
const handleLoadMore = () => {
fetchNextPage();
};
console after clicking next page:
lastPage: { data: Array(50), previousPage: null, nextPage: 1}
allGroups: [
{ data: Array(50), previousPage: null, nextPage: 1},
{ data: Array(50), previousPage: null, nextPage: 1},
]
Any help on why I'm getting the same groups is appreciated! :)
So, it turns out my structure wasn't correct
const {
fetchNextPage,
fetchPreviousPage,
hasNextPage,
hasPreviousPage,
isFetchingNextPage,
isFetchingPreviousPage,
...result
} = useInfiniteQuery(queryKey, ({ pageParam = 1 }) => fetchPage(pageParam), {
...options,
getNextPageParam: (lastPage, allPages) => lastPage.nextCursor,
getPreviousPageParam: (firstPage, allPages) => firstPage.prevCursor,
})
queryFn: (context: QueryFunctionContext) => Promise<TData>
The queryFn is supposed to be a synchronous function that returns a Promise
I was either passing an async function or I was returning the TData not a promise.
updated and working:
const getInfiniteArticles = ({ pageParam = 0 }) => axios.get('/api/articles', { params: { page: pageParam } });
const { data: articlePages, fetchNextPage } = useInfiniteQuery('articles', getInfiniteArticles, {
getNextPageParam: (lastPage, pages) => {
// the returned axios response
return lastPage.data.nextPage;
}
});
Reference Page

Cloud Firestore function triggers and transactions - how to return a promise correctly

I have a Cloud Firestore function trigger "onCreate". Depending on the value of a given field, I would like it to either update some documents via a transaction and to copy the created document as a record in Algolia, or to execute a completely different transaction. There are therefore several conditions and I am not sure that I am returning promises correctly, as sometimes the function is not copying the record in Algolia when expected.
I paste a simplified version of the code in case someone can help.
exports.createArticle = functions.firestore.document('articles/{articleId}').onCreate(async (snap, context) => {
const newDocData = snap.data()
if(newDocData) {
const userCreatorId = newDocData.createdBy
const userDocRef = imported.db.collection('users').doc(userCreatorId)
if(newDocData.type === 1) {
newDocData.objectID = newDocData.id
indexAlgolia.saveObject(newDocData)
.then(() => {
console.log('Article saved in Algolia with id:', newDocData.objectID )
})
.catch(err => {
console.log('ERROR while ADDING object inAlgolia:', err)
})
return imported.db.runTransaction(async t => {
// do some work
const userDoc = await t.get(userDocRef)
const userData = userDoc.data()
if (userData && userData.field1 > 0) {
t.update(userDocRef, {field2: true})
}
}).then(result => {
console.log('Transaction success')
}).catch(err => {
console.log('Transaction failure:', err)
})
}
else {
const colOneRef = imported.db.collection('colOne')
colOneRef.where('field2', '==', newDocData.field3).limit(1).get().then(snapshot => {
return imported.db.runTransaction(async t => {
if (snapshot.empty) {
t.update(userDocRef, {field3: false})
}
const decrement = imported.fieldValue.increment(-1)
t.update(userDocRef, {field4: decrement})
}).then(result => {
console.log('Transaction success')
}).catch(err => {
console.log('Transaction failure:', err)
})
}).catch(() => 'Error while querying colOneRef')
}
}
})
When you have multiple async/then calls you canmake them await the result and run them as if they are synchornous but from your code I see that the second doesn't depent on the first one so you can put them in a Promse.all() to make the function finish faster because they will run in parallel. Your code would look like this:
xports.createArticle = functions.firestore
.document("articles/{articleId}")
.onCreate(async (snap, context) => {
const newDocData = snap.data();
if (newDocData) {
const userCreatorId = newDocData.createdBy;
const userDocRef = imported.db.collection("users").doc(userCreatorId);
if (newDocData.type === 1) {
newDocData.objectID = newDocData.id;
const firstPromise = indexAlgolia
.saveObject(newDocData)
.then(() => {
console.log(
"Article saved in Algolia with id:",
newDocData.objectID
);
})
.catch((err) => {
console.log("ERROR while ADDING object inAlgolia:", err);
});
const secondPromise = imported.db
.runTransaction(async (t) => {
// do some work
const userDoc = await t.get(userDocRef);
const userData = userDoc.data();
if (userData && userData.field1 > 0) {
t.update(userDocRef, { field2: true });
}
})
.then((result) => {
console.log("Transaction success");
})
.catch((err) => {
console.log("Transaction failure:", err);
});
return Promise.all([firstPromise, secondPromise]);
} else {
const colOneRef = imported.db.collection("colOne");
return colOneRef
.where("field2", "==", newDocData.field3)
.limit(1)
.get()
.then((snapshot) => {
return imported.db
.runTransaction(async (t) => {
if (snapshot.empty) {
t.update(userDocRef, { field3: false });
}
const decrement = imported.fieldValue.increment(-1);
t.update(userDocRef, { field4: decrement });
})
.then((result) => {
console.log("Transaction success");
})
.catch((err) => {
console.log("Transaction failure:", err);
});
})
.catch(() => "Error while querying colOneRef");
}
return
}
});

findByIdAndUpdate do not update document

I am trying to update a field to the document with findByIdAndUpdate. The field I am trying to update is defined in the Bar Model. And I can also assure that req.body.bookId has a valid id.
Here's how my request looks,
app.patch("/foo", async (req, res) => {
try {
await validateId(req.body.bookId);
let doc = await Bar.findByIdAndUpdate(
req.body.bookId,
{ DateT: Date.now() },
{ new: true }
);
res.send(doc);
} catch (err) {
console.log(err);
}
});
Bar schema,
const mongoose = require("mongoose");
const barSchema = mongoose.Schema({
bookId: {
type: String,
unique: true,
},
DateT: {
type: Date,
default: null,
},
});
module.exports = mongoose.model("Bar", barSchema);
use updateOne, when you use async don't use .then() use try/catch
test it:
app.patch("/foo", async (req, res) => {
try {
let doc = await Bar.updateOne(
{ bookId : req.body.bookId },
{ DateT: Date.now() },
{ new: true }
);
res.send(doc);
} catch (error) {
console.log(error);
}
});
app.patch("/foo", async (req, res) => {
await Bar.findByIdAndUpdate(
req.body.bookId,
{ DateT: Date.now()},
(err, docs) => {
if (err) {
console.log(err);
} else {
res.send(docs);
}
}
);
});

Mongoose Library find / findOne Method is not returning value when the search dont match with the document

I am facing one issue with Mongoose. When I use find or findOne method and there is no matching results, then callback function is not returning null / err and hung the process. Using Mongoss 5.1.5 , MongoDB V3.4.2. Please advise
module.exports.validateappsubscripition = function (userid, appkey, cb) {
//console.error(userid + ' ' + appkey)
var userobj_id = mongoose.Types.ObjectId(userid);
appsubscripitions.model.findOne({'subscribersuserid': userobj_id , 'appkey'
:appkey }, function(err,doc){
console.error('test2');
if(doc ){
cb(null, doc );
}
else{
cb(null, null );
}
} );
}
Calling Block : Trying to validate the key from req header. I am trying to call the function validateappsubscripition from below block.
module.exports.sendEmail = function (req, res, next) {
let appkey;
let userid;
if (req.headers.appkey) {
appkey = req.headers.appkey;
console.error(appkey);
}
else {
appkey = '';
}
if(req.user._id){
userid = req.user._id ;
console.error(userid);
}
if (!req.body.fromEmail || !req.body.toEmail || !req.body.subject || !req.body.content) {
res.json({ success: false, msg: 'Please pass all the required parameters' });
next();
}
appsubcripitions.validateappsubscripition(userid, appkey, function (err, doc) {
console.error('test2');
if (err) {
res.json({ success: false, msg: 'Unauthorized. App Key is misssing on the header or App key is not valid' });
next();
}
else if (doc ) {
this.getSMTP('smtp.gmail.com', 'arbalu#gmail.com', function (err, userInfo) {
if (err) {
res.json({ success: false, msg: err.message });
next();
}
if (userInfo) {
//userInfo = user;
var send = require('gmail-send')({
user: userInfo.user,
pass: userInfo.pass,
from: req.body.fromEmail,
to: req.body.toEmail,
subject: req.body.subject, // Override value set as default
text: req.body.content
});
send({ // Overriding default parameters
// to: req.toEmail,
// subject: req.subject, // Override value set as default
// text: req.content
// files: [filepath],
}, function (err, response) {
//console.log('* [example 1.1] send() callback returned: err:', err, '; res:', res);
if (err) {
res.json({ success: false, msg: err.message });
next();
}
else {
res.json({ success: true, msg: response });
next();
}
});
}
})
}
else {
res.json({ success: false, msg: 'Some issue on sending email.Please contact the support.' });
next();
}
});
}

Why is forEach statement running more times than expect

I have the following function which is not working as expected for example I would like to create 24-irds and 3-smallparts, but instead I'm getting 24-irds and 72-smallparts. It seems like the smallparts forEach is iterrating the number of irds instead of smallparts. Any ideas why?
Thanks in advance
exports.pickup = function (req, res) {
async.waterfall([
function (callback) {
var order = createOrder(req);
callback(null, order);
},
function (order, callback) {
if (req.body.irds.length > 0) {
_(req.body.irds).forEach(function (n) {
var receiver = new Receiver(n);
receiver.order = order._id;
receiver.company = req.user.company;
receiver.user = req.user;
receiver.date = req.body.date;
receiver.location = req.user.location;
order.receivers.push(receiver._id);
receiver.save(function (err) {
callback(null, order);
if (err) {
console.log('error receiver exists');
}
});
});
} else {
callback(null, order);
}
},
function (order, callback) {
if (req.body.smallParts.length > 0) {
_(req.body.smallParts).forEach(function (n) {
var now = new Date();
var query1 = {'_id': req.user.company, 'products.product': n.product};
var query2 = {'_id': req.user.company};
var update1 = {
$inc: {
'products.$.quantity': n.quantityRequested,
'products.$.quantityOnhand': n.quantityRequested
},
'products.$.updated': now,
'products.$.lastPickUp.date': now,
'products.$.lastPickUp.quantity': n.quantityRequested
};
var update2 = {
$push: {
'products': {
'product': n.product,
'quantity': n.quantityRequested,
'quantityOnhand': n.quantityRequested,
'updated': now,
'lastPickUp.date': now,
'lastPickUp.quantity': n.quantityRequested
}
}
};
var options = {upsert: true};
Companies.findOneAndUpdate(query1, update1, function (err, doc) {
if (!doc) {
Companies.findOneAndUpdate(query2, update2, function (err, doc) {
if (err) {
throw err;
}
});
}
});
//save smallparts
n._id = new ObjectId();
var smallPart = new SmallPart(n);
smallPart.order = order._id;
smallPart.quantity = n.quantityRequested;
smallPart.company = req.user.company;
smallPart.user = req.user;
smallPart.location = req.user.location;
smallPart.date = req.body.date;
order.smallParts.push(smallPart._id);
smallPart.save(function (err) {
callback(null, order);
if (err) {
console.log(err);
}
});
})
} else {
callback(null, order)
}
},
function (order, callback) {
order.location = req.user.location;
order.company = req.user.company;
order.save(function (err) {
callback(null, 'done');
if (err) {
console.log(err);
}
});
}
], function (err) {
if (!err) {
res.status(200).json();
} else {
console.log(err);
}
});
};
I managed to figure out.
exports.pickup = function (req, res) {
var order = createOrder(req);
order.location = req.user.location;
order.company = req.user.company;
order.type = 'pickup';
async.series([
function (callback) {
if (req.body.irds.length > 0) {
_(req.body.irds).forEach(function (n) {
var receiver = new Receiver(n);
receiver.order = order._id;
receiver.company = req.user.company;
receiver.user = req.user;
receiver.date = req.body.date;
receiver.location = req.user.location;
order.receivers.push(receiver._id);
receiver.save(function (err) {
if (err) {
console.log('error saving receiver');
}
});
});
}
callback(null);
},
function (callback) {
if (req.body.smallParts.length > 0) {
_(req.body.smallParts).forEach(function (n) {
var now = new Date();
var query1 = {'_id': req.user.company, 'products.product': n.product};
var query2 = {'_id': req.user.company};
var update1 = {
$inc: {
'products.$.quantity': n.quantityRequested,
'products.$.quantityOnhand': n.quantityRequested
},
'products.$.updated': now,
'products.$.lastPickUp.date': now,
'products.$.lastPickUp.quantity': n.quantityRequested
};
var update2 = {
$push: {
'products': {
'product': n.product,
'quantity': n.quantityRequested,
'quantityOnhand': n.quantityRequested,
'updated': now,
'lastPickUp.date': now,
'lastPickUp.quantity': n.quantityRequested
}
}
};
var options = {upsert: true};
Companies.findOneAndUpdate(query1, update1, function (err, doc) {
if (!doc) {
Companies.findOneAndUpdate(query2, update2, function (err, doc) {
if (err) {
throw err;
}
});
}
});
//save smallparts
n._id = new ObjectId();
var smallPart = new SmallPart(n);
smallPart.order = order._id;
smallPart.quantity = n.quantityRequested;
smallPart.company = req.user.company;
smallPart.user = req.user;
smallPart.location = req.user.location;
smallPart.date = req.body.date;
order.smallParts.push(smallPart._id);
smallPart.save(function (err) {
// callback(null, order);
if (err) {
console.log(err);
}
});
})
}
callback(null, order)
}
],
function (err) {
if (!err) {
order.save(function (err) {
if (!err) {
res.status(200).json();
} else {
console.log('error saving order')
}
});
} else {
console.log(err);
}
});
};