How to create nested requests with sequelize between two tables - javascript

I have two tables in a sequelize backend I am building. The first table is groups and the second table is members. I want to:
In the end, I want to submit a an api request that contains a users Id. It will then grab all rthe records from the members table and for each record, grab the group which is referenced as a foreign key in the members table. I then want to return the group records to the frontend.
Is there a way to grab the foreign key records directly through the foreign key or do I need to make two requests?
Here is the code I have:
router:
router.route('/user_groups/:userId')
.get(memberController.getUserMember)
Controller:
getUserMember: (req, res) => {
let group_list = [];
let user_id = req.params.userId
Member.findAll({ where: { userId: user_id } })
.then((response) => {
for(let i = 0; i < response.length; i++){
Group.findByPk(response[i]['groupId'])
.then((group) => {
console.log(JSON.stringify(group))
group_list.push(group)
})
.catch((err) => {
console.log('Getting Group by Id error: ' + JSON.stringify(err))
})
}
console.log(group_list)
res.status(200).send(data)
})
.catch((err) => {
console.log('Getting member by Id error: ' + JSON.stringify(err))
})
},
the first request gets all of the member records containing the userId
the second request within the then function will cycle through the members and grab the
groups for each record based on its Id through the foreign key
each of the records are that is returned from the group request is supposed to be stored in an array and then the array will be returned at the end....
The objects are not storing in the array and the array is not being returned. not sure what to do.
model member:
const Member = database.define(
"member",
{
id: {type: seq.INTEGER, primaryKey: true, autoIncrement: true},
balance: {type: seq.FLOAT(9, 2), allowNull: true, defaultValues: '0.00',
validate: {isFloat: true}
},
open_tabs: {type: seq.INTEGER, allowNull: false, defaultValues: '0',
validate: {isInt: true}
},
reference: {type: seq.STRING, allowNull: false,
validate: {isAlphanumeric: true}
},
admin: {type: seq.BOOLEAN, allowNull: false, defaultValues: false,
validate: {isIn: [['true', 'false']]}
},
active: {type: seq.BOOLEAN, allowNull: false, defaultValues: false,
validate: {isIn: [['true', 'false']]}
},
},
{
createdAt: seq.DATE,
updatedAt: seq.DATE,
}
)
Member.belongsTo(Group)
Member.belongsTo(User)
model group:
const Group = database.define(
"group",
{
id: {type: seq.INTEGER, primaryKey: true, autoIncrement: true},
name: {type: seq.STRING, allowNull: false,
validate: {}
},
description: {type: seq.TEXT, allowNull: true,
validate: {}
},
// icon: {type: seq.STRING, allowNull: false,
// validate: {}
// },
members: {type: seq.INTEGER, allowNull: false,
validate: {isInt: true}
},
reference: {type: seq.STRING, allowNull: false,
validate: {isAlphanumeric: true}
},
active: {type: seq.BOOLEAN, allowNull: false, defaultValues: false,
validate: {isIn: [['true', 'false']]}
},
},
{
createdAt: seq.DATE,
updatedAt: seq.DATE,
}
)
Group.belongsTo(User, {as: "Host"})

The basic problem is the loop will complete before all the Group.findByPk() requests are completed and once loop completes the array is sent....but the array is still empty!
Map an array of the Group.findByPk() promises instead and use Promise.all() to do the send() after all those promises have resolved
Something like:
getUserMember: (req, res) => {
let user_id = req.params.userId
Member.findAll({ where: { userId: user_id } })
.then((response) => {
const groupPromises = response.map(member=>{
return Group.findByPk(member['groupId']).then(groups=>{
member.groups = groups;
return member;
});
});
return Promise.all(groupPromises).then(data=>res.status(200).send(data))
})
.catch((err) => {
console.log('Something went wrong in one of the steps ' + JSON.stringify(err))
})
},
I'm assuming you want an array that includes the original member objects and have assigned the groups to each of those objects as property groups

