Column not exist in order by clause - node.js

I'm using Sequelize to query in to a table like this:
const students = await User.findAll({
attributes: ['id', [Sequelize.literal(`"firstName" || ' ' || "lastName"`), 'name']],
where: {
[Op.or]: [
{
firstName: {
[Op.iLike]: `%${search}%`
}
},
{
lastName: {
[Op.iLike]: `%${search}%`
}
},
Sequelize.literal(`"firstName" || ' ' || "lastName" ILIKE '%${search}%'`)
]
},
order: Sequelize.literal(`"organization_users"."firstName" ${NATURAL_SORT} ${sort}`),
limit,
offset: limit * (page - 1),
distinct: true
});
And it work ok but if I include some model, the column "firstName" is not exist:
const students = await User.findAll({
attributes: ['id', [Sequelize.literal(`"firstName" || ' ' || "lastName"`), 'name']],
where: {
[Op.or]: [
{
firstName: {
[Op.iLike]: `%${search}%`
}
},
{
lastName: {
[Op.iLike]: `%${search}%`
}
},
Sequelize.literal(`"firstName" || ' ' || "lastName" ILIKE '%${search}%'`)
]
},
include: [
{ model: ProgressGrade, as: 'student_progress_grade', required: true, where: { courseId } },
{
model: CourseRole,
as: 'course_roles',
attributes: [],
where: { roleName: 'Student' },
required: true
},
{ model: Course, as: 'courses', attributes: [], where: { id: courseId }, required: true }
],
order: Sequelize.literal(`"organization_users"."firstName" ${NATURAL_SORT} ${sort}`),
limit,
offset: limit * (page - 1),
distinct: true
});
Error message:
error: SequelizeDatabaseError: column organization_users.firstName
does not exist
This is the first time I run into this problem, I did many search but hopeless. Anyone have an idea about how Sequelize work here?
I'm using Sequelize 5.21.5 running on Nodejs 13.8.0, PostgreSQL 10.12

Please set subQuery property to false as follows -
const students = await User.findAll({
attributes: ['id', [Sequelize.literal(`"firstName" || ' ' || "lastName"`), 'name']],
where: {
[Op.or]: [
{
firstName: {
[Op.iLike]: `%${search}%`
}
},
{
lastName: {
[Op.iLike]: `%${search}%`
}
},
Sequelize.literal(`"firstName" || ' ' || "lastName" ILIKE '%${search}%'`)
]
},
include: [
{ model: ProgressGrade, as: 'student_progress_grade', required: true, where: { courseId } },
{
model: CourseRole,
as: 'course_roles',
attributes: [],
where: { roleName: 'Student' },
required: true
},
{ model: Course, as: 'courses', attributes: [], where: { id: courseId }, required: true }
],
order: Sequelize.literal(`"organization_users"."firstName" ${NATURAL_SORT} ${sort}`),
limit,
offset: limit * (page - 1),
distinct: true,
subQuery: false
});
I hope it helps!

Related

Implementation of search function of Sequelize

