I'm using SequlizeJS ORM in my ExpressJS application to communicate with MariaDB. I'm working with existing database scheme so I can't change data types of the fields. Existing database uses unix timestamps in createdAt, updatedAt & deletedAt fields. Now I need to follow that for the new ExpressJS app too.
In each modal I use the following Sequelize hooks to convert createdAt & updatedAt fields to unix timestamps.
hooks: {
beforeCreate: (instance, options) => {
instance.dataValues.createdAt = Math.floor(Date.now() / 1000);
instance.dataValues.updatedAt = Math.floor(Date.now() / 1000);
},
beforeUpdate: (instance, options) => {
instance.dataValues.updatedAt = Math.floor(Date.now() / 1000);
}
}
but the thing is that I can't set deletedAt as a timestamp on beforeBulkDestroy hook. Can anyone please help me to resolve this ?
Thanks !
One thing to try - the individualHooks option calls the beforeDestroy() hook for each instance. This can be applied at the query level:
db.myFunkyModel.destroy({
where: {
'field' : { [Op.like]: '%someValue%' }
},
individualHooks : true
});
Or at a broader level:
const sequelizeDb = new Sequelize(
...
{
host: '127.0.0.1',
....
define: {
....
individualHooks : true
}
....
});
Take a look at the Model Hooks section of the manual for potential performance hits from individualHooks.
Related
I'm making a node api using prisma ORM and i'm trying to update a column which i had set with the type DateTime, here is the model, the column is the deleted_at one
model Employee {
id Int #id #default(autoincrement())
name String
created_at DateTime
deleted_at DateTime
}
how can i change it to the current time in my controller?
the controller looks like this
export const DeleteCompany = async (req:IEmployee, res:Response) => {
const data:ICompany = req
const deletedCompany = await prisma.employee.update({
where: {
id: Number(data.id)
},
data: {
deleted_at: //what should I put here?
}
})
return res.status(200).json(deletedCompany)
}
I've tried using
now()
but it didnt work.
Prisma supports plain javascript dates for setting date fields.
So new Date() should work fine:
deleted_at: new Date()
I would like to disable or silence the updatedAt field when I update a record. As per sequelize docs looks like this option was available on v5.9.2.
my sequelize version: ^5.22.5
I've tried the following however it performs the update but still proceeds to update the updatedAt field in the DB.
#1
model.update(
{
count: Number(model.count) + 2,
},
{
where: {
id: 10
},
silent: true
}
);
#2
model.increment('count', { by: 2, where: { id: 10 }, silent: true });
#3
model.save ({ silent: true })
silent is the app-level functionalities not to update the updatedAt column, however, if you have ON UPDATE CURRENT_TIMESTAMP, this is the DB-level automatic update on the column.
TL;DR
silent works by not passing updatedAt to UPDATE query. So if the DB's column doesn't have any automatic assignment, by not passing the new value for updatedAt, the column value should stay the same. While, if you have automatic update on the DB's column, not passing the value means it allows DB to automatically assign the new value.
How I can do "silence" without updating DB schema
To disable the DB's automatic field, you can pass the value that you want to assign for the column. By default, Sequelize also has an automatic assignment of updateAt column, so we need to overwrite it.
const model = sequelize.define('MyModel',
{
count: {
type: DataTypes.INTEGER
}
},
{
hooks: {
beforeUpdate: (record, options) => {
if(options['custom_silent']) { // Add my custom silencing option
// If custom_silent is true, pass previous(current value in DB) updatedAt value and assigned to it
record.dataValues.updatedAt = record._previousDataValues.updatedAt
}
// If custom_silent is not true, use default Sequelize behavior (updatedAt = (current timestamp))
}
}
}
)
How to use it
await modelInstance.update(
{
count: modelInstance.count + 1000,
},
{
custom_silent: true
}
);
When I execute a query that that has a data passed as replacement, the date is not set as UTC-Date in the query.
My code:
let startInterval = moment('2020-12-09').toDate();
db.query(`
SELECT kv.kpiId
FROM kpiValues kv
WHERE kv.insertDate >= :startInterval
`, {
type: QueryTypes.SELECT,
replacements: {
startInterval: startInterval,
}
}).catch(next)
Printing the startInterval variable results in 2020-12-08T23:00:00.000Z.
When I output the query it shows
SELECT kv.kpiId
FROM kpiValues kv
WHERE kv.insertDate >= '2020-12-09 00:00:00.000'
The column is a sequelize Date column (SQL Datetime). The value in the query is my local time - but the database should only use UTC values - so I would expect it to use 2020-12-08T23:00:00.000 in the query. What can I do?
The sequelize connection:
const sequelize = new Sequelize(dbName, dbUser, dbPassword, {
host: Settings.dbUrl,
port: dbPort,
dialect: 'mariadb',
dialectOptions: {},
timezone: '+00:00',
pool: {
max: 70,
min: 5,
acquire: 30000
},
define: {
timestamps: false,
freezeTableName: true
},
logging: true // Remove property when all statements that are executed should be printed
});
As there is no solution for this problem with mariaDB as DBMS using sequelize the following workaround works:
Just manually pass the replacement variable as UTC-String:
dateVariable.toISOString()
I need to get the current time, according to the database timezone (not my local timezone, and not default UTC timezone), using Sequelize. Is there a Sequelize method to do this?
My database is in Eastern time, and when I query the db directly SELECT CURRENT_TIMESTAMP; it returns the date/time in Eastern time (which is correct).
But when I query via Sequelize in Node const [[{time}]] = await db.sequelize.query('SELECT CURRENT_TIMESTAMP AS time'); it returns the date/time in UTC.
Two problems:
1 - I would prefer using a Sequelize method instead of a raw query.
2 - This still doesn't get me the result I want. The time needs to be Eastern.
This is my DB setup:
const sequelize = new Sequelize(dbUrl, {
dialectOptions: {
useUTC: false // for reading from database
},
timezone: '-04:00', // for writing to database
define: {
charset: 'utf8'
}
})
As mentioned above, when I query using the above queries, the date is always returned in UTC, which I did not expect, given I said useUTC: false. How do I get it in Eastern time (the database timezone)?
I'm not aware of a sequelize method like getCurrentDate().
The UTC conversion problem seems to bite everyone (myself included). Here are some details. Not sure if dialectOptions: {useUTC: false }, has any function at all - just adding the typeCast method solved the problem for me.
dialectOptions: {
typeCast: function (field, next) { // for reading from database
if (field.type === 'DATETIME') {
return field.string()
}
return next()
},
The result can be used for a new js Date object:
const sql = 'select current_timestamp';
my_app.my_DB.query(sql, {raw: true, type: Sequelize.QueryTypes.SELECT})
.then(data => {
console.log(data[0].current_timestamp);
let d1 = new Date(data[0].current_timestamp);
});
This works fine for me - but make sure to test thoroughly!
Add useUTC property in your dialectOptions like this
dialectOptions: {
encrypt: false ,
options: {
useUTC: false, // for reading from database
},
},
In my collection I'd like to have automatically generated createdAt and updatedAt fields that would contain the date of when the object was inserted / updated for the last time - kind of like it's happening in Ruby on Rails. Currently I'm doing this with an observer similar to this one:
MyCollection.find({}).observeChanges({
changed: function(id, changes) {
MyCollection.update(id, ...);
},
});
Is there a better / more efficient / more straightforward way?
I use Collection2. It supports autoValue in the schema, a function that computes the forced value of a field. As these two fields are used in all collections, you can save them to a variable:
#SchemaHelpers =
createdAt:
type: Date
autoValue: ->
if #isInsert
return new Date
if #isUpsert
return $setOnInsert: new Date
if #isUpdate
#unset()
return
updatedAt:
type: Date
autoValue: ->
return new Date
And then in the collection:
Schema = {}
Posts = new Meteor.Collection("posts")
Schema.Posts = new SimpleSchema
createdAt: SchemaHelpers.createdAt
updatedAt: SchemaHelpers.updatedAt
title:
type: String
max: 30
body:
type: String
max: 3000
Posts.attachSchema(Schema.Posts)
This solution makes updatedAt always present and its value will be very close to createdAt when it is just inserted (not necessarily the same). If you need updatedAt not to be set when inserting, you can use something like the example in the Collection2 readme:
updatedAt: {
type: Date,
autoValue: function() {
if (this.isUpdate) {
return new Date();
}
},
denyInsert: true,
optional: true
},
but this does not handle upserts. I don't know any good solution that handles upserts correctly and leaves the field empty at inserts.
I like https://github.com/matb33/meteor-collection-hooks
collection.before.insert (userId, doc) ->
doc.createdAt = new Date().valueOf #toISOString()