3

I have a very specific question and hopefully someone can lend me a hand here. I'm quite green when it comes to Javascript much more when it comes to NodeJS.

I am using lodash's _.forIn functionality to traverse and add into an object that's inside an array. The whole thing looks something like this:

[ 
  { id: 20,
    keywords: 'shirts'
  },
  { id: 18,
    keywords: 'shoes'
  } 
]

Basically it's list of keywords saved in in the db elasticsearch. I'm trying to find a way to add into this object a counter of the number of results these keywords would return.

This count will come from an elasticsearch setup that we use to search our product database with.

The output I'd like to get would be something like this:

[ 
  { id: 20,
    keywords: 'shirts',
    count: 4
  },
  { id: 18,
    keywords: 'shoes',
    count: 15
  } 
]

This is the code I have right now:

function get_list( data ) {

  var list_data = data;
  var list = _.forIn( data, function( item, n, list_data ) {
    get_count( item, function( count_number ) {
      list_data[n].count = count_number;
    })
  });
  console.log( list, list_data );
}

function get_count( item, callback ) {

  var query = { // elasticsearch query here };  

  // elasticsearch query here, that returns the count in a callback
  es.count( query, function(count_number) { callback(count_number) } );
} 

When I run this, the console will show an unchanged array. I've checked all the returned data on each function, everything checks out. I just can't seem to get the data right on neither list nor list_data.

PS. This isn't my actual code but it's pretty much the gist of it, so there may be some typos.

Also, list and list_data is a result of me trying to figure out how this works. I've tried reading up on how JS callbacks work but nothing I've seen seem to be able to help me with my problem.

clueless
  • 839
  • 1
  • 10
  • 24
  • 1
    I've already seen that question and none of the answers there make any sense to me. I've spent the whole day researching this stuff and most of it just goes over my head. This is why I've asked my own question which makes it easier for me to understand answers to because it's specific to my problem. And now noone can answer it because it's marked as a duplicate. – clueless Apr 24 '15 at 16:32
  • Understood, but if that's the case then it's helpful to reference the existing question you've already looked at and then ask something more specific about what you still don't understand. As it is now, it really is the exact same question as the dupe. – JohnnyHK Apr 24 '15 at 17:27
  • I can't see how these two are the exact same question. I'm not even using jQuery (something that the answers on that page seem to heavily rely on). How--then--do I make this question unique? – clueless Apr 24 '15 at 18:45
  • I'm not sure why you'd say that, jQuery is used in only one example out of four in the questions and answers. Anyway, look specifically at the solutions involving `fs.readFile` as it's the same basic node.js async query concept as what you're doing. – JohnnyHK Apr 24 '15 at 19:00

1 Answers1

3

You can check the list_data object only after all the callbacks have finished.

If you want to validate that your get_count function is working you can move the console.log inside the callback, this will result in multiple logs of the list_data in the console.

function get_list( data ) {

  var list_data = data;
  var list = _.forIn( data, function( item, n, list_data ) {
    get_count( item, function( count_number ) {
      list_data[n].count = count_number;
      console.log( list, list_data );
    })
  });

}

function get_count( item, callback ) {

  var query = { // elasticsearch query here };  

  // elasticsearch query here, that returns the count in a callback
  es.count( query, function(count_number) { callback(count_number) } );
} 

In order to do this i recommend using the async module with is pretty good to handle asynchronous calls like yours.

Here is the module link so you can start using it: https://github.com/caolan/async

Here is how i would implement the async module in our case:

var async = require('async');

function get_list(data) {

    var list_data = data;
    //Transform all the list_data objects adding the count prop.
    async.map(list_data, get_count, function(err, results) {
        console.log(results);
    });
}

function get_count(item, callback) {
    var query = { // elasticsearch query here 
    };
    // elasticsearch query here, that returns the count in a callback
    es.count(query, function(count_number) {
        //add the new prop to the item
        item.count = count_number;
        //return the transformed object
        callback(null, item);
    });
}
Kauê Gimenes
  • 1,278
  • 1
  • 13
  • 30
  • I don't want to validate the `list`, `list_data` variables inside the `get_count` function. I've already done that. What I want to know is how do I get the right results outside of the array return the data as results to an API route. – clueless Apr 24 '15 at 16:24
  • I'm using locomotivejs as an MVC framework by the way, if that helps. It's built on top of the express framework. – clueless Apr 24 '15 at 16:25
  • Hello Clueless! For that i would implement the `async.queue` you need help with that? https://github.com/caolan/async#queue – Kauê Gimenes Apr 24 '15 at 16:25
  • Yes Kaue. I dont understand entirely how javascript callbacks work (I'm new to javascript and programming mostly). I've read through the documentation on that link, but can't seem to understand how to apply that to my problem. – clueless Apr 24 '15 at 16:36
  • Hey @Clueless just included a sample code using the `.map` function, i modified the get_count function to transform the object instead of returing only the count. Check if this works for you =) – Kauê Gimenes Apr 24 '15 at 16:51
  • It gives me an `undefined is not a function error`. – clueless Apr 24 '15 at 17:09
  • Hello @Clueless try to replace `callback(item)` with `callback(null, item)` – Kauê Gimenes Apr 24 '15 at 17:18