sails won't populate user (undefined) - node.js

I try to associate a chat message with an user. But the populate function gives me an user undefined
//message.js
module.exports = {
attributes: {
user:{
model: 'User'
},
message:{
type:'string',
required:true
}
}
};
I save an entry of a message and try to populate the messages with the user afterward
module.exports = {
test: function(req, res){
var data = {
user : "5533b9f00fe12ff020c67225",
message : 'auto generated'
};
Message.create(data, function messageCreated(err, message){
if(err) return res.negotiate(err);
Message.find().populate('user').exec(function(err, messages){
if(err) return res.negotiate(err);
res.json(messages);
});
});
}
};
But it always gives me user undefined back, when I try to populate.
What I have tried so far:
get one user and populate all his messages: works!
find one user according the found Message's associated userId the step before, to ensure the user exists: works!
change the Assosiation so another Message instead of an User and than populate the Messages with the one "master message" : works!
I don't understand why it works with other models but not the User model

Related

How to save schema data to another collection from route function

Hello I have less idea in express route as I am new in backend with mongodb.
In the route below I am verifying email by resetting a schema value to true. Now I want to copy the new schema details to another existing collection. How can I do that ?
router.get('/:adminId/verifyAdmin',function(req,res){
console.log('request recieved');
Admin.findOne( {_id: req.params.adminId })
.exec()
.then(admin => {
const Thing = mongoose.model(admin.companyName);
const emailTokenn = req.query.id;
//console.log(emailTokenn);
Thing.updateOne( { emailResetTokenn: emailTokenn },{ $set: { verified: true }},(err) =>{
if(!err){
return res.redirect('https://localhost:3000/fw18/index.html');
}
else{
throw err;
}
});
});
});
Here I want to pass/copy/save Thingcollection details to existing collection name users in my db.
EDIT:- Tried this but getting error export :- const User = mongoose.model('User');
Thing.updateOne( { emailResetTokenn: emailTokenn },{ $set: { verified: true }},(err) =>{
if(!err){
//add Thing Schema to Users collection
Thing = mongoose.model(admin);
var copy = mongoose.model('admin', admin,'User');
admin.save(function(err){});
return res.redirect('https://s3.ap-south-1.amazonaws.com/fw18/index.html');
}
Error:-
MissingSchemaError: Schema hasn't been registered for model correct me .
Http is a stateless protocol. To maintain state of the application you can use
1) session
2) cookies and
3) query string.
On your case, you can handle using session.
Store information to the session and get stored information from different routes.

Post same objectID in to different table

I'm trying to post a data in my user then at the same time, post the _id of my user as a reference id in my donation table.
After I posted my data in the users table like this:
var User = require('../models/user');
var Blooddonation = require('../models/blooddonation');
router.post('/createBlooduser',function(req, res) {
var user = new User();
user.user_lastname = req.body.user_lastname;
user.status= "initial";
user.save(function(err) {});
});
How can I get the _id of the posted data and make a reference id in my donation table? Something like this:
**users.json**
{
_id:ObjectId("5c7e591eee959272cc5768cb"),
user_lastname:"Jhonson",
status:"initial"
}
**blooddonations.json**
{
donor_id:ObjectId("5c7e591eee959272cc5768cb")
}
The _id property is actually created as soon as you create new instance with a statement like new User(). So you can actually access that value before it's even stored in the collection, or at any time after instance creation really:
router.post('/createBlooduser',function(req, res) {
var user = new User();
user.user_lastname = req.body.user_lastname;
user.status= "initial";
user.save(function(err) {
if (err) throw err; // or really handle better
// You can also just create() rather than new Blooddonation({ donor_id: user._id })
Blooddonation.create({ donor_id: user._id }, function(err, donor) {
// check for errors and/or respond
})
});
});
Of if you might just want access to other properties that might "default on save", then you can access in the callback from save() or create():
router.post('/createBlooduser',function(req, res) {
User.create({
user_lastname: req.body.user_lastname;
status: "initial"
}, function(err, user) { // this time we pass from the callback
if (err) throw err; // or really handle better
Blooddonation.create({ donor_id: user._id }, function(err, donor) {
// check for errors and/or respond
});
});
});

Proper way to add a friend route in node.js and mongoose?

