How to make querys when tou have many to many relationships between models? - sails.js

i am trying to make a game. I need tu create a Match. I think the problem on this Way. The User create a Match. In a third table I save playerId and gameId. When another user join the match, I save again, playerId and gameId. Then, I make a query with player with gameId in common, and start the game.
first, One User may have many Games. second, One Match may have many Games. this is the Match model:
module.exports = {
attributes: {
name: {
type: 'string'
},
description: {
type: 'string'
},
game: {
collection: 'game',
via: 'gameId',
}
}
};
This is the User model:
var bcrypt = require('bcrypt');
module.exports = {
attributes: {
name: {
type:'string'
},
email: {
type: 'email',
required: true,
unique: true
},
password: {
type: 'string',
},
passwordConfirmation: {
type: 'string'
},
passwordEncrypted: {
type: 'string'
},
creator: {
collection: 'game',
via: 'playerId'
},
toJSON: function(){
var obj = this.toObject();
delete obj.password;
delete obj.passwordConfirmation;
delete obj._csrf;
return obj;
}
}, beforeCreate: function(values, next){
console.log("Acabo de entrar a eforeCreate");
var password = values.password;
var passwordConfirmation = values.passwordConfirmation;
if(!password || !passwordConfirmation || password != values.passwordConfirmation) {
var passwordDoesNotMatchError = [{
name: 'passwordDoesNotMatchError',
message: 'Las contraseñas deben coincidir'
}]
return next({
err: passwordDoesNotMatchError
});
}
require('bcrypt').hash(values.password, 10, function passwordEncrypted(err, EncryptedPassword){
values.EncryptedPassword = EncryptedPassword;
next();
});
}
};
This is the Game model:
module.exports = {
attributes: {
gameId: {
model: 'match'
},
playerId: {
model: 'user'
}
}
};
finally, this is my controller:
module.exports = {
createMatch: function(req,res){
var matchObj = {
name: req.param('name'),
description: req.param('description'),
}
Match.create(matchObj, function(err, match){
if(err){
console.log("el error fue: " + err);
return res.send(err);
} console.log("Entro en create");
return res.json(match);
})
var gameObj = {
gameId: 'aclaration: I dont know how do I get the match.id',
playerId: req.session.me
}
Game.create(gameObj,function(err,game){
console.log("entro a GameCreate");
if(err){
return res.send(err);
} return res.json(game);
})
}
};
I can create the Match, but Game.create send this error:
_http_outgoing.js:344 throw new Error('Can\'t set headers after they are sent.'); ^
Error: Can't set headers after they are sent.
Somebody can help me? probably, I have many errors. Thanks.

Couple of things here:
Having an explicit Game model is not required in Sails. It can manage it implicitly, unless you want to store more information than just gameId and userId. So, you can just do away with Game model.
Please refer for async programming: How do I return the response from an asynchronous call?
Below code should work for you. Hope it helps.
module.exports = {
createMatch: function(req, res) {
var matchObj = {
name: req.param('name'),
description: req.param('description'),
};
Match.create(matchObj, function(err, match) {
if (err) {
console.log("el error fue: " + err);
return res.send(err);
}
console.log("Entro en create");
var gameObj = {
gameId: match.id,
playerId: req.session.me
};
Game.create(gameObj, function(err, game) {
console.log("entro a GameCreate");
if (err) {
return res.send(err);
}
return res.json(game);
// return res.json(match);
});
});
}
};

Related

How to update a user profile which has a property which is a ref in MongooseJS?

I have a User schema which has reference to a profile schema.
const UserSchema = new Schema(
{
_id: mongoose.Schema.Types.ObjectId,
email: {
....email props...
},
password: {
...password props...
},
profile: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Profile",
}],
},
);
const Profile = new Schema({
_user: {
type: Schema.Types.ObjectId, ref: 'User'
},
'displayName': {
type: String,
default: ''
},
'interestedActivities': ['Ping-pong'], <---- This bad boy/girl is an array
'memberSince': { type: Date, default: Date.now }
}
)
I'd like to create a route which can update the User properties AND the Profile properties in one shot—with a caveat one of the properties on the Profile model is an array!!!
I tried this....
handler
.use(auth)
.put((req, res, next) => {
emailValidator(req, res, next, 'email');
},
async (req, res, next) => {
await connectDB()
const {
profileDisplayName,
profileEmail,
interestedActivities } = req.body;
const update = {
email: profileEmail,
'profile.$.displayName': profileDisplayName,
'profile.$.interestedActivities': interestedActivities
}
const filter = { _id: req.user.id };
const updatedUser = await User.findOneAndUpdate(filter, update, { new: true })
try {
console.log("updatedUser ", updatedUser);
if (updatedUser) {
return res.status(200).send({
updatedUser,
msg: `You have updated your profile, good job!`
});
}
} catch (error) {
errorHandler(error, res)
}
})
export default handler;
My response is:
Status Code: 500 Internal Server Error
Cast to ObjectId failed for value "[
{
id: 'ae925393-0935-45da-93cb-7db509aedf20',
name: 'interestedActivities',
value: []
}
]" (type Array) at path "profile.$"
Does anyone know how I could also afford for the property which is an array?
Thank you in advance!

