【发布时间】:2020-06-05 08:01:07
【问题描述】:
我正在尝试使用 Sequelize 获取基本关联/联接查询,但似乎无法获取从包含关联返回的数据。到目前为止,我尝试了多种方法均无济于事,但目前正在使用以下方法(为演示而简化):
// package.json
{
...
"dependencies": {
"pg": "^7.18.1",
"pg-hstore": "^2.3.3",
"sequelize": "^5.21.4"
}
}
// sequelize.js
const Sequelize = require('sequelize')
const options = {
...
databaseVersion: '8.0.2', // RedShift Postgres version
dialect: 'postgres',
dialectOptions: {
ssl: {
require: true,
rejectUnauthorized: true
}
}
define: {
timestamps: false,
freezeTableName: true
}
}
const sequelize = new Sequelize(<database>, <user>, <pass>, options)
module.exports = sequelize
// modelA.model.js
const { DataTypes } = require('sequelize')
const sequelize = require('./sequelize')
const schema = 'my_schema'
const tableName = 'my_table_a'
const modelName = 'ModelA'
const attributes = {
model_a_key: {
type: DataTypes.CHAR(32)
},
model_a_value: {
type: DataTypes.STRING
}
model_b_key: {
type: DataTypes.CHAR(32)
},
}
const options = {
schema
tableName
}
sequelize.define(modelName, attributes, options)
// modelB.model.js
const { DataTypes } = require('sequelize')
const sequelize = require('./sequelize')
const schema = 'my_schema'
const tableName = 'my_table_b'
const modelName = 'ModelB'
const attributes = {
model_b_key: {
type: DataTypes.CHAR(32)
},
model_b_value: {
type: DataTypes.STRING
}
}
const options = {
schema
tableName
}
sequelize.define(modelName, attributes, options)
// associations.js
const { models } = require('./sequelize.js')
ModelA.belongsTo(models.ModelB, { foreignKey: 'model_b_key' })
// Have left this out for now since I can't seem to even leverage the above association properly...
// ModelB.belongsTo(models.ModelA, { foreignKey: 'model_b_key' })
上述文件按sequelize、models、associations的顺序加载。在我看到的 Sequelize 示例之后,我尝试了以下查询来访问 ModelA 及其关联的 ModelB 的数据:
// Attempt #1
await sequelize.models.ModelA.findOne({ include: ['ModelB'] })
// returned data = { model_a_key, model_a_value, model_b_key }; missing all ModelB columns
// Attempt #2
await sequelize.models.ModelA.findOne({ attributes: ['ModelB.model_b_value'], include: ['ModelB'] })
// returned data = {}
// Attempt #3
await sequelize.models.ModelA.findOne({ include: [{ association: 'ModelB', required: true }] })
// returned data = { model_a_key, model_a_value, model_b_key }; missing all ModelB columns
// Attempt #4
await sequelize.models.ModelA.findOne({ include: [{ association: 'ModelB', attributes: ['model_b_value'] }] })
// returned data = { model_a_key, model_a_value, model_b_key }; missing model_b_value
生成的 SQL 似乎在实际获取感兴趣的列方面进行了检查,例如尝试 #1 产生:
SELECT "ModelA"."model_a_key", "ModelA"."model_a_value", "ModelA"."model_b_key", "ModelB"."model_b_key" AS "ModelB.model_b_key", "ModelB"."model_b_value" AS "ModelB.model_b_value"
FROM "my_schema"."my_table_a" AS "ModelA"
LEFT OUTER JOIN "my_schema"."my_table_b" AS "ModelB" ON "ModelA"."model_b_key" = "ModelB"."model_b_key"
LIMIT 1;
如果我设置选项raw: true,这些值也可用。但是,如果我尝试使用模型对象,则有关关联 ModelB 的所有信息似乎都会丢失/丢弃 - 返回的对象上既不存在 ModelB 的嵌套对象,也不存在 ModelB.model_b_key、ModelB.model_b_value 的属性。
我在这里错过了一些超级简单的东西吗?一直在拼命摆弄targetKey、sourceKey、foreignKey 值以及更多类似上面列出的 findOne/findAll 查询排列,但运气为零。
任何建议将不胜感激!
更新我
经过一番折腾后,在设置关联时似乎出现了一些区分大小写的奇怪行为。如果我使用别名as: 'modelb' 定义关联并通过association: 'modelb' 进行查询,则上述提取语句按预期工作并将所需的ModelB 属性映射到返回的对象上。将任何大写字符引入别名似乎会破坏此功能 - 例如别名为xxxxxx 工作正常,xxxxXx 导致上述原始问题。
直接的解决办法似乎是只用小写来给所有东西起别名,但我仍然想继续让它按预期工作。我希望这与 Postgres 和我的 Sequelize 配置与实际问题有关。
更新二
所以看起来所有大写的列引用/别名,虽然在构造的 SQL 查询中表示为定义(例如SELECT "ModelB"."model_b_key" AS "ModelB.model_b_key"),但从pg 客户端返回的行对象属性返回小写(例如row['modelb.model_b_key']) .结果,字段标签不再与其原始关联标签对齐,并且没有任何数据映射到模型实例。
设置 Sequelize 选项minifyAliases: true 似乎可以解决问题。启用此选项后,列别名在查询中表示为 _01、_02 等,然后映射回它们各自的字符串标签,避免了别名被无意转换为小写的可能性。
更新 III
如果有人感兴趣,这实际上似乎是 AWS Redshift 特定的行为。我不知道 Redshift 默认将所有列名(和别名)转换为小写(通过describe_field_name_in_uppercase 控制),这解释了为什么所有返回的行对象都将其别名转换为小写。我想这对于使用双引号 "" 列名/别名引用来尊重大写/小写的传统 Postgres 实现来说不是问题。
【问题讨论】:
-
返回的 ModelA 实例的关联属性中是否有 ModelB?
-
ModelA 类本身具有与 ModelB 列出的关联属性,但从
findAll()返回的 ModelA 实例没有关联属性。 -
看来你必须在调用 sequelize.define 之前在 ModelA.model.js 中移动 ModelA.belongsTo(models.ModelB, { foreignKey: 'model_b_key' }) 行
-
也许我理解错了,但是在模型本身定义之前,我将如何引用 ModelA 并声明它的关联?在定义了所有依赖模型之后,我认为需要将印象关联配置为最后一步。
-
所以我在其他帖子上看到过这种定义
associate函数的方法,但我发现的所有文档都只有 A.belongsTo(B)、A.hasMany(B) 等示例在。我最初给这个ModelA.associate = ...方法试了一下,看看它是否修复了任何东西,但似乎无法让事情正常工作,而且我也不确定我是否设置正确。对于我最初的问题,我至少似乎有一个解决方法/解决方案,但是您是否知道我可以在哪里找到这种其他关联方法的文档/示例?
标签: javascript node.js postgresql sequelize.js