2019/09/08

宮崎旅行

2018年7月の最後の週末に1日休みを追加して宮崎へ旅行へ行った。

1日目

早起きして羽田へ、7時過ぎには羽田に着いていた。
吉祥寺羽田間のバス回数券を持ってたので幾分楽に。
宮崎は初なのでちょっとドキドキ、羽田 - 女満別線と同サイズの小型機も久しぶりなので懐かしい気持ち。ソラシドエアーとANA共同運行。
やはり機内では寝てしまった。
朝10時に宮崎着。すぐにバスで鵜戸神社を目指す。
今回は良くも悪くも終始バスの旅となってしまった。途中で鬼の洗濯岩を見てかなり興奮。
人工物なんではないかと思ってしまった。
鵜戸神社は思ってたよりずっと遠く、やっと着いたという感じ。
やっと着いて、鵜戸神社を目指す参道はやはり南国へ来たと実感させる。
野生のバナナなどを見る。実際のとこ野生ではないかもしれないが、外に普通にバナナの木が生えているとはすごい。
鵜戸神社は岩の中にある。良い雰囲気の神社だった。
天気も晴れていて景色が良い。波が綺麗。そういえば台風が来ていて波は荒かった。
その後どこを観光するか迷うものの、全て出来るだけ見て回ることにして、バスでサンメッセ日南へ向かう。バス時間が合わず、鵜戸神社の茶屋で1時間ほどだらだら過ごす。
サザエのつぼ焼きがうまい。
その後サンメッセで降りて、炎天下上り坂を歩いてモアイへ。入場料も払ったがモアイはイマイチだったかも。
モアイの所のレストランではなく次の道の駅フェニックスで昼にすることにして、すぐ離れる。
ところがバスに乗り継いで行ったフェニックスではレストランが終了していた。
そこで名物っぽいソフトクリーム(日向夏)を食べ、再びバスで青島へ。
アオシマは立派な観光地で通りの屋台やら大きなお土産屋、カフェ、入らなかったが植物園などがあり、青島と砂浜がある。

青島観光の時点では疲れていたので、
早々に今度は電車で宿のある宮崎駅へ向かう。
ところがその青島駅と電車が混乱させる。
かなり人がいる観光地なのに駅は無人駅、しかも何番ホームにどこ行きが来るのか全く分からず。改札も券売機も無く、無人ホームで途方に暮れるが、スマホで調べ、2、3番ホームへ移動、来た電車に乗り、車内で切符を買う。
それが海幸山幸という名物の電車だった。内部が木でできていた。
宮崎に着いてホテルに着き、荷物を下ろすと一気に安心して疲れも吹っ飛ぶ。
宮崎の繁華街へ繰り出すと丁度道路を通行止にして、ソーラン祭りのような祭りをやっていた。今回は土日とも丁度その祭りで賑わっていて楽しかった。
ここだけ切り取ると祭り見学を目的に来た旅行客のよう。
まぁ地元の子供達の踊りにはあまり興味がなかったので早々に居酒屋へ。
鶏肉の藁焼き、牛肉の藁焼きがうまかった。生食はあまりしないのだが、ここではとてもうまかった。

店を出たらまだ19時過ぎで明るかった。
まだお祭りをやっていた。遠くの地でよさこいを見た。

2日目

1日目にほとんど見るべきところを見てしまったようで、さまようことになる日。
まずは宮崎市の博物館、資料館を見て回ってみた。修学旅行のようで悪くは無い。
その後有名であるシーガイアを横目に、宮崎市フェニックス自然動物園を目指してみる。
さすがに東京の近くにある動物園とは違って寂れた雰囲気は隠せないようだが、動物園、遊園地としてはまぁ楽しめた。近くの海岸を歩いている間に雨が降ってきて、市街にもどるため、バス停まで歩く。歩いている間に豪雨になり、びしゃびしゃになりながらバスを待つ。イオンモールで時間を潰し、再びバスでホテルに戻る。
2日目の夕食も市街地でふらふらと探したのだが、残念ながら1日目と同じ系列のお店に入ってしまったらしい。美味しいが、どうせなら別の店も覗いてみたかった。
そのため、2軒目としてスペイン料理屋をバーのように利用して、お酒を飲みつつ過ごす。
すっかり酔いながら、宮崎市の繁華街はまた来たいと思わせるいいところだった。
ホテルが提携しているというスーパー銭湯の入浴券をもらえたので行ってみる。
旅行に来て温泉に入るというのは本当によかった。

