2

I am new to javascript, I am trying to pass the result of a nested function, down to the rest of the parent function ? maybe?

I have documents in one mongodb collection, and company info in another. So I am trying to pair them up like this part of the code, but it is not doing what I am trying to get done. I think I am clearly missing something about javascript and the way it functions.

I am able to get the list of companies, and log to the console, but I can't do what I am doing at this line

'name': companies[0]['name'],

const mongoose = require('mongoose');
const express = require('express');
var cors = require('cors');
const bodyParser = require('body-parser');
const logger = require('morgan');
const {Data, Companies} = require('./data.js');


const API_PORT = 3001;
const app = express();
app.use(cors());
const router = express.Router();



const dbRoute = 'mongodb://192.168.200.20/matrix';

mongoose.connect(dbRoute, { useNewUrlParser: true, useUnifiedTopology: true});

let db = mongoose.connection;

db.once('open', () => console.log('connected to the database'));

db.on('error', console.error.bind(console, 'MongoDB connection error:'));

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(logger('dev'));


router.post("/searchData", (req, res, next) => {
  console.log("REQ---", req.body.search_obj)
  let answers = []
  let search_query = req.body.search_obj
  let arg_group = [];
  let arg_template = [];
  let arg_object = {}
  for (i in search_query){
    arg_query = {search_text: { $regex: new RegExp(search_query[i]), $options: 'i' }}
    arg_group = arg_group.concat(arg_query)
  }
  arg_template = arg_template.concat(arg_group)
  arg_list = "\"{$and: [" + arg_group + "]}\""
  arg_object['$and'] = arg_template

  Data.find(arg_object, function (err, data){
    if (err){
      console.log(err);
    }
    else {
      console.log("LENGTH", data.length)
      for (i in data) {
        console.log("Resolved to ... : ", data[i]['_id'], data[i]['title']);
        Companies.find({_id: data[i]['_id']})
          .then(companies => {
            console.log("Company Name:")
            console.log("NAME >>>> >>>> ", companies[0]['name']);

          })
          .catch(err => {
            console.log(err);
          })

        search_reply = {
          '_id': data[i]['_id'],
          'title': data[i]['title'],
          'url': data[i]['url'],
          'release_date': data[i]['release_date'],
          'document_date': data[i]['document_date'],
          'name': companies[0]['name'],
        }
        answers[i] = search_reply

    }

      console.log("LENGTH", data.length)
      console.log("ANSWERS", answers)
      console.log("LENGTH", data.length)
      return res.json(answers);
    }
  })

});



router.post('/updateData', (req, res) => {
  const { id, update } = req.body;
  Data.findByIdAndUpdate(id, update, (err) => {
    if (err) return res.json({ success: false, error: err });
    return res.json({ success: true });
  });
});


router.delete('/deleteData', (req, res) => {
  const { id } = req.body;
  Data.findByIdAndRemove(id, (err) => {
    if (err) return res.send(err);
    return res.json({ success: true });
  });
});

router.post('/putData', (req, res) => {
  let data = new Data();

  const { id, message } = req.body;

  if ((!id && id !== 0) || !message) {
    return res.json({
      success: false,
      error: 'INVALID INPUTS',
    });
  }
  data.message = message;
  data.id = id;
  data.save((err) => {
    if (err) return res.json({ success: false, error: err });
    return res.json({ success: true });
  });
});

app.use('/api', router);