Related

not seeing a sequelize model property that is required after seeding database

UPDATE
someone below suggested adding in a model id for shoutouts and I'm no longer getting the error, but now nothing is being saved to my database?
adding in the new information below:
I have a one to many relationship between users and shoutouts. Both models have email property,
I am trying to use a magic method to setup the shoutout. When I use user.createShoutout()
I can generate the shoutout, but the email property doesn't show up in the database.
const Sequelize = require('sequelize')
const db = require('../db')
const Shoutout = db.define('shoutout', {
//NEW
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
}, //OLD
name: {
type: Sequelize.STRING,
validate: {
notEmpty: true
},
email: {
type: Sequelize.STRING,
unique: true,
allowNull: false
}
},
message: {
type: Sequelize.TEXT,
allowNull: false
},
from: {
type: Sequelize.STRING
}
})
module.exports = Shoutout
associations:
User.hasMany(Shoutouts)
Shoutouts.belongsTo(User)
User.hasMany(Emails)
Emails.belongsTo(User)
when I use user.AddShoutout()
as follows:
let paramsObj = {
name: addEmail.firstName,
email:addEmail.email,
message: 'test msg',
userId: 3
}
//NEW
let id = 1;
const addInfo = await userThree.addShoutout(id,paramsObj)
//NEW
not getting the object error anymore, in fact not seeing any errors. But when I look in my shoutouts table nothing is getting added.
when I console.log addInfo
The user who tried to create the shoutout gets returned?
I need help with trying to get this user model magic method to generate a new shoutout!
Thanks for reading this, and any advice!
Your email field is nested within name field
const Sequelize = require('sequelize')
const db = require('../db')
const Shoutout = db.define('shoutout', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
}, //OLD
name: {
type: Sequelize.STRING,
validate: {
notEmpty: true
},
email: { # <----------------------------- nested too deep
type: Sequelize.STRING,
unique: true,
allowNull: false
}
},
message: {
type: Sequelize.TEXT,
allowNull: false
},
from: {
type: Sequelize.STRING
}
})
module.exports = Shoutout

save() does not change using mongoose

I have this code that is supposed to edit an object grabbed from a MongoDB. One of the fields is a map that has an array in it. I push to that array in the map. I log the object itself and it definitely got put into the array of the Map. It just doesn't update in the Database
Guild Model:
let {
Schema,
model
} = require('mongoose');
let Guild = new Schema({
guildID: {
type: String,
required: true
},
guildname: {
type: String,
required: true
},
prefix: {
type: String,
required: false,
default: "y!"
},
welcome_channel: {
id: {
type: String,
required: false,
default: null
},
message: {
type: String,
required: false,
default: "has joined the server!",
}
},
mod_log: {
type: String,
required: false,
default: null
},
spaces: {
type: Map,
required: false,
default: []
}
})
module.exports = model('Guild', Guild);
I grab the guild object in my code later on, it isn't null or undefined.
let fetch_guild = await Guild.findOne({"guildid": id});
// successfully grabs the object from the db
let temp_space = fetch_guild.spaces.get(args[0]) ? fetch_guild.spaces.get(args[0]) : null;
if(temp_space.admins.includes(message.mentions.users.first().id)) return message.reply("That person is already an admin!");
temp_space.admins.push(message.mentions.users.first().id);
console.log(fetch_guild.spaces)
Result of that console.log:
Map {
'test' => { name: 'test', admins: [] },
'test2' => { name: 'test2', admins: [] },
'test3' => { name: 'test3', admins: [ '500765481788112916' ] }
}
And I save it using:
await fetch_guild.save();
In the database, it is still an empty array
try using updateOne() of mongoose.
await fetch_guild.updateOne(fetch_guild);

Sequelize: inputValue cannot be an array or an object

