How to query many to many relationship sequelize? - javascript

Tables have many to many relationship, junction by an order table in between.
Outlet --> Order <-- Product
I want to get the list of Outlet for today Order.
So here is a function to get all outlets:
db.Outlet.findAll({include: [
{model:db.Product, attributes: ['id', 'name', 'nameKh']}
]}).then(function(outlets){
return res.jsonp(outlets);
})
I got this result:
I can only select with where Product Id by using this:
db.Outlet.findAll({include: [
{model:db.Product, attributes: ['id', 'name', 'nameKh'], where: {id: 2}
]}).then(function(outlets){
return res.jsonp(outlets);
})
How can I query by specific order amount, or today order date?
Here are my models:
Outlet:
var Outlet = sequelize.define('Outlet', {
outletCode: DataTypes.STRING,
outletName: DataTypes.STRING,
outletNameKh: DataTypes.STRING,
outletSubtype: DataTypes.STRING,
perfectStoreType: DataTypes.STRING,
address: DataTypes.STRING
},
{
associate: function(models){
Outlet.belongsToMany(models.Product, {through: models.Order});
Outlet.belongsTo(models.Distributor);
// Outlet.hasMany(models.Order);
}
}
);
Product:
var Product = sequelize.define('Product', {
inventoryCode: DataTypes.STRING,
name: DataTypes.STRING,
nameKh: DataTypes.STRING,
monthlyCaseTarget: DataTypes.INTEGER,
pieces: DataTypes.INTEGER,
star: DataTypes.BOOLEAN,
price: DataTypes.FLOAT,
active: DataTypes.BOOLEAN
},
{
associate: function(models){
Product.belongsToMany(models.Outlet, {through: models.Order});
Product.belongsTo(models.Category);
// Product.hasMany(models.Order);
}
}
);
Order:
var Order = sequelize.define('Order', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
amount: DataTypes.INTEGER
},
{
associate: function(models){
Order.belongsTo(models.Outlet);
Order.belongsTo(models.Product);
Order.belongsTo(models.User);
}
}
);

Try it:
db.Outlet.findAll({
include: [{
model:db.Product,
attributes: ['id', 'name', 'nameKh'],
through: { where: { amount: 10 } }
}]
})

Similar case
Super many to many relatioship
User.belongsToMany(Profile, { through: Grant });
Profile.belongsToMany(User, { through: Grant });
User.hasMany(Grant);
Grant.belongsTo(User);
Profile.hasMany(Grant);
Grant.belongsTo(Profile);
This way, we can do all kinds of eager loading:
// All these work:
User.findAll({ include: Profile });
Profile.findAll({ include: User });
User.findAll({ include: Grant });
Profile.findAll({ include: Grant });
Grant.findAll({ include: User });
Grant.findAll({ include: Profile });
We can even perform all kinds of deeply nested includes:
User.findAll({
include: [
{
model: Grant,
include: [User, Profile]
},
{
model: Profile,
include: {
model: User,
include: {
model: Grant,
include: [User, Profile]
}
}
}
]
});
From sequelize doc

Related

how to relational data one-to-one and many-to-many in sequalize in model same?

I have a problem in findById data with relational sequelize.
Where I have the condition of the One-to-One and Many-to-Many relations
TB_User is related on-to-one with tb_corporate
TB_User is related to many-to-many with TB Corporate and this data relations are saved in TB_corporate_Group
My problem, when I want to get corporate and corporate group. The data I get is data from the user table where the relationship is one-to-one.
this is code in model users
module.exports = (sequelize :any ) => {
class users extends Model{
static associate(models : any) {
// define association here
users.belongsToMany(models.corporate, {
through: 'corporateGroup',
as: 'dataCorporateGroup'
})
users.hasOne(models.corporate, {foreignKey: 'userId', as: 'corporate'})
}
}
users.init({
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
field: 'name',
type: DataTypes.STRING,
allowNull: false,
},
}, {
sequelize,
modelName: 'users',
});
return users;
};
code for model corporate
module.exports = (sequelize :any ) => {
class corporate extends Model{
static associate(models : any) {
// define association here
corporate.belongsTo(models.users, {foreignKey: 'userId', as: 'userCorporate'})
corporate.belongsToMany(models.users, {
through: 'corporateGroup',
as: 'userCorporate'
})
}
}
corporate.init({
userId: {
field: 'user_id',
type: DataTypes.INTEGER,
allowNull: false,
unique: true,
},
corporateName: {
field: 'name',
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
}, {
sequelize,
modelName: 'corporate',
});
return corporate;
};
code for model corporate group
module.exports = (sequelize :any ) => {
class corporateGroup extends Model{
static associate(models : any) {
// define association here
}
}
corporateGroup.init({
corporateId: {
field: 'corporate_id',
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'corporate',
key: 'id'
}
},
userId: {
field: 'user_id',
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'users',
key: 'id'
}
}
}, {
sequelize,
tableName: 'corporate_group',
modelName: 'corporateGroup',
});
return corporateGroup;
};
and this code for find by id
await corporate.findById(id, {
include: [{
model: db.users,
as: 'userCorporate',
}]
})
You need to indicate the unique aliases for associations
corporate.belongsTo(models.users, {foreignKey: 'userId', as: 'userCorporate'})
corporate.belongsToMany(models.users, {
through: 'corporateGroup',
as: 'corporateGroupUsers'
})
and then you can indicate included models with these aliases to get both of them along with corporate record:
await corporate.findById(id, {
include: [{
model: db.users,
as: 'userCorporate',
}, {
model: db.users,
as: 'corporateGroupUsers',
}]
})

