0

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.

1 Answer 1

1

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().

Sign up to request clarification or add additional context in comments.

7 Comments

Neil you are very good at this!! You used map instead!! - I didn't know about the .exec() command either. So what you do here is Object.keys(data.quotes).map is that like saying for k=0;k<data.quotes.length;k++ just in a much more elegant way? I really wanna understand this cause its such a cool way to do it. Object.keys that means for all keys inside this object (data.quotes) correct? and then you .map but shouldn't you tell it what to map from the object? Can you explain it a bit more?
@torbenrudgaard There's plenty of documentation readily available and they all come up in google if you just type in their names. I've added all of those to the answer. Basically put *"get the list of keys and iterate over that list, to return a "different" list. Hence Object.keys().map(). Get list. Transform.
@torbenrudgaard By all rights I could have closed your question as a duplicate of How do I return the response from an asynchronous call? because you were basically calling .update() inside a loop without waiting for the calls to complete before exiting the loop. But I was actually nice enough to give you an explanation as an answer instead. So I hope that "upvote" button gets some attention.
Of cause Neil - I always upvote you all I can :) and thanks for the links and the information. I learned a lot from your code, in fact I called the whole team to look at your elegant solution and we are now changing several other places because of your code :)
Neil - sorry to disturb again. Everything works great and it is updating all records, but it is throwing a duplicate error, and I cant see where and what is causing it. I checked the array but there does not seem to be any null values in it. Here is the error: db.tt/Pffeg1UruT
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.