Organize promise code - javascript

I have the following code. But this looks similar to a callback hell. How do i reorganize this code to a more proper promise way
FacilityBooking.forge({
"booking_id": req.params.id
})
.fetch({
require: true
})
.then(function(collection) {
if(0) { //#todo check if admin
throw new NotAuthorised(CFG_MESSAGES.error[403]);
} else {
Bookshelf.transaction(function(t) {
collection
.save({
"is_valid": 0,
"updated_by": req.user.id
}, {transacting: t})
.tap(function(model) {
new FacilityBooking(model.toJSON())
.save({
"is_valid": 1,
"reason_for_reject": req.body.disapprovereason || '' ,
"status": approval_status[req.body.moderation_action]
}, {transacting: t})
.then(function(collection) {
res.json({
status: true,
message: CFG_MESSAGES.facility_booking.moderate.success
});
})
.catch(function(err) {
res.json({
status: false,
message: CFG_MESSAGES.facility_booking.moderate.error
});
});
});
});
}
})
.catch(function(err) {
ErrorHandler.handleError(res, err);
});

Well, you just can divide it into functions:
var onErrorProcessingBooking = function(err) {
ErrorHandler.handleError(res, err);
};
var tap = function(model) {
var onSuccessSave = function(collection) {
res.json({
status: true,
message: CFG_MESSAGES.facility_booking.moderate.success
});
};
var onErrorSave = function(err) {
res.json({
status: false,
message: CFG_MESSAGES.facility_booking.moderate.error
});
};
new FacilityBooking(model.toJSON())
.save({
"is_valid": 1,
"reason_for_reject": req.body.disapprovereason || '',
"status": approval_status[req.body.moderation_action]
}, {
transacting: t
})
.then(onSuccessSave)
.catch(onErrorSave);
}
var onSuccessProcessingBooking = function(collection) {
if (0) {
throw new NotAuthorised(CFG_MESSAGES.error[403]);
return;
}
Bookshelf.transaction(function(t) {
collection
.save({
"is_valid": 0,
"updated_by": req.user.id
}, {
transacting: t
})
.tap(tap);
});
}
FacilityBooking
.forge({"booking_id": req.params.id})
.fetch({require: true})
.then(onSuccessProcessBooking)
.catch(onErrorProcessingBooking);

Promises chain, then is an abstraction over flow control itself and you can return a promise from another promise. Better yet, you can use coroutines to take care of this with bluebird which is already included in bookshelf:
const forger = Promise.coroutine(function*() {
const collection = yield FacilityBooking.forge({ "booking_id": req.params.id })
.fetch({ require: true });
if(0) throw new NotAuthorised(CFG_MESSAGES.error[403]);
yield Bookshelf.transaction(Promise.coroutine(function *(t) {
yield collection.save({
"is_valid": 0,
"updated_by": req.user.id
}, {transacting: t});
const model = new FacilityBooking(model.toJSON());
yield model.save({
"is_valid": 1,
"reason_for_reject": req.body.disapprovereason || '' ,
"status": approval_status[req.body.moderation_action]
}, {transacting: t});
res.json({
status: true,
message: CFG_MESSAGES.facility_booking.moderate.success
});
}));
}).catch(function(err) {
// filter error here, and do the res.json with the failure here
if(isRelevantError) {
res.json({
status: false,
message: CFG_MESSAGES.facility_booking.moderate.error
});
}
ErrorHandler.handleError(res, err);
});

I tried to answer your question please check below code
Bookshelf.transaction(function(t) {
FacilityBooking.forge({
"booking_id": req.params.id
})
.fetch({
require: true
})
.then(function(collection) {
if(0) { //#todo check if admin
throw new NotAuthorised(CFG_MESSAGES.error[403]);
} else {
return collection
.save({
"is_valid": 0,
'updated_by': req.user.id
}, {
transaction: t,
patch: true
});
}
})
.then(function(model) {
var data = model.toJSON();
delete data.id;
return new FacilityBooking(data)
.save({
"is_valid": 1,
"reason_for_reject": req.body.disapprovereason || 'bbbbbbbbbbb' ,
"status": approval_status[req.body.moderation_action]
}, {
transaction: t,
method: 'insert'
})
})
.then(function(collection) {
res.json({
status: true,
message: CFG_MESSAGES.facility_booking.moderate.success
});
})
.catch(function(err) {
res.json({
status: false,
message: CFG_MESSAGES.facility_booking.moderate.error
});
})
});

