Working on mongo DB: need an explanation

Hallo,
I am currently working on a challenge and at the moment I need to implement a code doing this actions:
1-check if a username is in the database
2–is so, return “username exists”
3-if not add the username

I have created this function for searching the database
(User is the mongoose model)

const findUser = function(user) {
  User.findOne({username : user},function(err, userFound){
    if(err) return res.json({err: "search failed"});
    else return userFound;
     })
};

but if I test with a user already existing (I saw it in the db collection),

console.log(findUser("existing-user"));   //it prints "undefined"

what am i doing wrong?
in the findUser function, do i Have to return the UserFound or can I just return true/false?

thanks

Hey again :wave:

This is an issue of scoping. Think about:

  • What you want to return
  • Where you want to return
  • When you want to return

Examples:

  • Equivalent to what you currently have:
function num1() {
  return "I am from 1";
}
function findOne(callback) {
  // I am a simple example implementation of Mongoose's
  // Model.findOne function
  return callback();
}

function num2() {
  findOne(num1);
}

const resultOfNum2 = num2();
console.log(resultOfNum2);

Give that a run in your favourite text editor/browser.

  • What you want:
function num1() {
  return "I am from 1";
}
function findOne(callback) {
  // I am a simple example implementation of Mongoose's
  // Model.findOne function
  return callback();
}

function num2() {
  return findOne(num1); // Difference is here!
}

const resultOfNum2 = num2();
console.log(resultOfNum2);

Now, the above is an oversimplification, because it ignores the “when”.

Database logic (in-fact, most server logic too) is asynchronous. So, now you need to consider when a function should be async, and when you need to await a few YouTube tutorials on how common apps like this are built, and look at a few examples in decent documentation. I suspect Mongoose has some full examples.

Hope this helps

ok, I understood the code bove was lacking of the return statement and I modified the find() method accordingly. in this was

var findUser = function(user) {
  let result;
  User.findOne({username : user},function(err, data){
    if(err) return "search failed";
    else result=data;    
  })
  return result;
};

app.post('/api/users', function(req, res) {
    let searchResult = findUser(req.body.username);
    if(searchResult!=null) {
        return console.log("username already exists") ;
    } else {
         var newUser = new User({username : req.body.username});
          newUser.save(function (err, data){
               if(err) return res.json({err: "saving failed"});
               else return res.json({username: req.body.username, _id: data._id.toString()});      
    })
    }
})

however the test does not occur and I get the username logged in the db even if is already exists.
Before I used this findOne() function and I could console log the correct result that , however, is not considered in the app.post

var findUser = function(user) { 
  User.findOne({username : user},function(err, data){
    if(err) return "search failed";
    return console.log(data);    
  })  
};

