How to Iterate over the whole array? and then update the database - javascript

I am working on an event-related project where I need to make sure that two events can't be set in the same timings for the same venue.
For this, whenever a new event is added, I use find() to get all the events having same event venue and then iterate to check whether the new event timing slots are clashing with other events in the database with the same venue.
I am getting two errors:
1) can't set headers after they are sent
2) I think my logic of iteration is not correct, I want the array to be checked completely and then insert if the same event venue has different timings for any XYZ event.
I tried using the filter, reduce etc but can't seem to have the desired results.
_this.insert = function(req, res) {
var obj = new _this.model(req.body);
obj.save(function(err, item) {
if (err && err.code === 11000) {
res.sendStatus(400);
}
if (err) {
return console.error(err);
}
_this.model.find({ event_ground: req.body.event_ground }, function(
err,
docs
) {
events = docs;
events.map(event => {
if (event.event_date_time && event.event_end_time) {
endTimeofEvent = moment(event.event_end_time);
timeofEvent = moment(event.event_date_time);
let isStartTime = moment(req.body.event_date_time).isBetween(
timeofEvent,
endTimeofEvent
);
debugger
let isEndTime = moment(req.body.event_end_time).isBetween(
timeofEvent,
endTimeofEvent
);
debugger
if (isStartTime === false && isEndTime === false) {
console.log('DB UPDATED');
debugger
_this.userModel.updateOne(
{
_id: req.params.id
},
{
$push: {
events: item._id
}
},
function(err) {
if (err) {
res.status(404).json('Something Went Wrong');
}
res.sendStatus(200);
}
);
debugger
}
if(isStartTime === true || isEndTime === true) {
console.log('DB WONT BE UPDATED');
res.status(400).json({
success: false,
msg:
'This Venue is booked from ' +
timeofEvent.format('LLL') +
' & ' +
endTimeofEvent.format('LLL'),
status: false
});
}
}
});
});
});
};
If the events with the same name do not have time clash, the new event gets added, else if says event can be added, etc.

1) can't set headers after they are sent
function(err) {
if (err) {
res.status(404).json('Something Went Wrong');
}
res.sendStatus(200);
}
res.json() does not end the script. Always use return res.json() or add else condition if you don't want the code below executes.
2) I think my logic of iteration is not correct, I want the array to be checked completely and then insert if the same event venue has different timings for any XYZ event.
You are running map to iterate all the result but instantly update database once the condition matched (isStartTime === false && isEndTime === false).
It's fine if you have exactly one record but fail if the duplicate record is not the first result. I think it is better for you to do the condition check when searching database. If you have good reason to not filter when searching database, I suggest you to do as below:
let duplicate = events.filter(event => {
// filter records that match (isStartTime === true || isEndTime === true)
});
if (duplicate.length > 0) {
// return error message
} else {
// update database
}

