Issue with flow function in lodash - node.js

I started to work in a new project where I found lodash's flow function and I saw the uses of it here docs but in my project, in the following code, I found over there flow([...])(state) here what is (state) at the end of the function?
module.exports = (async function published(state) {
return flow([
setColumnIndex('my_pay_table', 1, 'rate_mode', getColumn('pay_structure', 'pay_per_id', state)),
setColumnIndex('my_pay_table', 1, 'rate_amount', getColumn('pay_structure', 'pay_rate', state)),
setColumnIndex('my_wo_title_table', 1, 'user_id', buildArtifact(ownerAlias, 'user', 'id', 1)),
setColumnIndex('my_wo_title_table', 1, 'date_added', Date.now() / 1000),
])(state);
});
Can anyone help me?

According to the lodash documentation, flow returns a function. In JavaScript it is possible to return functions without executing them.
We could refactor the code you provided to the following
module.exports = (async function published(state) {
// `func` here is a function
const func = flow([
setColumnIndex('my_pay_table', 1, 'rate_mode', getColumn('pay_structure', 'pay_per_id', state)),
setColumnIndex('my_pay_table', 1, 'rate_amount', getColumn('pay_structure', 'pay_rate', state)),
setColumnIndex('my_wo_title_table', 1, 'user_id', buildArtifact(ownerAlias, 'user', 'id', 1)),
setColumnIndex('my_wo_title_table', 1, 'date_added', Date.now() / 1000),
]);
// Here we execute that function with an argument `state`
return func(state);
});

So far I found the solution. It actually uses Lodash's curry function.
let state = "Initial State";
const setColumnIndex = _.curry((table, index, column, value, state) => {
if (typeof index !== 'number') {
throw new Error(`Tried to setColumnIndex and specified a non-numeric index parameter (1): ${index}, did you mean to call setColumn?`);
}
return "New state"; // state = "Setting up new state here";
});
let result =_.flow([
setColumnIndex('my_wo_table', 1, 'status_id', 2),
setColumnIndex('my_wo_table', 1, 'label_id', 1),
setColumnIndex('my_wo_table', 1, 'date_added', Date.now() / 1000),
])(state);
console.log(result); //=> "New state"
In the above code, if we notice that for setColumnIndex function has 5 parameters when we are calling from flow function, actually passing 4 parameters and with (state) in curry style total 5 parameters.

Related

Sequelize Query Execution In Loop

