从多对多关系中获取结果

Posted

技术标签:

【中文标题】从多对多关系中获取结果【英文标题】:Getting results from a many-to-many relation 【发布时间】:2020-06-06 20:12:37 【问题描述】:

我试图从 GraphQL 解析器中的多对多关系(用户和角色之间)获取结果,但我对 Sequelize 很陌生,不了解查询模型的正确方法。

这是我的用户模型:

module.exports = function(sequelize, DataTypes) 
var User = sequelize.define('users', 
	id: 
		type: DataTypes.INTEGER(10).UNSIGNED,
		allowNull: false,
		primaryKey: true,
		autoIncrement: true
	,
	name: 
		type: DataTypes.STRING(50),
		allowNull: false
	,
	surname: 
		type: DataTypes.STRING(50),
		allowNull: false
	,
	email: 
		type: DataTypes.STRING(256),
		allowNull: false
	
, 
	tableName: 'users',
	timestamps: false,
);

User.associate = function (models) 
	User.belongsToMany(models.roles, as: 'UserRoles', through: 'users_roles', foreignKey: 'user_id')
;

return User
;

这是我的角色模型:

module.exports = function(sequelize, DataTypes) 
var Role = sequelize.define('roles', 
	id: 
		type: DataTypes.INTEGER(10).UNSIGNED,
		allowNull: false,
		primaryKey: true,
		autoIncrement: true
	,
	name: 
		type: DataTypes.STRING(50),
		allowNull: false
	
, 
	tableName: 'roles',
	timestamps: false,
);

Role.associate = function (models) 
	Role.belongsToMany(models.users, through: 'users_roles', foreignKey: 'role_id')
;

return Role
;

这是我的连接表模型:

module.exports = function(sequelize, DataTypes) 
	return sequelize.define('users_roles', 
		id: 
			type: DataTypes.INTEGER(10).UNSIGNED,
			allowNull: false,
			primaryKey: true,
			autoIncrement: true
		,
		user_id: 
			type: DataTypes.INTEGER(10).UNSIGNED,
			allowNull: false,
			references: 
				model: 'users',
				key: 'id'
			
		,
		role_id: 
			type: DataTypes.INTEGER(10).UNSIGNED,
			allowNull: false,
			references: 
				model: 'roles',
				key: 'id'
			
		
	, 
		tableName: 'users_roles',
		timestamps: false
	);
;

到目前为止,这是我的 GraphQL 定义:

import  gql  from 'apollo-server-express'
import * as db from '../database'

export const typeDefs = gql`
    extend type Query 
        users: [User]
        user(id: ID!): User
    
    type User 
        id: ID!
        email: String
        name: String
        surname: String
        roles: [String]
    
`

export const resolvers = 
    Query: 
        users: async () => db.users.findAll(),
        user: async (obj, args, context, info) => db.users.findByPk(args.id),
    ,
    User: 
        roles: async (obj, args, context, info) => db.roles.findAll(), // wrong!!
    

所以基本上我的问题是我不知道应该如何编写查询来获取分配给一个用户的所有角色的列表。

我最终想要得到的是一个包含所有角色名称的字符串数组(如用户类型定义中所示)。

【问题讨论】:

从版本 3 开始不支持使用classMethods,最新版本已经是版本 6。您应该关注extensive documentation and examples available,而不是关注其他可能已经过时的教程。 @DanielRearden 正如我所说,我对 Sequelize 不太熟悉。经过广泛的谷歌搜索,看起来在各种文件中定义的模型之间进行关联似乎很复杂,正如this page 所示......我从那里得到了关联方法解决方案。 @DanielRearden 无论如何,我已经更新了我的代码,从用户和角色中删除了 classMethods(请参阅问题中更新的 sn-ps)。我已经检查了一个 console.log 并且这些方法确实被调用了。感谢您的提醒。 【参考方案1】:

给定一个User 实例,我们已经正确定义了与另一个模型的HasManyBelongsToMany 关系,我们可以通过在我们的实例上调用适当的方法来获取关联模型的数组。方法名称通常为get[PLURAL_NAME_OF_MODEL](即getRoles),但由于您为您的关系提供了as 选项,因此该方法应为getUserRoles。所以只要调用就足够了:

return obj.getUserRoles()

请注意,您也可以在根解析器中eager load 关联模型。在这种情况下,如果as 选项与您的字段名称匹配(即roles),您可以完全省略roles 的解析器。如果您渴望加载关联的模型,但 as 与您的字段名称不匹配(如本例中),那么您可以返回 obj[WHATEVER_THE_AS_OPTION_IS](即 obj.UserRoles)。

编辑:由于roles 是字符串列表而不是对象,我们需要执行以下操作:

const roles = await obj.getUserRoles()
return roles.map(role => role.name)

【讨论】:

我当然知道db.roles.findByPk(obj.user_id) 是不正确的,因此紧随其后的是“错误”评论。这只是一些占位符代码。我已将其替换为 db.users.getUserRoles(),现在当我尝试调用 GraphQL 查询时得到的是这个错误:_database__WEBPACK_IMPORTED_MODULE_1__.users.getUserRoles is not a function 没有静态的getUserRoles 方法。只是一个实例方法。您需要在 User 的实例上调用它,这就是解析器的第一个参数。在实例上调用它会得到该特定实例的关联模型。 哦,是的,谢谢!我明白了。还有一件事,如果你愿意的话:我怎样才能得到名字的数组?我正在这样做obj.getRoles(attributes: ['name']) 但收到错误String cannot represent value: dataValues: name: \"Guitar\", users_roles: [users_roles] 等等 糟糕,我的错。我期待roles 返回一个对象列表——我没有意识到您的架构将其键入为[String]。请查看我的编辑。 很抱歉再次打扰您...是否有可能一次性完成您建议的两件事(急切加载和地图功能)?现在我已经将obj.getUserRoles() 放入它自己的解析器中,但这当然意味着对每个用户执行角色表的查询。但是,如果我删除它并添加例如 users: async () => db.users.findAll(include: db.roles) 我会再次收到错误(当然因为 graphql 需要一个字符串数组而不是对象数组)。

以上是关于从多对多关系中获取结果的主要内容,如果未能解决你的问题,请参考以下文章

从多对多关系中获取数据

从多对多关系中获取核心数据

如何在 Laravel 中从多对多关系的一对多关系中获取项目?

使用非关联数据 (LINQ) 从多对多关系中获取值

Django 从多对多关系中删除对象

使用实体框架从多对多关系中选择数据