I'm trying to create an instance with associations.
I am passing instances of multiple creates to pass down its values.
const newTemplate = await productTemplate.create({
friendlyName,
isActive: true,
productCode,
inscoCode,
}).then((productTemplate) => {
// Creating Form Input instance
formInput.create({
inputName
}).then((formInput) => {
// Creating Form Input Values Instance
formInputValue.create({
formInputID: formInput.id,
inputValue: {
[Op.in]: inputValues
}
}).then((formInputValue) => {
// Creating Product Template Instance
productTemplateInput.create({
productTemplateID: productTemplate.id,
formInputID: formInput.id,
isRequired: true
}).then((productTemplateInput) => {
// Creating Product Template Input Instance
productTemplateInputValue.create({
productTemplateInputID: productTemplateInput.id,
formInputValueID: formInputValue.id,
isDefault: true
})
})
})
})
});
Here is my formInputValue model.
module.exports = function(sequelize, DataTypes) {
return sequelize.define('formInputValue', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true,
field: 'id'
},
formInputID: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'formInputID'
},
inputValue: {
type: DataTypes.ARRAY(DataTypes.STRING),
allowNull: false,
field: 'inputValue'
}
}, {
tableName: 'formInputValue',
timestamps: false
});
};
I would like to create this instance but stops running when I try to add an array of values to inputValues. Any soutions?
Why do you have a query operator [Op.in] in your create?
formInputValue.create({
formInputID: formInput.id,
inputValue: {
[Op.in]: inputValues
}
})
If you remove that, and your inputValues is an array of strings (as defined by your model) it should create them. See below with that operator removed.
formInputValue.create({
formInputID: formInput.id,
inputValue: inputValues
})

Associate in Sequelize not working as intended

I am trying to associate two tables in Sequelize but I am getting the SequelizeEagerLoadingError that one table is not associated to another despite trying all the available fixes on this platform.
I have two tables, User and Item.
User (user.js)
const User = dbconnection.sequelize.define('users', {
id: { type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true},
name: {
type: Sequelize.STRING(80),
allowNull: false
},
email: {
type: Sequelize.STRING(120),
allowNull: false,
unique: true
},
dob: {
type: Sequelize.DATEONLY,
allowNull: false
},
password: {
type: Sequelize.STRING(256),
allowNull: false
}
});
User.associate = models => {
User.hasMany(models.Item, { as: 'items',foreignKey: 'user_id' })
}
dbconnection.sequelize.sync({ force: false })
.then(() => {
//console.log('Table created!')
});
module.exports = {
User
};
Item (item.js)
const Item = dbconnection.sequelize.define('items', {
id: { type: Sequelize.INTEGER, unique: true, autoIncrement: true, primaryKey: true},
item: {
type: Sequelize.STRING(80),
allowNull: true
},
item_type: {
type: Sequelize.STRING(10),
allowNull: false
},
comment: {
type: Sequelize.STRING(1000),
allowNull: true
},
user_id: {
type: Sequelize.INTEGER,
allowNull: false,
references: { model: 'users', key: 'id' }
},
});
Item.associate = models => {
Item.belongsTo(models.User, { as: 'users',foreignKey: 'user_id' })
}
dbconnection.sequelize.sync({ force: false })
.then(() => {
// console.log('Table created!')
})
});
module.exports = {
Item
};
User hasMany(Item) while Item belongsTo(User) as shown above.
However, when I make a query to the Item table (as below),
const usersdb = require('./userdb')
const itemsdb = require('./itemdb')
class ItemsController {
static async getAllItems(req, res, next) {
try{
let allitems = await itemsdb.Item.findAll({
include: [{
model: usersdb.User
}]
})
return {items: allitems, status: true}
}
catch (e) {
return {items: e, status: false}
}
}
}
module.exports = ItemsController;
I get the SequelizeEagerLoadingError that "users is not associated to items!"
I have tried all the available fixes including this and this among others but to no success.
I have finally found a workaround. First, I dropped the tables and discarded the model definitions. Second, I generated migrations and models using the sequelize model:create --name ModelName --attributes columnName:columnType command. I then used the generated models to associate the two tables just as I had done earlier. Lastly, I ran the sequelize db:migrate command to create the tables and on running the query, it worked!
Earlier, I was creating the models manually. I was also creating the tables using the sequelize.sync({force: false/true}) command after loading the models.
User Model (user.js)
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
email: {
type: DataTypes(120),
allowNull: false,
unique: true
},
dob: {
type: DataTypes.DATEONLY,
allowNull: false
},
password: {
type: DataTypes.STRING(256),
allowNull: false
}
}, {});
User.associate = function(models) {
User.hasMany(models.Item, {as: 'Item', foreignKey: 'user_id'})
};
return User;
};
Item model (item.js)
'use strict';
module.exports = (sequelize, DataTypes) => {
const Item = sequelize.define('Item', {
item: {
type: DataTypes.STRING(80),
allowNull: true
},
item_type: {
type: DataTypes.STRING(10),
allowNull: false
},
comment: {
type: DataTypes.STRING(1000),
allowNull: true
},
user_id: {
type: DataTypes.INTEGER,
allowNull: false,
references: { model: 'User', key: 'id' }
}
}, {});
Item.associate = function(models) {
Item.belongsTo(models.User, { as: 'User',foreignKey: 'user_id' })
};
return Item;
};
Query (queryitem.js)
const Item = require('../models').Item
const User = require('../models').User
class ItemsController {
static async getAllItems() {
try{
let allitems = await Item.findAll({
include: [{
model: User,
as: 'User'
}]
})
return {items: allitems, status: true}
}
catch (e) {
return {items: e, status: false}
}
}
}
module.exports = ItemsController;

