Ruby on Railsでサブクエリ(副問合せ)を書く
Rubyスキルがほぼ0なのですが、Rubyでサブクエリを書く必要があったので備忘録として…
どんなクエリ書きたいの?
select * from hoge_hoge where hoge_id in (select hoge_id from hoge_hoge where type = 'Hoge' and hoge_aaa_id = 100) and type = 'Hogeee';
というクエリですね。
hoge_hoge
テーブルからhoge_id
を取ってきて、取ってきたhoge_id
とHogeee
タイプを元にhoge_hoge
テーブルからデータを取得するクエリです.
説明すると結構ややこしいですね!
2回クエリ走らせりゃええやんけ と思う方もいるかと思いますが(自分もそう)
キャッシュ使ってないシステムなのでクエリ発行回数を減らしたくて・・・
キャッシュ入れりゃええやんけ と思う方が大多数だと思いますが(自分もそう)
とりあえず、まぁそんな感じのクエリを書く必要があったできたわけです。
どうやるのか?
載ってありました. http://tokyo.supersoftware.co.jp/code/7546
つまり、こういうことらしい。
hoge_ids = HogeHoge.where(type: 'Hoge').and(hoge_aaa_id: 1200) hoge = HogeHoge.where(hoge_id: hoge_ids).and(type: 'Hogeee')
え?ちょっと待って。これ2回クエリ走らせてない?
と、書きながら思いました。しかし、log/development.logを見てみると...
SELECT `hogehoge`.* FROM `hoge_hoge` WHERE `hogehoge`.`hoge_id` IN (SELECT `hogehoge`.`hoge_id` FROM `hoge_hoge` WHERE `hogehoge`.`type` = 'Hoge' AND `hogehoge`.`hoge_aaa_id` = 100) AND `hogehoge`.`type` = 'Hogeee';
意図したクエリになっていました。。。不思議。。。
おわりに
Ruby初心者、ActiveRecord初心者なので内部でどうなってるのか調査しないといけないですが、
なんとかできました。
これってどういう仕組なんですかね?次くらいに調べたいと思います。。。
それでは〜
golangのテストについて
Go言語を調べている間、いろいろなライブラリとかサンプルコードとか見てるとテストファイルを同じ階層に入れていました。 goはそういうお作法なのかと思い今回実験してみました。
今回のぎもん
こんな感じになってる構成がおおいけど、同じ階層にテストファイルをいれないといけないのか
▶ tree . ├── controller │ ├── hogehoge.go │ ├── hogehoge_test.go
試してみた
フォルダ構成
▶ tree . ├── controller │ └── hogehoge.go ├── test │ └── hogehoge_test.go
実装
import ( "fmt" "testing" "../controller" ) func TestHogehoge(t *testing.T) { count := 1 for i := 0; i < count; i++ { key := fmt.Sprintf("key_%d", i) value := fmt.Sprintf("value_%d", i) println("---------------------") controller.Hogehoge(key, value) } }
実行結果
--------------------- ← hogehoge_test.go での出力 ///////////////////// ← hogehoge.go での出力 PASS ok _/hogehogehoge/test 0.106s
結果
testファイルを同じ階層にいれないといけないということはありませんでした。
しかし、同じ階層(package)内に入れないとpublic function以外参照できないので同じ階層に入れている様でした。
ちょっとどうにかしたいけどこれはしょうがないんですね…うーん。
それでは〜
Goのフレームワークを調べてみた
前回、前々回はbeego, echo と実際触って見ましたが、全部触ると時間がかかるので今回はGoで使えるその他ウェブアプリケーション用フレームワークを何点か調べてみました。
Goで使えるウェブフレームワーク
iris
特徴
- 超高速webフレームワーク
- すごくシンプル
- githubで公開している速度の数値は良さげ
しかし、ぐぐると権利問題やらこのフレームワークだけは選んではいけないみたいなことが書かれていました…
Goji
特徴
- 軽量フレームワーク系
- echoと同じ匂いを感じる
- Middlewareという仕組みがあり、リクエスト毎に必要な処理を集約することが可能
- Sinatra的高度な正規表現マッチも使えるルータ
- 簡単に実装出来るミドルウェアスタック
- グレースフルシャットダウン
- マルチプレクサ
- Goji自体にはほとんど機能はないらしい
ドキュメント
- 英語でならある。日本語はちらほら
Gin
特徴
- 比較的軽量なフルスタックWebアプリケーションフレームワーク
- マティーニと類似のAPIを提供
- HTTPルーターがレスポンスの速度を上げている
- 早い
- 軽量なため多くの機能はないが、WEBの最低限の機能はありそう。
ドキュメント
- 情報は豊富。日本語でも多い印象
Revel
特徴
- JavaのPlay Frameworkを参考に設計されたフルスタックフレームワーク
- MVCフレームワーク
- Webアプリケーション開発に必要な機能が一式揃ってる
- ホットリロード対応
- Go Templatesを使用
- ルーティング、 パラメータ解析、 検証、 セッション/フラッシュ、 テンプレート作成、 キャッシング、 ジョブ実行、 テストフレームワーク、国際化
- devモードでロギングが可能
- Filters機能がある(SpringのFileterと同じような機能。認証とかに便利)
- ライブラリ管理はdepを使用
ドキュメント
公式サイトのドキュメントは豊富。
日本語の情報も色々とある。
Martini
特徴
- モジュール形式のWebアプリケーションサービスフレームワーク
- PythonのFlaskのような感覚でコーディング
- ハンドラ、ルーティング、サービス、静的ファイル配信、ミドルウェアハンドラ(Filterのようなもの)、認証はある
ドキュメント
日本語の公式ドキュメントあるにはあるが、量が少ない。はじめのチュートリアルレベル。
深い情報は英語になっている。しかし知りたいことの情報は豊富にありそう。
Gorilla
特徴
- httpパッケージなどが揃っている
- パッケージを組み合わせて利用するイメージ
ドキュメントある?
- 日本語のドキュメントはあまりない。
ここからはマイクロフレームワーク
go-kit
特徴
- microservices用ツールキット
- tracing packageで、ログなのか追いやすい
- DNSやconsulなど様々なプラグインを追加可能
- Go-MicroやGizmoでも使われている
- go-kit用のCLIあり(kit)
- github star 12841(2019/03/11)
- パッケージを組み合わせて利用するイメージ
- 他のWAFにある軽量系のFWとあまり違いがわからない…
ドキュメント
- 日本語に訳された公式Docは無い
- qiitaとかの記事は結構見かける
Go Micro
特徴
- 一通り思想から理解しなければ使いこなすのが難しそう
- RPCベースのライブラリ(proto-rpc. json-rpc)
- microservices運用のpacageが一通り揃っている
- サービスをトレースするためのtrace-web
- Webサービスを展開するためのgo-web
- モニタリングツールのmonitor-web
ドキュメント
- 調べてない
goa
特徴
ドキュメント
- そんなに多くなさそうだが、公式の日本語Docはある
gizmo
特徴
- NY Timesが開発したFW
ドキュメント
- 調べてない
とまぁいろいろありまして、自分が今回使おうと思ったのは Gin
でした。
Pairsでの実例もありなかなか良さげなので Gin を使おうかなと思います。
それでは!
Go echo を使ってみる
特徴
- リソース量に比例して全体のスループットが向上する設計
- スケーラブルなフレームワークまたREST API向け
- Ginを超える程の高速処理
- RESTAPI向けに最適化されているフレームワーク
- スタンダードなHTTPサーバで動作する
- URI(PATH)単位でなにか処理を入れられる
- ミドルウェアに対して拡張性がある。
- urlごとにログ出したり出さなかったりすることできる
- JSON,XML,フォームペイロードなどへのデータバインディングのサポートがある
- HTTPの様々なレスポンスを送るための使いやすい関数をサポート
- どんなテンプレートエンジンででもテンプレートレンダリングできる
- Loggerに対して任意の定義を行える
install
go get github.com/labstack/echo go get github.com/dgrijalva/jwt-go
./main.go
goって package が main のやつがないと起動しないんですね…
package main import ( "github.com/labstack/echo" "github.com/labstack/echo/middleware" "./handler" ) func main() { e := echo.New() e.Use(middleware.Logger()) e.Use(middleware.Recover()) e.GET("/", handler.MainPage()) e.Start(":8080") }
/handler/handler.go
package handler import ( "github.com/labstack/echo" "net/http" ) func MainPage() echo.HandlerFunc { return func(c echo.Context) error { return c.String(http.StatusOK, "Hello World!!!!") } }
起動
起動するとこんな感じ. echoって感じですね.
▶ go run main.go ____ __ / __/___/ / ___ / _// __/ _ \/ _ \ /___/\__/_//_/\___/ v4.0.0 High performance, minimalist Go web framework https://echo.labstack.com ____________________________________O/_______ O\
アクセス
表示されます
▶ curl http://localhost:8080 Hello World!!!!%
ついでにDB周りも触って見る。
dbrというORMがあるらしい?
dbrって何ぞ?
golang の orm ですね。 サポートしているドライバーは下記の模様
- MySQL
- PostgreSQL
- SQLite3
今回は、というかいっつもMySQL使ってるのでMySQL使います。
dbr install
▶ go get -u github.com/gocraft/dbr
データ作成
mysql> create table user( -> id varchar(36) not null, -> name varchar(50) not null, -> email varchar(128) not null, -> primary key(id)); Query OK, 0 rows affected (0.02 sec) mysql> insert into user (id, name, email) values ('D7EEFCBD-CD03-4524-893A-4850D9828D57', 'kusobutayarow', 'kusobutayaro-w@hogehogehogeho.jp'); Query OK, 1 row affected (0.01 sec) mysql> select * from user; +------------------------------------------------+----------------+---------------------------------------+ | id | name | email | +------------------------------------------------+----------------+---------------------------------------+ | D7EEFCBD-CD03-4524-893A-4850D9828D57 | kusobutayarow | kusobutayaro-w@hogehogehogeho.jp | +------------------------------------------------+----------------+---------------------------------------+ 1 row in set (0.00 sec)
main.go 修正
package main import ( "github.com/gocraft/dbr" "github.com/labstack/echo" "github.com/labstack/echo/middleware" "./handler" _ "github.com/go-sql-driver/mysql" "net/http" ) type ( user struct { ID string `db:"id"` Email string `db:"email"` Name string `db:"name"` } ) var ( tableName = "user" seq = 1 conn, _ = dbr.Open("mysql", "root:@tcp(127.0.0.1:3306)/hogehogehogeho", nil) sess = conn.NewSession(nil) ) func selectUser(c echo.Context) error { var m user name := c.Param("name") sess.Select("*").From(tableName).Where("name = ?", name).Load(&m) return c.JSON(http.StatusOK, m) } func main() { e := echo.New() e.Use(middleware.Logger()) e.Use(middleware.Recover()) e.GET("/", handler.MainPage()) e.GET("/user/:name", selectUser) e.Start(":8080") }
実行.
取得成功
▶ curl http://localhost:8080/user/kusobutayarow | jq { "ID": "D7EEFCBD-CD03-4524-893A-4850D9828D57", "Email": "kusobutayaro-w@hogehogehogeho.jp", "Name": "kusobutayarow" }
無事取れた. ※jqコマンドに関してはこちら > https://stedolan.github.io/jq/
所感
- 最初にbeegoを触ったからか、すごいシンプルだなぁという印象を受けました。
参考: refer
beego使ってみた
なんでbeego?
- なんとなくGo言語使ってみようとなり、どのFWが良いか選定するため
- echo, beego, ginはやってみた。
特徴
- Webアプリ用のフルスタックフレームワーク
- Rubyっぽい
- beego用のORMがある
- beego用のCLIツール(bee)がある
- プロジェクト作るとcontrollerディレクトリ等がすでにあり、作り方がある程度決まっている
- Rubyっぽい
- scaffoldを使ってのコード自動生成ができる
- MVCモデル
- Viewを必要としないAPIモードあり
- i18n対応
- LiveReloadあり
- ハチがかわいい
CLIツール
beego専用のCLIツールがある.
bee new {PROJECT_NAME} bee api {PROJECT_NAME}
LiveReload
bee
で起動していればファイル編集時に検知して再コンパイルしてくれる。嬉しい.
Swaggerが自動生成される
実装したあとのAPIのテストが楽かと思った. が、本来の開発だと先にSwaggerを作らないといけないと思う.
起動
bee run -downdoc=true -gendoc=true
DB接続など
beego用のormがある.
実装
apiモード
bee api {PROJECT_NAME}
でプロジェクトを作成するとapiモードになる。- apiモードで作るとviewが無い状態でテンプレが作られる
- デフォルトで作ったやつはRooterの設定がアレなのでアクセスしても404になった.
- 修正が必要
apiモードのRooter実装
http://localhost:8080/user/:name
func init() { ns := beego.NewNamespace("/api", beego.NSNamespace("/object", beego.NSInclude( &controllers.ObjectController{}, ), ), beego.NSNamespace("/user", beego.NSRouter("/:name", &controllers.UserController{}), <- ここ ), ) beego.AddNamespace(ns) }
bee/orm
models
structをmodelに書いてたら起動時にテーブル作ってくれた.Rubyっぽい
create table `user` -- -------------------------------------------------- -- Table Structure for `_/hogehogehogeho/models.User` -- -------------------------------------------------- CREATE TABLE IF NOT EXISTS `user` ( `id` varchar(255) NOT NULL PRIMARY KEY, `name` varchar(255) NOT NULL DEFAULT '' , `email` varchar(255) NOT NULL DEFAULT '' ) ENGINE=InnoDB;
model内実装,
func init() { orm.RegisterModel(new(User)) } type User struct { Id string `orm:"pk" json:"id"` Name string `json:"name"` Email string `json:"email"` } func GetUserByName(name string) (u *User, err error) { o := orm.NewOrm() _ = o.Read(&name, "Name") if u, ok := UserList[name]; ok { return u, nil } return nil, errors.New("User not exists") }
main
import ( ... "github.com/astaxie/beego/orm" ) func init() { ... dataSource := "root:@tcp(127.0.0.1:3306)/hogehogehogeho?charset=utf8" orm.RegisterDataBase("default", "mysql", dataSource, 30) orm.SetMaxIdleConns("default", 100) orm.SetMaxOpenConns("default", 100) ... }
実行
これで取れると.
curl http://localhost:8080/user/DBにある適当な名前
所感
iTerm2が起動しなくなった場合の対処
やりかた
bashに戻す
sudo chsh -s /bin/bash
再インストール
brew reinstall zsh && brew unlink zsh && brew link zsh
起動しないときは焦りましたが、自分の場合はこれで起動できました。