Related

How to increment a value inside an array object in MongoDB using nodejs?

Here's my MongoDB post model that I am using with node.js.I want to update the number of likes under each object in comments array i.e number of likes on each comment.How can I increment the number of likes value.
_postid:6045b7a3b0b0423790d6484b
photo:Object
likes:Array
text:"hey there"
comments:Array
0:Object
1:Object
2:Object
_id :6045c9251f99b81ee4dbc0f6
text:"tetstst"
postedBy:6045c36dd8df2f2f00b115d5
likes:0
created:2021-03-08T06:50:13.851+00:00
created:2021-03-08T05:35:31.524+00:00
postedBy:6045116e37280f0970cf63a5
here's what I am trying to do using FindoneandUpdate:
Post.findOneAndUpdate(
model,
{ $inc: { "comments.likes": 1 } },
{ new: true }
).exec((err, result) => {
if (err) {
return res.status(400).json({
error: err,
});
}
res.json(result);
});
};
Here's my post schema that I am using:
text: {
type: String,
required: "Name is required",
},
photo: {
data: Buffer,
contentType: String,
},
likes: [{ type: mongoose.Schema.ObjectId, ref: "User" }],
comments: [
{
text: String,
created: { type: Date, default: Date.now },
postedBy: { type: mongoose.Schema.ObjectId, ref: "User" },
likes: Number,
},
],
If you want to increment the likes for each comment by 1, (I assumed here model to be your query object.)
Post.findOneAndUpdate(
model,
{ $inc: { "comments.$[].likes" : 1 } },
{ new: true }
).exec((err, result) => {
if (err) {
return res.status(400).json({
error: err,
});
}
res.json(result);
});
};
If you want to increment only the first comment in the post,
Post.findOneAndUpdate(
model,
{ $inc: { "comments.0.likes" : 1 } },
{ new: true }
).exec((err, result) => {
if (err) {
return res.status(400).json({
error: err,
});
}
res.json(result);
});
};
If you want to increment likes for a comment posted By 6045c36dd8df2f2f00b115d5
Post.findOneAndUpdate(
{ ...model, { "comments.postedBy": 6045c36dd8df2f2f00b115d5 }},
{ $inc: { "comments.$.likes" : 1 } },
{ new: true }
).exec((err, result) => {
if (err) {
return res.status(400).json({
error: err,
});
}
res.json(result);
});
};
Also Ref: https://docs.mongodb.com/manual/reference/operator/update-array/

mongodb unable to find match

Enroll.updateOne(
{
"reviewers._id": userID,
},
{
$set: {
"reviewers.$.scores": req.body.scores,
},
},
(errUpdate, resultUpdate) => {
if (errUpdate) {
return res.status(500).json({ success: false, error: errUpdate });
} else {
return res.status(200).json({ success: true, data: resultUpdate });
}
}
);
I'm new to mongodb. Above is a function within an api that is used to update certain data.
The schema of Enroll would look like this:
[
{
_id: xxxxx,
otherdata: xxxx,
reviewers: [ { _id: xxxx, otherdata: xxxx } , { _id: xxxx2, otherdata: xxxx2 } ]
},
{
second enroll item...
}
]
but when I called the api, it returns n:0 which indicates no match is found. Am I missing some steps here?

How to update and upsert documents in MongoDB (Mongoose). NodeJS

