Sequelize .save() on instance not working - node.js

I have a model defined as below. I then try to run the code below it to update the isTeamLead property of a retrieved instance but I get the error teamMember.save() is not a function.
const TeamMember = sequelize.define('teamMember', {
userId: Sequelize.INTEGER,
teamId: Sequelize.INTEGER,
slotNumber: Sequelize.INTEGER,
isTeamLead: Sequelize.BOOLEAN
});
Promise.all([db.models.TeamMember.findOne({ where: { $and: [{ userId: lead.id }, { teamId: id }] } })]).then((teamMember) => { teamMember.isTeamLead = true; teamMember.save() });

I was able to solve this by making sure I was operating on the instance. Above I was operating on the array not the actual teamMember instance.
Promise.all([db.models.TeamMember.findOne({ where: { $and: [{ userId: lead.id }, { teamId: id }] } })]).then((teamMember) => { teamMember[0].isTeamLead = true; teamMember[0].save() });

If you are using sequelize, you could do something like.
1) first find
2) if exist update
const model = await Model.findById(id);
if(!model) {
return res.status(400).json({ msg: 'Bad Request: Model not found' });
}
const updatedModel = await Model.update({
key: value,
)};

Related

API for updating user role using a id - Node

I am trying to create a node API and test it using Postman but I keep running into errors. I can't seem to get to update the userRole of my user table. I have tried console.log(req.body.userRole) to print the incoming field I want to update the user role but it is coming up as undefined or throwing an error relating to my Cors settings / unhandledPromiseRejection error when I remove the try catch loop.
In the code below I get a message that it is updated but nothing happens to the value of the table. The userRole is the 7th column of my table and it is spelt correctly. There is a foreign key attached to the userRole table that i'm not sure is causing the error.
It is likely an error in the code as i haven't many update functions working correctly yet.
exports.updateUser = async (req, res) => {
try{
const userID = req.params.userId;
//const role = req.body.userRole;
const user = await User.findByPk(userID);
//console.log("userRole", req.body.userRole)
if(!user){
// return a response to client
res.status(404).json({
message: "Not Found for updating a user with id = " + user,
user: "",
error: "404"
});
} else {
console.log("userRole", req.body.userRole)
// update new change to database
let updatedUser = {
userID: userID,
//userRole: role
}
let userRes = user.update(updatedUser, {
returning: true,
where: {userID: userID}
});
// return the response to client
if(!userRes) {
res.status(500).json({
message: "Error -> Can not update a user with username = " + req.params.userId,
error: "Can't Update",
});
}
res.status(200).json({
message: "Updated role successfully user = " + req.params.userId,
user: updatedUser,
});
}
} catch(error){
res.status(500).json({
message: "Error -> Can not update a customer with userName = " + req.params.userId,
error: error.message
});
}
}
model
module.exports = (sequelize, Sequelize) => {
const userLogin = sequelize.define("userLogins", {
userID: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false
},
username: {
type: Sequelize.TEXT
},
password: {
type: Sequelize.TEXT
},
userEmail: {
type: Sequelize.TEXT
},
userFirstName: {
type: Sequelize.TEXT
},
userSurname: {
type: Sequelize.TEXT
},
userRole: {
type: Sequelize.INTEGER
},
photos: {
type: Sequelize.TEXT,
}},
{
timestamps: false
})
userLogin.associate = function(models){
userLogin.userRole.hasOne(models.userRole, {foreignKey: 'userRoleID', as: 'userRoleID'})
};
return userLogin;
};
role model
module.exports = (sequelize, Sequelize) => {
const userRole = sequelize.define("userRoles", {
userRoleID: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
userRoleName: {
type: Sequelize.TEXT
}},
{
timestamps: false
},{});
return userRole;
};
example:
model: User
fields:
Id ( PrimaryKey )
RoleId ( Foreign Key )
model: Role
fields:
Id ( PrimaryKey )
Association in User model is like This (if a user just have a role):
User.belongsTo(models.Role);
If you change default scheme then you have to determine it:
model: User
fields:
Id ( PrimaryKey )
i_am_reference_iD ( Foreign Key to Role ID)
User.belongsTo(models.Role, {foreignKey: 'i_am_reference_iD'});
Sequelize has a complete documentation. You need to study it to avoid mistakes which they wasting too much time on dev time.
Also you need to clean your codes too.

