2021.11 v1.15


Leoric supports several timestamps:

Both createdAt and updatedAt are the widely received ones, which can be switched on in Sequelize with Model.init(attributes, { timestamps: true }). deletedAt is the one used to implement soft removal, which is automatically active if deletedAt presents.

In the November releases, timestamps in snake cases such as created_at and updated_at are recognized as well.

logQuery & logQueryError

Changes regarding query logging are as follow:

In v1.15.x versions, logQuery(sql, duration) will return more precise duration values, which helps performance auditing.

The breaking change will be released in v2.x, which changed from logQueryError(sql, err, duration, options) to logQueryError(err, sql, duration, options), by moving the err argument to a position that is more conventional in Node.js

model.sync({ force | alter })

Another breaking change that is scheduled in v2.x is the argument defaults of model.sync(), which is default to model.sync({ alter: true }) in v1.x

Since v2.x, there will be three sync modes:

Related changes are as below:

WHERE clause in JOIN queries


When carrying out JOIN queries, the WHERE conditions might be one of the following three cases:

There was an optimization to hoist the columns in WHERE clause that belong to the join target table, and have them put in subquery, then JOIN other tables on the subquery itself. This optimization might (or might not) gets better query plan, but falls short when dealing with the second case in the list above.

For example, if Posts and Comments were like:

await Post.bulkCreate([
  { id: 2, title: 'Archbishop Lazarus' },
  { id: 3, title: 'Archangel Tyrael' },

await Comment.bulkCreate([
  { articleId: 2, content: 'foo' },
  { articleId: 2, content: 'bar' },
  { articleId: 3, content: 'baz' },

and the query condition was like below:

await Post.include('comments').order('posts.id').where({
  'posts.title': { $like: 'Arch%' },
  'comments.content': 'baz',

the generated SQL will filter records of the target table in advance, which returns insufficient result.

   SELECT `posts`.*, `comments`.*
     FROM (
       SELECT *
         FROM `articles`
        WHERE `title` LIKE 'Arch%'
          AND `gmt_deleted` IS NULL
        LIMIT 1) AS `posts`
LEFT JOIN `comments` AS `comments`
       ON `posts`.`id` = `comments`.`article_id`
      AND `comments`.`gmt_deleted` IS NULL
    WHERE `comments`.`content` = 'baz'

Therefore this optimization is removed in this month, to make sure correct number of rows can be fetched in any of the cases listed above.

TypeScript Declarations

A few issues about the .d.ts added in October were found when testing it in real battlefield such as cnpm/cnpmcore, all of which has been persisted as test cases in test/types

There still are a few features unlisted in .d.ts, which should be complemented in December releases.


When Model.findOne() is unable to retrieve any record from database, either null or undefiend could be returned. It is changed into a more consistent way by always returning null if nothing could be found.