SequelizeでJSONカラムを扱う

MySQLのJSONのdata typeをSequelizeで使ってみました。

MySQLのバージョンは5.7.24を使用しています。

テーブル構成

name type
id int(11)
title varchar(255)
info json
createdAt datetime
updatedAt datetime

infoの中にはこの様なデータを入れています。

key val
isbn10 string
isbn13 string
page integer

マイグレーションファイル

JSONのカラムのtypeにはSequelize.JSONを指定します。

'use strict';
module.exports = {
  up: (queryInterface, Sequelize) => {
    return queryInterface.createTable('Books', {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER
      },
      title: {
        type: Sequelize.STRING
      },
      info: {
        type: Sequelize.JSON
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE
      }
    });
  },
  down: (queryInterface, Sequelize) => {
    return queryInterface.dropTable('Books');
  }
};

初期データ

id title info createdAt updatedAt
1 青春を山に賭けて {“page”: 297, “isbn10”: “4167178060”, “isbn13”: “978-4167178062”} 2019-03-30 17:31:04 2019-03-30 17:31:04
2 エベレストを越えて {“page”: 307, “isbn10”: “4167178052”, “isbn13”: “978-4167178055”} 2019-03-30 17:31:04 2019-03-30 17:31:04
3 {“page”: 366, “isbn10”: “4101235171”, “isbn13”: “978-4101235172”} 2019-03-30 17:31:04 2019-03-30 17:31:04

検索

JSONデータ内のisbn10に対して検索してみます。

db.Book.findOne({where: {
    "info.isbn10": "4167178060"
  }
}).then((books)=>{
  console.log(books.dataValues)
}).then(()=>{
  db.sequelize.close()
})

SQL

SELECT `id`, `title`, `info`, `createdAt`, `updatedAt` FROM `Books` AS `Book` WHERE (`Book`.`info`->>'$."isbn10"') = '4167178060' LIMIT 1;

実行結果

{ id: 1,
  title: '青春を山に賭けて',
  info:
   { page: 297, isbn10: '4167178060', isbn13: '978-4167178062' },
  createdAt: 2019-03-30T08:31:04.000Z,
  updatedAt: 2019-03-30T08:31:04.000Z }

JSONのカラムはdictionary形式で返ってくるのでこのようにアクセスできます。

books.dataValues.info.page // 297

特定の値のみ更新

JSONデータのアップデート時は以下のように実行すると他のJSONデータが消えてしまうので

db.Book.update({
  "info.isbn10": "xxxxx"
}, {
  where: {id: 1}
}).then(()=>{
  db.sequelize.close()
})

このように指定してMySQLのJSON_SET関数を呼び出して指定の項目のみ更新します。

db.Book.update({
  info: db.sequelize.fn("JSON_SET", db.sequelize.col("info"), "$.isbn", "xxxxx")
}, {
  where: {id: 1}
}).then(()=>{
  db.sequelize.close()
})

SQL

UPDATE `Books` SET `info`=JSON_SET(`info`, '$.isbn', 'xxxxx'),`updatedAt`=? WHERE `id` = ?

実行後データ

id title info createdAt updatedAt
1 青春を山に賭けて {“page”: 297, “isbn10”: “xxxxx”, “isbn13”: “978-4167178062”} 2019-03-30 17:31:04 2019-03-30 20:31:45
2 エベレストを越えて {“page”: 307, “isbn10”: “4167178052”, “isbn13”: “978-4167178055”} 2019-03-30 17:31:04 2019-03-30 17:31:04
3 {“page”: 366, “isbn10”: “4101235171”, “isbn13”: “978-4101235172”} 2019-03-30 17:31:04 2019-03-30 17:31:04

削除

検索の場合と同じように指定できます。

db.Book.destroy({
  where: {"info.isbn10": "xxxxx"}
}).then(()=>{
  db.sequelize.close()
})

SQL

DELETE FROM `Books` WHERE (`info`->>'$."isbn10"') = 'xxxxx'

実行後データ

id title info createdAt updatedAt
2 エベレストを越えて {“page”: 307, “isbn10”: “4167178052”, “isbn13”: “978-4167178055”} 2019-03-30 17:31:04 2019-03-30 17:31:04
3 {“page”: 366, “isbn10”: “4101235171”, “isbn13”: “978-4101235172”} 2019-03-30 17:31:04 2019-03-30 17:31:04