首页 > 解决方案 > 续集 findOne/findAll 查询不返回关联属性

问题描述

我正在尝试使用 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

我在这里错过了一些超级简单的东西吗?一直在拼命摆弄关联targetKey、、sourceKeyforeignKey以及更多类似上面列出的 findOne/findAll 查询排列,但运气为零。

任何建议将不胜感激!

更新一

经过一番摆弄之后,在设置关联时似乎存在一些区分大小写的奇怪行为。如果我定义与别名的关联as: 'modelb'并通过 查询association: 'modelb',上面的 fetch 语句按预期工作,并将所需的 ModelB 属性映射到返回的对象上。将任何大写字符引入别名似乎会破坏此功能 - 例如,别名xxxxxx工作正常,xxxxXx会导致上述原始问题。

直接的解决方法似乎是只用小写来给所有东西起别名,但我仍然想继续按预期工作。我希望这只是与 Postgres 和我的 Sequelize 配置与实际问题有关。

更新二

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

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

更新三

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

标签: javascriptnode.jspostgresqlsequelize.js

解决方案


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

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

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

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


推荐阅读