LoginSignup
0
0

More than 3 years have passed since last update.

1:多のテーブルのそれぞれの結果をマージしてネストされたJSONにするのをRubyとClojureで関数を書いて比べてみた。

Last updated at Posted at 2019-08-03

先に先日書いた
JOINのあるSQL結果をJSONにしたかったけれどRubyとPythonでサッとわからなかったのでClojureでやってみた。
でClojureでやっていたのですが勉強中のRubyでもやってみました。
免責事項的でずるいかもですが、私は業務ではどちらの言語も使ったことがないのでもっといい書き方があるかもしれません。比べる、という観点のネタ記事になります。

そもそもタイトルにあるJSONとはどんなものか

以下の関係の2つのTABLE、staffdepartmentから

staff

id last_name first_name age department_id
1 taro yamada 37 1
2 ichiro suzuki 30 1
3 hanako hujiwara 25 2

department

id name
1 人事
2 営業

以下のようなSQLを書いて

SQL
select * from staff s 
inner join department dep 
on s.department_id = dep.id;

以下のようなJSONがサッと欲しかったです。


[
  {
    "id": 1,
    "name": "人事",
    "staff": [
      {
        "id": 1,
        "last_name": "taro",
        "first_name": "yamada",
        "age": 37,
        "department_id": 1
      },
      {
        "id": 2,
        "last_name": "ichiro",
        "first_name": "suzuki",
        "age": 30,
        "department_id": 1
      }
    ]
  },
  {
    "id": 2,
    "name": "営業",
    "staff": [
      {
        "id": 3,
        "last_name": "hanako",
        "first_name": "hujiwara",
        "age": 25,
        "department_id": 2
      }
    ]
  }
]


上記のようなJSONをサッとできるメソッドが見当たらなかったので(多分実装が簡単だから?)結果的に2つの結果をマージするような関数を書くことにし、はじめは勉強がてらClojureで書いていたのですがRubyも勉強していたので書いてClojureと比べました。

Ruby

準備

require 'pg'
require 'json'
# postgresqlで接続
connect = PG::connect(host: "localhost", user: "user", dbname: "playground")


JSON出力まで

# mapにしておく
staff_map = connect.exec("select * from staff").collect{|o| o}
department_map = connect.exec("select * from department").collect{|o| o}


def to_parent_child(parents, child_name, child, foreign_key)
  # foreign_key => valueの形にしておく
  child_group_by_foreign_key = child.group_by {|c| c[foreign_key]}
  parents.each {|p|
    p.store(child_name, child_group_by_foreign_key[p["id"]])
  }
end

parent_child =
    to_parent_child(department_map, "staffs", staff_map, "department_id")

puts(JSON.pretty_generate(parent_child))


結果


[
  {
    "id": "1",
    "name": "人事",
    "staffs": [
      {
        "id": "1",
        "last_name": "taro",
        "first_name": "yamada",
        "age": "37",
        "department_id": "1"
      },
      {
        "id": "2",
        "last_name": "ichiro",
        "first_name": "suzuki",
        "age": "30",
        "department_id": "1"
      }
    ]
  },
  {
    "id": "2",
    "name": "営業",
    "staffs": [
      {
        "id": "3",
        "last_name": "hanako",
        "first_name": "hujiwara",
        "age": "25",
        "department_id": "2"
      }
    ]
  }
]

Clojure

準備
;cheshireはmapからjsonにするライブラリ
(ns json-try
    (:require [clj-postgresql.core :as pg])
    (:require [clojure.java.jdbc :as jdbc])   
    (:require [cheshire.core :refer :all ]))


(def playground (pg/pool
                    :host "localhost"
                    :user "user"
                    :dbname "playground"))

;jdbc/queryメソッドが少々冗長なためラップする。               
(defn q [sql]
  (jdbc/query playground sql))


JSON出力まで
;Clojureはクエリの結果はそのままmap
(def staff-map (q "select * from staff"))
(def department-map (q "select * from department"))


(defn to-parent-child [parent-map child-name child-map foreign-key]
    (let [child-group-by-foreign-key (group-by foreign-key child-map)]
        (map #(assoc % child-name (child-group-by-foreign-key (:id %)))
                 parent-map)))

;上記関数を使用して結果を束縛
(def parent-child
  (to-parent-child department-map :staffs staff-map :department_id))

(println (generate-string parent-child {:pretty true}))


結果


[ {
  "id" : 1,
  "name" : "人事",
  "staffs" : [ {
    "id" : 1,
    "last_name" : "taro",
    "first_name" : "yamada",
    "age" : 37,
    "department_id" : 1
  }, {
    "id" : 2,
    "last_name" : "ichiro",
    "first_name" : "suzuki",
    "age" : 30,
    "department_id" : 1
  } ]
}, {
  "id" : 2,
  "name" : "営業",
  "staffs" : [ {
    "id" : 3,
    "last_name" : "hanako",
    "first_name" : "hujiwara",
    "age" : 25,
    "department_id" : 2
  } ]
} ]

比べてみて

これくらいのものなら特に大きな違いとかないですね。。
ちょっと気になったのが

  • Rubyは関数の中のeachをはじめはmapで書いていたが関数の評価の結果はdepartment_mapのはじめの状態(mapの結果ではなかった)が返ってきた。eachで副作用を明示的にしたらうまくいった。
  • Clojureは(当たり前だけれど)mapの結果が期待通りだった。

以上です。

0
0
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
0
0