0
\$\begingroup\$

I am faced yet again with a callback headache:

Simply I need to return to the client the results of multiple mongo queries, the number of which I don't know in advance. In short:

  • fetch the first document in a collection
  • if the document has some fields with alphabetic values, fetch all distinct values

Since I can only do the next query in the result callback of the previous one, that's what am doing keeping track of how many fields I have queried so far:

function getFieldsFromCollection (collection, socket) {
  MongoClient.connect(MAIN_DB, function (err, db) {
    if (err) {
      return console.log(err)
    }
    var c = db.collection(collection)
    c.findOne({}, {}, function (err, doc) {
      if (err) {
        return log(err)
      }
      if (!doc) {
        return log('document is null')
      } else {
        var alphaFields = []
        Object.keys(doc).forEach(function (key) {
          if (/^[A-Z]/.test(doc[key])) {
            alphaFields.push(key)
          }
        })
        // prepare the response
        var reply = {
          alphaFields: alphaFields,
          alphaFieldsValues: {}
        }
        fetchDistinctValues(c, alphaFields, 0, reply, socket)
      }
    })
  })
}

function fetchDistinctValues(collection, fields, index, reply, socket) {
  if (index < fields.length) {
    collection.distinct(fields[index], function (err, docs) {
      reply.alphaFieldsValues[fields[index]] = docs
      fetchDistinctValues(collection, fields, ++index, reply, socket)
    })
  } else {
    socket.emit('getFieldsFromCollectionReply', reply)
  }
}

Needless to say this feels very hackysh.. I've looked into some libraries or promises but they seem to offer solution for when one knows exactly how many queries have to be made. Please tell me there is a better way

\$\endgroup\$
1
\$\begingroup\$

I'm pretty sure Mongo has native Promise support, that in conjunction with async/await would really tidy this up

async getFieldsFromCollection (collection, socket) {
  try {
    const db = await MongoClient.connect(MAIN_DB);
    const c = db.collection(collection);
    const doc = await c.findOne({}, {});
    if (!doc) throw new Error('document is null');

    const alphaFields = Object.keys(doc).filter(k => /^[A-Z]/.test(doc[k]));
    const alphaFieldsValues = {};
    const subQueries = alphaFields.map(async (f) => {
      const docs = await c.distinct(f);
      alphaFieldsValues[f] = docs;
    });
    await Promise.all(subQueries);
    return socket.emit('getFieldsFromCollectionReply', {
      alphaFields,
      alphaFieldsValues
    });
  } catch (e) {
    return log(e);
  }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.