I have a service which get data from server using GET request. I repeat this request every 10 seconds and after every request I save these data to my database. My code attached below.
But I need filter my new data which I received from server to not repeat data in database. I read that I need to do update in my database with upset: true but I guess that I do something incorrect. Could you please help me with this task?
app.js code:
const Tenders = require('./libs/mongoose');
const request = require('request');
let url = `http://public.api.openprocurement.org/api/2.4/tenders?offset=${new Date().toISOString()}+02.00`;
function getTenders() {
request(url, { json: true }, (err, res, body) => {
if (err) {
return console.log(err);
}
url = `http://public.api.openprocurement.org/api/2.4/tenders?offset=${body.next_page.offset}`;
const tendersList = [];
let tendersData = new Tenders({ tenderId: String, tenderDate: String });
body.data.forEach((item) => {
tendersData = {
tenderId: item.id,
tenderDate: item.dateModified,
};
tendersList.push(tendersData);
});
Tenders.findAll({ tenderId: tendersData.tenderId }, (err, tenderId) => {
if (!tenderId) {
Tenders.insertMany(tendersList)
.then((item) => {
console.log('Saved to db');
})
.catch((err) => {
console.log(err);
});
} else {
console.log('Data is already in db');
}
});
});
}
getTenders();
setInterval(getTenders, 10000);
and just in case mongoose.js:
const mongoose = require('mongoose');
const config = require('../config');
mongoose.Promise = global.Promise;
mongoose.connect(
config.get('mongoose:uri'),
{ useMongoClient: true },
);
const tender = new mongoose.Schema({
tenderId: String,
tenderDate: String,
});
const Tenders = mongoose.model('Tenders', tender);
module.exports = Tenders;
I guess that my code with Tenders.find.... and Tenders.insertMany looks only for one item not all of them. So please, can you help me with inserting my first part of data and after that 'upsert' data in database with new data from server?
your insert/update logic can be simplified using update function with upsert flag true
Here is an implementation, (hope tenderId is indexed)
const TenderSchema = new Schema({ tenderId: String, tenderDate: String });
const Tender = mongoose.model('Tender', TenderSchema, 'tenders');
var tenders = [
{tenderId :'tender-1', tenderDate : '1-1-2018'},
{tenderId :'tender-2', tenderDate : '2-1-2018'},
{tenderId :'tender-3', tenderDate : '3-1-2018'},
{tenderId :'tender-2', tenderDate : '4-1-2018'},
{tenderId :'tender-1', tenderDate : '5-1-2018'},
{tenderId :'tender-2', tenderDate : '4-1-2018'}
];
for (var t of tenders){
Tender.update(
{'tenderId' : t.tenderId },
{$set : t},
{upsert : true, multi : true},
function(err, doc){
if(err) throw err;
console.log(doc);
}
)
}
collection
> db.tenders.find()
{ "_id" : ObjectId("5a5d87d8a5f292efd566d186"), "tenderId" : "tender-1", "__v" : 0, "tenderDate" : "5-1-2018" }
{ "_id" : ObjectId("5a5d87d8a5f292efd566d187"), "tenderId" : "tender-2", "__v" : 0, "tenderDate" : "4-1-2018" }
{ "_id" : ObjectId("5a5d87d8a5f292efd566d188"), "tenderId" : "tender-3", "__v" : 0, "tenderDate" : "3-1-2018" }
>
console log
saravana#ubuntu:~/node-mongoose$ node so4.js
`open()` is deprecated in mongoose >= 4.11.0, use `openUri()` instead, or set the `useMongoClient` option if using `connect()` or`createConnection()`. See http://mongoosejs.com/docs/connections.html#use-mongo-client
Mongoose: tenders.update({ tenderId: 'tender-1' }, { '$set': { tenderId: 'tender-1', tenderDate: '1-1-2018' }, '$setOnInsert': { __v: 0 } }, { multi: true, upsert: true })
Mongoose: tenders.update({ tenderId: 'tender-2' }, { '$set': { tenderId: 'tender-2', tenderDate: '2-1-2018' }, '$setOnInsert': { __v: 0 } }, { multi: true, upsert: true })
Mongoose: tenders.update({ tenderId: 'tender-3' }, { '$set': { tenderId: 'tender-3', tenderDate: '3-1-2018' }, '$setOnInsert': { __v: 0 } }, { multi: true, upsert: true })
Mongoose: tenders.update({ tenderId: 'tender-2' }, { '$set': { tenderId: 'tender-2', tenderDate: '4-1-2018' }, '$setOnInsert': { __v: 0 } }, { multi: true, upsert: true })
Mongoose: tenders.update({ tenderId: 'tender-1' }, { '$set': { tenderId: 'tender-1', tenderDate: '5-1-2018' }, '$setOnInsert': { __v: 0 } }, { multi: true, upsert: true })
Mongoose: tenders.update({ tenderId: 'tender-2' }, { '$set': { tenderId: 'tender-2', tenderDate: '4-1-2018' }, '$setOnInsert': { __v: 0 } }, { multi: true, upsert: true })
{ n: 1, nModified: 1, ok: 1 }
{ n: 1, nModified: 1, ok: 1 }
{ n: 1, nModified: 0, ok: 1 }
{ n: 1, nModified: 1, ok: 1 }
{ n: 1, nModified: 1, ok: 1 }
{ n: 1, nModified: 0, ok: 1 }
^C
saravana#ubuntu:~/node-mongoose$