Empty Array when use random with Sequelize

I'm working on an API in Node.js with the Sequelize ORM.
I make an API route to retrieve a random question, it works one time out of three but often returns an empty array. I can't find a solution in the documentation to prevent this...
app.get('/:certifName/:levelName/random', async function (req, res) {
return await Certification.findOne(
{
attributes: ['id', 'label'],
include: [{
model: CertificationLevel,
as: "tcl",
required: true,
attributes: ['id', 'label', 'question_number', 'exam_duration'],
include: [{
attributes: ['id', 'label'],
model: CertificationChapter,
as: "tcc",
required: true,
limit: 1,
order: [ [ Sequelize.fn('RANDOM') ] ],
include: [{
model: Question,
as: "tq",
required: true,
include: [{
model: QuestionChoice,
as: "tqc",
required: false,
attributes: ['id', 'label_fr', 'is_answer'],
}],
}],
}],
where: { label: req.params.levelName }
}],
where: { label: req.params.certifName }
})
.then(data => {
if (data) {
res.send(data);
}
else
res.sendStatus(204);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Error retrieving certification details"
});
});
});
The last model :
module.exports =
class CertificationLevel extends Sequelize.Model {
static init(sequelize) {
return super.init(
{
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true
},
label: Sequelize.STRING,
slug: Sequelize.STRING,
description: Sequelize.TEXT,
question_number: Sequelize.INTEGER,
exam_duration: Sequelize.INTEGER,
created_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
},
updated_at: Sequelize.DATE,
deleted_at: Sequelize.DATE
},
{
tableName: 't_certifications_levels',
sequelize,
underscored: true,
timestamps: false
},
);
}
static associate(models) {
this.belongsTo(models.Certification, {
onDelete: "CASCADE",
foreignKey: {
allowNull: false,
name: "certification_id"
}
}),
this.hasMany(models.CertificationChapter, { as:'tcc', foreignKey: 'level_id' })
this.hasMany(models.Subscription, { as:'ts',foreignKey: 'level_id' })
}
}
And this is a SQL query generate :
SELECT "Certification".*,
"tcl"."id" AS "tcl.id",
"tcl"."label" AS "tcl.label",
"tcl"."question_number" AS "tcl.question_number",
"tcl"."exam_duration" AS "tcl.exam_duration"
FROM
(SELECT "Certification"."id",
"Certification"."label"
FROM "t_certifications" AS "Certification"
WHERE "Certification"."label" = 'ISTQB'
AND
(SELECT "tcl"."certification_id"
FROM "t_certifications_levels" AS "tcl"
WHERE ("tcl"."label" = 'Fondation'
AND "tcl"."certification_id" = "Certification"."id")
LIMIT 1) IS NOT NULL
LIMIT 1) AS "Certification"
INNER JOIN "t_certifications_levels" AS "tcl" ON "Certification"."id" = "tcl"."certification_id"
AND "tcl"."label" = 'Fondation';
I don't know why I have two executing to my route?
I am a real beginner in back development...
Thank for you help

Sequelize - how to include a custom column details

