Unable to retrieve records from database - sails.js

I'm trying to retrieve a record with this:
var users = User.find({ username: "andy" }).then(function(users){
return users;
});
console.log(users);
return res.send(users);
But I get returned:
{
"isFulfilled": false,
"isRejected": false
}

Try writing your code like this, based on the waterline examples:
Users.find({username:'andy'}).exec(function(err, result) {
if (err) {
return res.send(500, {error: err});
}
return res.json(result);
});

The console.log() is outside the then() statement hence it's being executed before User.find() completes and all you get is an unfulfilled promise (not the query results). Try:
var users = User.find({ username: "andy" }).then(function(users){
console.log(users);
res.send(users);
return users;
});

Related

.find() returning nothing even when data exists

I've a mongo database with 3 collections for 3 different kind of users as User,Partner,Admin. Whenever a new user of any type signup I'm searching all three collections to check if username and email exist already. I'm trying to achieve this by calling a function as:
function checkAttribute(attr,val,callback){
User.find({attr: val},function(err,user){
if(err){
console.log(err);
}else{
if(user.length === 0){
Partner.find({attr: val},function(err,partner){
if(err){
console.log(err);
}else{
if(partner.length === 0){
Admin.find({attr: val},function(err,admin){
if(err){
console.log(err);
}else{
if(admin.length === 0){
return callback(null,true);
}else{
return callback(null,false);
}
}
});
}else{
return callback(null,false);
}
}
});
}else{
return callback(null,false);
}
}
});
};
Calling function line:
checkAttribute("username",newUser.username,function(error,response){
.......
});
But this is not working as it returns true always even when users with passed username/email exists already. I am unable to find the problem. Any one knows why this is happening?
Thanks in advance.
Since you are passing in the attribute as a variable in the function parameters, the query document
{ attr: val } is an object with the key "attr", not the dynamic attribute you pass in.
To fix this, you need to use computed property names in your query object as
{ [attr]: val }
Also, the function can use async/await pattern to be more readable and for the purpose of finding if a document exist findOne does the job so
well as it returns a document if it exists and null otherwise.
So your function can be refactored as
async function checkAttribute(attr, val, callback) {
try {
const query = { [attr]: val }
const user = await User.findOne(query).exec()
const partner = await Partner.findOne(query).exec()
const admin = await Admin.findOne(query).exec()
const found = (user || partner || admin) ? true: false
return callback(null, found)
} catch (err) {
console.error(err)
return callback(err, null)
}
};
attr: in your queries will search for a db field called attr. If you want to use the function parameter attr, use [attr]: as the key.
Example:
attr = 'username'
User.find({ [attr]: val }, function (err, user) {
if (err) {
console.log(err);
}
})
This is a feature available since ES6 so should work fine. See the docs here for more info

How to update a model with Sails JS

Ok, I've got the following in one of my controllers:
User.find({email: 'email#example.com'}).then(function (user) {
user[0].field = 'new_value';
user[0].field_2 = 'new_value';
console.log(user[0], 'before saving');
user[0].save();
console.log(user[0], 'after saving');
});
If I console user[0] at this stage I can see the updated fields. However the changes were not saved to the db. If I do the following:
User.find({email: 'email#example.com'}).then(function (user) {
user[0].field = 'new_value';
user[0].field_2 = 'new_value';
user[0].save();
User.find(user[0].id).then(function (updateduser) {
console.log(updateduser[0])
});
});
The updateduser does not have the updated fields... Why is that? How can should I proceed in this case?
Actually
user[0].save();
will return a promise like you have done for User.find().then();
user[0].save() is an asynchronous call so the next call to find the user will run even though the user[0] is not updated in the database.
so place the second find command inside the then of save() function and you will get the updated user.
user[0].save().then(function(err){
User.find(user[0].id).then(function (updateduser) {
console.log(updateduser[0])
});
}))
Why you not use updated() method?
User.find({ email: 'email#example.com' })
.then(function(user) {
if (!user) return res.notFound();
User.update({ eamil: 'eamil#example.com' }, {
field: 'new_value',
field_2: 'new_value'
})
.then(function(updated_user) {
console.log(updated_user);
return res.ok();
})
.catch(function(err) {
sails.log.error(err);
return res.serverError();
});
})
.catch(function(err) {
sails.log.error(err);
return res.serverError();
});
First of all, you want to update only one user data because you are using user[0](I think).
So it is easy to use findOne().
Simple code
User
.findOne({email: 'email#example.com'})
.exec(function(err,user){
if(err || !user) {
//handle here
}
else {
user.key1 = 'new_value';
user.key2 = 'new_value';
user.save(function(err){
if(err){
//handle error
}
console.log('updatedUser',user)
})
}
})
Thank you.

Rollback Transaction for Multiple Models in Waterline ODM in Sails (For Mongo DB )