3日目

まぁ市街地やお土産やをみてさっさと空港に行き、時間まで過ごした。
いろいろ回りすぎて1度の旅行では見切れないような感想だったが、青島あたりで1日遊んで過ごすのが良いのかなというリゾート地の旅行に慣れていない感じ。
でも市街地でのんびり過ごしてお酒を飲むようなおっさん旅行をするのも良いかな。

2019/09/03

MySQLで複数カラムの値が重複している行をSELECTしたい

または

MySQLでUnique制約を後付けする時にDuplicateエラーが起きてしまった時の解消法

結論としては、以下のSQLを修正して流し、出てきた重複行を1行だけ残して削除する。
以下のSQLは2カラムの重複を検索する例だが、3カラムにすることも出来る。
SELECT
  *
FROM
  one_table t0
INNER JOIN (
  SELECT
    aaa_id, bbb_id
  FROM
    one_table
  GROUP BY
    aaa_id, bbb_id
  HAVING COUNT(*) >= 2
) t1
ON
  t0.aaa_id = t1.aaa_id
AND
  t0.bbb_id = t1.bbb_id

Unique制約を後付けしなければならない状況とは?

本来 aaa_idカラムとbbb_idカラムは以下のように2つでUniqueであるべきだったが、
aaa_id, bbb_id
    1 ,    1
    2 ,    2
うっかり運用開始時に付け忘れていて、しかもシステムで
aaa_id, bbb_id
    1 ,    1
    1 ,    1
    2 ,    2
    2 ,    2
のように重複させちゃっていて、慌てて後でUnique制約を追加する、ということが不幸にも起きた場合。
すでに重複してしまってると、Uniqueを付けることはできないのでDuplicateエラーが発生する。
そこで、処理時点で重複している行を上記の手順で炙り出し、出てきた重複行を、1行を残して全て消す、という修正をしてからUnique制約を付ける、というお話。全部消すとデータが消えるので注意が必要だ。

サービスを運用してると不幸にも何度か遭遇するので、すぐ呼び出せるようにメモしておく。

2019/09/02

DIとDIコンテナ

DIを理解していなくてもDIは使える

そもそもこの記事はDI、Dependency Injectionがどうしても理解できないけど不自由なく使ってる人の感想である。特にベースがJava脳なのでJavaのDIを使っている感想。
どのくらい使っているかと言うと書いているソースのほとんどに@Injectionが書かれているくらい毎日使っている。

DIの浅はかな理解

インジェクションする〇〇Serviceと言うクラスは、ぶっちゃけると〇〇Utilsクラスとして作りたいし、全てpublic staticなメソッドとして処理を書きたい。しかし、アプリケーション始動時に動的に読み込む設定などがあり、どうしてもstaticでは作れないクラス。

そんなときにDIコンテナーに渡してやるとアプリケーション全体からstaticインスタンスのように使えるようになる。newを使わずインスタンス化されたインスタンスをインスタンスプールから引っ張り出して使えるようになる。

あっさりとしたまとめ

staticでは作れず、うかつにnewするのも難しいようなクラスがある場合(こういう時、という例は思いつかないが)、
staticクラスにする方法を考えたり、シングルトンにする方法と一緒に、DIコンテナーに登録するという方法を調査してみると良いかもしれない。

おそらくこうかなと思うDIの理解

DI、DIコンテナの利点は「インターフェースとその実装に分けることで本番処理のクラスとモックアップ用のクラスに分けて実装できる」という説明をよく見る。
実際JavaのInterfaceで十分なんじゃ無いかと思ったりして、理解できない。

ではなぜInjectionを毎日のように使うか、というとフレームワークがDIを標準でサポートしているからだ。毎日使っているPlayFrameworkでは、Springのレイヤーモデルである、Controller、Service、Repositoryの相互保有関係をDIで行うことを標準としているからだ。
DIの、Injectionされる側、ServiceクラスやRepositoryクラスのインスタンス化、インスタンスプールへの登録は裏で勝手にやってくれるので@Injectionと書くと自然と使えるようになる。
DIの利点はFramework側の構築で十分に受けており、Frameworkを使って開発する側は、newを使った単なるインスタンス化、staticインスタンスで利用とあまり変わらない意識で使える、ということだろう。

2019/08/25