I have 2 tables: User and Follower
The basic schema looks like this:
User Table
var User = sequelize.define(
"User",
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
username: DataTypes.STRING,
},
{
timestamps: true,
tableName: "users",
underscored: true
);
//Associations;
User.associate = function(models) {
User.hasMany(models.Follower, {
constraints: false,
onDelete: "cascade",
hooks: true
})
};
Follower Table
var Follower = sequelize.define(
"Follower",
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
follower_id: {
type: DataTypes.INTEGER,
foreignKey: true
},
following_id: {
type: DataTypes.INTEGER,
foreignKey: true
}
},
{
timestamps: true,
tableName: "followers",
underscored: true
}
);
//Associations;
Follower.associate = function(models) {
Follower.belongsTo(models.User, {
foreignKey: "following_id",
constraints: false
}),
Follower.belongsTo(models.User, {
foreignKey: "follower_id",
constraints: false
});
};
I want get a list of followers for a user but I want to show the user they are following and now their own data in.
I am currently running this procedure:
const following = await models.Follower.findAndCountAll({
attributes: models.Follower.fields,
where: { follower_id: user_id },
include:[
{
model: models.User,
attributes: models.User.fieldsPublic,
required: yes
}
]
})
The issue is that the include is returning the current user and not the user details of the person they are following.
Essentially the join seems to be between User.id = Follower.follower_id
How can I get it to include and join with User.id = Follower.following_id
You can add as both in your Model definition and include to be able to define which foreign key should be used (docs)
So do:
Follower.associate = function(models) {
Follower.belongsTo(models.User, {
foreignKey: "following_id",
as: 'following',
constraints: false
}),
Follower.belongsTo(models.User, {
foreignKey: "follower_id",,
as: 'follower',
constraints: false
});
};
and
...
include:[
{
model: models.User,
attributes: models.User.fieldsPublic,
as: 'follower'
required: yes
}
]
...
Alternatively, you can remove required: yes and use the where attribute to define the join condition
Furthermore, include seems to have an on attribute that is said to be similar to where (according to the docs), but I was unable to find an example.

How can I achieve this SQL QUERY in sequelize (multiple joins and multiple AND/OR)

