Update Data from Within a Loop - javascript

I am trying to get my nodejs controller to update the rate in the currency table.
Everything works fine in S3T / RoboMongo, but for some reason it just wont fire the update inside the nodejs controller.
Here is my currency table
{
"_id" : "USD",
"index" : NumberInt(6),
"name" : "Dollar",
"currency" : "USD",
"symbol" : "$",
"active" : true,
"default" : false,
"rate" : 0
}
{
"_id" : "EUR",
"index" : NumberInt(2),
"name" : "Euro",
"currency" : "EUR",
"symbol" : "€",
"active" : true,
"default" : false,
"rate" : 0
}
I tried both of these, works fine in S3T but not inside nodejs:
db.currency.update (
{ _id : "EUR" },
{ $set: { rate : 123 }},
{ upsert: true }
)
db.currency.updateOne (
{ _id : "EUR" },
{ $set: { rate : 123 }},
{ upsert: true }
)
Here is my nodejs code:
var mongoose = require('mongoose');
var currencyModel = require('../models/currencyModel');
var currencyTable = mongoose.model('currencyModel');
var updateRates = () => {
return new Promise((resolve, reject) => {
for (var key in data.quotes) {
var currencyID = key.substring(3);
var newRate = (data.quotes[key] * THBUSD).toFixed(5);
console.log("currencyID: " + currencyID)
console.log("newRate: " + newRate)
currencyTable.update (
{ _id: currencyID },
{ $set: { rate : newRate }},
{ upsert: true }
),function (err, data) {
if (err) {
reject(new Error('updateRates: ' + err));
};
};
};
resolve();
})};
And here is my currencyModel (which is where I think the problem is?!?)
// Currency Model
// This model is the structure containing data from the Currency table
//
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var currencySchema = new Schema({
_id: String, // Unique Currency code
index: Number, // Indes for sorting
name: String, // Currency name
symbol: String, // Currency symbol
active: Boolean, // Active True False
rate: Number // Exchange rate (multiply with THB price)
});
module.exports = mongoose.model('currencyModel', currencySchema, 'currency');
I cannot see why it wont fire the currencyTable.update from inside nodejs.
I turned debug on in mongoose, and I see all other mongodb operations in the console like Mongoose: price.findOne({ _id: 'ATL-D406' }, { fields: {} }) etc.. but I do not see this currency.update in the console, which is why I dont think its fired off to mongodb - and I cannot see the reason.

You have a "loop" that completes execution before the inner callbacks fire. Instead just use Promises all the way through and call Promise.all() to collect all the iterated Promises and resolve them:
var updaterates = () => {
return Promise.all(
Object.keys(data.quotes).map(k => {
return currencyTable.update(
{ _id: k.substring(0,3) },
{ $set: { rate : (data.quotes[k] * THBUSD).toFixed(5) }},
{ upsert: true }
).exec()
});
)
};
The returned response of Promise.all() is an array of the response objects from the updates. Also note that this is a "fast fail" operation and calls will be made in parallel.
Object.keys() returns an "array of the key names" in the specified object. .map() iterates those keys and returns an "array" of the return value for the iterator.
We use the k as the "key name" to access the wanted key from data.quotes and use the values to perform each .update() with .exec() to return a "real" Promise. The iterator returns an "array" Promise which becomes the argument to Promise.all().

Related

Sorting MongoDB result by another MongoDB result is returning [null, null, null]