Using output of one mongoose query for the input of another in express (async/await)

I am using express and mongoose to implement a server/db. I have a working route that gets all the games involving a player by playerID. I am now trying to implement one that can take username instead of playerID.
PLAYER_SCHEMA:
const mongoose = require('mongoose');
const PlayerSchema = mongoose.Schema( {
username: {
type:String,
required:true,
unique:true
},
date_registered: {
type: Date,
default:Date.now
}
});
module.exports = mongoose.model('Player', PlayerSchema);
GAME_SCHEMA:
const mongoose = require('mongoose');
const GameSchema = mongoose.Schema( {
player_1: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Player',
required: true
},
player_2: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Player',
required: true
},
status: {
type:String,
},
hero_1: {
type:String
},
hero_2: {
type:String
},
date_registered: {
type: Date,
default:Date.now
}
});
module.exports = mongoose.model('Game', GameSchema);
Here's what I have to query all games involving a player by playerId:
//GET GAMES INVOLVING PLAYER BY PLAYER_ID
router.get('/player/:playerId', async (req, res) => {
try {
const games = await Game.find({$or:[{ player_1: req.params.playerId }, { player_2: req.params.playerId}]});
console.log(games)
res.json(games);
// weird cuz doesn't throw error if not found, just returns empty list...
}
catch (err) {
res.json({ message: err });
}
});
The following outlines what I want to do, but it doesn't work, for I'm sure many reasons:
I am trying to get the userId from username first, then pass that into a query for the games.
//GET ALL GAMES ASSOCIATED WITH PLAYER BY USERNAME
router.get('/username/:username', async (req, res) => {
try {
const player = await Player.findOne({username:req.params.username});
console.log(player);
const games = Game.find({ $or:[{ player_1: player._id }, { player_2: player._id }] });
res.json(games);
}
catch (err) {
res.json({ message: err });
}
});
I've been reading about .populate(), promises, and waterfalls, but I'm new to this and would love some guidance!
Please try this :
//GET ALL GAMES ASSOCIATED WITH PLAYER BY USERNAME
router.get('/username/:username', async (req, res) => {
try {
const player = await Player.findOne({ username: req.params.username });
console.log(player);
/**
* .findOne() should return a document or null - if no match found..
*/
if (player) {
/**
* .find() will return empty [] only if it didn't find any matching docs but won't throw an error in successful DB operation
* (irrespective of whether docs matched or not, if op is successful then there will be no error).
*/
const games = await Game.find({ $or: [{ player_1: player._id }, { player_2: player._id }] }).lean();
(games.length) ? res.json(games) : res.json(`No games found for ${player._id}`);
} else {
res.json('No player found')
}
}
catch (err) {
res.json({ message: err });
}
});

How to access model attributes of different controller in Sails.js?

I have two controllers/models in my Sails project which is Clubs and Members. One club can have many members.
I try to put the id of 'Clubs' as a reference id (like a foreign key) in 'Members', so that I can retrieve the members of a club by using the reference id in 'Members'. I want to display the members according to their clubs at the homepage. However I could not find a way to pass the id value of 'Clubs' to the 'Members' controller. Below are some of the codes:
Clubs.js
module.exports = {
attributes: {
clubName: {
type: 'string',
},
clubDesc: {
type: 'string',
},
},
};
Members.js
module.exports = {
attributes: {
memberName: {
type: 'string',
},
clubId: {
type: 'string',
},
},
};
ClubsController.js
module.exports = {
list: function(req, res) {
Clubs.find({}).exec(function(err, club) {
if(err) {
res.send(500, {error: 'Database Error'});
}
res.view('pages/club-list', {clubs:club});
});
},
add: function(req, res) {
res.view('pages/club-add');
},
create: function(req, res) {
var clubName = req.body.clubName;
var clubDesc = req.body.clubDesc;
Clubs.create({clubName:clubName, clubDesc:clubDesc}).exec(function(err){
if(err) {
res.send(500, {error: 'Database Error'});
}
res.redirect('/clubs/list');
});
},
};
MembersController.js
module.exports = {
list: function(req, res) {
Members.find({}).exec(function(err, member) {
if(err) {
res.send(500, {error: 'Database Error'});
}
res.view('pages/member-list', {members:member});
});
},
add: function(req, res) {
res.view('pages/member-add');
},
create: function(req, res) {
var memberName = req.body.memberName;
var clubId = req.body.clubId;
Members.create({memberName:memberName,
clubId:clubId}).exec(function(err){
if(err) {
res.send(500, {error: 'Database Error'});
}
res.redirect('/members/list');
});
},
};
routes.js
module.exports.routes = {
'/': {
view: 'pages/homepage',
},
'/clubs/list': {
view: 'pages/club-list',
controller: 'Clubs',
action: 'list'
},
'/clubs/add': {
view: 'pages/club-add',
controller: 'Clubs',
action: 'add'
},
'/clubs/create': {
controller: 'Clubs',
action: 'create',
},
'/members/list': {
view: 'pages/member-list',
controller: 'Members',
action: 'list'
},
'/members/add': {
view: 'pages/member-add',
controller: 'Members',
action: 'add'
},
'/members/create': {
controller: 'Members',
action: 'create',
},
};
I'm really new to Sails.js here and I find that it's quite difficult to get resources on this matter. I'm not sure if I put this in a way that you guys could understand. But do ask for more details if you guys need more understanding. Thank you in advance.
If I understand correctly, you're looking to create a one-to-many association between Clubs and Members. Here's how it should look in Clubs.js, your 'many':
attributes: {
...
members: {
collection: 'Members',
via: 'club'
}
}
Then in Members.js, your 'many':
attributes: {
...
club: {
model: 'Clubs'
}
}
When you do Club.find(), the members key will be an array of member ids. If you do Club.find().populate('member'), the members key will be an array of fully-populated member objects.
Here are the docs on associations.
This isn't directly related to your question, buy since you are new to Sails, I am including a comment that will give you some advice on how to best use the framework. I hope it goes well!