I've been struggling to achieve this (below SQL statement) in sequelize for a while now with no luck. I initially had to make separate sequelize queries to get the data but that just posed many limitations.
`SELECT "Documents".* FROM "Documents"
INNER JOIN "AccessTypes"
ON "AccessTypes"."id" = "Documents"."accessTypeId"
INNER JOIN "Users"
ON "Users"."id" = "Documents"."userId"
INNER JOIN "Departments"
ON "Departments"."id" = "Users"."departmentId"
WHERE
(("AccessTypes".name != 'private'
AND "Departments"."id" = ${req.decoded.departmentId})
OR "Users".id = ${req.decoded.id})
AND ("Documents"."title" ILIKE '%${searchQuery}%'
OR "Documents"."content" ILIKE '%${searchQuery}%'`
This is as far as I got
var dbQuery = {
where: {
$or: [
{
title: {
$iLike: `%${searchQuery}%`
}
},
{
content: {
$iLike: `%${searchQuery}%`
}
}
]
},
include: [{
model: db.Users,
where: { departmentId: req.decoded.departmentId }
},
{
model: db.AccessTypes,
where: { name: { $ne: 'private'}}
}]
};
db.Documents.findAll(dbQuery)
I still need to fetch another set of documents based on the userId supplied. I feel the way to go will be to perform an 'Include' within an '$or' statement. however my research so far leads me to believe that's not possible.
Here are my models
Access Types
export default (sequelize, DataTypes) => {
const AccessTypes = sequelize.define('AccessTypes', {
name: {
type: DataTypes.STRING,
allowNull: false,
isUnique: true
}
}, {
classMethods: {
associate: (models) => {
// associations can be defined here
AccessTypes.hasMany(models.Documents, {
foreignKey: 'accessTypeId',
onDelete: 'CASCADE'
});
}
}
});
return AccessTypes;
};
Users
export default (sequelize, DataTypes) => {
const Users = sequelize.define('Users', {
username: {
type: DataTypes.STRING,
unique: true,
allowNull: false
},
firstname: {
type: DataTypes.STRING,
allowNull: false
},
lastname: {
type: DataTypes.STRING,
allowNull: false
},
email: {
type: DataTypes.STRING,
unique: true,
allowNull: false,
validate: {
isEmail: true
}
},
password: {
type: DataTypes.STRING,
allowNull: false
},
roleId: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 3
},
departmentId: {
type: DataTypes.INTEGER,
allowNull: false
}
}, {
classMethods: {
associate: (models) => {
// associations defined here
Users.belongsTo(models.Roles, {
onDelete: 'CASCADE',
foreignKey: 'roleId'
});
Users.belongsTo(models.Departments, {
onDelete: 'CASCADE',
foreignKey: 'departmentId'
});
Users.hasMany(models.Documents, {
as: 'documents',
foreignKey: 'userId',
onDelete: 'CASCADE'
});
}
}, ...
Departments
export default (sequelize, DataTypes) => {
const Departments = sequelize.define('Departments', {
name: {
type: DataTypes.STRING,
allowNull: false,
isUnique: true
}
}, {
classMethods: {
associate: (models) => {
// associations can be defined here
Departments.hasMany(models.Users, {
foreignKey: 'departmentId',
onDelete: 'CASCADE'
});
}
}
});
return Departments;
};
and Documents
export default (sequelize, DataTypes) => {
const Documents = sequelize.define('Documents', {
title: {
type: DataTypes.STRING,
allowNull: false
},
content: {
type: DataTypes.TEXT,
allowNull: false
},
userId: {
type: DataTypes.INTEGER,
allowNull: false
},
accessTypeId: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 1
},
docTypeId: {
type: DataTypes.INTEGER,
allowNull: false
}
}, {
classMethods: {
associate: (models) => {
// associations can be defined here
Documents.belongsTo(models.Users, {
foreignKey: 'userId',
as: 'user',
onDelete: 'CASCADE'
});
Documents.belongsTo(models.DocumentTypes, {
foreignKey: 'docTypeId',
onDelete: 'CASCADE'
});
Documents.belongsTo(models.AccessTypes, {
foreignKey: 'accessTypeId',
onDelete: 'CASCADE'
});
}
}
});
return Documents;
};
Any Pointers Will be greatly appreciated
Thanks in Advance
This is quite complex query (in Sequelize way of course), so you need to build it differently than you did. You should use functions like sequelize.col(), sequelize.where(), sequelize.or() and sequelize.and(). Moreover, in order to include the Departments model in the query, you need to use nested include statement in the options object of the findAll query. You can nest the includes as much as you want.
where: sequelize.and(
sequelize.or(
sequelize.and(
sequelize.where(sequelize.col('AccessTypes.name'), '!=', 'private'),
sequelize.where(sequelize.col('Departments.id'), '=', req.decoded.departmentId)
),
sequelize.where(sequelize.col('Users.id'), '=', req.decoded.id)
),
sequelize.or(
{ title: { $iLike: `%${searchQuery}%` } },
{ content: { $iLike: `%{searchQuery}%` } }
)
),
include: [
{
model: db.Users,
include: [ db.Departments ]
},
{
model: db.AccessTypes
}
]
You need to briefly get through documentation of above mentioned functions. In a short, col() creates a proper column selection basing on model name and field, where() creates WHERE statement with use of three attributes - column, condition (comparator) and logic, or() creates OR statement and and() creates AND statement. Both or() and and() can obtain multiple arguments that allows you to create complex statements, like yours.

sequelize querying by association - nested includes?

Here is my account Model:
module.exports = {
attributes: {
accountPkey: {
type: Sequelize.INTEGER,
primaryKey: true,
field: 'account_pkey'
},
accountName: {
type: Sequelize.STRING,
allowNull: false,
field: 'account_name'
},
imagePkey: {
type: Sequelize.INTEGER,
field: 'image_pkey'
}
},
associations: function() {
Account.hasMany(Privilege, {
as: 'Privileges',
foreignKey: 'account_pkey'
});
Account.hasMany(AccountContact, {
as: 'Contacts',
foreignKey: 'account_pkey'
});
},
options: {
tableName: 'v_account',
timestamps: false,
classMethods: {
whereUserIs(user_pkey, privileges) {
return Account.findAll({
include: [{
model: Privilege,
where: {
'Privilege.privilege_type_name': {
$in: privileges
},
'Privilege.user_pkey': {
$eq: user_pkey
}
}
}]
});
}
},
instanceMethods: {},
hooks: {}
}
};
In my whereUserIs class method I am trying to return all accounts for which there exists a privilege which has the user_pkey and has any of the privileges passed in.
Please assume that the Privilege model has account_type_name and user_pkey properties. Is my syntax correct?
Edit: I am actually using this sails hook to load sequelize: https://www.npmjs.com/package/sails-hook-sequelize
Second Edit:
Here is a more complicated query: I would like to find all users which have a privelege on any of the accounts that were queried above:
User.findAll({
include: [
{
model: Privilege,
include: [
{
model: Account,
include: [{
model: Privelege,
where: {
'Privilege.privilege_type_name': {
$in: privileges
},
'Privilege.user_pkey': {
$eq: user_pkey
}
}
}]
}
]
}
]
})
Does this second one make sense?
In both of these queries, will I get a list of entities (i.e. accounts or users) or will they be nested results (of all the includes)? How can I ensure that my lists of accounts and users are unique/distinct?

Categories

Resources