Adding Attributes on a Join Table in Sequelize

I'm having trouble setting an attribute on a junction table.
I have a Many-to-Many association defined between two models UserModel and HangModel, through a custom table HangUsers.
const HangModel = rootRequire('/models/Hang');
const UserModel = rootRequire('/models/User');
const HangUsers = database.define('HangUsers', {
id: {
type: Sequelize.INTEGER(10).UNSIGNED,
primaryKey: true,
autoIncrement: true,
},
hangId: {
type: Sequelize.INTEGER(10).UNSIGNED,
references: {
model: HangModel,
key: 'id',
},
},
userId: {
type: Sequelize.INTEGER(10).UNSIGNED,
references: {
model: UserModel,
key: 'id',
},
},
rsvp: {
type: Sequelize.STRING,
allowNull: false,
validate: {
isIn: {
args: [ 'pending', 'joined' ],
msg: 'The rsvp provided is invalid',
},
},
},
});
UserModel.hasMany(HangUsers, { as: 'invitations' });
HangModel.hasMany(HangUsers, { as: 'invites' });
UserModel.belongsToMany(HangModel, { through: HangUsers });
HangModel.belongsToMany(UserModel, { through: HangUsers });
The through table has a column rsvp, that I'm trying to populate when I add users to a hang:
const hang = await HangModel.create();
await hang.addUser(user, { through: { rvsp: 'joined' } });
However, I'm getting an error:
AggregateError
at recursiveBulkCreate (/Users/sgarza62/ditto-app/api/node_modules/sequelize/lib/model.js:2600:17)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async Function.bulkCreate (/Users/sgarza62/ditto-app/api/node_modules/sequelize/lib/model.js:2824:12)
at async Promise.all (index 0)
at async BelongsToMany.add (/Users/sgarza62/ditto-app/api/node_modules/sequelize/lib/associations/belongs-to-many.js:740:30)
at async /Users/sgarza62/ditto-app/api/routes/hangs.js:121:3 {
name: 'AggregateError',
errors: [
BulkRecordError [SequelizeBulkRecordError]: notNull Violation: HangUsers.rsvp cannot be null
at /Users/sgarza62/ditto-app/api/node_modules/sequelize/lib/model.js:2594:25
at processTicksAndRejections (internal/process/task_queues.js:97:5) {
name: 'SequelizeBulkRecordError',
errors: [ValidationError],
record: [HangUsers]
}
]
}
When I allow null on the rsvp column, the HangUsers row is created, but the rsvp value is NULL.
It seems the { through: { rsvp: 'joined' } } parameter is being ignored.
I've done this all according to the BelongsToMany docs and the Advanced M:N Associations docs, where it says:
However, defining the model by ourselves has several advantages. We can, for example, define more columns on our through table:
const User_Profile = sequelize.define('User_Profile', {
selfGranted: DataTypes.BOOLEAN
}, { timestamps: false });
User.belongsToMany(Profile, { through: User_Profile });
Profile.belongsToMany(User, { through: User_Profile });
With this, we can now track an extra information at the through table,
namely the selfGranted boolean. For example, when calling the
user.addProfile() we can pass values for the extra columns using the
through option.
Example:
const amidala = await User.create({ username: 'p4dm3', points: 1000 });
const queen = await Profile.create({ name: 'Queen' });
await amidala.addProfile(queen, { through: { selfGranted: false } });
const result = await User.findOne({
where: { username: 'p4dm3' },
include: Profile
});
console.log(result);
Just a typo on the attribute name: 'rvsp' should be 'rsvp'.

Mongoose: how to only populate, sort and return a nested object?

