LoginSignup
9
4

More than 5 years have passed since last update.

DatascriptでDatomicのチュートリアルをなぞってみる

Last updated at Posted at 2017-07-17

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の詳細
  • 複雑な応用例
9
4
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
9
4