我使用sequelize/mySQL具有GraphQl/Apollo服务器。我的GraphQL类型(员工,承包商(每个人都实现了一个人界面。我的数据库模型包含一个员工,承包商和活动表。我希望我的人类型与事件有"许多"关系。我的活动类型"属于"人类型员工或承包商。
我猜想它与事件类型中的person_id
字段有关。我可以在没有一个表"员工"上的接口,然后将person_id
更改为 employee_id
。所以我猜想这只是不知道如何区分员工和承包商来参考该表?
//typeDefs.js
const typeDefs = gql`
type Event {
id: ID!
person_id: ID!
location: String!
escort: String!
}
interface Person {
id: ID!
first_name: String!
last_name: String!
department_company: String!
events: [Event]
}
type Employee implements Person {
id: ID!
first_name: String!
last_name: String!
department_company: String!
events: [Event]
employee_sh_id: String
}
type Contractor implements Person {
id: ID!
first_name: String!
last_name: String!
department_company: String!
events: [Event]
escort_required: Boolean!
}
//Employee model
module.exports = (sequelize, DataTypes) => {
const Employee = sequelize.define('Employee', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
first_name: DataTypes.STRING,
last_name: DataTypes.STRING,
department_company: DataTypes.STRING,
emplyee_sh_id: DataTypes.STRING
}, {});
Employee.associate = function(models) {
Employee.hasMany(models.Event);
};
return Employee;
};
// Contractor model
module.exports = (sequelize, DataTypes) => {
const Contractor = sequelize.define('Contractor', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
first_name: DataTypes.STRING,
last_name: DataTypes.STRING,
department_company: DataTypes.STRING,
escort_required: DataTypes.BOOLEAN,
}, {});
Contractor.associate = function(models) {
Contractor.hasMany(models.Event);
};
return Contractor;
};
// Event model
module.exports = (sequelize, DataTypes) => {
const Event = sequelize.define(
"Event",
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
person_id: DataTypes.INTEGER,
location: DataTypes.STRING,
escort: DataTypes.STRING
},
{}
);
Event.associate = function(models) {
Event.belongsTo(models.Employee),
Event.belongsTo(models.Contractor)
};
return Event;
};
// resolvers.js
const resolvers = {
Query: {
async employee(root, { id }, { models }) {
return models.Employee.findByPk(id);
},
async contractor(root, { id }, { models }) {
return models.Contractor.findByPk(id);
},
async employees(root, args, { models }) {
return models.Employee.findAll();
},
async contractors(root, args, { models }) {
return models.Contractor.findAll();
},
async event(root, { id }, { models }) {
return models.Event.findByPk(id);
},
async events(root, args, { models }) {
return models.Event.findAll();
}
},
Mutation: {
async addEmployee(
root,
{
first_name,
last_name,
department_company,
employee_sh_id
},
{ models }
) {
return models.Employee.create({
first_name,
last_name,
department_company,
employee_sh_id
});
},
async addContractor(
root,
{
first_name,
last_name,
department_company,
escort_required,
},
{ models }
) {
return models.Contractor.create({
first_name,
last_name,
department_company,
escort_required,
});
},
async addEvent(
root,
{ person_id, location, escort },
{ models }
) {
return models.Event.create({
person_id,
location,
escort
});
},
Person: {
__resolveType: person => {
if (person.employee) {
return "Employee";
}
return "Contractor";
}
},
Employee: {
events: (parent, args, context, info) => parent.getEvents(),
},
Contractor: {
events: (parent, args, context, info) => parent.getEvents(),
}
};
您甚至需要接口吗?
诸如接口和工会之类的抽象类型背后的主要目的是,它们允许特定字段解析为一组类型之一。如果您具有Contractor
和Employee
类型,并且希望特定字段返回任何一种类型,则添加接口或联合以处理该方案是有意义的:
type Event {
contractors: [Contractor!]!
employees: [Employee!]!
people: [Person!]! # could be either
}
如果您不需要此功能,则实际上不需要任何抽象类型。(旁注:您 can 使用接口仅在共享字段的类型之间执行一致性,但是那时您只是将它们用作验证工具,并且它们的存在可能不应影响您设计基础数据层的方式(。
没有银弹
处理关系数据库中的继承可能很棘手,并且没有一个大小适中的答案。使用续集或其他ORM时,事情变得怪异,因为您的解决方案也必须在该特定库的范围内工作。这是您可以解决此问题的几种不同的方法,尽管它不是详尽的列表:
- 如果您只有几个字段返回
Person
类型,则可以摆脱单独的表格和单独的模型,然后自己合并结果。类似:
people: async (event) => {
const [employees, contractors] = await Promise.all([
event.getEmployees(),
event.getContractors(),
])
const people = employees.concat(contractors)
// Sort here if needed
return people
}
这确实意味着您现在正在查询DB两次,并且可能会花更多的时间进行分类,而DB本来可以为您做的。但是,这意味着您可以为承包商和员工维护单独的表格和模型,这意味着对这些实体进行查询。
- 使用某种
type
字段来区分两者。然后,您可以使用范围来帮助您在续集中适当建模:
Event.hasMany(models.Person, { as: 'employees', scope: {type: 'EMPLOYEE'} ... })
Event.hasMany(models.Person, { as: 'contractors', scope: {type: 'CONTRACTOR'} ... })
Event.hasMany(models.Person, { as: 'people', /** no scope **/ ... })
这有效,即使没有"感觉"将所有内容都放在一张桌子中。您必须记住正确范围的关联和查询。
- 如果您严格用作ORM的续集,而不是从续集模型中生成数据库(即未调用
sync
(,则也可以将视图模拟为续集模型。观点有点痛苦,但这将使您可以为员工和承包商保留单独的桌子,同时与其他两个可以用来查询所有人的虚拟表。