[Kails] Koa2を使ったRails風のnode.jsオープンソースフレームワーク

オリジナル文書は Embbnuxさんのブログです。再掲載にあたっては元記事へ参照とリンクを保持してください。
This article was first published in Blog of Embbnux. For reprinting, please indicate the source of the original text and keep the link of the original text.
https://www.embbnux.com/2016/09/04/kails_with_koa2_like_ruby_on_rails
直接の翻訳元はhttps://developpaper.com/kails-an-open-source-project-of-nodejs-similar-to-rails-based-on-koa2/ です。

私たちはkoa2フレームワークを調査し、ミドルウェアというアイデアがとても気に入りました。ミドルウェアで簡単にいろんなサービスを利用できるのは便利ですが、Webサイトを高速に開発できるようなフルスタックのフレームワークからは少し離れていることも分かりました。
そこで一般的なrailsフレームワークを参考に、koa2ベースのrails風のフレームワークとしてkailsを作りました。
postgresとredisを使用し、MVCフレームワークと、フロントエンドwebpack、およびisomorphic フロントエンド開発が含まれています。ここでは主に、kailsの構築におけるさまざまな技術スタックとアイデアを紹介します。

koaは、元々expressを開発していたチームが開発しています。ミドルウェアのアイデアに基づいた新しいフレームワークの実装で、主にES6のジェネレーター機能を使用しています。ただ、koaはexpressとは異なり、Webサイト開発に対応するためのフレームワークを提供しようとはしていません。基本機能のモジュールや、Webサイトを作るための多くのモジュールを導入する必要があります。そのため、モジュールの選択によって様々なKoaプロジェクトがあります。kailsはRuby on Railsに似せたkoaプロジェクトで、その名前の由来もrailsにあります。

プロジェクトURL: https://github.com/embbnux/kails (Pull Requestを歓迎します)

kailsのディレクトリ構成です。
├── app
│   ├── assets
│   │   ├── images
│   │   ├── javascripts
│   │   └── stylesheets
│   ├── controllers
│   ├── helpers
│   ├── models
│   ├── routes
│   ├── services
│   ├── views
│   └── index.js
├── config
│   ├── config.js
│   └── webpack
│       ├── base.js
│       ├── development.js
│       └── production.js
├── db
│   └── migrations
├── index.js
├── package.json
├── public
└── test

2019/08/19

Sequelize-autoでリレーションも付ける(NodeJSでER図先行開発)

NodeJSのRDB向けのORMである、Sequelizeを使ってER図先行開発をする方法を紹介します。Sequelizeの使い方ではなくSequelizeで使うためのモデルクラスを、既存のDBのテーブルから自動生成する方法です。
これができると、

  1. ツールを使ってER図を描く
  2. SQL(AlterSQL)を発行してテーブルを作る
  3. モデルクラスを自動生成してコーディングする

という開発手順を回せます。この手順を確立するとプロジェクトファイルにER図が常に正しい状態で存在することになり、開発の継続性能がぜんぜん違います。
もし説明を長々と読むのが嫌な場合、erf_devのmodels/index.jsを見てください。また、これのためにモデルを生成するコマンドはpackage.jsonのdb:modelsを見てください。
既存のテーブルからモデルを自動生成するためにSequelize-autoを使います。Sequelize-autoはモデルクラスを自動生成するツールなので、贅沢を言わなければ「MySQL Workbench」などのER図ツールからSQLを発行してテーブルを構築し、Sequelize-autoでモデルクラスを生成すれば終わりです。ですがこれだけだと外部キーによる子テーブルのデータ取得ができず、ORMとしては致命的です。

Sequelizeでリレーションを扱うには、モデルクラスを定義した後でそれぞれのモデルに対し、hasMany()やbelongsTo()などのリレーションを設定します。
Sequelize-autoではそこまでのコードは生成しないようなので、ややトリッキーなことをします。

① 1対1の関係、1対多の関係を設定する

元々のアイデアはsequelize-auto/issues/34 にあるもので、例えばuserテーブルとuser_bookテーブルが「1対多」の関係の場合、user_tableにはuserのidとして「user_id」があるはずです。