I tested it just calling the function with a brand new user name
console.log(findUser(“newuser”); //null
console.log(findUser(“existingone”); //user object
and on the console it displayed “null”, that’s why in the app.post I used this condition to decided it to add a new user or if to display that user already exists

Again, you are dealing with asynchronous data. So, you cannot expect this to return anything but undefined:

var findUser = function(user) {
  let result; //1 - order line is executed
  User.findOne({username : user},function(err, data){
    if(err) return "search failed"; //3
    else result=data;    //4
  })
  return result; //2
};

You need to define asynchronous functions, and await their responses:

async function someAsyncFunc() {
  return await someAsyncDBCall();
}
console.log(await someAsyncFunc()); // Technically, await
// cannot be used outside of an async function, but this
// is here to show the idea.

Hope this helps

ok, so if I write

var findUser = function(user) { 
  User.findOne({username : user},function(err, data){
    if(err) return "search failed";
    else return data;    
  })  
};

won’t work to define a generic “search” function to be called in different occasions? Do i then need to define the search IN each particular route/use?

Correct. That function does nothing useful.

I am not sure I understand what you mean with this…

I like looking at examples. So, here is an example:

const findingTheDoc = new Promise((res, _) => res("I took time to find"));

async function myFindOneDataBaseExample(callback) {
  // I find a document in a database model,
  // and return it through a callback.
  const randomExampleDoc = { name: "Francesca" };
  // To pretend this function takes time looking for an actual
  //  document in a real database - not instant!
  const t = await findingTheDoc(); // Just takes time...
  return callback(null, randomExampleDoc);
}

async function myGetDocumentFunc(done) {
  return await myFindOneDataBaseExample((err, data) => {
    if (err) return //null so does not matter
    console.log(data);
    // DO something with the data!
    // Common: use a callback like 'done'
    // i.e. done(data);
    // OR, just return it, and use it within myGetDocumentFunc
    return data;
  });

// Within some other function (remember what I said about await outside a function...)
console.log(await myGetDocumentFunc());

This is a lot of abstract stuff, until you spend time writing the code :slightly_smiling_face:

yes, that was not very clarifying, I am sorry :frowning:
Unfortunately I have just lost ALL of my code (including the one that was working :frowning: )
it is ok to do it again but I was still stuck with that finding an item and then using the result of the finding.
If I may dare, if could you please fix “my” code so that for me it is just easier to make my way to try to understand. I mean, I understand that some functions take time but nothing of that was mentioned and the example for searching the DB was something like this

var findPersonByName = (personName, done) => {
   Model.find({user : user}, function(err, data) {
    if(err) return console.log(err);
   else done(null, data);
  }
}

i tried this , too but again with no success. Unfortunately I have not found a decent video on youtube (and I prefer reading rather than watching) so I am stuck because the “done” stuff has appeared here for the firt time.
so, for the moment I am in the app.post, got the parameter to find, written the findOne function, but still struggling with using the resulting data (null if the username does not exists

app.post('/api/users', function(req, res){
  let user=req.body.username;
  let userFound = User.findOne({username:user}, function(err, data){
    if(err) return res.send("error");
    else return data;
  })
  if (userFound==null) {
    let newUser = new User({username:user});
    newUser.save(function(err, data){
      if(err) return res.send("error in saving");
      else return res.json({username:user})
    })  
  }
}) 

eventually I used this and works, but I would understand the concepts you tried to explain me

app.post('/api/users', function(req, res){
  let user=req.body.username;
  let findUser = User.findOne({username:user}, function(err, data){
    if(err) return res.send("error");
    else if (data!=null) return res.send("exists")
    else {
      let newUser = new User({username:user});
      newUser.save(function(err, data){
      if(err) return res.send("error in saving");
      else return res.json({username:user})
    })  
    }
  })   
}) 

Hm… This is not something I particularly want to try teach any further, as it does presume a lot of prior knowledge (which you may very-well have, but I do not know what you know). So, best thing I can think is to give you some resources:

  1. First mention of await in a Mongoose.js context: Mongoose v5.12.12: API docs (in the last line of the example)
  2. For Mongoose.js specifically: Mongoose v5.12.12: Queries - sounds like going with async..await and promises is not the safest option - can cause unwanted behaviour)

So, databases are probably not the best place to be learning about asynchronous JS. I frequently use it with MongoDB (no Mongoose.js), but that is just because I prefer the granularity MongoDB offers.

This is the first lesson in the fCC curriculum teaching Promises: https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/es6/create-a-javascript-promise

Then, search Coding Train Async Await in your favourite search engine, and you will find some excellent content on the matter.


Lastly, what you have done with the callback within findOne is perfectly fine. There is not reason to assign it to findUser, but the use of the callback is what is common. Your previous method:

var findUser = function(user) {
  let result; //1 - order line is executed
  User.findOne({username : user},function(err, data){
    if(err) return "search failed"; //3
    else result=data;    //4
  })
  return result; //2
};

This involves something called side-effects - not an ideal method, in most cases. You can search if you want to know more on the matter.

One thing:

If err does not exist, is data not always not null?

I will DEFINITELY go back to the async and promise stuff: since I have not used it at all in my code experience I feel very awkward with it.

Regarding your last observation, and following one of our precedent conversation about error, I can see a logic for my code:
-an error is something going wrong in the searching process

  • otherwise we have some returned data that can be an user (if that user exist) or a null object if the user does not exist yet.
    Actually I came to the “Null” option because I console.log the result of an new username and I didn’t get an error but a Null.
    I have just tested it again and just with a if(err), else and and new user does not throw an error

btw, incredibly I finished the work… but I am struggling with the logs tests: I am scatching my ehad because the output is identical to the example page and I am not getting information from the developer tool , so far :frowning:

I believe it is because of date formatting in the output json object when I perform the from-to query. I find different options but either
1-I get the right format but all the optional query elements (from, to) appear even if they are not requested for example {"_id":“60b7c2fb978d3d0527b817f8”,“username”:“ranran”,“from”:“Invalid Date”,“to”:“2013”,“count”:4,“log”:[{“description”:“mini”,“duration”:144,“date”:“Tue Feb 21 2012”},{“description”:“ermi”,“duration”:44,“date”:“Tue Feb 21 2012”},{“description”:“arya”,“duration”:23,“date”:“Tue Feb 21 2012”},{“description”:“athos”,“duration”:12,“date”:“Tue Feb 21 2012”}]}
2-or i automatically get rid of the unrequested query elements but with uncorrect format, for example
{"_id":“60b7c2fb978d3d0527b817f8”,“username”:“ranran”,“to”:“2021-06-01”,“count”:4,“log”:[{“description”:“mini”,“duration”:144,“date”:“Tue Feb 21 2012”},{“description”:“ermi”,“duration”:44,“date”:“Tue Feb 21 2012”},{“description”:“arya”,“duration”:23,“date”:“Tue Feb 21 2012”},{“description”:“athos”,“duration”:12,“date”:“Tue Feb 21 2012”}]}

is there a way to accomplish that?

My bad. This is correct.

Blockquote
My bad. This is correct.

I know you were just testing me :stuck_out_tongue:

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.