Determine when async execution is finished

I have to process an array of entries that requires to perform to async tasks for each file entry: getFile and uploadResult both are async task. My question is how can I know when the array doc.entries is being processed using an async library like asyncjs. The code below is just an illustration of what I am trying to accomplish.
var doc = {
version: '1.7',
entries: [{
date: '11/11/10',
files: [{
name: 100,
executable: false
},
{
name: 101,
executable: false
}]
},
{
date: '11/12/10',
files: [{
name: 200,
executable: false
},
{
name: 201,
executable: false
}]
},
{
date: '11/13/10',
files: [{
name: 300,
executable: false
}]
},
{
date: '11/14/10',
files: [{
name: 400,
executable: false
}]
}]
};
doc.entries.map(function(entry){
entry.files.map(function(file){
getFile(file, function(err, result){
if(err){
throw Error(err)
}
uploadResult(result, function(err, status){
WriteOnDb(file.name, status, function(err, result){ ... });
});
})
});
});
How can I know when the last file is being store on the db and then do something else?
Thanks.
The easiest way is to use promises, or better observables, but you do it with callbacks too - for example you can count how many tasks are in total and how many was finished:
var total = doc.entries
.map(function (entry) {
return entry.files.length;
})
.reduce(function (x, acc) {
return acc + x
}, 0);
var finished = 0;
function finishCallback(err) {
if (err === null) {
/// all async tasks are finished;
}
}
doc.entries.map(function (entry) {
entry.files.map(function (file) {
getFile(file, function (err, result) {
if (err) {
finishCallback(err);
} else {
uploadResult(result, function (err, status) {
WriteOnDb(file.name, status, function (err, result) {
if (err) {
finishCallback(err);
} else {
finished += 1;
if (finished === total) finishCallback(null);
}
});
});
}
})
});
});

Javascript variable scope when mongoose query

I'm working with node.js, mongoose and foursquare API.
foursquare.getVenues(params, function(err, venues) {
if(err) return res.json(JSON.stringify({status: 'error', returnData: err}));
// variable initialization
var rooms = [];
var vanueItem;
// iterate foursquare return list (venue item)
venues.response.venues.forEach(function(item) {
Room.aggregate(
[
{ "$group": {
"_id": '$mobileUser.genderType',
"genderTypeCount": { "$sum": 1 }
}}
],
function(err,result) {
if(err) return res.json(JSON.stringify({status: 'error', returnData: err}));
// build it to return after
vanueItem =
{
id: item.id,
name: item.name,
description: item.description,
contact: item.contact.formattedPhone,
lat: item.location.lat,
lng: item.location.lng,
distance: item.location.distance,
city: item.location.city
};
// insert it into venue array
rooms.push(vanueItem);
}
);
});
return res.json(JSON.stringify({ status: 'success', returnData: rooms }));
});
I'm having a problem with rooms array. When I remove the 'Room.aggregate' query, works fine (all rooms was ok), but when I use the aggregate, the return function gives me empty room.
I already tried remove var from 'var rooms = [];'
Room.aggregate is asynchronous function, if you want iterate over asynchronous function you can use async library, like this
var async = require('async');
foursquare.getVenues(params, function(err, venues) {
if (err) return res.json(JSON.stringify({
status: 'error',
returnData: err
}));
var rooms = [];
var vanueItem;
async.each(venues.response.venues, function (item, next) {
Room.aggregate(
[{
"$group": {
"_id": '$mobileUser.genderType',
"genderTypeCount": {
"$sum": 1
}
}
}],
function(err, result) {
if (err) {
return next(err);
}
// build it to return after
vanueItem = {
id: item.id,
name: item.name,
description: item.description,
contact: item.contact.formattedPhone,
lat: item.location.lat,
lng: item.location.lng,
distance: item.location.distance,
city: item.location.city
};
rooms.push(vanueItem);
next(null);
}
);
}, function (err) {
if (err) {
return res.json(JSON.stringify({
status: 'error',
returnData: err
}));
}
return res.json(JSON.stringify({
status: 'success',
returnData: rooms
}));
});
});

Categories

Resources