【问题标题】:Sequelize findOne/findAll query not returning association attributes续集 findOne/findAll 查询不返回关联属性
【发布时间】: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_keyModelB.model_b_value 的属性。

我在这里错过了一些超级简单的东西吗?一直在拼命摆弄targetKeysourceKeyforeignKey 值以及更多类似上面列出的 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


【解决方案1】:

意识到我可以发布答案 - 所以如果其他人遇到这个问题,这些是我目前的发现/解决方案:

所以看起来所有大写的列引用/别名,虽然在构造的 SQL 查询中表示为定义(例如SELECT "ModelB"."model_b_key" AS "ModelB.model_b_key"),但从 pg 客户端返回的行对象属性返回小写(例如row['modelb.model_b_key'])。结果,字段标签不再与其原始关联标签对齐,并且没有任何数据映射到模型实例。

设置 Sequelize 选项minifyAliases: true 似乎可以解决问题。启用此选项后,列别名在查询中表示为_0_1 等,然后映射回它们各自的字符串标签,避免别名被无意转换为小写的可能性。

这实际上似乎是与 Sequelize/pg 一起出现的 AWS Redshift 特定行为。我不知道 Redshift 默认将所有列名(和别名)转换为小写(通过describe_field_name_in_uppercase 控制),这解释了为什么所有返回的行对象都将其别名转换为小写。我想这对于使用双引号 "" 列名/别名引用来尊重大写/小写的传统 Postgres 实现来说不是问题。

【讨论】:

  • 我正在使用 node/express 和 PostgreSQL。我正在使用 pg,但如果我使用 Redshift,我不知道。我也有这个问题。我注意到无论我如何更改它,它都完全忽略了大小写。感谢您分享您的发现
猜你喜欢
  • 2015-06-02
  • 1970-01-01
  • 1970-01-01
  • 2023-01-02
  • 2011-09-15
  • 2014-10-04
  • 1970-01-01
  • 2022-10-19
  • 2017-04-15
相关资源
最近更新 更多