I'm performing two MongoDB queries, and then I want to synchronize the resulting arrays, to make sure they are in the same order.
The first array is a set of (20) questions ids (this is the correct order):
q_id_arr: [
"5f86da2d37e3d200040ba523",
"5f86b6ce37e3d200040ba4c6",
"5ffc4abea04f3c0004e46cf3",
"5f86b66537e3d200040ba4c5",
"5f87f368554f370004ed17b4",
"5f86e48c37e3d200040ba53c",
"5ffc4dc4a04f3c0004e46d0b",
"5f86e19037e3d200040ba534",
"5f86aaa237e3d200040ba49b",
"5ffc479ba04f3c0004e46ce0",
"5f86b9dc37e3d200040ba4d2",
"5f85828e0e1bd30004361430",
"5f8700c937e3d200040ba548",
"5f86d81737e3d200040ba51c",
"5f8708d237e3d200040ba568",
"5f87060d37e3d200040ba55c",
"5f857dac0e1bd3000436141c",
"5f85703e0e1bd300043613ec",
"5f87e9d4554f370004ed178e",
"5f8073c04ad88e00041f015f"
]
The second array is a set of (20) results associated with the question ids:
team_trends: [
{
"_id":"5f87e9d4554f370004ed178e",
"positive":0.93,
"engaged":0.558
},
{
"_id":"5f86e19037e3d200040ba534",
"positive":0.585,
"engaged":0.567
},
{
"_id":"5f85828e0e1bd30004361430",
"positive":0.7,
"engaged":0.666
},
{
"_id":"5f8073c04ad88e00041f015f",
"positive":0.31,
"engaged":0.30999999999999994
},
{
"_id":"5f87f368554f370004ed17b4",
"positive":0.5449999999999999,
"engaged":0.57
},
{
"_id":"5f86b6ce37e3d200040ba4c6",
"positive":0.855,
"engaged":0.46599999999999997
},
{
"_id":"5f857dac0e1bd3000436141c",
"positive":0.92,
"engaged":0.524
},
{
"_id":"5f85703e0e1bd300043613ec",
"positive":0.15,
"engaged":0.39
},
{
"_id":"5f86aaa237e3d200040ba49b",
"positive":0.15000000000000002,
"engaged":0.584
},
{
"_id":"5f86b66537e3d200040ba4c5",
"positive":0.37,
"engaged":0.386
},
{
"_id":"5f86e48c37e3d200040ba53c",
"positive":0.615,
"engaged":0.548
},
{
"_id":"5ffc479ba04f3c0004e46ce0",
"positive":0.42000000000000004,
"engaged":0.583
},
{
"_id":"5f86b9dc37e3d200040ba4d2",
"positive":0.68,
"engaged":0.662
},
{
"_id":"5f86d81737e3d200040ba51c",
"positive":0.03,
"engaged":0.516
},
{
"_id":"5f87060d37e3d200040ba55c",
"positive":0.14,
"engaged":0.454
},
{
"_id":"5f86da2d37e3d200040ba523",
"positive":0.47,
"engaged":0.41500000000000004
},
{
"_id":"5f8708d237e3d200040ba568",
"positive":0.17,
"engaged":0.76
},
{
"_id":"5ffc4dc4a04f3c0004e46d0b",
"positive":0.395,
"engaged":0.53
},
{
"_id":"5ffc4abea04f3c0004e46cf3",
"positive":0.365,
"engaged":0.679
},
{
"_id":"5f8700c937e3d200040ba548",
"positive":0.93,
"engaged":0.6980000000000001
}
]
I want to reorganize team_trends into the same order as q_id_arr
Here is the code I'm using (following this SO Answer):
let c = [];
q_id_arr.forEach((q_oid => c.push(team_trends.find((obj => obj._id == q_oid)))));
However when I print console.log("the result of c"+ c) I get this result:
the result of c: [null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]
Is this the right approach? Any suggestions are appreciated!
More Details:
Before this step I acquired the q_id_arr through mapping over an aggregate result, like this:
let q_id_arr = await user_trends.map(({ question_oid }) => question_oid)
When I tested console.log(typeof q_id_arr) it returned object.
How can I sort through the object??
Final code that fixed the issue.
user_trends.forEach((user => c.push(team_trends.find((obj => obj._id.toString() === user.question_oid.toString())))));
A couple of points to note here:
If you are using mongoose then it already returns an array from the aggregate function.
Mongoose uses the MongoDB NodeJs native driver at its core. In core driver ObjectId has a function .equals(otherId). It is always best to use this function for id comparisons.
References:
Comparing mongoose _id and strings
.aggregate(...).toArray is not a function
https://mongodb.github.io/node-mongodb-native/api-bson-generated/objectid.html#equals
You can try this using the arrays map method:
let sorted_team_trends = q_id_arr.map( q => team_trends.find(t => t._id === q) );
Assuming the two arrays are defined as fields like this:
let q_id_arr = [
"5f86da2d37e3d200040ba523",
"5f86b6ce37e3d200040ba4c6",
"5ffc4abea04f3c0004e46cf3",
...
];
let team_trends = [
{
"_id" : "5f87e9d4554f370004ed178e",
"positive" : 0.93,
"engaged" : 0.558
},
{
"_id" : "5f86e19037e3d200040ba534",
"positive" : 0.585,
"engaged" : 0.567
},
...
]

