Maikkeyy
Maikkeyy

Reputation: 1047

Why does Sequelize add extra columns to SELECT query?

When i want to get some records with joined data from the referenced tables, Sequelize adds the reference columns twice: the normal one and a copy of them, written just a little bit different.

This is my model:

module.exports = function(sequelize, DataTypes) {
return sequelize.define('result', {
id: {
  type: DataTypes.INTEGER(10),
  allowNull: false,
  primaryKey: true,
  autoIncrement: true
},
test_id: {
  type: DataTypes.INTEGER(10),
  allowNull: false,
  references: {
    model: 'test',
    key: 'id'
  }
},
item_id: {
  type: DataTypes.INTEGER(10),
  allowNull: false,
  references: {
    model: 'item',
    key: 'id'
    }
 },
}, // and many other fields
{
tableName: 'result',
timestamps: false, // disable the automatic adding of createdAt and    updatedAt columns
underscored:true
});
}

In my repository I have a method, which gets the result with joined data. And I defined the following associations:

const Result = connection.import('../../models/storage/result');
const Item = connection.import('../../models/storage/item');
const Test = connection.import('../../models/storage/test');

Result.belongsTo(Test, {foreignKey: 'test_id'});
Test.hasOne(Result);

Result.belongsTo(Item, {foreignKey: 'item_id'});
Item.hasOne(Result);

// Defining includes for JOIN querys
var include = [{
model: Item,
attributes: ['id', 'header_en']
}, {
model: Test,
attributes: ['label']
}];

var getResult = function(id) {

    return new Promise((resolve, reject) => { // pass result
        Result.findOne({
            where: { id : id },
            include: include,
        //     attributes: ['id',
        // 'test_id',
        // 'item_id',
        // 'result',
        // 'validation'
        // ]
        }).then(result => {
            resolve(result);
            });
    }); 
}

The function produces the following query:

SELECT `result`.`id`, `result`.`test_id`, `result`.`item_id`,  `result`.`result`, `result`.`validation`, `result`.`testId`, `result`.`itemId`, `item`.`id` AS `item.id`, `item`.`title` AS `item.title`, `test`.`id` AS `test.id`, `test`.`label` AS `test.label` FROM `result` AS `result` LEFT OUTER JOIN `item` AS `item` ON `result`.`item_id` = `item`.`id` LEFT OUTER JOIN `test` AS `test` ON `result`.`test_id` = `test`.`id` WHERE `result`.`id` = '1';

Notice the extra itemId, testId it wants to select from the result table. I don't know where this happens. This produces:

Unhandled rejection SequelizeDatabaseError: Unknown column 'result.testId' in 'field list'

It only works when i specify which attributes to select.

EDIT: my tables in the database already have references to other tables with item_id and test_id. Is it then unnecessary to add the associations again in the application code like I do?

A result always has one item and test it belongs to.

How can i solve this?

Thanks in advance,

Mike

Upvotes: 4

Views: 6104

Answers (3)

PellucidWombat
PellucidWombat

Reputation: 109

Although your solution fixes your immediate problem, it is ultimately not what you should be doing, as the cause of your problem is misunderstood there. For example, you MUST make that sort of association if making a Super Many-to-Many relationship (which was my problem that I was trying to solve when I found this thread). Fortunately, the Sequelize documentation addresses this under Aliases and custom key names.

Sequelize automatically aliases the foreign key unless you tell it specifically what to use, so test_id becomes testId, and item_id becomes itemId by default. Since those fields are not defined in your Result table, Sequelize assumes they exist when generating the insert set, and fails when the receiving table turns out not to have them! So your issue is less associating tables twice than it is that one association is assuming extra, non-existing fields.

I suspect a more complete solution for your issue would be the following:

Solution

Result.belongsTo(Test, {foreignKey: 'test_id'});
Test.hasMany(Result, {foreignKey: 'test_id'});

Result.belongsTo(Item, {foreignKey: 'item_id'});
Item.hasOne(Result, {foreignKey: 'item_id'});

A similar solution fixed my nearly identical problem with some M:N tables.

Upvotes: 1

Maikkeyy
Maikkeyy

Reputation: 1047

SOLUTION:

Result.belongsTo(Test, {foreignKey: 'test_id'});
// Test.hasMany(Result);

Result.belongsTo(Item, {foreignKey: 'item_id'});
// Item.hasOne(Result);

Commenting out the hasOne, hasMany lines did solve the problem. I think I messed it up by defining the association twice. :|

Upvotes: 3

Shahzeb Khan
Shahzeb Khan

Reputation: 3642

Sequelize uses these column name by adding an id to the model name by default. If you want to stop it, there is an option that you need to specify.

underscored: true

You can specify this property on application level and on model level.

Also, you can turn off the timestamps as well. You need to use the timestamp option.

timestamps: false

Upvotes: 2

Related Questions