Mongoose, async/await, combining documents from two collections

Hello. I want to find all documents in one collection, and then loop through those documents and look up each of them in another collection and combine some of the data to send to the client. I’m having an asynchronous problem, even with using async/await - the getIPInfo function isn’t being waited for. Can anyone help?

app.get('/connections', (request, response) => {
  const data = [];
  Connection.find({}).sort('-_id').exec((err, documents) => {
    if (err) {
      console.log(err);
    } else {
      const loop = async () => {
        for (const document of documents) {
          const IPInfo = await getIPInfo(document.hostname);
          data.push(IPInfo, document.uid);
        }
      };
      loop().then(() => {
        console.log(data);
        response.send(data);
      });
    }
  });
  
  function getIPInfo(hostname) {
    const info = [];
    IPaddress.findOne({ip: hostname}).exec((err, doc) => {
      if (err) {
        console.log(err);
      } else {
        const {flag, organisation, ip, country, region, city} = doc;
        info.push(flag, organisation, ip, country, region, city);
        //console.log(info);
        return info;
      }
    });
  }
});

Code Block 1

  Connection.find({}).sort('-_id').exec((err, documents) => {
    if (err) {
      console.log(err);
    } else {
      const loop = async () => {
        for (const document of documents) {
          const IPInfo = await getIPInfo(document.hostname);
          data.push(IPInfo, document.uid);
        }
      };
      loop().then(() => {
        console.log(data);
        response.send(data);
      });
    }
  });

Code Block 2

  function getIPInfo(hostname) {
    const info = [];
    IPaddress.findOne({ip: hostname}).exec((err, doc) => {
      if (err) {
        console.log(err);
      } else {
        const {flag, organisation, ip, country, region, city} = doc;
        info.push(flag, organisation, ip, country, region, city);
        //console.log(info);
        return info;
      }
    });
  }

Looking at code block 1 and code block 2, given the way async/await is implemented currently, is there any way code block 2 would be forced to “wait” for code block 1 to finish executing?

Hmm. I need the for…of loop in code block 1 to wait for code block 2 to return ‘info’ (I’d normally use .forEach(), but I read async/await doesn’t work as expected with it). I’ve tried adding async/await to code block2 as well, but it makes no difference. And I’ve tried making code block 2 a global function.

What about something like

app.get('/connections', async (request, response) => {
  const data = [];
 await Connection.find({}).sort('-_id').exec((err, documents) => {
    if (err) {
      console.log(err);
    } else {
     ...

?

@lpgm Do you have a link to your entire project (like Glitch) which would allow us to test your code?

Thanks for getting back to me. In the end, I decided on a different approach. I go and get all the ‘connections’ and all the ‘ipaddresses’ and then put what I want together. I use this structure:

Promise.all([
    Connection.find({}).sort('-_id'),
    IPaddress.find({})
  ])
    .then(results => { REST OF CODE });

But thanks willjw3, I’ll give this a go too.

1 Like