Below iam calling addUpdateDailyLeads with an array like
[{
"yyyymmdd": "20191124",
"admin_login":"rasheed.s",
"category":"PO",
"amount":10,
"office_id":10000,
"new_leads_attempted":10
},
{
"yyyymmdd": "20191124",
"admin_login":"rasheed.s",
"category":"PO",
"amount":10,
"office_id":10000,
"new_leads_attempted":10
},
{
"yyyymmdd": "20191125",
"admin_login":"prajeed.av",
"category":"FHL",
"amount":10,
"office_id":10000,
"new_leads_attempted":10
}
]
So,key 0 should insert,
key 1 should update because duplicate key constratint,
key 2 will insert,
but im getting duplicate key constraint error on key 1,because array map not waiting for the query to be executed .
const addUpdateDailyLeads = async (req, res) => {
let admin_login,category,office_id,new_leads_attempted,yyyymmdd,where,values;
let data = req.body;
req.body.map(async function(item,i){
admin_login = item.admin_login,
category = item.category,
office_id = item.office_id,
new_leads_attempted = item.new_leads_attempted,
yyyymmdd = item.yyyymmdd;
where = {yyyymmdd:yyyymmdd, admin_login:admin_login, category:category};
values = {yyyymmdd:yyyymmdd, admin_login:admin_login, category:category,office_id:office_id,new_leads_attempted:new_leads_attempted,update_date:moment().format('YYYYMMDDHHmmss')};
console.log("calling ",i);
let chck = await addUpdateDailyLeadsCollection({where:where,values:values})
console.log("")
console.log("called")
})
res.json({ code: '200', message: `Advisor Daily Leads Updated ${admin_login}` });
}
const addUpdateDailyLeadsCollection = async data => {
let transaction;
let where = data.where
let values = data.values
var Sequelize = require("sequelize");
console.log("startef 1");
await AdvisorLeads.findOne({ where: where }, { useMaster: true }).then( async(data)=>{
console.log("waited");
if(data){
await data.update({new_leads_attempted: Sequelize.literal('new_leads_attempted + '+values.new_leads_attempted)}).then(data=>{
console.log("updated")
return Promise.resolve(1);
})
}else{
AdvisorLeads.create(values).then(data=>{
console.log("inserted")
return Promise.resolve(1);
})
}
})
};
final output on console
calling 0
startef 1
waiting 1
calling 1
startef 1
waiting 1
calling 2
startef 1
waiting 1
waited
waited
waited
called
called
called
inserted
inserted
My expected output like
calling 0
startef 1
waiting 1
waited
inserted
called
calling 1
startef 1
waiting 1
waited
updated
called
calling 2
startef 1
waiting 1
waited
inserted
called
Finally whait i need is to wait for each item ,execute all queries and then process next item
I think you can solve by using await on the update and create statements....
But also take a look at the UPSERT method, which could simplify your code quite a bit. From
The Sequelize API Reference: "Insert or update a single row. An update will be executed if a row which matches the supplied values on either the primary key or a unique key is found."
Addendum: for synchronizing async/await, there are many ways to do this, as detailed in this post. Here's some code I set up following the ES7 method:
let params = [{id : 1, sal : 10}, {id : 44, sal: 30}, {id : 1, sal : 20}];
async function doUpsertArrayInSequence(myParams) {
let results = [];
for (let i = 0; i < myParams.length; i++) {
let x = await User.findByPk(myParams[i].id).then(async (u) => {
if (u != null) {
await u.update({ sal : u.sal + myParams[i].sal});
} else {
await User.create({id: myParams[i].id, sal: myParams[i].sal});
}
});
results.push(x);
}
return results;
}
await doUpsertArrayInSequence(params).then(function(result) {
User.findAll().then(proj => {
res.send(proj);
next();
});
})
From the log, I can see
a) a SELECT, followed by an UPDATE or INSERT for each row (in sequence).
b) the 2nd occurrence of id=1 reflects the update of the 1st occurrence.
c) the final findAll reflects all inserts and updates.
HTH

Nodejs express API 500 internal server error when remove a semicolon ;