Sequelize WHERE NOT EXISTS()

I have a many to many relations that looks like this:
var Genres = db.define('Movie', {
name: {
type: Sequelize.STRING(100),
allowNull: false
},
description: {
type:Sequelize.STRING(),
allowNull: true
},
thumbnail: {
type: Sequelize.BLOB(),
allowNull: true
},
urlposter: {
type: Sequelize.STRING(245),
allowNull: true
}
});
var Users = db.define('User', {
username: {
type: Sequelize.STRING(25),
allowNull: false
},
password: {
type: Sequelize.STRING(25),
allowNull: false
}
});
Movies.belongsToMany(Users, {through: UM, foreignKey: 'Id_Movies'});
Users.belongsToMany(Movies, {through: UM, foreignKey: 'Id_Users'});
what I will do is return all Movies that have no link to a specific user
this is my SQL query i want to achive:
SELECT m.*
FROM Movies m
WHERE NOT EXISTS(SELECT NULL
FROM Users_Movies sc
WHERE sc.Id_Movies = m.id AND sc.Id_Users = 1)
This is the closest I've got but this just return all movies that have a link u user with ID 1
Movies.findAll({
include: [
// {
// model:Genres,
// through:{attributes:[]}
// },
{
model:Users,
through:{attributes:[]},
where: {id: 1},
required: true,
}],
})
.then(function(movies){
done(null, movies);
})
.catch(function(error){
done(error, null);
});
But I want to invert it.
right now my only option is to make 2 queries one with all movies and one with all movies that have a link and loop through the list and remove them from the first list.. this is not pretty code.
I know that I'm late on the matter, but I think that using the literal 'users.id IS NULL' in the query's 'where' will do the trick:
Movie.findAll({
where: sequelize.literal("users.id IS NULL"),
include: [
{
model: Users,
through: { attributes: [] }
}],
})
.then(function (movies) {
done(null, movies);
})
.catch(function (error) {
done(error, null);
});
This solutions is based on the following Github issue: https://github.com/sequelize/sequelize/issues/4099
Use rawQuery
const projects = await sequelize.query('SELECT * FROM projects', {
model: Projects,
mapToModel: true // pass true here if you have any mapped fields
});

Categories