我正在将 Sequelize ORM 与 mysql 数据库一起用于 Nodejs 应用程序。在某个地方,我被一个 api 困住了,它的工作只是根据 queryparam 值显示列表。问题是当我没有应用分页时,即 url 就像
{{url}}/tasks?pagesize=&page=&start=2024-04-01&end=2024-05-13&task_status_id=&priority_id=&search=Dummy
将页面大小和页面设置为空字符串,则“搜索”功能工作正常。但是当我添加 pagesize=10 和 size=1 时,它会抛出错误:
original: Error: Unknown column 'customer.contact_person_name' in 'where clause'
at Packet.asError (xxxx/node_modules/mysql2/lib/packets/packet.js:728:17)
at Query.execute (xxxx/node_modules/mysql2/lib/commands/command.js:29:26)
at Connection.handlePacket (xxxx/node_modules/mysql2/lib/connection.js:481:34)
at PacketParser.onPacket (xxxx/node_modules/mysql2/lib/connection.js:97:12)
at PacketParser.executeStart (xxxx/node_modules/mysql2/lib/packet_parser.js:75:16)
at Socket.<anonymous> (xxxx/node_modules/mysql2/lib/connection.js:104:25)
at Socket.emit (node:events:514:28)
at addChunk (node:internal/streams/readable:545:12)
at readableAddChunkPushByteMode (node:internal/streams/readable:495:3)
at Readable.push (node:internal/streams/readable:375:5) {
code: 'ER_BAD_FIELD_ERROR',
errno: 1054,
sqlState: '42S22',
sqlMessage: "Unknown column 'customer.contact_person_name' in 'where clause'",
sql: "SELECT `tasks`.*, `customer`.`id` AS `customer.id`, `customer`.`organization_name` AS `customer.organization_name`, `customer`.`address` AS `customer.address`, `customer`.`latitude` AS `customer.latitude`, `customer`.`longitude` AS `customer.longitude`, `customer`.`contact_person_name` AS `customer.contact_person_name`, `customer`.`contact_person_email` AS `customer.contact_person_email`, `customer`.`contact_person_phone` AS `customer.contact_person_phone`, `customer`.`contact_person_alternate_phone` AS `customer.contact_person_alternate_phone`, `customer`.`fence_id` AS `customer.fence_id`, `customer`.`country_id` AS `customer.country_id`, `customer`.`state_id` AS `customer.state_id`, `customer`.`status` AS `customer.status`, `fence`.`id` AS `fence.id`, `fence`.`title` AS `fence.title`, `fence`.`area` AS `fence.area`, `fence`.`radius` AS `fence.radius`, `employee`.`id` AS `employee.id`, `employee`.`name` AS `employee.name`, `employee`.`personal_email` AS `employee.personal_email`, `task_status`.`id` AS `task_status.id`, `task_status`.`name` AS `task_status.name`, `task_status`.`color_code` AS `task_status.color_code`, `priority`.`id` AS `priority.id`, `priority`.`name` AS `priority.name`, `priority`.`color_code` AS `priority.color_code`, `task_documents`.`id` AS `task_documents.id`, `task_documents`.`task_id` AS `task_documents.task_id`, `task_documents`.`employee_id` AS `task_documents.employee_id`, `task_documents`.`document_type` AS `task_documents.document_type`, `task_documents`.`media_id` AS `task_documents.media_id`, `task_documents->media`.`id` AS `task_documents.media.id`, `task_documents->media`.`name` AS `task_documents.media.name`, `task_documents->media`.`file_name` AS `task_documents.media.file_name`, `task_documents->media`.`mime_type` AS `task_documents.media.mime_type`, `task_documents->media`.`directory` AS `task_documents.media.directory`, `task_documents->media`.`media_size` AS `task_documents.media.media_size`, `task_documents->media`.`disk` AS `task_documents.media.disk`, `task_documents->document_category`.`id` AS `task_documents.document_category.id`, `task_documents->document_category`.`title` AS `task_documents.document_category.title`, `task_documents->document_category`.`prefix` AS `task_documents.document_category.prefix`, `task_documents->document_category`.`storage` AS `task_documents.document_category.storage` FROM (SELECT `tasks`.`id`, `tasks`.`task_custom_id`, `tasks`.`title`, `tasks`.`description`, `tasks`.`customer_id`, `tasks`.`fence_id`, `tasks`.`planned_date`, `tasks`.`task_status_id`, `tasks`.`priority_id`, `tasks`.`employee_id`, `tasks`.`otp_requirement`, `tasks`.`doc_requirement`, `tasks`.`start_of_service_time`, `tasks`.`end_of_service_time`, `tasks`.`created_at` FROM `tasks` AS `tasks` WHERE (`tasks`.`deleted_at` IS NULL AND (((`tasks`.`id` LIKE '%Dummy%' OR `tasks`.`task_custom_id` LIKE '%Dummy%' OR `tasks`.`title` LIKE '%Dummy%' OR `tasks`.`description` LIKE '%Dummy%' OR `tasks`.`customer_id` LIKE '%Dummy%' OR `tasks`.`fence_id` LIKE '%Dummy%' OR `tasks`.`planned_date` LIKE 'Invalid date' OR `tasks`.`task_status_id` LIKE '%Dummy%' OR `tasks`.`priority_id` LIKE '%Dummy%' OR `tasks`.`employee_id` LIKE '%Dummy%' OR `tasks`.`otp_requirement` LIKE '%Dummy%' OR `tasks`.`doc_requirement` LIKE '%Dummy%' OR `tasks`.`start_of_service_time` LIKE 'Invalid date' OR `tasks`.`end_of_service_time` LIKE 'Invalid date' OR `customer`.`contact_person_name` LIKE '%Dummy%' OR `customer`.`organization_name` LIKE '%Dummy%' OR `task_status`.`name` LIKE '%Dummy%' OR `priority`.`name` LIKE '%Dummy%') AND (`tasks`.`planned_date` > '2024-04-01 00:00:00' AND `tasks`.`planned_date` <= '2024-05-14 00:00:00')))) ORDER BY `tasks`.`planned_date` DESC LIMIT 0, 10) AS `tasks` LEFT OUTER JOIN `customers` AS `customer` ON `tasks`.`customer_id` = `customer`.`id` AND (`customer`.`deleted_at` IS NULL) LEFT OUTER JOIN `fences` AS `fence` ON `tasks`.`fence_id` = `fence`.`id` AND (`fence`.`deleted_at` IS NULL) LEFT OUTER JOIN `employees` AS `employee` ON `tasks`.`employee_id` = `employee`.`id` AND (`employee`.`deleted_at` IS NULL) LEFT OUTER JOIN `task_statuses` AS `task_status` ON `tasks`.`task_status_id` = `task_status`.`id` AND (`task_status`.`deleted_at` IS NULL) LEFT OUTER JOIN `priorities` AS `priority` ON `tasks`.`priority_id` = `priority`.`id` AND (`priority`.`deleted_at` IS NULL) LEFT OUTER JOIN `task_documents` AS `task_documents` ON `tasks`.`id` = `task_documents`.`task_id` AND (`task_documents`.`deleted_at` IS NULL) LEFT OUTER JOIN `medias` AS `task_documents->media` ON `task_documents`.`media_id` = `task_documents->media`.`id` AND (`task_documents->media`.`deleted_at` IS NULL) LEFT OUTER JOIN `document_categories` AS `task_documents->document_category` ON `task_documents`.`document_type` = `task_documents->document_category`.`id` AND (`task_documents->document_category`.`deleted_at` IS NULL) ORDER BY `tasks`.`planned_date` DESC;",
parameters: undefined
问题出在哪里?没有分页它工作正常
下面是表的架构
任务架构:
"use strict";
const {
Model
} = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class tasks extends Model {
static associate(models) {
tasks.belongsTo(models.employees, {
foreignKey: "employee_id"
});
tasks.belongsTo(models.customers, {
foreignKey: "customer_id"
});
tasks.belongsTo(models.fences, {
foreignKey: "fence_id"
});
tasks.belongsTo(models.task_statuses, {
foreignKey: "task_status_id"
});
tasks.belongsTo(models.priorities, {
foreignKey: "priority_id"
});
tasks.hasMany(models.task_documents, {
foreignKey: "task_id"
});
}
}
tasks.init({
task_custom_id: DataTypes.STRING,
title: DataTypes.STRING,
description: DataTypes.TEXT,
customer_id: DataTypes.BIGINT,
fence_id: DataTypes.BIGINT,
planned_date: DataTypes.DATE,
task_status_id: DataTypes.BIGINT,
priority_id: DataTypes.BIGINT,
employee_id: DataTypes.BIGINT,
otp_requirement: DataTypes.TINYINT,
doc_requirement: DataTypes.TINYINT,
start_of_service_time: DataTypes.DATE,
end_of_service_time: DataTypes.DATE
}, {
sequelize,
modelName: "tasks",
freezeTableName: true,
paranoid: true,
createdAt: "created_at",
updatedAt: "updated_at",
deletedAt: "deleted_at"
});
return tasks;
};
顾客表
"use strict";
const {
Model
} = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class customers extends Model {
static associate(models) {
customers.hasMany(models.tasks, {
foreignKey: "customer_id"
});
customers.belongsTo(models.fences, {
foreignKey: "fence_id"
});
customers.belongsTo(models.countries, {
foreignKey: "country_id"
});
customers.belongsTo(models.states, {
foreignKey: "state_id"
});
}
}
customers.init({
organization_name: DataTypes.STRING,
address: DataTypes.STRING,
latitude: DataTypes.STRING,
longitude: DataTypes.STRING,
contact_person_name: DataTypes.STRING,
contact_person_email: DataTypes.STRING,
contact_person_phone: DataTypes.STRING,
contact_person_alternate_phone: DataTypes.STRING,
fence_id: DataTypes.BIGINT,
country_id: DataTypes.BIGINT,
state_id: DataTypes.BIGINT,
status: DataTypes.TINYINT
}, {
sequelize,
modelName: "customers",
freezeTableName: true,
paranoid: true,
createdAt: "created_at",
updatedAt: "updated_at",
deletedAt: "deleted_at"
});
return customers;
};
栅栏桌
"use strict";
const {
Model
} = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class fences extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
fences.hasMany(models.tasks, {
foreignKey: "fence_id"
});
fences.hasMany(models.customers, {
foreignKey: "fence_id"
});
fences.belongsToMany(models.employees, {
foreignKey: "fence_id",
through: models.employees_fences,
as: "employees"
});
}
}
fences.init({
title: DataTypes.STRING,
area: DataTypes.STRING,
radius: DataTypes.TEXT,
status: DataTypes.TINYINT
}, {
sequelize,
modelName: "fences",
freezeTableName: true,
paranoid: true,
createdAt: "created_at",
updatedAt: "updated_at",
deletedAt: "deleted_at"
});
return fences;
};
员工表
"use strict";
const helper = require("../api/helper.js");
const utils = require("../api/utils");
const {
Model
} = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class employees extends Model {
static associate(models) {
employees.belongsTo(models.users, {
foreignKey: "user_id"
});
employees.belongsTo(models.medias, {
foreignKey: "profile_img"
});
employees.belongsTo(models.addresses, {
as: "present_address",
foreignKey: "present_address_id"
});
employees.belongsTo(models.addresses, {
as: "permanent_address",
foreignKey: "permanent_address_id"
});
employees.belongsTo(models.designations, {
foreignKey: "designation_id"
});
employees.belongsTo(models.departments, {
foreignKey: "department_id"
});
employees.belongsTo(models.users, {
as: "reported_to",
foreignKey: "report_to"
});
employees.belongsTo(models.users, {
as: "create_by",
foreignKey: "created_by"
});
employees.belongsTo(models.users, {
as: "employeeType",
foreignKey: "user_id"
});
employees.belongsTo(models.users, {
as: "update_by",
foreignKey: "updated_by"
});
employees.belongsTo(models.users, {
as: "delete_by",
foreignKey: "deleted_by"
});
employees.hasMany(models.attendances, {
foreignKey: "employee_id"
});
employees.hasMany(models.tasks, {
foreignKey: "employee_id"
});
employees.belongsToMany(models.fences, {
foreignKey: "employee_id",
through: models.employees_fences,
as: "fences"
});
}
}
employees.init({
name: DataTypes.STRING,
personal_email: DataTypes.STRING,
company_email: DataTypes.STRING,
phone: DataTypes.STRING,
gender: DataTypes.ENUM(helper.elementsOfArr(utils.constants.genderEnum)),
date_of_birth: DataTypes.DATEONLY,
is_married: DataTypes.TINYINT,
profile_img: DataTypes.BIGINT,
//Address
present_address_id: DataTypes.BIGINT,
permanent_address_id: DataTypes.BIGINT,
is_same_address_flag: DataTypes.TINYINT,
// Employeement Details
designation_id: DataTypes.BIGINT,
department_id: DataTypes.BIGINT,
user_id: DataTypes.BIGINT,
status: DataTypes.TINYINT,
//Who altered
created_by: DataTypes.BIGINT,
updated_by: DataTypes.BIGINT,
deleted_by: DataTypes.BIGINT,
created_at: DataTypes.DATE,
updated_at: DataTypes.DATE,
deleted_at: DataTypes.DATE
}, {
sequelize,
modelName: "employees",
freezeTableName: true,
paranoid: true,
createdAt: "created_at",
updatedAt: "updated_at",
deletedAt: "deleted_at"
});
return employees;
};
我的续集查询:
//Query for fetching all tasks
function checkEndStart(startD, endD, i18next){
let startDate = moment(startD, "YYYY-MM_DD").startOf("day");
let endDate =moment(endD, "YYYY-MM_DD").startOf("day");
//check the endDate and startDate
if(endDate.isBefore(startDate)) throw new Error(i18next("DATE_END_CANNOT_BE_AHEAD_OF_START"));
//add 1 date to endDate
endDate = endDate.add("1", "day");
return {
startDate,
endDate
};
}
function pagination(pagesize, page) {
const parseSize = parseInt(pagesize);
const parsePage = parseInt(page);
return {
"limit": (parseSize) ? parseSize : null,
"offset": (page) ? ( parsePage - 1) * parseSize : null,
};
}
function addSearchCondForAllCol(rawAttributes, search, Op) {
let condition = Object.keys(rawAttributes).filter(key => !["created_at", "updated_at", "deleted_at"].includes(key)).map(key => {
return { [key]: { [Op.like]: "%" + search + "%" } };
});
return condition;
},
getAllTasksForEmployee: async (pagesize, page, start, end, search, task_status_id, priority_id, t, employee_id) => {
try {
let condition = { };
const getPagination = pagination(pagesize, page);
let getDate = checkEndStart(start, end, t);
//any values exists for these fields then append it in the main condition object
if (employee_id) condition = { employee_id };
if(search) {
//build all conditions for every columns except created_at, updated_at and deleted_at
let conditionForAllCol = addSearchCondForAllCol(db.tasks.rawAttributes, search, sequelize.Op);
//adding extra condition as I also need to search based on the customer name too
conditionForAllCol = [
...conditionForAllCol,
sequelize.where(sequelize.col("customer.contact_person_name"), { [sequelize.Op.like]: "%" + search + "%" }),
sequelize.where(sequelize.col("customer.organization_name"), { [sequelize.Op.like]: "%" + search + "%" }),
sequelize.where(sequelize.col("task_status.name"), { [sequelize.Op.like]: "%" + search + "%" }),
sequelize.where(sequelize.col("priority.name"), { [sequelize.Op.like]: "%" + search + "%" }),
];
//building the final condition for the search feature
let searchCondition = {
[sequelize.Op.or]: conditionForAllCol
};
condition = { ...condition, ...searchCondition };
}
if(!(task_status_id === null || task_status_id === undefined || task_status_id === "")) condition = { ...condition, task_status_id };
if(!(priority_id === null || priority_id === undefined || priority_id === "")) condition = { ...condition, priority_id };
//final query
const instance = await db.tasks.findAll({
limit: getPagination.limit,
offset: getPagination.offset,
where: {
[sequelize.Op.and]: [
{
"planned_date": {
[sequelize.Op.gt]: getDate.startDate,
[sequelize.Op.lte]: getDate.endDate
},
...condition
}
]
},
order: [["planned_date", "DESC"]],
attributes: {
exclude: ["updated_at", "deleted_at"]
},
include: [
{
model: db.customers,
require: false,
attributes: {
exclude: ["created_at", "updated_at", "deleted_at"]
}
},
{
model: db.fences,
require: false,
attributes: {
exclude: ["created_at", "updated_at", "deleted_at", "status"]
}
},
{
model: db.employees,
attributes: ["name", "personal_email"]
},
{
model: db.task_statuses,
attributes: ["name", "color_code"]
},
{
model: db.priorities,
attributes: ["name", "color_code"]
},
{
model: db.task_documents,
require: false,
attributes: {
exclude: ["created_at", "updated_at", "deleted_at", "status"]
},
include: [
{
model: db.medias,
attributes: {
exclude: ["created_at", "updated_at", "deleted_at", "status"]
},
},
{
model: db.document_categories,
attributes: {
exclude: ["created_at", "updated_at", "deleted_at", "status"]
},
},
]
}
]
});
return instance;
} catch (error) {
console.log(error);
throw new Error(error);
}
},
我的要求:简单地根据搜索过滤器、排序名称、排序值获取数据。应根据栅栏表的“标题”列、任务状态和优先级表的“名称”列,搜索客户表的“contact_person_name,organization_name”列。
不知道问题出在哪里。
我将包含所需列的表格。 根据sequelize给出的rawAttributes属性,这将迭代任务表的所有列以检查搜索到的值。 此外,我使用添加客户、优先级和任务状态表的特定列
sequelize.where(sequelize.col(....
这对于“pagesize=&size=&”工作得非常好,并给出正确的结果,但对于“pagesize=10&size=1&”则不起作用。
在主
where
中使用带有嵌套模型条件的分页可能不起作用,因为默认情况下Sequelize会尝试使用子查询来限制主模型记录(否则你会得到错误的结果)。subQuery: false
并在包含的模型选项中指示 separate: true
来关闭子查询,以避免错误的结果。
const instance = await db.tasks.findAll({
limit: getPagination.limit,
offset: getPagination.offset,
subQuery: false,
where: {
[sequelize.Op.and]: [
{
"planned_date": {
[sequelize.Op.gt]: getDate.startDate,
[sequelize.Op.lte]: getDate.endDate
},
...condition
}
]
},
order: [["planned_date", "DESC"]],
attributes: {
exclude: ["updated_at", "deleted_at"]
},
include: [
{
model: db.customers,
require: false,
separate: true,
attributes: {
exclude: ["created_at", "updated_at", "deleted_at"]
}
},
如果您在关闭
separate: true
时未指示 subQuery
,那么任务中的记录将乘以客户中的记录,例如,如果您有 1 个任务,有 10 个客户,并将限制设置为 10,那么您将结束仅完成一个任务(因为在 SQL 查询中,您将获得具有相同任务和不同客户的 10 条第一条记录)。
尝试使用生成的 SQL 来查看差异