Compare 2 values from same doc in mongo

I want to compare 2 values in same doc id
For example
{
"_id" : ObjectId("5f180441ad1cd40008dc6a5a"),
"fcount" : 5,
"key" : "27b6e581-796c-4f3a-882b-0e0c2a0a8a64",
"student_id" : "5f0ffbcdd67d70c1a3b143aa",
"__v" : 0,
"dcount" : 5
}
I have this doc, Now If I want to check fcount and dcount is same then return true. OR false
How can I do this with mongoose query?
update:
const result = await execute_reports_model.find({ _id: _id}).lean().cursor({batchSize: 10}).eachAsync(async ({fcount, dcount}) => {
if (fcount === dcount) {
// true Logic
} else {
// false logic
}
}, { parallel: 10})
So here I will be passing the ID in find. and then compare values that I found from that ID. values are fcount and dcount
Using Mongoose's Model.aggregate you can compare a document's two fields and return a boolean:
const result = await model.aggregate([
{ $match: { _id: id } },
{ $project: { _id: 0, isMatch: { $eq: [ "$fcount", "$dcount" ] } } }
])
result value: { "isMatch" : true }
This precursor code will help you to solve your problem. It will allow you to find the necessary documents and then deal with all true and false cases.
{batchSize: 10} and {parallel:10} will help you to do your task at parallel. You could easily modify them.
Don't forget to add callback or handle all cases somehow, as you need it to.
await collection_name.find({ your_query: here}).lean().cursor({batchSize: 10}).eachAsync(async ({fcount, dcount}) => {
if (fcount === dcount) {
//true case
} else {
//false case
}
}, { parallel: 10})

How to use custom script inside aggregation in elasticsearch with nodejs