実際には「user_id」という名前でないカラム名を付けることもできますが、慣例として(Javaであれば大文字始まりはクラス名というくらい常識的な命名規則として)「テーブル名_id」というカラム名で外部キーを設定するはずです。
この命名を利用して「Id」でsplitして連携先のテーブルモデルを探し出し、そのモデルと連携させます。文字列処理をするため、少し違う命名でテーブルを作ってしまうと正常に動かなくなってしまう、という危険はありますが、逆にこのことさえ理解してテーブル設計をすれば非常に便利に連携まで作れるようになります。

上の例ではuser.hasMany(user_book)と、user_book.belongsTo(user)の2つを設定します。
上記Issuesには完全なリレーション設定までのサンプルコードは載っていないですが、少し工夫すれば作れます(erf_devのmodels/index.jsを見てください)
もし1対1ならhasMany, belongsToの代わりにお互いにhasOne( )を設定します。

② 多対多の関係を設定する

多対多の場合はさらにトリッキーとなり、元々RDBでは多対多のテーブルは扱えないため、間にJunctionテーブルを設けます。よくある例で、学生は複数の講義を受講し、講義は複数の学生が受講する、と言う関係では、学生テーブル、受講テーブル、講義テーブル、と言う構成にします。

Sequelizeでトリッキーなことをしないでも基礎的なhasMany( )belongsTo( )だけで、学生から複数の受講を取得し、受講から各講義を取り出す、という2段構えで取得できます(講義も同様で講義から複数の受講を取得し、受講から各学生の情報を取り出すという方法です)。

ただSequelizeには多対多の関係を扱える仕組みが使えます。そのためには間のJunctionテーブルを「テーブル名_has_テーブル名」という命名規則にして、「_has_」でsplitし、互いのモデルクラスを直接多対多として結びつける、ということを行います。
学生.belongsToMany(講義)と、講義.belongsToMany(学生)として、間のJunctionテーブルを隠して直接学生から受講している講義を、講義からは受講生を取得できるようになります。

③ その他の設定

最後にテーブルのカラム名はスネークケースですが、JavaScriptの要素としてはキャメルケースで取る、という慣例に沿ってSequelize-auto、Sequelizeの設定をして完成です。

①、②のコードの主要な部分は以下です。
/**
 *  setup
 *  belongsTo(), belongsToMany(), hasMany(), hasOne()
 *  based on references
 */
log('────────────────────────────────────────────────');
sequelize.modelManager.models.forEach(model => {

  const isJunctionTable = model.tableName.split('_has_').length === 2;
  if (isJunctionTable) {
    const tables = Object.getOwnPropertyNames(model.rawAttributes)
      .filter(attributeName => undefined !== model.rawAttributes[attributeName].references)
      .map(attributeName => {
          const refModel = sequelize.modelManager.models
            .find(testModel => testModel.tableName === model.rawAttributes[attributeName].references.model);
          return {
            model: refModel,
            through: {
              through: model,
              as: attributeName.split('Id')[0],
            }
          };
        }
      );

    log(tables[0].model.tableName, ' 1..* ──────── 1..* ', tables[1].model.tableName);

    tables[1].model.belongsToMany(tables[0].model, tables[0].through);
    tables[0].model.belongsToMany(tables[1].model, tables[1].through);

  } else {
    Object.getOwnPropertyNames(model.rawAttributes).forEach(attributeName => {
      if (model.rawAttributes[attributeName].references) {

        const refModel = sequelize.modelManager.models
          .find(testModel => testModel.tableName === model.rawAttributes[attributeName].references.model);
        const belongsToOptions = {
          foreignKey: model.rawAttributes[attributeName].field.toString(),
          as: attributeName.split('Id')[0],
        };

        const options = {};
        if (model.rawAttributes[attributeName].primaryKey) {
          log(refModel.tableName, ' 1 ──────── 1 ', model.tableName);
          refModel.hasOne(model, options);
        } else {
          log(refModel.tableName, ' 1 ──────── 1..* ', model.tableName);
          refModel.hasMany(model, options);
        }
        model.belongsTo(refModel, belongsToOptions);
      }
    });
  }
});
log('────────────────────────────────────────────────');
また、erf_devのmodels/index.jsも見てください。
①、②の命名規則はerf_devのER_Diagram.mdを読んでください。
③については、
erf_devのpackage.jsonと、erf_devのsequelize-auto_additional.jsonを見てください。

バラバラでは分かりにくいはずなので、全てをまとめて動かしてみるには、
是非erf_devのTutorial.mdを試してみください。


How to Automatic generate "sequelize models" with All associations from MySQL Tables.