1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

GoでMySQLのJSON型を扱う

Last updated at Posted at 2019-05-24

はじめに

最近GoとMySQLのJSON型と戯れたのでその足跡を残します。
近頃ののMySQLやPostgreSQL(MySQL5.7やPostgreSQL9.4以降)はJSON型をサポートしています。
https://dev.mysql.com/doc/refman/5.7/en/json.html

上手に使えばRDBの利点を活かしながらNoSQLの利点も享受することができます。
もちろん単純にMongoDB等やPaaSやDaaSなどと比較するのは早計なのでユースケースや要件をよく確認しましょう。

JSON型の利点

ここでは例として多言語対応フィールドについて考えてみます。
JSON型を利用しない場合は多言語フィールドを別テーブルに保存することもあるでしょう。

CREATE TABLE IF NOT EXISTS `user` (
  id         VARCHAR(36) PRIMARY KEY,
  created_at DATETIME NOT NULL,
  updated_at DATETIME NOT NULL
);

CREATE TABLE IF NOT EXISTS `i18n_user_name` (
  user_id VARCHAR(36) NOT NULL,
  locale  VARCHAR(15) NOT NULL,
  name    VARCHAR(50) NOT NULL,
  FOREIGN KEY (user_id) REFERENCES `user`(id) ON DELETE CASCADE,
  CONSTRAINT `i18n_user_name_pk` PRIMARY KEY (`user_id`, `locale`)
);

もし仮に名前だけでなく別のリソース(例えば自己紹介文とか?)が存在したとしてそれらも多言語対応しようとすると、さらにテーブルを増やさなくてななりません。

しかしJSON型を使うとこのように次のように表現可能です。

CREATE TABLE IF NOT EXISTS `user` (
  user_id    VARCHAR(36) PRIMARY KEY,
  name       JSON NOT NULL,
  created_at DATETIME NOT NULL,
  updated_at DATETIME NOT NULL
);

以下のように多言語リソースを保持すればJSONのままでゆるくデータを保存できます。

{
  "ja": "日本語",
  "en": "English"
}

またプレーンテキストではなくリッチテキストかもしれません。
Draft.jsなどもReactプロジェクトでよく使われています。
JSON型であればこのようなデータもHTMLに変換することなく構造保ったまま保存できます。

ここではJSON型の詳しい扱いかたには触れません。
様々なJSON型用の関数があるのでクエリに組み込むことも可能ですし、生成カラムを使って特定のキーにインデックスを作成したりもできます。
参考 https://dev.mysql.com/doc/refman/8.0/en/json-function-reference.html

ScannerとValuer

database/sqlScannerインターフェースとdatabase/sql/driverValuerインターフェースを使うとスッキリかけます。

まずこんな感じの型を用意しておきます。

type JsonObject map[string]interface{}

func (j *JsonObject) Scan(src interface{}) error {
	var _src []byte
	switch src.(type) {
	case []byte:
		_src = src.([]byte)
	default:
		return errors.New("failed to scan JsonObject")
	}
	if err := json.NewDecoder(bytes.NewReader(_src)).Decode(j); err != nil {
		return err
	}
	return nil
}

func (j JsonObject) Value() (driver.Value, error) {
	b := make([]byte, 0)
	buf := bytes.NewBuffer(b)
	if err := json.NewEncoder(buf).Encode(j); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}
1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?