Define conversion when changing a column type with Sequelize - type-conversion

I want to change a column type from BLOB to TEXT, and I think that it would be logical to somehow specify what to do with each field (i.e. create a string with utf8 encoding), but I cannot find anywhere if you can define JS transformations during a migration.

It's not currently possible because the underlying SQL models don't support it. Instead, you could create a placeholder column, copy the values over, delete the old column, then rename the new column.
More info in the sequelize Github thread here. Taking an example from the thread:
module.exports = {
up: async (queryInterface, Sequelize) => {
return [
await queryInterface.addColumn('jobs', 'matterInfo', {
type: Sequelize.STRING(150),
defaultValue: ''
}),
await queryInterface.sequelize.query(
'UPDATE jobs SET matterInfo = CONCAT( matterNumber, " / ", matterName);'
),
// remove unused fields
await queryInterface.removeColumn('jobs', 'matterName'),
await queryInterface.removeColumn('jobs', 'matterNumber')
];
},
down: (queryInterface, Sequelize) => {
/*
Add reverting commands here.
Return a promise to correctly handle asynchronicity.
Example:
return queryInterface.dropTable('users');
*/
}
};

Related

Why is double underscore needed here for accessing table field?

I came across a GitHub issue about sorting rows with TypeORM. I found this comment did work for my problem.
Quote:
async sortWithRelations(entityRepository) {
// Assuming repository classname is RepoX
let repoOptions = {
relations: ['relationA', 'relationB'],
where: qb => {
// Filter if required
qb.where('RepoX__relationA.fieldY = :val', {val: 'searchedValue'});
// Then sort
qb.orderBy({
'RepoX__relationA.fieldYYY': 'DESC',
'RepoX__relationB.fieldZZZ': 'DESC'
// '{repositoryClassName}__{relationName}.fieldName
// [+ __{childRelations} for every child relations]
});
}
};
However, I have no idea why RepositoryClassName__ accompanied with double underscore is needed to access the table column?
'RelationName.FieldName': 'DESC' will result in error instead.

Postgsql 11 Creating an object using UUID but beforeCreate is not being fired to update the id with UUID before save

This seems to occur when using an object as join table in a M:N association.
The beforeCreate hook is not updating the id before DB update.
I have created an instance hook in postgresql Version 11 within the instance model.
I have attempted to use both methods 2 and 3 neither of which work. The id column is not updated with UUID generated at create time
When I log the value within the hook I have a unique uuid but in the creating SQL the default uuid is used resulting in unique violation error.
I expected the values in the before hook to be used for the DB create statement
Log messages:
UserPersonalRelationship.beforeCreate: 19bbc538-c82e-41f5-9bd3-f0eabf9c0193
UserPersonalRelationship.beforeCreate: 3af7d252-ecfe-49e9-b850-a72a78e9773d
In the postgresql error message the field value is shown as:
fields: { id: '2cfe1fb0-4b3c-496b-80fe-f9073302afe8' },
My instance model is:
"use strict";
const { uuid } = require("uuidv4");
module.exports = (sequelize, DataTypes) => {
const UserPersonalRelationship = sequelize.define(
"UserPersonalRelationship",
{
User_rowID: DataTypes.UUID,
PersonalRelationship_rowID: DataTypes.UUID,
},
{}
);
UserPersonalRelationship.associate = function (models) {
// associations can be defined here
};
UserPersonalRelationship.beforeCreate(
async (userPersonalRelationship, options) => {
const objUUID = uuid();
userPersonalRelationship.id = objUUID;
console.log("UserPersonalRelationship.beforeCreate: ", objUUID);
}
);
UserPersonalRelationship.addHook(
"beforeValidate",
(userPersonalRelationship, options) => {
userPersonalRelationship.id = uuid();
console.log("Add hook called");
}
);
return UserPersonalRelationship;
};

Feather.js + Sequelize + postgres 11 : How to patch a jsonb column?

I would like to update several row of my db with the same object.
let say I have a column customText type jsonb which contains an array of object
here my sequelize model :
customText: {
type: DataTypes.JSONB,
allowNull: true,
field: "custom_text"
}
Now from client I send an object:
const obj = {}
const data = {
textid: "d9fec1d4-0f7a-2c00-9d36-0c5055d64d04",
textLabel: null,
textValue: null
};
obj.customText = data
api.service("activity").patch(null, obj).catch(err => console.log(err));
Like the documentation from feathers.js said if I want to replace multiple record, I send an id equal to null.
So now here come the problem, if I do that my column customText will contain the new object only but I want an array of object, so I want to push the new data in the array. How can I patch the data?
My guess is to use a hook in feathers.js and a raw query with sequelize. But I'm not sure how to do that.
I'm not really sure of my answer but this hook work :
module.exports = function() {
return async context => {
debugger;
const sequelize = context.app.get("sequelizeClient");
const customText = JSON.stringify(context.data.customText[0]);
console.log(customField);
let query =
"UPDATE activity SET custom_text = custom_text || '" +
customText +
"' ::jsonb";
console.log(query);
await sequelize
.query(query)
.then(results => {
console.log(results);
context.results = results;
})
.catch(err => console.log(err));
return context;
I still have a problem because after this hook in feathers, the patch continue so it will update my db again.. so i put a disallow() hook.
Also, with this hook i lost the abilities to listening to event
Also i have a concern with the query, i'm not sure if it's better to use :jsonb_insert over ||

Sequelize migration add "IF NOT EXISTS" to addIndex and addColumn

Is there a way to force Sequelize.js to add IF NOT EXISTS to the Postgres SQL created by the queryInterface.addColumn and queryInterface.addIndex methods?
According to the Postgres Docs this is supported for Alter Table Add Column as well as Create Index
I have looked through the Sequelize.js docs without any luck, and I have tried to go through the code to figure out how the SQL is generated, but I have not had any luck yet.
A bit of background, or "Why"
I am trying to create a migration strategy for an existing postgres instance, and I have currently created a Sequelize migration set which migrates from "nothing" to the current schema. Now I would like to simply get this up and running on my production server where all of the data already exists such that the next time I create a migration, I can run it.
All of this works well for every queryInterface.createTable because the IF NOT EXISTS is automatically added.
I had a similar issue, except in my case I was only interested in addColumn IF NOT EXIST.
You can achieve this with a two step solution, using queryInterface.describeTable.
Given the table name the function will return the table definition which contains all the existing columns. If the column you need to add does not exist then call the queryInterface.addColumn function.
const tableName = 'your_table_name';
queryInterface.describeTable(tableName)
.then(tableDefinition => {
if (tableDefinition.yourColumnName) {
return Promise.resolve();
}
return queryInterface.addColumn(
tableName,
'your_column_name',
{ type: Sequelize.STRING } // or a different column
);
});
addColumn function comes from queryGenerator method called addColumnQuery, which accepts three parameters - table, key and dataType. With use of them it creates a query, which looks like that
let query = `ALTER TABLE ${quotedTable} ADD COLUMN ${quotedKey} ${definition};`;
So, as you can see, there is no option to add the IF NOT EXISTS clause to the query string. The same concerns the addIndex method unfortunately. However, you can use plain query in order to perform some atypical operations
queryInterface.sequelize.query(...);
The statement if (!tableDefinition.yourColumnName) won't be able to check if column exists.
Correct way is
return queryInterface.describeTable(tableName).then(tableDefinition => {
if (!tableDefinition[columnName]){
return queryInterface.addColumn(tableName, columnName, {
type: Sequelize.JSON
});
} else {
return Promise.resolve(true);
}
});
A small working example:
module.exports = {
/**
* #description Up.
* #param {QueryInterface} queryInterface
* #return Promise<void>
*/
up: async (queryInterface) => {
const tableDefinition = await queryInterface.describeTable('group');
const promises = [];
return queryInterface.sequelize.transaction((transaction) => {
if (!tableDefinition.column1) {
promises.push(queryInterface.addColumn(
'group',
'column1',
{
type: queryInterface.sequelize.Sequelize.STRING,
allowNull: true,
},
{transaction},
));
}
if (!tableDefinition.oauth2_token_expire_at) {
promises.push(queryInterface.addColumn(
'group',
'column2',
{
type: queryInterface.sequelize.Sequelize.DATE,
allowNull: true,
},
{transaction},
));
}
return Promise.all(promises);
});
},
/**
* #description Down.
* #param {QueryInterface} queryInterface
* #return Promise<void>
*/
down: (queryInterface) => {
...
},
};

How can I drop all tables with Sequelize.js using postgresql?

I am trying:
if (process.NODE_ENV === 'test') {
foreignKeyChecks = 0;
forceSync = true;
} else {
foreignKeyChecks = 1;
forceSync = false;
}
global.db.sequelize.query("SET FOREIGN_KEY_CHECKS = " + foreignKeyChecks).then(function() {
return global.db.sequelize.sync({
force: forceSync
});
}).then(function() {
return global.db.sequelize.query('SET FOREIGN_KEY_CHECKS = 1');
}).then(function() {
var server;
console.log('Initialzed database on:');
console.log(config.db);
return server = app.listen(port, function() {
return console.log("Server listening at http://" + (server.address().address) + ":" + (server.address().port));
});
})["catch"](function(err) {
return console.log('err', err);
});
module.exports = app;
But I get: SequelizeDatabaseError: unrecognized configuration parameter "foreign_key_checks"
I assume I can't have that keyword in postgres? But is there an equivalent way to drop all tables and recreate?
This is an updated answer, targeted at the googlers who wound up here like me.
Sequelize offers a drop function:
drop(options) => promise
Drop all tables defined through this sequelize instance. This is done by calling Model.drop on each model. Sequelize docs
Example
var sequelize = new Sequelize(config.database, config.username, config.password, config);
var someModel = sequelize.define('somemodel', {
name: DataTypes.STRING
});
sequelize
.sync() // create the database table for our model(s)
.then(function(){
// do some work
})
.then(function(){
return sequelize.drop() // drop all tables in the db
});
For wiping out data and create all again from scratch (like in tests):
sequelize.sync({force: true});
I don't know anything about that JavaScript library, but Postgres provides a single command to drop everything that is owned by a user:
drop owned by <our_user_name cascade
This will only work if everything is owned by the same user and that user doesn't have some tables (or views, sequences, ...) that you do not want to drop.
More details in the manual:
http://www.postgresql.org/docs/current/static/sql-drop-owned.html
For anyone looking for a solution with sequelize-cli checkout this link Sequelize CLI:
You can just run:
sequelize_cli db:drop
sequelize_cli db:create
To create or drop your db using the cli tool. This way you will have a empty db.