Before the question, the post table of my current project has the following structure.
module.exports = class Post extends Model {
static init(sequelize) {
return super.init({
title: {
type: DataTypes.TEXT,
allowNull: false,
},
desc: {
type: DataTypes.TEXT,
},
ingredient: {
type: DataTypes.TEXT,
allowNull: false,
},
recipes: {
type: DataTypes.TEXT,
allowNull: false,
},
tips: {
type: DataTypes.TEXT,
},
tags: {
type: DataTypes.TEXT,
},
}, {
modelName: 'Post',
tableName: 'posts',
charset: 'utf8mb4',
collate: 'utf8mb4_general_ci',
sequelize,
});
}
static associate(db) {
db.Post.belongsTo(db.User);
db.Post.belongsToMany(db.Hashtag, { through: 'PostHashtag' });
db.Post.hasMany(db.Comment);
db.Post.hasMany(db.Image);
db.Post.belongsToMany(db.User, { through: 'Like', as: 'Likers' });
}
};
I implemented the hashtag post search function using the following router.
router.get('/:tag', async (req, res, next) => {/hashtag/1
try {
const where = {};
if (parseInt(req.query.lastId, 10)) {
where.id = { [Op.lt]: parseInt(req.query.lastId, 10)};
}
const posts = await Post.findAll({
where,
limit: 10,
order: [['createdAt', 'DESC']],
include: [{
model: Hashtag,
where: { name: decodeURIComponent(req.params.tag) },
}, {
model: User,
attributes: ['id', 'nickname'],
}, {
model: User,
as: 'Likers',
attributes: ['id'],
}, {
model: Comment,
include: [{
model: User,
attributes: ['id', 'nickname'],
}],
}, {
model: Image,
}]
});
res.status(200).json(posts);
} catch (error) {
console.error(error);
next(error);
}
});
The execution result was successful, and I was able to get the results I wanted.
However, we thought that it was not enough to search only hashtags, so we added the following conditions to additionally search the title and contents of the post.
router.get('/:tag', async (req, res, next) => {
try {
const where = {
title: { [Op.like]: "%" + decodeURIComponent(req.params.tag) + "%" }, // Search for the title of a post
recipes: { [Op.like]: "%" + decodeURIComponent(req.params.tag) + "%" }, // Search the content of the post
};
if (parseInt(req.query.lastId, 10)) {
where.id = { [Op.lt]: parseInt(req.query.lastId, 10)};
}
const posts = await Post.findAll({
where,
limit: 10,
:
:
After that, the result of running the code did not load any posts, contrary to what I expected.
What part should be modified to implement a search function that includes the title, content, and hashtag of a post?
I guess the problem with your function is that you are using where with AND
instead of OR.
Post.findAll({
where: {
[Op.and]:
[{ title: {[Op.like]...} },
{ recipes: {[Op.like]...} }], // (title= %S) AND (recipes = %S)
...
)}
// that is the same as your example
const where = {
// Search for the title of a post
title: { [Op.like]: "%"...,
// Search the content of the post
recipes: { [Op.like]: "%"... },
};
// this is an AND operation
What you should do is use OR. More info: operators
[Op.or]: [{ a: 5 }, { b: 6 }], // (a = 5) OR (b = 6)
In your case:
Post.findAll({
where: {
[Op.or]:
[{ title: {[Op.like]...} },
{ recipes: {[Op.like]...} }], // (title= %S) AND (recipes = %S)
...
)}
That is also not the proper way of searching. Maybe you should consider looking for full-text search implementations like Elastic Search or look if the database you are using has this feature built-in.

Unable to find a valid association for model when ordering nested model | Sequelize

I followed the official docs from sequelize but it doens't work ,I wanna order sessions by end_date , help me please.
GrouspSession hasMany Session
const groupSessions = await groupSessionRepo.findAllGroupSessions({
where: { teacher_id: userId },
include: [
{
model: Class,
attributes: {
exclude: ['created_at', 'updated_at', 'deleted_at', 'id'],
},
},
{
model: Session,
where: { end_date: { [Op.lte]: new Date() } },
attributes: ['name', 'end_date', 'participants_nb'],
},
],
order:[Session,"end_date","ASC"],
attributes: [],
});
Finally I found the solution, beacuse i am using sequelize-typescript so i faced a problem to define alias but i found that answer : here
then I solved like that:
const groupSessions = await groupSessionRepo.findAllGroupSessions({
where: { teacher_id: userId },
include: [
{
model: Class,
attributes: {
exclude: ['created_at', 'updated_at', 'deleted_at', 'id'],
},
},
{
model: Session,
where: { end_date: { [Op.lte]: new Date() } },
attributes: ['name', 'end_date', 'participants_nb'],
as: 'sessions',
},
],
order: [[{ model: Session, as: 'sessions' }, 'name', 'DESC']],
attributes: [],
});

using "or" operator when using .populate on a query?

I have my invoices model which has a "sales" field ,which is array of sales. In my sales model I have item field which either can come from Vehicles schema or AfterSale schema.
export const Sales = new Schema<ISale>(
{
customer: { type: Schema.Types.ObjectId, ref: "Customer", required: true },
category: { type: String, enum: enu, required: true },
item: {
type: Schema.Types.ObjectId,
required: true,
ref: (doc) => doc.category,
},
total: { type: Number, required: true },
discount: { type: Number },
purchaseDate: { type: Date, required: true },
branch: { type: Schema.Types.ObjectId, ref: "Branch", required: true },
},
{ timestamps: true },
);
I want to populate the sales.item on my invoice schema, but the problem is , since item can reference to multiple schemas, I can only populate one of them.
async findAll(req: any, query: SharedPreferences): Promise<InvoiceClass[]> {
const invoices = this.invoiceModel
.find(req.searchObj)
.sort({ [query.sort]: query.orderBy === "desc" ? -1 : 1 })
.populate([
"customer",
"sales",
{
path: "sales",
populate: [
{
path: "customer",
model: "Customer",
},
{
path: "branch",
model: "Branch",
},
{
path: "item",
model: "Vehicle",
},
{
path: "item",
model: "AfterSale", //in this case only AfterSale will be populated since
// its last and has same path
}
],
},
]);
my perfect solution would have been using the or operator , but that doesnt work
async findAll(req: any, query: SharedPreferences): Promise<InvoiceClass[]> {
const invoices = this.invoiceModel
.find(req.searchObj)
.sort({ [query.sort]: query.orderBy === "desc" ? -1 : 1 })
.populate([
"customer",
"sales",
{
path: "sales",
populate: [
{
path: "customer",
model: "Customer",
},
{
path: "branch",
model: "Branch",
},
{
path: "item",
model: { $or: ["Vehicle", "AfterSale"] },
},
],
},
]);
any reference/help will be appreciated, thanks.

How to server side filtering each field of a table in sequelize (nodejs orm)

I have some trouble to find out how i have to do the filtering part in sequelize ORM.
I already did, server-side pagination and sorting, Following this tutorial for Angular https://blog.angular-university.io/angular-material-data-table/
I'am using Sequelize 5.8.5 but not in typescript.
But, in my case, the backend is in nodejs using Sequelize. And i want to do a search of the filter string parameter on each fields of my joined table.
If "string" like field1 OR "string" like field2 OR .... etc
Here is my router.get in sequelize.
router.get('', auth.checkToken, function(req, res) {
let options = {
include: [{
model: models.intervention,
attributes: ['id','start_date','end_date','agent_id','state_id'],
required: false,
include: [{
model: models.agent,
required: false,
},
{
model: models.state,
required: false,
},
{
model: models.intervention_participation,
required: false,
include: [{
model: models.agent,
attributes: ['id','access_token','agent_type_id','firstname','lastname','mail','password'],
required: false,
include: [{
model: models.shift_do,
attributes: ['id','shift_id','agent_id','shift_date'],
required: false,
on: {
'agent_id': {
[Op.eq]: Sequelize.col('intervention->agent.id')
},
'shift_date': {
[Op.eq]: Sequelize.col('intervention.start_date')
},
},
include: [{
model: models.shift,
required: false,
}]
}]
}]
},
{
model: models.operating_range_do,
required: false,
include: [{
model: models.operating_range,
required: false,
}]
},
{
model: models.equipment_intervention,
required: false,
include: [{
model: models.equipment,
required: false,
include: [{
model: models.equipment_type,
required: false,
include: [{
model: models.work_field,
required: false,
}]
},
{
model: models.equipment_location,
required: false,
include: [{
model: models.structure,
required: false,
},
{
model: models.local,
required: false,
},
{
model: models.place,
required: false,
}]
}]
}]
},
{
model: models.intervention_helper,
required: false,
include: [{
model: models.external_actor,
required: false,
}]
}]
},
{
model: models.work_type,
required: false
},
{
model: models.di_externe,
required: false
}]
};
if (req.query.filter) {
options.where = {
$or: [
{ id: { $like: req.query.filter}},
{ title: { $like: req.query.filter}},
{ description: { $like: req.query.filter}},
{ start_date: { $like: req.query.filter}},
]
};
}
if (req.query.sort) {
options.order = [['id', req.query.sort || 'DESC']];
}
if (req.query.page && req.query.pageSize) {
options.offset = req.query.page * req.query.pageSize;
options.limit = parseInt(req.query.page, 10) + parseInt(req.query.pageSize, 10);
}
models.intervention_maincourante
.findAll(options)
.then(all => {
res.send(all);
});
});
its this part who don't work :
if (req.query.filter) {
options.where = {
$or: [
{ id: { $like: req.query.filter}},
{ title: { $like: req.query.filter}},
{ description: { $like: req.query.filter}},
{ start_date: { $like: req.query.filter}},
]
};
}
I also tried this way because sometimes the alias for operators didn't works? But it didn't change anything.
[Op.or]: [
{ id: { [Op.like]: "%" + req.query.filter + "%"}},
{ title: { [Op.like]: "%" + req.query.filter + "%"}},
{ description: { [Op.like]: "%" + req.query.filter + "%"}},
{ start_date: { [Op.like]: "%" + req.query.filter + "%"}},
]
Then i tried :
options.where = {
[Op.or]: [{
title: {
[Op.like]: "%" + req.query.filter + "%",
},
description: {
[Op.like]: "%" + req.query.filter + "%",
},
start_date: {
[Op.like]: "%" + req.query.filter + "%",
}
}]
};
What's in the documentation :
{
[Op.or]: [
{
title: {
[Op.like]: 'Boat%'
}
},
{
description: {
[Op.like]: '%boat%'
}
}
]
}
Thanks a lot, if someone know how to do it.
As you suggested in the comments that query doesn't logs with filter. So assuming there should be any error.
Try to catch the error and log it to identify the issue
models.intervention_maincourante
.findAll(options)
.then(all => {
res.send(all);
}).catch(err => {
console.log("err", err);
});
with the help of this, you will be identifying the exact root cause.
Update:
Based on the error, Please check your like clause.
Your like clause should be something like this. Also, make sure you are referring right column names.
options.where = {
[Op.or]: [{
title: {
[Op.like]: `%${req.query.filter}%`,
},
description: {
[Op.like]: `%${req.query.filter}%`,
},
start_date: {
[Op.like]: `%${req.query.filter}%`,
}
}]
};
In Sequelize ORM when you use $like operator you can pass the search string directly or wrapped inside % OR _. if you want to match some part of the string you can use $substring operator.
if your query is a plain string like this 'examplematch' $like operator will look for an exact match.
Refer this doc https://sequelize.org/master/manual/querying.html#operators
It's working.
const whereStatement = {}
const { status, startDate, endDate, search } = req.query;
if (status) {
whereStatement.status = { [Op.eq]: status };
}
if (startDate && endDate) {
whereStatement.createdAt = { [Op.between]: [startDate, endDate] }
}
if (search) {
whereStatement.username = { [Op.like]: `%${search}%` }
}
let Datefilter = "";
if (startDate && endDate) {
Datefilter =
startDate && endDate ? {
createdAt: {
$gte: moment(startDate).startOf("day").toDate(),
$lte: moment(endDate).endOf("day").toDate(),
},
} : {};
console.log("startDateto", Datefilter);
} else if (startDate) {
console.log("startDate");
Datefilter = startDate
? {
createdAt: {
$gte: moment(startDate).startOf("day").toDate(),
$lte: moment(new Date()).endOf("day").toDate(),
},
} : {};
}
feedback = await feedbackModel.paginate({
where: whereStatement,
Datefilter
});

Sequelize set associated value

I have two models, Employees and Offices. Every Employee belongs to one Office, and an Office has many Employees.
I am having difficulty figuring out how to update an Employee's office using Sequelize.
The Employee model is as follows:
let Employee = sequelize.define("Employee", {
id: {
field: 'id',
type: DataTypes.INTEGER,
primaryKey: true,
allowNull: false,
autoIncrement: false
},
name: {
field: 'name',
type: DataTypes.STRING(255),
allowNull: false
}
}, {
freezeTableName: true,
timestamps: false,
deletedAt: false
})
Employee.associate = models => {
Employee.belongsTo(models.Office, {
foreignKey: 'id'
})
}
The Office model is as follows:
let Office = sequelize.define("Office", {
id: {
field: 'id',
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
field: 'name',
type: DataTypes.STRING(255),
allowNull: false
}
}, {
freezeTableName: true,
tableName: 'Lkup_Office',
timestamps: false,
deletedAt: false
})
Office.associate = models => {
Office.hasMany( models.Employee, {
foreignKey: 'id'
})
}
In the database I have the following Employee:
{
"id": "2",
"name" : "John",
"office": {
"id": 2,
"name" : "Seattle"
}
}
... and the following Offices:
[
{
"id": 1,
"name" : "Chicago"
},
{
"id": 2,
"name" : "Seattle"
}
]
I want to change the ID of Employee(1)'s office from 2 ('Seattle') to 1 ('Chicago'); the problem is that with the following query
// PUT /2/office/1
router.put('/:employeeId/office/:officeId', (req, res) => {
models.Employee.findOne({
where:{id:req.params.employeeId},
include:[{model:models.Office}]
}).then( employee => {
models.Office.findOne({
where:{id:req.params.officeId},
include:[{model:models.Employee}]
}).then( office => {
employee.setOffice( office ).then( result => {
res.send( result )
})
})
})
})
... my Employee's office is not updated:
{
"id": "2",
"name" : "John"
"office": {
"id": 2
"name" : "Seattle"
}
}
It doesn't, in fact, do anything at all: no errors, the DB isn't changed. I have a suspicion that there's something I'm not doing correctly, but Sequelize isn't throwing any errors.
router.put('/:employeeId/office/:officeId', (req, res) => {
/*In this first part, I just separated the two queries into two
separate variables*/
let employeeInfo = models.Employee.findOne({
where: {
id: req.params.employeeId
},
include: [{
model: models.Office
}]
});
let officeInfo = models.Office.findOne({
where: {
id: req.params.officeId
},
include: [{
model: models.Employee
}]
});
/*In this next part, we have to use Promise.all, which comes with
bluebird, which is already a part of Sequelize.js. How this next
part is executed will depend on how you have your Sequelize.js code
structured in your application. This may require some trial and error*/
models.sequelize.Promise.all([employeeInfo, officeInfo]).then(([Employee, Office])=> {
return Employee.setOffice(Office);
}).catch(err => {
console.log(err);
});
});

Resources