I have a User schema, with a messages array. The message array is filled by conversations id and referenced to a Conversation schema.
I want to fetch all conversations from a user, sort them by unread and then most recent messages. Finally, I must only return an array of lastMessage object.
For the moment, I have only managed to populate the whole user object.
Here is the Conversation Schema:
const conversationSchema = new mongoose.Schema(
{
name: { type: String, required: true, unique: true },
messages: [{ message: { type: String }, authorId: { type: String } }],
lastMessage: {
authorId: { type: String },
snippet: { type: String },
read: { type: Boolean },
},
},
{ timestamps: true }
);
conversationSchema.index({ name: 1 });
module.exports = mongoose.model("Conversation", conversationSchema);
And here is my code:
router.get("/conversations", async (req, res) => {
try {
const { userId } = req.query;
const user = await User.findById({ _id: userId }).populate("messages");
.sort({ updatedAt: 1, "lastMessage.read": 1 });
return res.json({ messages: user.messages });
} catch (err) {
console.log("error", err);
return res.json({ errorType: "unread-messages-list" });
}
});
How to do this?

How to implement a follow system, return a list of followers and following users

In my application there are 4 features I need to implement:
A user can follow another user.
A user can unfollow another user.
A user can see a list of all of their followers.
A user can see a list of all whom they are following.
I believe I have implemented 1. and 2. correctly. I created a follow schema as you can see below in my follow.model and I have created follow.controller with two methods, to store (follow) and destroy (unfollow).
Now I want to to implement 3. and 4. I created two arrays in the user.model schema, one for following and one for followers. When I return the user in my user.controller, how do I populate the following and followers array? At the moment they are empty.
follow.model.js
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var FollowSchema = new Schema({
follower: {
type: Schema.Types.ObjectId,
ref: 'User'
},
followee: {
type: Schema.Types.ObjectId,
ref: 'User'
}
},
{
timestamps: {createdAt: 'created_at', updatedAt: 'updated_at'}
});
module.exports = mongoose.model('Follow', FollowSchema);
follow.controller.js
'use strict';
const User = require('../models/user.model');
const Follow = require('../models/follow.model');
class FollowController {
constructor() {
}
store(req, res) {
let follower = req.body.follower;
let followee = req.params.id;
let follow = new Follow({
follower: follower,
followee: followee,
});
follow.save(function (err) {
if (err) {
return res.status(404).json({
succes: false,
status: 404,
data: {},
message: "There was an error trying follow the user."
});
}
return res.status(200).json({
success: true,
status: 200,
data: follow,
message: 'Successfully followed user'
});
});
}
destroy(req, res) {
let follower = req.params.followerid;
let followee = req.params.id;
Follow.remove({ 'follower': follower, 'followee': followee }, (err, result) => {
if (err) {
return res.status(404).json({
success: false,
status: 404,
data: {},
message: "Error removing record"
});
}
return res.status(201).json({
success: true,
status: 201,
data: {},
message: "Successfully unfollowed user"
})
});
}
}
module.exports = FollowController;
user.model.js
let UserSchema = new Schema({
email: {
address: {
type: String,
lowercase: true,
//unique: true,
},
token: String,
verified: {
type: Boolean,
default: false,
},
},
password: {
type: String,
},
following: [{
type: Schema.Types.ObjectId, ref: 'Follow'
}],
followers: [{
type: Schema.Types.ObjectId, ref: 'Follow'
}],
{
timestamps: {createdAt: 'created_at', updatedAt: 'updated_at'}
});
user.controller.js
show(req, res) {
let id = req.params.id;
User.findOne({ '_id': id },
function (err, user) {
if (err) {
return res.json(err);
}
return res.json(user);
});
}
You just need to populate these fields:
User.findOne({ '_id': id }, (err, user) => {
if (err) return res.json(err);
return res.json(user);
}).populate([
{ path: 'following' },
{ path: 'followers' }
]);

Sequelize Many to Many failing with 'is not associated to' when trying to associate entries?

I am having a problem with my many to many configuration with Sequelize, where it complains that site_article_keyword is not associated to article_keyword. The code below represents a minimal test case to try to understand what I am doing wrong (I hoped to provide something smaller, but this is what I have). I am using bluebird for the Promise API.
const Sequelize = require('sequelize');
var sequelize = new Sequelize(undefined, undefined, undefined, {
dialect: 'sqlite',
storage: './mydatabase',
});
const SiteArticle = sequelize.define('site_article', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
ownerId: {
type: Sequelize.INTEGER,
field: 'owner_id'
},
title: Sequelize.STRING
// other fields omitted
}, {
timestamps: true
});
const ArticleKeyword = sequelize.define('article_keyword', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: Sequelize.STRING,
language: Sequelize.STRING
// other fields omitted
}, {
timestamps: true
});
const SiteArticleKeyword = sequelize.define('site_article_keyword', {
siteArticleId: {
type: Sequelize.INTEGER,
field: 'site_article_id',
references: {
model: SiteArticle,
key: 'id'
}
},
articleKeywordId: {
type: Sequelize.INTEGER,
field: 'article_keyword_id',
references: {
model: ArticleKeyword,
key: 'id'
}
}
// other fields omitted
}, {
timestamps: true
});
(ArticleKeyword).belongsToMany(
SiteArticle, { through: SiteArticleKeyword });
(SiteArticle).belongsToMany(
ArticleKeyword, { through: SiteArticleKeyword });
That's the model defined, now for trying to create the source and destination entries, that I then want to associate. The failure happens on the line where I call ArticleKeyword.findAll():
sequelize.sync({}).then(function() {
// populate some data here
let siteArticle;
SiteArticle.create({
ownerId: 1,
title: 'hello world'
}).then(function(entry) {
siteArticle = entry;
console.log('site article: ', JSON.stringify(entry, undefined, 2));
return ArticleKeyword.findOrCreate({
where: {
name: 'keyword1',
language: 'en'
}
});
}).spread(function(entry, success) {
console.log('article keyword: ', JSON.stringify(entry, undefined, 2));
return siteArticle.addArticle_keyword(entry);
}).spread(function(entry, success) {
console.log('site article keyword: ', JSON.stringify(entry, undefined, 2));
const siteArticleId = 1;
const language = 'en';
return ArticleKeyword.findAll({
where: {
language: language,
},
include: [{
model: SiteArticleKeyword,
where: {
siteArticleId: siteArticleId
}
}]
});
}).then(function(articleKeywords) {
if (articleKeywords) {
console.log('entries: ', JSON.stringify(articleKeywords, undefined, 2));
} else {
console.log('entries: ', 'none');
}
}).catch(function(error) {
console.log('ERROR: ', error);
}.bind(this));
}).catch(function(error) {
console.log(error);
});
I am basing my code on the 'Mixin BelongsToMany' example in the Sequelize documentation.
Can anyone suggest what I am doing wrong?
The issue turns out that the reason site_article_keyword is not associated is because it is the association! With that in mind the code becomes:
return ArticleKeyword.findAll({
where: {
language: language,
},
include: [{
model: SiteArticle,
as: 'SiteArticle',
siteArticleId: siteArticleId
}]
});
BTW one minor tweak to my code, is in the inclusion of 'as' to the belongsToMany:
ArticleKeyword.belongsToMany(
SiteArticle,
{ through: SiteArticleKeyword, as: 'SiteArticle' }
);
SiteArticle.belongsToMany(
ArticleKeyword,
{ through: SiteArticleKeyword, as: 'ArticleKeyword' }
);
This allows for addArticleKeyword() instead of addArticle_Keyword().
In through relationships the following should work
ArticleKeyword.findAll({
include: [{
model: SiteArticle,
through: {
attributes: ['createdAt', 'startedAt', 'finishedAt'],
where: {
siteArticleId: siteArticleId
}
}
}]
});

Resources