I've no habit to use semicolon ; in javascript code. This is my API but this is not working. Here response showing 500 Internal Server Error
router.post('/addRows', function (req, res) {
const saveData = []
[1,2,3,4,5,6,7,8,9,10].map((i) => {
console.log(i)
saveData.push(i)
if (saveData.length === 10) {
res.status(200).json({'data': saveData});
}
})
})
but If I add semicolon ; after 2nd line then this code is working. Here response showing 200 OK
router.post('/addRows', function (req, res) {
const saveData = [];
[1,2,3,4,5,6,7,8,9,10].map((i) => {
console.log(i)
saveData.push(i)
if (saveData.length === 10) {
res.status(200).json({'data': saveData});
}
})
})
What is issue. Please describe
You are experiencing one of the issues with automatic semicolon insertion, the feature of JavaScript that allows semicolons to be "optional".
In your case, when you miss out the semicolon after the array assignment, the array literal on the following line is being interpreted as a reference to an element within the array, which is of course undefined. You can read your code as:
router.post('/addRows', function (req, res) {
const saveData = [][1,2,3,4,5,6,7,8,9,10].map((i) => {
console.log(i)
saveData.push(i)
if (saveData.length === 10) {
res.status(200).json({'data': saveData});
}
})
})
That may not look valid but it is - the second array literal is being parsed as a property lookup containing an expression with comma ("grouping") operators. The comma operator returns its final operand, so you can simplify the above:
router.post('/addRows', function (req, res) {
const saveData = [][10].map((i) => {
console.log(i)
saveData.push(i)
if (saveData.length === 10) {
res.status(200).json({'data': saveData});
}
})
})
When you add the semicolon you remove the ambiguity and cause the second array literal to be parsed as an array literal rather than a property accessor.
If the ‘;’ is missed, your code will look like:
const saveData = [][1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(...)
it is going to access element from the empty array [], this code is equals to below:
const saveData = [][10].map(...)
obviously the 10th index of an empty array is undefined, so you will get TypeError: Cannot read property 'map' of undefined
Update:
Here are posts on this topic:
Why I Prefer To Use Semicolon In JavaScript
An Open Letter to JavaScript Leaders Regarding No Semicolons

nodejs function in class returns undefined

So, I have a function in my class that should return an object, but instead it returns undefined, which I dont know why it is happening, here's the code:
method.getQuests = function(){
var id = this._data[0].id;
var lvl = this._data[0].level;
var structure = [];
connection.query("SELECT * FROM quest WHERE player_id = ?", [id], function(errors, rowss, fieldss) {
for(var i = 0; i < rowss.length; i++){
var rewards = {
"coins":rowss[i].gold,
"xp":rowss[i].experience,
"honor":rowss[i].honor,
"premium":rowss[i].donut,
"statPoints":0,
"item":0
};
structure.push({
"id": rowss[i].id,
"character_id": id,
"identifier": rowss[i].name,
"type": 1,
"stage": rowss[i].stage,
"level": lvl,
"status": 1,
"duration_type": 1,
"duration_raw": (rowss[i].duration * 60) * 4,
"duration": rowss[i].duration * 60,
"ts_complete": 0,
"energy_cost": rowss[i].duration,
"fight_difficulty": 0,
"fight_npc_identifier": "",
"fight_battle_id": 0,
"used_resources": 0,
"rewards": JSON.stringify(rewards)
});
}
return structure;
});
}
What is also happening is that after I call some mysql query my user data stored in this._data doesn't exist anymore, so Im totally lost at this point.
connection.query is async, so when you return structure; it will return undefined because the async function connection.query() hasn't completed yet. Try using a callback. When you define your function, define it like this:
method.getQuests = function(callback){
And instead of return structure; try
callback(structure);
Then, when you call it and want to use it, use it like this:
method.getQuests(function(structure){
console.log(structure);
});

Shuffle sub documents in mongoose query

I have following models:
Question Model
var OptionSchema = new Schema({
correct : {type:Boolean, default:false}
value : String
});
var QuestionSchema = new Schema({
value : String
, choices : [OptionSchema]
, quiz : {type:ObjectId, ref:'quizzes'}
, createdOn : {type:Date, default:Date.now}
...
});
var Question = mongoose.model('questions', QuestionSchema);
Quiz Model
var QuizSchema = new Schema({
name : String
, questions : [{type:ObjectId, ref:'questions'}]
,company : {type:ObjectId, ref:'companies'}
...
});
var Quiz = mongoose.model('quizzes', QuizSchema);
Company Model
var CompanySchema = new Schema({
name :String
...
});
I want to shuffle choices of each question per each query, and I am doing It as follows :
shuffle = function(v){
//+ Jonas Raoni Soares Silva
//# http://jsfromhell.com/array/shuffle [rev. #1]
for(var j, x, i = v.length; i; j = parseInt(Math.random() * i), x = v[--i], v[i] = v[j], v[j] = x);
return v;
};
app.get('/api/companies/:companyId/quizzes', function(req, res){
var Query = Quiz.find({company:req.params.companyId});
Query.populate('questions');
Query.exec(function(err, docs){
docs.forEach(function(doc) {
doc.questions.forEach(function(question) {
question.choices = shuffle(question.choices);
})
});
res.json(docs);
});
});
My Question is :
Could I randomize the choices array without looping through all documents as now I am doing?
shuffle = function(v){
//+ Jonas Raoni Soares Silva
//# http://jsfromhell.com/array/shuffle [rev. #1]
for(var j, x, i = v.length; i; j = parseInt(Math.random() * i), x = v[--i], v[i] = v[j], v[j] = x);
return v;
};
app.get('/api/companies/:companyId/quizzes', function(req, res){
var Query = Quiz.find({company:req.params.companyId});
Query.populate('questions');
Query.exec(function(err, docs){
var raw = docs.toObject();
//shuffle choices
raw.questions.map(el => shuffle(el.choices))
//if you need to shuffle the questions too
shuffle(raw.questions);
//if you need to limit the output questions, especially when ouput questions needs to be a subset of a pool of questions
raw.questions.splice(limit);
res.json(raw); // output quiz with shuffled questions and answers
});
});
The essence of the question comes down to "Can I randomly shuffle results and have MongoDB do the work for me?". Well yes you can, but the important thing to remember here is that "populate" is not longer going to be your friend in helping you do so and you will need to perform the work that is doing yourself.
The short part of this is we are going to "hand-off" your client side "shuffle" to mapReduce in order to process the shuffling of the "choices" on the server. Just for kicks, I'm adding in a technique to shuffle your "questions" as well:
var Query = Quiz.findOne({ company: "5382a58bb7ea27c9301aa9df" });
Query.populate('company', 'name -_id');
Query.exec(function(err,quiz) {
var shuffle = function(v) {
for(var j, x, i = v.length; i; j = parseInt(Math.random() * i), x = v[--i], v[i] = v[j], v[j] = x);
};
if (err)
throw err;
var raw = quiz.toObject();
shuffle( raw.questions );
Question.mapReduce(
{
map: function() {
shuffle( this.choices );
var found = -1;
for ( var n=0; n<inputs.length; n++ ) {
if ( this._id.toString() == inputs[n].toString() ) {
found = n;
break;
}
}
emit( found, this );
},
reduce: function() {},
scope: { inputs: raw.questions, shuffle: shuffle },
query: { "_id": { "$in": raw.questions } }
},
function(err,results) {
if (err)
throw err;
raw.questions = results.map(function(x) {
return x.value;
});
console.log( JSON.stringify( raw, undefined, 4 ) );
}
);
});
So the essential part of this is rather than allowing "populate" to pull all the related question information into your schema object, you are doing a manual replacement using mapReduce.
Note that the "schema document" must be converted to a plain object which is done by the .toObject() call in there in order to allow us to replace "questions" with something that would not match the schema type.
We give mapReduce a query to select the required questions from the model by simply passing in the "questions" array as an argument to match on _id. Really nothing directly different to what "populate" does for you behind the scenes, it's just that we are going to handle the "merge" manually.
The "shuffle" function is now executed on the server, which since it was declared as a var we can easily pass in via the "scope", and the "options" array will be shuffled before it is emitted, and eventually returned.
The other optional as I said was that we are also "shuffling" the questions, which is merely done by calling "shuffle" on just the _id values of the "questions" array and then passing this into the "scope". Noting that this is also passed to the query via $in but that alone does not guarantee the return order.
The trick employed here is that mapReduce at the "map" stage, must "emit" all keys in their ascending order to later stages. So by comparing the current _id value to where it's position is as an index value of the "inputs" array from scope then there is a positional order that can be emitted as the "key" value here to respect the order of the shuffle done already.
The "merging" then is quite simple as we just replace the "questions" array with the values returned from the mapReduce. There is a little help here from the .map() Array function here to clean up the results from the way mapReduce returns things.
Aside from the fact that your "options" are now actually shuffled on the server rather than through a loop, this should give you ideas of how to "custom populate" for other functions such as "slicing" and "paging" the array of referenced "questions" if that is something else you might want to look at.

Is it possible to pass an extra parameter to a mongoose update callback

I have mongoose update call and would like to pass an extra parameter...like so:
Trying to pass isLoopOver
UserInfo.update({_id: userInfo._id}, {'value': someval}, function(err, numAffected, isLoopOver ) {
console.log('IsLoopOver ' + JSON.stringify(isLoopOver) );
if (isLoopOver){
doSomething(isLoopOver);
}
});
Tried the above but I get an object (inside the callback) as below:
{"updatedExisting":true,"n":1,"connectionId":117,"err":null,"ok":1}
No idea why it is showing the status from mongo.
Question: How can I pass an extra parameter thru the callback?
The common way is:
var isLoopOver = false;
UserInfo.update({_id: userInfo._id}, {'value': someval}, function(err, numAffected) {
console.log('IsLoopOver ' + JSON.stringify(isLoopOver) );
if (isLoopOver){
doSomething(isLoopOver);
}
});
If you are worried about some code will change the value of isLoopOver before the update's callback function being called, using the following code:
(function (isLoopOver) {
UserInfo.update({_id: userInfo._id}, {'value': someval}, function(err, numAffected) {
console.log('IsLoopOver ' + JSON.stringify(isLoopOver) );
if (isLoopOver){
doSomething(isLoopOver);
}
});
}(isLoopOver));
The reason why your isLoopOver variable is showing the status from mongo is that in the callback function, the isLoopOver is a formal parameter instead of a actual parameter.
You can use Underscore's partial function:
UserInfo.update(..., _.partial(function( isLoopOver, err, numAffected ) {
}, isLoopOver ))

Resources