The reason for
can't set headers after they are sent
Response for the request is already sent, try sending the response out of the map function. Also, add a return statement after sending a response if there are lines of code that can be executed afterward.
You don't need to iterate through all the docs in the find query. Use $gte and $lte operator to find whether there any event in the time.
your query can be modified to
_this.model.find({ event_ground: req.body.event_ground,event_date_time : {"$gte":req.body.event_date_time},event_end_time : {"$gte" :req.body.event_date_time } },

Related

How to query LRS by agent via javascript

I feel like I've tried everything, but I keep coming up short. I am working on a course in Storyline 360, and I am able to return statements just fine when using verbs and object IDs, but no matter what I do to try and return statements for a specific Agent, I cannot get a query to go through.
Here's my code as it stands now - where I do return plenty of statements...what I need to know is how to have it query the current learner's statements for matches. I'm able to pull in their name or mbox, but trying to pass those through in my params fails on me every time.
Any help is very much appreciated!
var lrs;
var statementFound = false;
var player = GetPlayer();
try {
lrs = new TinCan.LRS(
{
endpoint: "https://cloud.scorm.com/lrs/MYINFO/",
username: "MYINFO",
password: "MYINFO",
allowFail: false
}
);
}
catch (ex) {
console.log("Failed to setup LRS object: ", ex);
// TODO: do something with error, can't communicate with LRS
};
var myObj = JSON.parse(getParameterByName('actor'));
lrs.queryStatements(
{
params: {
verb: new TinCan.Verb(
{
id: "http://adlnet.gov/expapi/verbs/answered"
}
)
},
callback: function (err, sr) {
if (err !== null) {
console.log("Failed to query statements: " + err);
// TODO: do something with error, didn't get statements
return;
}
if (sr.more !== null) {
// TODO: additional page(s) of statements should be fetched
}
if (sr.statements.length > 0) {
statementFound = true;
console.log(sr.statements);
player.SetVar("sf",statementFound);
}
}
}
);
var myObj is able to pull in the necessary info to ID the learner if needed - but again, I just can't figure out how to get it passed in the query.
You need to set an agent property in the params object passed in the first argument. Assuming the Agent is the actor in statements.
lrs.queryStatements(
{
params: {
agent: TinCan.Agent.fromJSON(getParameterByName('actor'))
}
},
...
);

Check Store before API call NgRx Angular

I am creating NgRx application but I am quite confused about its implementation as it is my first app with NgRx.
I have a store with "Companies" state. I gotta search the companies and return if found.
If the required company is not found it should call the API and fetch the results likewise but the process is circular and runs infinite time.
Here is my code:
this.companySearchCtrl.valueChanges
.pipe(
debounceTime(300),
distinctUntilChanged()
)
.subscribe(val => {
if (val !== ' ' || val !== '') {
this.store.select(getCompanys).subscribe(data => {
console.log(data);
//filter companies on the basis of search text
const filteredData = data.filter(x =>
x['name']
.toLowerCase()
.startsWith(this.companySearchCtrl.value.toLowerCase())
);
console.log(filteredData);
if (filteredData.length === 0) { //if data is not found in store
console.log('got a call');
this.store.dispatch(
new CompanyActions.find({
where: { name: { regexp: `${val}/i` } } // call to API to search with regExp
})
);
} else {
// if required data found in store
console.log('got no call');
this.filteredCompanies$ = of(filteredData);
}
});
}
});
This process runs fine if data is found in store. If data is not found in store or I dont get any results from API call it runs infinitely.
How can I make this correct?
Make a few conventions:
state.Companies = null is initial state if no request to server has been sent yet
state.Companies = [] is state after first request was sent but no companies returned from server
use createSelector that filters your companies based on criteria you need
use withLatestFrom in your effects which will enable you to check store state within effects
Now, turn the logic the other way around:
when you look for companies, first fire an action that will trigger effects
in that effect, check if state.Companies is null or not
if its null > fire api request
if its not null > fire an action that will trigger selector for filtering
if data was not found even if state.Companies was not null that means either you need to refresh your Companies collection or the value doesn't exist on server
Create another action named dataNotFound. If you found data then set its state isFound to true and if data does not find, set its state isFound to false and always before sending call with regex check isFound that either data was found in previous call or not. If data was not found then don't send call again.
I've made a little bit change in your code to manage this. You just have to create an action dataNotFound now.
this.companySearchCtrl.valueChanges
.pipe(
debounceTime(300),
distinctUntilChanged()
)
.subscribe(val => {
if (val !== ' ' || val !== '') {
this.store.select(getCompanys).subscribe(data => {
console.log(data);
//filter companies on the basis of search text
const filteredData = data.filter(x =>
x['name']
.toLowerCase()
.startsWith(this.companySearchCtrl.value.toLowerCase())
);
console.log(filteredData);
if (filteredData.length === 0) { //if data is not found in store
console.log('got a call');
this.store.select(isDataFound).subscribe(isFound => {
if(isFound) {
this.store.dispatch(
new CompanyActions.find({
where: { name: { regexp: `${val}/i` } } // call to API to
search with regExp
})
);
} else {
this.store.dispatch(new CompanyActions.dataNotFound({isFound: false}));
}
});
} else {
// if required data found in store
console.log('got no call');
this.store.dispatch(new CompanyActions.dataNotFound({isFound: true}));
this.filteredCompanies$ = of(filteredData);
}
});
}
});

Insert document loop - RangeError: Maximum call stack size exceeded

I am literally giving my first steps with node and mongodb and I have recently hit this RangeError wall.
Here's what I am trying to do, I have a file that contains a list of countries that I would like to add to my mongo db. This would be part of my "seed" mechanism to get the app running.
I load the json and then I iterate through the collection of objects and add them one by one to the 'Countries' collection.
However, everytime I run the code, I get a "RangeError: Maximum call stack size exceeded".
I have googled around but none of the suggested solutions seem to apply for me.
My guess is there is something wrong with my insertCountry function...
Anyways, here's my code:
var mongoose = require('mongoose');
var countries = require('./seed/countries.json');
// mongodb
var Country = mongoose.Schema({
name: String,
code: String,
extra: [Extra]
});
var Extra = mongoose.Schema({
exampleField: Boolean,
anotherField: Boolean
});
var mCountry = mongoose.model('Countries', Country);
var mExtra = mongoose.model('Extras', Extra);
// do connection
mongoose.connect('...');
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error'));
db.once('open', function callback() {
});
// async function
var insertCountry = function(document, callback) {
db.model('Countries').count({code: document.code}, function (err, count) {
if (count < 1) {
db.collection('Countries').insert(document, function (err, result) {
if (!err) {
console.log('country ' + document.name + ' added');
}
else {
console.log('- [' + document.name + '] ' + err);
}
});
}
callback(null,document);
});
};
// doing countries
var Country = mongoose.model('Countries');
var Extras = mongoose.model('Extras');
for(i = 0; i < countries.length; i++)
{
nCountry = new Country();
nCountry.name = countries[i].name;
nCountry.code = countries[i].code;
nCountry.benefits = new Extras();
nCountry.benefits.exampleField = false;
nCountry.benefits.anotherField = false;
insertCountry(nCountry, function (err, value) {
console.log(value.name + ' added to collection (callback)');
});
}
I have been using some guides I have found to build this so this might not be optimal code. Any best pratices, standards, guides or tutorials you can share are most welcome!
Your callback is in the wrong place. It is not waiting for the insert operation to complete before you return from it's own callback. Altering your code:
var insertCountry = function(document, callback) {
db.model('Countries').count({code: document.code}, function (err, count) {
if (count < 1) {
db.collection('Countries').insert(document, function (err, result) {
if (!err) {
console.log('country ' + document.name + ' added');
}
else {
console.log('- [' + document.name + '] ' + err);
}
callback(null,document);
});
}
});
};
That is part of your problem, but it does not completely solve it. The other part is the loop which also does not wait for the wrapping function to complete before moving on. You want something like asyc.eachSeries in order to wait for inserts to complete before performing the next iteration. This is mostly why you are exceeding the call stack:
async.eachSeries(
countries,
function(current,callback) {
// make your nCountry object
insertCountry(nCountry,function(err,value) {
// do something, then
callback(err);
})
},
function(err) {
// called where done, err contains err where set
console.log( "done" );
}
);
There is really still and issue with the array, which must be reasonably large if you are exceeding the call stack limit. You probably should look at using event streams to process that rather that load everything in memory to the array.
Personally, if you were just trying not to insert duplicates for a field and had MongoDB 2.6 available I would just use the Bulk Operations API with "unordered operations" and allow non fatal failures on the duplicate keys. Coupled with the fact that bulk operations are sent in "batches" and not one at a time, this is much more efficient than checking for the presence on every request:
var Country = mongoose.Schema({
name: String,
code: { type: String, unique: true }, // define a unique index
extra: [Extra]
});
var insertCountries = function(countries,callback) {
var bulk = Country.collection.initializeUnorderedBulkOp();
var counter = 0;
async.eachSeries(
countries,
function(current,callback) {
// same object construction
bulk.insert(nCountry);
counter++;
// only send once every 1000
if ( counter % 1000 == 0 ) {
bulk.execute(function(err,result) {
// err should generally not be set
// but result would contain any duplicate errors
// along with other insert responses
// clear to result and callback
bulk = Country.collection.initializeUnorderedBulkOp();
callback();
});
} else {
callback();
}
},
function(err) {
// send anything still queued
if ( counter % 1000 != 0 )
bulk.execute(function(err,result) {
// same as before but no need to reset
callback(err);
});
}
);
};
mongoose.on("open",function(err,conn) {
insertCountries(countries,function(err) {
console.log("done");
});
});
Keeping in mind that unlike the methods implemented directly on the mongoose models, the native driver methods require that a connection is actually established before they can be called. Mongoose "queues" these up for you, but otherwise you need something to be sure the connection is actually open. The example of the "open" event is used here.
Take a look at event streams as well. If you are constructing an array large enough to cause a problem by missing callback execution then you probably should not be loading it all in memory from whatever your source is. Stream processing that source combined with an approach as shown above should provide efficient loading.

Having trouble trying to execute find with MongoDB in node.js asynchronously

I'm grabbing a request parameter from my route e.g. mydomain.com/topic/animals where requestParam = req.params.topicName and in this case, animals.
I loop through an object containing all possible topics, and then if I find a topicName that matches the requestParam, then I want to execute a call to the database to return all collections for that topic.
The problem is it's being executed synchronously because it'll always execute the else clause e.g.
if (requestParam === topicName) {
// fetch submission
} else {
// return 404
}
So it's always returning the 404, but if I get rid of the else clause here, then it works. I looked into underscore's _.after() but couldn't get it to work properly (and not even sure if that's what I should be using?
My code:
_.each(topics, function(key, topic) {
var topicName = key['topicName'],
if (requestParam === topicName) {
Submission.getTopicSubmissions({ topicName : topicName }, function(err, submissions) {
if (err) {
res.redirect('/');
} else if (submissions) {
res.render('topic', {
submissions: submissions
});
} else {
res.redirect('/topics');
}
});
} else {
res.render('errors/404', {
title: 'Page Not Found -',
status: 404,
url: req.url
});
}
});
The problem is that you should not render 404 inside the each iteration. Because you do an asynchronous lookup, it is scheduled to execute at some point in the future while the current function keeps going. Undoubtedly you're going to run into a different one at some point and render 404 at least once. Use a breakable iteration, mark when you search, and do 404 outside of the iteration, like so:
var isWaitingForResult = false;
topics.every(function(topic, key) { // TODO: Check if this iterator matches _.each
var topicName = key['topicName'],
if (requestParam === topicName) {
isWaitingForResult = true; // Wait for the result.
Submission.getTopicSubmissions({ topicName : topicName }, function(err, submissions) {
if (err) {
res.redirect('/');
} else if (submissions) {
res.render('topic', {
submissions: submissions
});
} else {
res.redirect('/topics');
}
});
return false; // stop iteration, we did start our search after all
}
return true; // continue iteration so we have another chance.
});
if (!isWaitingForResult) { // did a search NOT start?
res.render('errors/404', {
title: 'Page Not Found -',
status: 404,
url: req.url
});
}
Note that I am unsure whether I re-wrote each to every correctly. Check this. :)