I'm planning to to create a route where a user could add another user as his/her friend, so that they could chat to each other once they are friends.
So basically once User A has sent a request to User B, User B will get a live notification about the request via socket.io
The problem right now is that, I couldn't come up with my own solution on how to implement the above scenario, from what I know, I should create two routes GET and POST
I'm using mongoose for database query, insert , update and delete
Here's my code
// GET route for getting the user's information -- Simple route
router.get('/users/:facebook_name', function(req, res) {
User.findOne(facebook_name, function(err, user) {
if (!user) {
res.json({message: "Couldn't find a user by that name"});
return;
}
res.json(user);
});
});
// POST route for adding a friend
router.post('/friendships/create/:facebook_name', ensureAuthenticated, function(req, res) {
// What should i put in this route to make the adding a friend feature works?
User.findOneAndUpdate(facebook_name, function(err, user) {
if (user) {
res.json({message: "You already send a friend request to that person"});
return;
}
// Send a live notification to the other user
socket.emit('sending request', {message: "added you as a friend"});
});
});
user Schema code -- Not really sure about this one either
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var UserSchema = new Schema({
friends: [{ type: Schema.Types.ObjectId, ref: 'User'}],
facebook: {
id: String,
token: String,
// email: String,
displayName: String,
photo: String
}
});
// Should I even create this schema?
var FriendsRequest = new Schema({
madeBy: [{ type: Schema.Types.ObjectId, ref: 'User'}],
})
module.exports = mongoose.model('User', UserSchema);
module.exports = mongoose.model('FriendsRequest', FriendsRequest);
I'm not entirely honest with you guys, in the POST route, i have no freaking idea on how to write the logic, because I'm really confuse right now, how the User B gonna get the live request notification? Should i create another route for that?
This is my problem when it comes to building slightly complex apps , i just couldn't come up with a good logic on how to do a certain feature even though it looks pretty easy. I've been stuck in this problem for almost 4 hours, browsing and reading the net, but I believe SO is the only place for me to find a clue on how to do something.
Thank you.
What you can do is create socket for each facebookName(if unique).
On Client Side:
socket.on('connection', function (data) {
socket.emit('setFacebookName', facebookName); });
}
Server saves each socket with facebookName:
socket.on('setFacebookName', function (facebookName) {
users[facebookName]=socket;
});
Now, when user sends chat request to that user in this request
// POST route for adding a friend
router.post('/friendships/create/:facebook_name', ensureAuthenticated, function(req, res) {
// What should i put in this route to make the adding a friend feature works?
User.findOneAndUpdate(facebook_name, function(err, user) {
if (user) {
res.json({message: "You already send a friend request to that person"});
return;
}
// Send a live notification to the other user
sendLiveNotification(facebook_name);
});
});
function sendLiveNotification(facebookName){
socket.on('send notification', function (facebookName) {
users[facebookName].emit('sending request', "has sent friend request");
});
}
You're trying to get a two step process, so you will need at least two calls where one is a request from the requester, and the other is the decision whether or not to allow that request from the requestee. You can handle your callback for the first function utilizing a Boolean where if it's a new request the user could be prompted with a popup on the client.
A good purpose of Mongoose is the extensions to the Schema that you can make, so here I'm adding two functions: one from the requester requesting requestee's friendship, and the other the decision of the requestee
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var UserSchema = new Schema({
friendsAccepted: [{ type: Schema.Types.ObjectId, ref: 'User'}],
friendsRequested: [{ type: Schema.Types.ObjectId, ref: 'User'}],
friendsPending: [{ type: Schema.Types.ObjectId, ref: 'User'}],
friendsRejected: [{ type: Schema.Types.ObjectId, ref: 'User'}],
facebook: {
id: String,
token: String,
// email: String,
displayName: String,
photo: String
}
});
UserSchema.statics.requesterInitiatedRequestForFriendship = function(requesterID, requesteeID, cb) {
mongoose.model('UserSchema').findOne({_id: requesterID}).exec(function(err, requester) {
if (err) return cb(err);
mongoose.model('UserSchema').findOne({_id: requesteeID}).exec(function(err, requestee) {
if (err) return cb(err);
if (requestee.friendsAccepted(requesterID) === -1 &&
requestee.friendsRequested(requesterID) === -1 &&
requestee.friendsPending(requesterID) === -1 &&
requestee.friendsRejected(requesterID) === -1) {
requestee.friendsPending.push(requesterID);
requester.friendsRequested.push(requesterID);
requestee.save();
requester.save();
cb(null, true);
} else {
cb(null, false);
};
});
});
};
UserSchema.statics.requesteeDecidedOnFriendship = function(requesterID, requesteeID, allowed, cb) {
mongoose.model('UserSchema').findOne({_id: requesterID}).exec(function(err, requester) {
if (err) return cb(err);
mongoose.model('UserSchema').findOne({_id: requesteeID}).exec(function(err, requestee) {
if (err) return cb(err);
if ((requestee.friendsAccepted(requesterID) === -1 &&
requestee.friendsRequested(requesterID) === -1 &&
requestee.friendsPending(requesterID) > -1 &&
requestee.friendsRejected(requesterID) === -1) &&
requester.friendsRequested(requesteeID) > -1) {
requestee.friendsPending.forEach(function(uid, idx) {
if (uid === requesterID) {
requestee.friendsPending.splice(idx, 1);
return;
};
});
requester.friendsRequested.forEach(function(uid, idx) {
if (uid === requesteeID) {
requester.friendsRequested.splice(idx, 1);
return;
};
});
if (allowed) {
requestee.friendsAccepted.push(requesterID);
requester.friendsAccepted.push(requesteeID);
} else {
requestee.friendsRejected.push(requesterID);
requester.friendsRejected.push(requesteeID);
}
requestee.save();
requester.save();
};
cb(null);
});
});
}
module.exports = mongoose.model('User', UserSchema);
So a couple things happening:
hasn't been tested
it's not DRY
it's limited without an additional Friendship Schema
With a Friendship Schema, you can define levels of rejection (e.g. "not at this time", etc.), you can more well flush out details and granular control for the changing behavior of friendships. In the above, you can see that once you're rejected, it's pretty fatalistic in that it's determined at no time you shall become friends! So to get more of that type of behavior, I'd definitely go with a Friendship Schema with it's statics and methods flushed out, as should be users.

