Clojure界では有名なDatomicというプロダクトとAPIの互換性があるOSSライブラリである
Datascriptに興味を持ったので触ってみた。
題材としてDatomicのGetting Startedをなぞってみることにする。
1. インストール
[datascript "0.16.1"]
を :dependencies
に追加する
2. コネクションを作る
(require '[datascript.core :as dc])
(def con (dc/create-conn))
3. Schemaをトランザクトする
この例では映画をモデリングする。
映画にはリリース年、ジャンル、タイトルの属性(attribute)があるものとする。
attributeの定義には以下が必要となる。
-
:db/ident
attributeのユニークな名前 -
:db/valueType
attributeが保持するデータの型 -
:db/cardinality
attributeのデータが単一の値か、もしくはコレクションを保持するか
また、任意の :db/doc でattributeのdocstringを定義することが推奨される。
attributeはデータのマップで定義される。
タイトルは以下のように定義する
{:db/ident :movie/title
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "The title of the movie"}
ジャンルは以下のように
{:db/ident :movie/genre
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "The genre of the movie"}
リリース年は整数であるため :db/valueType
を :db.type/longに変更して以下のようにして定義する
{:db/ident :movie/release-year
:db/valueType :db.type/long
:db/cardinality :db.cardinality/one
:db/doc "The year the movie was released in theaters"}
スキーマはひとつづつトランザクトすることも可能だが、ここではまとめてトランザクトすることにする。
(dc/transact con [{:db/ident :movie/title
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "The title of the movie"}
{:db/ident :movie/genre
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "The genre of the movie"}
{:db/ident :movie/release-year
:db/valueType :db.type/long
:db/cardinality :db.cardinality/one
:db/doc "The year the movie was released in theaters"}])
conを評価してみると何かが入っているのがわかる。
user> con
#<Atom@4afb7219:
{1 :db/cardinality,
1 :db/doc,
1 :db/ident,
1 :db/valueType,
2 :db/cardinality,
2 :db/doc,
2 :db/ident,
2 :db/valueType,
3 :db/cardinality,
3 :db/doc,
3 :db/ident,
3 :db/valueType}
4. データをトランザクトしてみる
以下のようなデータを定義する
(def first-movies [{:movie/title "The Goonies"
:movie/genre "action/adventure"
:movie/release-year 1985}
{:movie/title "Commando"
:movie/genre "action/adventure"
:movie/release-year 1985}
{:movie/title "Repo Man"
:movie/genre "punk dystopia"
:movie/release-year 1984}])
スキーマと同じようにトランザクトしてDBに追加する
(dc/transact con first-movies)
conを再度評価すると何かが増えていることがわかる。
user> con
#<Atom@4afb7219:
{1 :db/cardinality,
1 :db/doc,
1 :db/ident,
1 :db/valueType,
2 :db/cardinality,
2 :db/doc,
2 :db/ident,
2 :db/valueType,
3 :db/cardinality,
3 :db/doc,
3 :db/ident,
3 :db/valueType,
4 :movie/genre,
4 :movie/release-year,
4 :movie/title,
5 :movie/genre,
5 :movie/release-year,
5 :movie/title,
6 :movie/genre,
6 :movie/release-year,
6 :movie/title}
5. クエリを投げてみる
まずコネクションからdb
なるものを作る。
(def db (dc/db con))
このdbはイミュータブルで、同じクエリに対して同じ結果が返ってくることが保証されている(注目ポイントらしい)。
DBから値を取得する方法は2種類ある:
-
query
Datalogという宣言的なクエリ言語でクエリを投げる -
pull
エンテティに関する情報を宣言的に階層化してセレクトする
:movie/title
のattributeを持つ物をdbの中から探してみる
(dc/q '[:find ?e
:where [?e :movie/title]]
db)
=>#{[4] [6] [5]}
エンテティIDのかわりに映画のタイトルを抜き出すには以下のようにする
(dc/q '[:find ?movie-title
:where [_ :movie/title ?movie-title]]
db)
=> #{["The Goonies"] ["Commando"] ["Repo Man"]}
リリース年が1985の映画のタイトルは以下のように取得する
(dc/q '[:find ?title
:where [?e :movie/title ?title]
[?e :movie/release-year 1985]]
db)
=>#{["The Goonies"] ["Commando"]}
リリース年が1985年の映画の全てのattributeの情報は以下の様にして取得することができる
(dc/q '[:find ?title ?year ?genre
:where [?e :movie/title ?title]
[?e :movie/release-year ?year]
[?e :movie/genre ?genre]
[?e :movie/release-year 1985]]
db)
=> #{["The Goonies" 1985 "action/adventure"]
["Commando" 1985 "action/adventure"]}
pullは以下の様に使う
(dc/pull db
'[*] ;;selector
4 ;; entity id
)
=>{:db/id 4,
:movie/genre "action/adventure",
:movie/release-year 1985,
:movie/title "The Goonies"}
6. TODO
- 用語の定義
- Datomic/Datascriptの何が嬉しいのかを実感する
- pullの詳細
- 複雑な応用例