4

I want to get the info from a collection inside a feathers.js hook. How can i make the hook wait, until the mongodb call is complete? At the moment it sends the hook without waiting for the call to finish, i tried it with returns and promieses, but nothing worked

// Connection URL
const url = 'mongodb://localhost:27017/db';

//Use connect method to connect to the server

module.exports = function(hook) {
  MongoClient.connect(url, function(err, db) {
  const userCollection = db.collection('question');

  userCollection.count().then(function(N) {

    const R = Math.floor(Math.random() * N)

    const randomElement = userCollection.find().limit(1).skip(R).toArray(function(err, docs) {
    console.log("Found the following records");
    console.log(docs)
    //update hook with data from mongodb call
    hook.data.questionid = docs._id;
  });
  })
  })
};
Hydish
  • 53
  • 8

4 Answers4

4

The ideal way is to make the hook asynchronous and return a Promise that resolved with the hook object:

// Connection URL
const url = 'mongodb://localhost:27017/db';
const connection = new Promise((resolve, reject) => {
  MongoClient.connect(url, function(err, db) {
    if(err) {
      return reject(err);
    }

    resolve(db);
  });
});

module.exports = function(hook) {
  return connection.then(db => {
      const userCollection = db.collection('question');
      return userCollection.count().then(function(N) {
        const R = Math.floor(Math.random() * N);

        return new Promise((resolve, reject) => {
          userCollection.find().limit(1)
            .skip(R).toArray(function(err, docs) {
              if(err) {
                return reject(err);
              }

              hook.data.questionid = docs._id;

              resolve(hook);
            });
        });
      });
    });
  });
};
Daff
  • 43,734
  • 9
  • 106
  • 120
  • 1
    Thank you a lot, i think this solution is better, but i needed to move the `return new Promise((resolve, reject) => {` befor the `MongoClient.connect` – Hydish Sep 28 '16 at 07:59
1

The way to solve the thing is to use

module.exports = function(hook, next) {
    //insert your code
    userCollection.count().then(function(N) {
        const R = Math.floor(Math.random() * N)
        const randomElement = userCollection.find().limit(1).skip(R).toArray(function(err, docs) {
        console.log("Found the following records");
        hook.data.questionid = docs[0].email;
        //after all async calls, call next
        next();
      });

}
Hydish
  • 53
  • 8
0

You can use async.waterfall() of async module

const async=require('async');

async.waterfall([function(callback) {
  userCollection.count().then(function(N) {
    callback(null, N);
  });
}, function(err, N) {
  if (!err) {
    const R = Math.floor(Math.random() * N)
    const randomElement = userCollection.find().limit(1).skip(R).toArray(function(err, docs) {
      console.log("Found the following records");
      console.log(docs)
        //update hook with data from mongodb call
      hook.data.questionid = docs._id;
    });
  }
}])
abdulbarik
  • 6,101
  • 5
  • 38
  • 59
  • 1
    Thanks for your input, i tried it and found a couple of errors on my side, but it still doesnt update the hook befor it is written to the db. my current code is: https://codeshare.io/tmLfO – Hydish Sep 27 '16 at 10:10
  • does `hook` has `data` key? – abdulbarik Sep 27 '16 at 10:19
  • It just posts the data from the api call i am making(like this: https://codeshare.io/B4bjA) Then i made a befor hook with the code i shared with you in my last comment(i just use the user collection as a test). It executes the hook, but there only with the data from the frontend call. The console.logs i made in the are shown on the server(http://prntscr.com/cmw6km) and there are no error, it just doesnt add the questionid field. – Hydish Sep 27 '16 at 10:27
  • when i add a `console.log(hook.data)` after `hook.data.questionid = docs[0]._id;` it shows me the hook with the questionid `{ userid: '57e951d9a63af63948f32bef', questionid: 57e951d9a63af63948f32bef }` – Hydish Sep 27 '16 at 10:29
  • in your screenshot `docs` value is your _id. then why you are accessing it with docs._id? – abdulbarik Sep 27 '16 at 10:30
  • But i think for some reasons the hook still gets executed befor the async call finishes. – Hydish Sep 27 '16 at 10:30
  • Where is your hook object and from where you are getting it? – abdulbarik Sep 27 '16 at 10:32
  • i just use the same data to test i could use other data like this: `hook.data.questionid = docs[0].email;` and then the console.log gives me: `{ userid: '57e951d9a63af63948f32bef', questionid: '1234@123.com' }` – Hydish Sep 27 '16 at 10:33
  • from `module.exports = function(hook) {` its like this in the feathersjs wiki. When i do something like this it works: `module.exports = function(hook) { hook.data.questionid ="test"; };` – Hydish Sep 27 '16 at 10:34
  • There is some conflict in your hook object. if here data is updating then it must be appear wherever you access it – abdulbarik Sep 27 '16 at 10:35
  • Can you show from where you are calling this function? – abdulbarik Sep 27 '16 at 10:36
  • You have define `hook` as `const` and `const` variable is immutable. Can you change it with `var` and then try – abdulbarik Sep 27 '16 at 10:49
  • Nothing changed sadly – Hydish Sep 27 '16 at 10:56
  • You only can debug, where your hook value is being updated. So try to debug and make modification – abdulbarik Sep 27 '16 at 11:29
  • When i try to debug it, it updates the hook when i reach `MongoClient.connect(url, function(err, db) { ` – Hydish Sep 27 '16 at 12:25
  • ahhh, you should not write your connectivity code in waterfall. Declare it outside or at top of the file.No need to create connection every time – abdulbarik Sep 27 '16 at 12:28
  • Found a way to solve it, will update with an answer, thank you for your help – Hydish Sep 27 '16 at 12:40
0

The solution of Daff didn't work for me. I got the following error:

info: TypeError: fn.bind is not a function

The solution was: It seems that normal hooks can be registered with brackets, but this hook has to be registered without brackets. findEnemy

exports.before = {
  all: [
    auth.verifyToken(),
    auth.populateUser(),
    auth.restrictToAuthenticated()],
  find: [],
  get: [],
  create: [findEnemy],
  update: [],
  patch: [],
  remove: []
};

findEnemy() does not work. Maybe others run into the same issue. Could someone explain why?

  • Because the first three functions when passed in like that will actually run, and what do they return? A function that looks like function(hook) {} Now look at Daff's hook, he cut out the middleman, there is no function that returns function(hook) {}, it's that function straight away. Here's how his code will look if you wanted to use "findEnemy()" https://gist.github.com/medv/834cdb72ac1284bd19a47a3eb7cd582b – medv Feb 23 '17 at 19:21