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

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) => {
...
},
};

Related

Prisma: Finding items where two fields have the same value

I would like to find items in a Prisma db where the values for two columns are the same. The use case is to compare the 'created_at' and 'updated_at' fields to find items that have never been updated after their initial creation. In raw SQL I would do something like:
select updated_at,
cast(sign(sum(case when updated_at = created_at then
1
else
0
end)) as int) as never_modified
from tab
group by updated_at
Is it possible to achieve this in Prisma?
You would need to use Raw Queries to compare time values from the same table.
Here's an example of how you could achieve this, assuming a PostgreSQL database for the following query.
import { PrismaClient } from '#prisma/client'
const prisma = new PrismaClient()
async function initiateDatesComparisonRawQuery() {
const response =
await prisma.$queryRaw`SELECT * FROM "public"."Project" WHERE "created_at" = "updated_at";`;
console.log(response);
}
await initiateDatesComparisonRawQuery();
you can use the preview feature fieldReference of prisma.
schema.prisma
generator client {
provider = "prisma-client-js"
previewFeatures = ["fieldReference"]
}
your code
prisma.project.findMany({
where: { created_at: prisma.project.fields.updated_at }
})

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.

How to update with returning values in TypeORM?

I wanted to update values and to return specified columns with PostgreSQL.
So far, what I found was update the value then use findOne, but then again it will always use two queries to achieve what I want.
Another is using RAW SQL, UPDATE ... SET ... WHERE ... RETURNING * and this seems a great solution so is there a way to achieve this with TypeORM using UpdateQueryBuilder?
You can use createQueryBuilder:
const firstUser = await connection
.getRepository(User)
.createQueryBuilder("user")
.update<User>(User, {firstName: 'new first name'})
.where("user.id = :id", { id: 1 })
.returning(['id', 'email'])
.updateEntity(true)
.execute();
Notice: there are many ways in type orm to use createQueryBuilder such as: BaseEntity, Repository, EntityManager.
If using repository, this is slightly modified answer and inspired from Noam's answer:
import {
EntityRepository,
Repository,
} from "typeorm";
import { User } from "../entities/user";
#EntityRepository(User)
export class UserRepository extends Repository<User> {
// ... other functions
updateUser = async (payload: User, id: string): Promise<User> => {
const updatedData = await this.createQueryBuilder("user")
.update<User>(User, { ...payload })
.where("user.id = :id", { id: id })
.returning("*") // returns all the column values
.updateEntity(true)
.execute();
return updatedData.raw[0];
};
}

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 ||

Define conversion when changing a column type with Sequelize

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');
*/
}
};