How to check values against the DB when using the pre-save hook?

On a User schema, I'd like to check if the specified email already exists for the specified shop, before saving.
var UserSchema = new Schema({
_shop: {
type: Schema.Types.ObjectId,
ref: 'Shop',
required: true
},
email: String,
//...
});
UserSchema.pre('save', function(next) {
if (!this.isNew) return next();
// How to do use the static method isThatEmailFreeForThisShop here?
});
UserSchema.statics.isThatEmailFreeForThisShop = function(email, shop_id, cb) {
this.find({email: email, _shop: shop_id}, function(err, users) {
// ...
});
});
There could be different users with the same email as long as they are from different shops.
I do not know how to use the static method in the pre-save hook...
Thanks!
You've created a User Model instance somewhere (I'll call it User):
var User = mongoose.model('user', UserSchema);
So, the isThatEmailFreeForThisShop function is available on the User model:
User.isThatEmailFreeForThisShop(...)
From your save hook:
UserSchema.pre('save', function(next) {
if (!this.isNew) return next();
User.isThatEmailFreeForThisShop(this.email, this._shop,
function(err, result) {
if (result) { // found
// do something
return next({ error: "duplicate found" });
}
return next();
});
});
You may also want to switch to using the pre-validate rather than save.
I'd expect in your function, isThatEmailFreeForThisShop that you'd call the cb parameter when the results have been "found".
You probably would use findOne (reference) rather than find. Given that there's still a race condition, you'd want to add an index as a compound index email and shop_id and set the unique attribute to true to prevent duplicates from sneaking in (then, you'll need to handle the fact that a save on a model instance may throw an error.)
UserSchema.statics.isThatEmailFreeForThisShop = function(email, shop_id, cb) {
this.findOne({email: email, _shop: shop_id}, function(err, user) {
// ...
cb(err, user != null);
});
});

Compound JS Relationship Access

I have defined 2 schema objects as below (for use in a mongodb)
var User = describe('User', function () {
property('name', String);
property('email', String);
property('password', String);
set('restPath', pathTo.users);
});
var Message = describe('Message', function () {
property('userId', String, { index : true });
property('content', String);
property('timesent', Date, { default : Date });
property('channelid', String);
set('restPath', pathTo.messages);
});
Message.belongsTo(User, {as: 'author', foreignKey: 'userId'});
User.hasMany(Message, {as: 'messages', foreignKey: 'userId'});
But I am unable to access the related messages object:
action(function show() {
this.title = 'User show';
var that = this;
this.user.messages.build({content:"bob"}).save(function(){
that.user.messages(function(err,message){
console.log('Messages:');
console.log(message);
});
});
// ... snip ...
}
});
Despite a new message being added to the message collection the array of messages is always empty.
I ran db.Message.find({userId:'517240bedd994bef27000001'}) through the mongo shell and that displayed the messages as you would expect, so I am begining to wonder if there is an issue with the mongo adapter.
One to Many relationship in CompoundJS Shows a similar issue (I think).
As far as I can work out from the docs, this should work. What am I doing wrong?
EDIT:
After applying the changes to my schema as suggested by Anatoliy I dropped my mongo database and updated npm but then when I tried to create a new user I got the below:
Express
500 TypeError: Object #<Object> has no method 'trigger' in users controller during "create" action
at Object.AbstractClass._initProperties (/mnt/share/chatApp2/node_modules/jugglingdb/lib/model.js:123:10)
at Object.AbstractClass (/mnt/share/chatApp2/node_modules/jugglingdb/lib/model.js:31:10)
at Object.ModelConstructor (/mnt/share/chatApp2/node_modules/jugglingdb/lib/schema.js:193:23)
at Function.AbstractClass.create (/mnt/share/chatApp2/node_modules/jugglingdb/lib/model.js:222:15)
at Object.create (eval at (/mnt/share/chatApp2/node_modules/compound/node_modules/kontroller/lib/base.js:157:17), :16:10)....
EDIT2:
Create action:
action(function create() {
User.create(req.body.User, function (err, user) {
respondTo(function (format) {
format.json(function () {
if (err) {
send({code: 500, error: user && user.errors || err});
} else {
send({code: 200, data: user.toObject()});
}
});
format.html(function () {
if (err) {
flash('error', 'User can not be created');
render('new', {
user: user,
title: 'New user'
});
} else {
flash('info', 'User created');
redirect(path_to.users);
}
});
});
});
});
It's an issue with ObjectID. In your schema code:
property('userId', String, { index : true });
So userId is string, but when you call user.messages user.id used (and it's an ObjectID).
As a solution just remove this line from your schema definition.
P.S. in your case you can define relations as:
Message.belongsTo('author', {model: User, foreignKey: 'userId'});
User.hasMany('messages');

Resources