Here is the my code:
const convertCurrency = async (currrencyType) => {
try{
const res = await got("https://api.exchangerate-api.com/v4/latest/USD");
return (JSON.parse(res.body)['rates'][currrencyType]);
}
catch(err){
console.log(err);
}
}
async function search(){
try{
client.search({
index : '03776182',
body: {
aggs : {
price_ranges : {
range : {
field : "price",
script : {source : "convertCurrency(/EUR/) * _value"},
ranges : [
{ "from" : 10, "to" : 20 },
]
}
}
}
}
},(err,resp,status)=>{
err ? console.log(err): console.log(JSON.stringify(resp));
});
}
catch(err){console.log(err);}
}
I am trying to insert the custom script inside the source field. But it gives below error.
response: `{"error":{"root_cause":[{"type":"script_exception","reason":"compile error","script_stack":["convertCurrency(/EUR/) * _value"," ^---- HERE"],"script":"convertCurrency(/EUR/) * _value","lang":"painless"}],"type":"search_phase_execution_exception","reason":"all shards failed","phase":"query","grouped":true,"failed_shards":[{"shard":0,"index":"03776182","node":"C4p_ha96QhK2uB47jV9pxg","reason":{"type":"script_exception","reason":"compile error","script_stack":["convertCurrency(/EUR/) * _value"," ^---- HERE"],"script":"convertCurrency(/EUR/) * _value","lang":"painless","caused_by":{"type":"illegal_state_exception","reason":"Regexes are disabled. Set [script.painless.regex.enabled] to [true] in elasticsearch.yaml to allow them. Be careful though, regexes break out of Painless's protection against deep recursion and long loops."}}}]},"status":500}`,
toString: [Function],
At the same time normal expression are working well.
You cannot invoke a JS callback from an ES script, it doesn't make sense as they don't execute in the same context.
What you should do is to first retrieve the EUR exchange rate in your Node.js code and then send your query with that exchange rate as a parameter to your script.
In pseudo code, it would look like this:
// 1. retrieve EUR/USD exchange rate
const EUR_USD = convertCurrency('EUR');
// 2. send your query
client.search({
index : '03776182',
body: {
aggs : {
price_ranges : {
range : {
field : "price",
script : {
source : "params.rate * _value"
params: {
rate: EUR_USD
}
},
ranges : [
{ "from" : 10, "to" : 20 },
]
}
}
}
}

how do i replace some properties returned from database in sails

So i'm trying to replace fields on the data queried from the database in sails.
async.waterfall( [
function getscores(callback) {
Score.find({course : courseId}).paginate({page : 1 , limit: 10}).populate('course')
.exec(function(err,data) {
callback(null,data);
});
}
, function addUserInfo(result,callback) {
for(var i=0; i < result.length; i++){
result[i].user = User.findOne({id : result[i].user}).exec(function(err,data) {
var temp = {
"name" : data.name,
"id" : data.id,
"user_id" : data.user_id
}
return temp;
});
}
res.json(messageGenerator(200, 'Sucecss', result));
}],function(err) {
console.log(err);
}
);
the first function 'getScores' returns the scores array but each score property only has a user id. Now in addUserInfo function, i want to be able to add the user's name to the score property.
But the above code fails to return the users inside. the user property of score is empty. i believe the response is already sent before the program gets to add the user property ( due to asyncronousness of the database adapter).
Following a brief comment chat, the following replacement for the addUserInfo function should help you achieve what you desire:
function addUserInfo(results,callback) {
async.map(results, function(result, callback) {
User.findOne({id : result.user}).exec(function(err, data) {
callback(err, Object.assign(result, {
"user": {
"name" : data.name || null,
"id" : data.id || null,
"user_id" : data.user_id || null
}
}));
});
}, function(err, output) {
return res.json(messageGenerator(200, "Success", output))
});
}
Async map allows us to asynchronously iterate over results, allowing us to amend the result by supplying the callback with the new result as the second parameter. The Final function is our final callback, that gets provided with any err's that have occured along the way and our new Array as output.

Mongodb - Search by id and embedded array value

Im trying to write a function that will search for an object by ID and whether or not a value is contained in an embedded array within the object.
{
"_id" : ObjectId("569bea91c0e1fee4063527ac"),
"user" : ObjectId("568c65174fee132c36e199dd"),
"votes" : 9,
"image" : "./modules/deals/client/img/uploads/1546f914dba7e1732ea853cd70d79148.jpg",
"price" : "12.95",
"retailer" : "argos.co.uk",
"voters" : [{
"user" : ObjectId("568c65174fee132c36e199dd"),
},
{
"user" : ObjectId("568c65174fee132c36e199dd"),
},
{
"user" : ObjectId("568c65174fee132c36e199dd"),
}]
I would like to search by the _id and the voters.user.
I believe i need to finish this function correctly
exports.dealByIdAndVoter = function(req, res) {
Deal.count({
$where: function () {
}
},
function(err, dealByIdAndVoter) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
console.log(dealByIdAndVoter);
var data = {};
data.count = dealByIdAndVoter;
res.json(data);
}
});
};
If you do not need to use the $where function, you can construct the query with the $or operator in this manner:
Deal
.find({
$or: [
// Match by _id
{ _id: req.id },
// Match by any user in the 'voters' array with a matching 'user' field.
{ 'voters.$.user': req.id }
]
})
.count(function(err, count) {
// Handle error here
res.json(count);
});

Categories

Resources