0

i want use a function recursive to look if the usernames exist in mongodb with mongoose and nodejs.

I use the callback, but i don't understand why my function return undefined result. Could you help Me ?

Thanks ;)

var mongoose = require('mongoose');
var debug = require('debug')('gc:model:User');

var UserSchema = new Schema({

  username: {type: String, required: true, trim: true, index: {unique: true, dropDups: true}},
  email: {type: String, trim: true},

  role: {type: String, required: true, default: 'user'},

});

generateUsername = function (username, number) {
  'use strict';

  var i = 0;
  var usernames = [];
  usernames.push(username);

  while (i < number) {
    var count = parseInt(usernames[i].substr(-1));
    if (count >= 0) {
      count += 1;
    } else {
      count = 0;
    }
    usernames.push(usernames[i].substring(0, count === 0 ?     usernames[i].length : usernames[i].length - 1) + count);
    i++;
  }

  return usernames;
};


findUniqueUsername = function (usernames, cb) {
  'use strict';
  if (usernames.length === 0) {
    return cb(null);
  }

  // If one of the username is undefined, go the next one
  if (typeof usernames[0] === 'undefined') {
    usernames.shift();
    findUniqueUsername(usernames);
  }

  _db.User.findOne({'username': usernames[0]}).exec(function (err, user) {
    if (err) return cb(err);

    if (user) {
      debug('Bu ! => ', usernames[0]);
      usernames.shift();
      findUniqueUsername(usernames);
    }
    else {
      debug('GooD ! => ', usernames[0]); // Value OK i have
      return usernames[0]; // Value Not OK undefined :(
    }
  });

};

var namestart = "jobs";

var usernameTries = generateUsername(namestart, 100);
var username = findUniqueUsername(usernameTries); // is undefined
ddtraceweb
  • 11
  • 3
  • Possible duplicate of [How do I return the response from an asynchronous call?](http://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – nem035 Jan 03 '17 at 20:31
  • which function call is returning undefined? are there any errors in your console? – Cruiser Jan 03 '17 at 20:31
  • probably `findUniqueUsername`, which contains an asynchronous call but the OP tries to read a result from it in a synchronous way, i.e. `var username = findUniqueUsername(usernameTries);`. Most likely should be closed as a duplicate of the above suggestion – nem035 Jan 03 '17 at 20:32

2 Answers2

0

The variable username will stay undefined, as you are preforming an async call.

If you start by generating 100 usernames, you can query all of them, and then filter out the non-existing. You can still use this recursive, by calling findUniqueUsernames again and passing the callback.

'use strict';

const mongoose = require('mongoose');

let UserSchema = new Schema({

  username: {
    type: String,
    required: true,
    trim: true,
    index: {
      unique: true,
      dropDups: true}
    },
  email: {
    type: String,
    trim: true
  },

  role: {
    type: String,
    required: true,
    default: 'user'
  },

});

var generateUsernames = function (username, number) {

  let usernames = new Array(number - 1)
  .fill(username)
  .map((username, index) => username + index);

  usernames.unshift(username);

  return usernames;
};

var findUniqueUsernames = function (usernames, callback) {
  if (usernames.length === 0) return callback(null, null);

  _db.User.find({
    username: {
      $in: usernames
    }
  })
  .select('username')
  .exec((err, results) => {
    if (err) return callback(err, null);
    else {
      let existingUsernames = results.map(r => r.username);
      let uniqueUsernames = usernames
        .filter(username => existingUsernames.indexOf(username) < 0);
      return callback(null, uniqueUsernames);
     // as an alternative, you could check here, 
     // if uniqueUsernames.length === 0
     // and use findUniqueUsernames recursive by passing the same callback again
     // return findUniqueUsernames(newSetOfPossibleUsernames, callback)
    }
  });
};

var namestart = 'jobs';

var usernameTries = generateUsernames(namestart, 100);

findUniqueUsernames(usernameTries, (err, uniqueUsernames) => {
  if (err) {
    console.log(err);
  } else {
    console.log(uniqueUsernames);
  }
});

In a more mongoosie world, I would make findUniqueUsernames at least a static method of UserSchema:

UserSchema.statics.findUniqueUsernames = function( ...

and replace _db.User.find( ... with

this.find( ...
Eydrian
  • 10,258
  • 3
  • 19
  • 29
0

This is async call and username will be undefined.

try

else {
  debug('GooD ! => ', usernames[0]); // Value OK i have
  return usernames[0]; // Value Not OK undefined :(
}

change to

else {
  debug('GooD ! => ', usernames[0]);
  return cb(null, usernames[0]);
}

and

var username = findUniqueUsername(usernameTries);

change to

var username;
findUniqueUsername(usernameTries, function(err, data){
  username = data;
});

Callback is a piece of executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at some convenient time. The invocation may be immediate as in a synchronous callback, or it might happen at a later time as in an asynchronous callback. (wikipedia). In your case it is called when findUniqueUsername function is done.

IARKI
  • 197
  • 1
  • 6