app.listen(API_PORT, () => console.log(`LISTENING ON PORT ${API_PORT}`));
Brett
  • 29
  • 6
  • Does this answer your question? [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – derpirscher Aug 11 '22 at 22:21
  • I went through a few changes based on some of the suggestionsin it, but I could not make any of them work any better. I think it comes from a shallow understanding of what JavaScript does :( – Brett Aug 12 '22 at 02:17

2 Answers2

1

You can either move all of your search_reply logic inside promise then callback. The other solution which could be easier is to use async/await for handling Promises. Let's first assume that your parent function is called getSearchResults, so based on that your code would look like:

try {
    const data = await Data.find(arg_object);
    console.log("LENGTH", data.length)
    for (const answer in data) {
        console.log("Resolved to ... : ", answer['_id'], answer['title']);
        const companies = await Companies.find({ _id: answer['_id'] });

        search_reply = {
            '_id': answer['_id'],
            'title': answer['title'],
            'url': answer['url'],
            'release_date': answer['release_date'],
            'document_date': answer['document_date'],
            'name': companies[0]['name'],
        }
        answers.push(search_reply);
    }

    console.log("LENGTH", data.length)
    console.log("ANSWERS", answers)
    console.log("LENGTH", data.length)
} catch (err) {
    console.log(err);
}

Updates: This updated piece of code should replace DB access code inside router.post("/searchData", (req, res, next) => {...} endpoint. Starting from Data.find(arg_object, function(err, data) {...}

Fixed answers to match the for .. in loop, instead of using index.

It's using async/await for handling promises and try/catch for handling errors

Fadi Hania
  • 705
  • 4
  • 11
  • now the full code is there is the solution any different ? I tried what you said, but in the `return` at the bottom, it returns nothing. – Brett Aug 12 '22 at 01:26
  • Can't believe I am struggling so hard with this, have watched videos on promises and callbacks, and kind of get the gist of it. but I can't implement it in this file, so I must have something else out-of-whack... – Brett Aug 12 '22 at 03:11
  • anyone else care to have a crack at this ... I am stumped, every tutorial I read, or video, they are just not quite the same as my case ... but a more skilled operator might see it straight away ? – Brett Aug 12 '22 at 04:15
  • I've used the little detailed code you've supplied to show you how to do it, for the return you can simply just return `res.json(answers)`. I'll modify the code, my suggested solution is shorter and uses ES6 new async/await – Fadi Hania Aug 12 '22 at 12:07
  • Code updated supposing that this piece of code would be executed inside a middleware and would return the answers as JSON. Could you at least try it and let me know? It's shorter, more modern solution and it was first – Fadi Hania Aug 12 '22 at 12:14
  • Fair enough, I put it into service, and had to make a few small adjustments, `const companies = await Companies.find({_id: data[answer]['_id']});` but yes it works, thanks... If it matters who was first with a workable solution, then yes my original pick of @traynor 's answer would be correct. But in the interest of neater more correct code, this one is the winner – Brett Aug 12 '22 at 21:43
  • Thanks Brett for testing the suggested solution – Fadi Hania Aug 12 '22 at 21:45
  • FWIW, `await Companies.find` is a slower solution than `for` loop.. – traynor Aug 13 '22 at 09:33
  • Can you explain more? What's the relation between MongoDB `find()` function and `for` loop in the code provided? Thanks – Fadi Hania Aug 13 '22 at 21:22
  • every iteration in the `for..in` loop here will pause the code until `await Companies.find` is finished, unlike the `for` loop + callback (which actually might be a better option in this case, with refactoring maybe using `Promise.all`) – traynor Aug 14 '22 at 04:35
1

the problem is that node.js runs asynchronously, so you return results before Companies.find is done. so you should return results only when everything is finished, which means you need to send results from Companies.find.

since you're iterating over many results, a quick and dirty way (the whole code should be rewritten otherwise) to know when everything is done is to add a counter and then return when the last loop is done (replace for..in loop with for loop to get a numeric index)

try this:

  Data.find(arg_object, function (err, data){
    if (err){
      console.log(err);
    }
    else {
      console.log("LENGTH", data.length)
      for (let i = 0; i < data.length; i++) {
        console.log("Resolved to ... : ", data[i]['_id'], data[i]['title']);
        Companies.find({_id: data[i]['_id']})
          .then(companies => {
            console.log("Company Name:")
            console.log("NAME >>>> >>>> ", companies[0]['name']);


           let search_reply = {
              '_id': data[i]['_id'],
              'title': data[i]['title'],
              'url': data[i]['url'],
              'release_date': data[i]['release_date'],
              'document_date': data[i]['document_date'],
              'name': companies[0]['name'],
            }
            answers.push(search_reply)

            if(i+1==data.length) {
                  console.log("LENGTH", data.length)
                  console.log("ANSWERS", answers)
                  console.log("LENGTH", data.length)
                  return res.json(answers);
            }

          })
          .catch(err => {
            console.log(err);
          })

    }
      
    }
  })
traynor
  • 5,490
  • 3
  • 13
  • 23
  • very close, in the logging I am getting a couple of `<1 empty item>` in the `console.log("ANSWERS", answers)`, then when that is finished printing to console, I get a belated `NAME >>>>> etc" with the "empty" ones ?? I will investigate further – Brett Aug 12 '22 at 07:21
  • @Brett did you replace the for loop? try the new code – traynor Aug 12 '22 at 07:37
  • yes, I replaced it with yours, – Brett Aug 12 '22 at 07:44
  • @Brett did it work now? I also added `.push` to the `answers`, that's probably what's causing empty elements.. – traynor Aug 12 '22 at 07:50
  • Boom, that did it, thank you **SO** much ... I have spent to long trying to solve this – Brett Aug 12 '22 at 08:26
  • @Brett great. that code now needs some heavy refactoring. here's a link that explains the problem with this code: [Asynchronous flow control](https://nodejs.dev/learn/asynchronous-flow-control), and how it might be changed: [Modern Asynchronous JavaScript with Async and Await](https://nodejs.dev/learn/modern-asynchronous-javascript-with-async-and-await) – traynor Aug 12 '22 at 08:33
  • thanks I will check that out ... it's been a big week learning and building in a language I don't know :) – Brett Aug 12 '22 at 10:19