nodejs and mongodb (mongojs): Trying to query and update database within a for loop

I'm writing a multiplayer game(mongojs, nodejs) and trying to figure out how to update user stats based on the outcome of the game. I already have the code written to compute all the post game stats. The problem comes when I try to update the users' stats in a for loop. Here's what i got:
//Game Stats
var tempgame = {
gameid: 1234,
stats: [
{
score: 25,
user: 'user1'
},
{
score: 25,
user: 'user2'
}
]
}
for(i = 0; i < tempgame.stats.length; i++){
db.users.find({ username: tempgame.stats[i].user }, function(err, res){
if( err != null){
//handle errors here.
} else {
var userstats = res[0].stats;
if( tempgame.stats[i].score > userstats.bestscore ){ //this is where it chokes
userstats.bestscore = tempgame.stats[i].score;
}
//code here to pass back new manipulated stats
}
});
}
Everything works fine until i try to use the tempgame object within the callback function. It says "cannot read property 'score' of undefined". Is this just a scoping issue?
Also i was thinking it could be an issue with the callback function itself. Maybe the loop would increment before the callback is even run. But even in that case, the score should be be there it would just be pulling from the wrong array index... that's what lead me to believe it may just be a scope issue.
Any help would be greatly appreciated.
You've been tripped up by the notorious "defining functions inside a loop" problem.
Use "forEach" instead:
tempgame.stats.forEach(function (stat) {
db.users.find({ username: stat.user }, function(err, res){
if( err != null){
//handle errors here.
} else {
var userstats = res[0].stats;
if( stat.score > userstats.bestscore ){ //this is where it chokes
userstats.bestscore = stat.score;
}
//code here to pass back new manipulated stats
}
});
});
Part of your problem is as mjhm stated in his answer to your question, and is as you have suspected. The i variable is changing before the callback is invoked.
The other half of your problem is because your database calls have not returned yet. Due to the asynchronous nature of NodeJS, your loop will finish before your database calls complete. Additionally, your database calls are not necessarily coming back in the same order you called them. What you need is some sort of flow control like async.js. Using async.map will allow you to make all calls to the DB in parallel and return them as an array of values you can use, after all db calls have been completed.
async.map(tempgame.stats, function(stat, callback){
db.users.find({ username: stat.user }, function(err, res){
if( err != null){
callback(err);
} else {
callback(null, res[0].stats);
}
});
}, function(err, stats){
if(err){
//handle errors
} else{
stats.forEach(function(stat){
//do something with your array of stats
//this wont be called until all database calls have been completed
});
}
});
In addition to the above, if you want to return results back to the application,
http://nodeblog.tumblr.com/post/60922749945/nodejs-async-db-query-inside-for-loop

Categories