I am using Sails and Waterline ORM with Mongo Database .
I have two models User and Profile with One to One relationship.
Below is the code I've written for Transaction with Rollback logic. I think there can be a much better logic than this as the current logic is very clumsy.
Questions :
Does Waterline or Sails provide any functionality for Rollback purpose?
Is there any better way of doing this ?
User.create(newUser).then(function (data) {
var newProfile = {
displayName: data.name,
email: data.email,
user: data._id
}
return Profile.create(newProfile);
}).then(function (profileData) {
sails.log.info("Profile Data " + JSON.stringify(profileData));
// Update the user with Profile Info
User.update(newUser._id, {profile: profileData._id}).then(function (updatedUser) {
return updatedUser;
}, function (err) {
// TODO Rollback logic if the User Updation Fails
})
}, function (err) {
sails.log.error("Failed to Create Profile for the User . Deleting the created User");
var criteria = {
email: data.email
}
User.destroy(criteria).then(function (user) {
sails.log.error("Deleted the Created User " + JSON.stringify(user));
throw new Error("ERROR CREATING User");
}, function (err) {
sails.log.error("ERROR DELETING USER");
throw new Error("ERROR DELETING USER", err);
})
});
To question 1 : no.
I would be tempted to do something like this:
async.waterfall([
function(callback){
User.create(newUser).then(function(data){
callback(null, data);
})
.catch(function(err){
callback(err, null, null); // don't need to rollback anything
})
},
function(data, callback){
Profile.create({
displayName: data.name,
email: data.email,
user: data._id
})
.then(function(profile){
callback(null, data, profile)
})
.create(function(err){
callback(err, data._id, null); // only need user's id
})
},
function(userData, profileData, callback){
User.update(userData._id, {profile: profileData._id})
.then(function(updatedUser){
callback(null, updatedUser);
})
.catch(function(err){
callback(err, userData._id, profileData._id); // can roll back both
})
}
], function(err, userData, profileData){
if(err) {
sails.log.error(err);
// do rollback, userData is user's ID, profileData is profile id
// if either one is undefined, then it doesn't exist
} else {
// userData is user object from the last update, return it!
}
})
I don't know if it is better, but it seems more readable, and it handles the errors for any of the three writing phases.

Sails inconsistent record creation

I use the following piece of code to create some records. If I provide incorrect values, say(password and passwordConfirmation does not match), then sometimes an institute record is created without a rollback and sometimes, rollback happens properly.
I would appreciate any help. Is there a better way to do this?
create: function (req, res) {
User.query("BEGIN TRANSACTION", function(result){
if(result) {
sails.log.info(result);
return res.serverError(result);
} else {
Institute.create({
name: req.param('name'),
shortName: req.param('shortName'),
phoneNumber: req.param('phoneNumber'),
subdomain: req.param('subdomain'),
managerEmail: req.param('email')
}, function(error, institute){
if(error) {
sails.log.info(error);
Institute.query("ROLLBACK", function(result) {
sails.log.info(result);
return res.badRequest(error);
});
} else {
User.create({
email: req.param('email'),
password: req.param('password'),
passwordConfirmation: req.param('passwordConfirmation'),
account: institute.id
}, function(error, user) {
if(error) {
sails.log.info(error);
Institute.query("ROLLBACK", function(result) {
sails.log.info(result);
return res.badRequest(error);
});
} else {
User.query("COMMIT", function(result){
sails.log.info(result);
return res.created(user);
});
}
});
}
});
}
});
}
You have a few of options, in no particular order.
1. Write a function that makes all the possible security checks before creation occurs, or use the beforeCreate life cycle call for your models.
For example, you could write a function verifyParams(params) that makes checks such as password comparison (and any other checks you want) for your user creation parameters before you create the institution, or you could just include these checks in your institution creation's beforeCreate method.
2. Delete if there is an error during your user creation
Delete theInstitute model instance in your error case of user creation:
...
User.create(..., function (error, user) {
if (error) {
Institute.destroy(institute.id, function instDestroyed(err) {
...
});
} else {
...
}
});
3. Create a user in your institute model's beforeCreate method.
module.exports = {
attributes: { ... },
beforeCreate: function(values, next) {
User.create(..., function (err, user) {
if (err) { return next(err) }
return next();
});
}
}
Personally, I use method #2 in my own apps.

Node Express Trouble returning object to view from query

I'm trying to:
Pass user's ID to a model query, that should return the user record from mongo.
Render this user object to my view so I can use its fields.
I'm not quite sure what's going wrong - the query function finds the correct user and I can console.dir to see all the fields. When I try to return it to my view with res.render I get nothing:
Here's my route:
app.get('/account', function(req, res) {
res.render('account', {title: 'Your Account', username: req.user.name, user:account.check(req.user.id) });
});
And my query function:
exports.check = function(userId) {
MongoClient.connect('mongodb://127.0.0.1:27017/test', function(err, db) {
if(err) throw err;
var collection = db.collection('test');
collection.findOne({userId : userId}, function(err, user) {
if (err) throw err;
console.log("account.check logging found user to console: ");
console.dir(user);
return user;
});
});
}
Again, this shows the proper entry
Finally my view:
<h1>Account Page</h1>
<hr>
<p>Why, Hello, there <b> {{username}} </b> </p><br/>
<p>You came from {{user.provider}}</p>
<p>{{user.lastConnected}}</p>
Go Home ~ Log Out
Any held would be most appreciated!
The MongoDB findOne function is asynchronous (it takes a callback as an argument). This means that your check function also needs to be asynchronous and take a callback as an argument (or return a promise).
Then you should call res.render() inside the callback you pass to query on success.
app.get('/account', function(req, res) {
account.check(req.user.id, function(error, user) {
if (error) {
// do something smart like res.status(500).end()
return;
}
res.render('account', {title: 'Your Account', username: req.user.name, user:user });
}
});
And the check function should be something like:
exports.check = function(userId, callback) {
MongoClient.connect('mongodb://127.0.0.1:27017/test', function(err, db) {
if(err) {
callback(err);
}
var collection = db.collection('test');
collection.findOne({userId : userId}, function(err, user) {
if(err) {
callback(err);
}
console.log("account.check logging found user to console: ");
console.dir(user);
callback(null, user);
});
});
}
Of course if you don't need to do any additional processing, you can just pass your the callback argument as the callback to collection.findOne(). I just kept it this way because it was closer to what you were doing initially.