Document is not being saved in database

In Article model, I want to save a list of categories:
var CategorySchema = new Schema({
name: String,
active: Boolean
});
var ArticleSchema = new Schema({
title: String,
description: String,
categories: [{ type : Schema.Types.ObjectId, ref: 'Category' }]
})
From the endpoint, I want to update article with categories. The update method looks like:
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
Article.findById(req.params.id, function (err, article) {
if (err) { return handleError(res, err); }
if(!article) { return res.send(404); }
var updated = _.merge(article, req.body);
updated.save(function (err, doc) {
console.log(doc);
if (err) { return handleError(res, err); }
return res.json(200, article);
});
});
};
Notice the console.log statement. In request.body, if I'm sending list of category ids, the console prints out an article with categories. However, when I look into the database, the category array is empty. Any pointer to how to solve this?

How to update embedded document in mongoose?

I've looked through the mongoose API, and many questions on SO and on the google group, and still can't figure out updating embedded documents.
I'm trying to update this particular userListings object with the contents of args.
for (var i = 0; i < req.user.userListings.length; i++) {
if (req.user.userListings[i].listingId == req.params.listingId) {
User.update({
_id: req.user._id,
'userListings._id': req.user.userListings[i]._id
}, {
'userListings.isRead': args.isRead,
'userListings.isFavorite': args.isFavorite,
'userListings.isArchived': args.isArchived
}, function(err, user) {
res.send(user);
});
}
}
Here are the schemas:
var userListingSchema = new mongoose.Schema({
listingId: ObjectId,
isRead: {
type: Boolean,
default: true
},
isFavorite: {
type: Boolean,
default: false
},
isArchived: {
type: Boolean,
default: false
}
});
var userSchema = new mongoose.Schema({
userListings: [userListingSchema]
});
This find also doesn't work, which is probably the first issue:
User.find({
'_id': req.user._id,
'userListings._id': req.user.userListings[i]._id
}, function(err, user) {
console.log(err ? err : user);
});
which returns:
{ stack: [Getter/Setter],
arguments: [ 'path', undefined ],
type: 'non_object_property_call',
message: [Getter/Setter] }
That should be the equivalent of this mongo client call:
db.users.find({'userListings._id': ObjectId("4e44850101fde3a3f3000002"), _id: ObjectId("4e4483912bb87f8ef2000212")})
Running:
mongoose v1.8.1
mongoose-auth v0.0.11
node v0.4.10
when you already have the user, you can just do something like this:
var listing = req.user.userListings.id(req.params.listingId);
listing.isRead = args.isRead;
listing.isFavorite = args.isFavorite;
listing.isArchived = args.isArchived;
req.user.save(function (err) {
// ...
});
as found here: http://mongoosejs.com/docs/subdocs.html
Finding a sub-document
Each document has an _id. DocumentArrays have a special id method for looking up a document by its _id.
var doc = parent.children.id(id);
* * warning * *
as #zach pointed out, you have to declare the sub-document's schema before the actual document 's schema to be able to use the id() method.
Is this just a mismatch on variables names?
You have user.userListings[i].listingId in the for loop but user.userListings[i]._id in the find.
Are you looking for listingId or _id?
You have to save the parent object, and markModified the nested document.
That´s the way we do it
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
Profile.findById(req.params.id, function (err, profile) {
if (err) { return handleError(res, err); }
if(!profile) { return res.send(404); }
var updated = _.merge(profile, req.body);
updated.markModified('NestedObj');
updated.save(function (err) {
if (err) { return handleError(res, err); }
return res.json(200, profile);
});
});
};