読者です 読者をやめる 読者になる 読者になる

日曜プログラミング

休日趣味でやってるプログラミング関連記事をダラダラと

datomic チュートリアルを試す(1) - 導入・DB 作成・最初のクエリ実行

前振り

使う前に Datalog って何なんだろうと思って先に調べると、datomic のクエリ言語は Datalog をベースとして拡張したものらしく、特に独自に何か名前が付けられているわけではないようだ。
Datomic Development Resources

ちょっと勘違いしてたのだが datomic のクエリ言語を Datalog と呼ぶと思っていたのだが、Datalog 自体は独立した言語らしい。とは言え、書いていくのに名前がないと何かと不便なので datomic 記事内でのクエリ言語は Datalog と書くことにする。

SQL は個人的には JOIN や相関サブクエリ、スカラサブクエリ、CASE 句、WITH 句、GROUP BY、PARTITION BY など RDBMS から一撃で欲しいデータを取るのにはなかなか便利な言語だと思っとります。ベンダー毎の標準規格対応度合いとか NULL のイヤらしさとかあるけどまあそれはそれで。書いてて何となく Lisp っぽいなとも時々思ったりしたりで割と親近感はある。

なので、SQL の便利さは失わずにより便利になってないとあまり覚える気が起きない。Datalog はどうなんだろうか。

まずは最初に挙げたリンクを読んでみる。

  • 文法がシンプル
  • 宣言的
  • 論理プログラミングベース
  • joins are implicit
  • Unification
  • Blanks
  • The implicit data source - $
  • Querying References
  • Rules
  • Aggregates
  • Aggregates Returning a Single Value
  • Aggregates Returning Collections
  • Control Grouping via :with
  • Custom Aggregates
  • Filtering Databases
  • Joining Multiple Versions of the Same Database
  • Filtering on Transaction Attributes
  • Queries and Peer Memory
  • A Warning

ズラっと項目を並べて途中から英語のままになっちゃってるのは何となくでも良く分からんかったからw

こりゃあ大人しくチュートリアルを読みつつ実際にやってみた方が良さそうだ。
自分の中で概念からまるきりハマってない感がすごいある。

事前準備

公式の 下記チュートリルをある程度なぞってみる。
Datomic Development Resources
DB 作成、スキーマ読込、データ初期化、データのクエリや操作について一通り見ていくとの事。

以降、transactor 起動と peer からの transactor 接続確認まで済んでいるものとして進める。それとチュートリアルでは beanshell だけどここではせっかくなので Clojure で試す。英語でもいいから Clojure でのチュートリアルがあれば紹介できるんだけど、見つからなかったので何となくで読み換えていく。

~\bin\repl を使うと Clojure でもいきなり試せるっぽいが、実際使うとなるとこの辺りの初期設定は必ずやる事になるので一度やっておく。と言っても殆ど難しい部分はない。

datomic を Maven ローカルリポジトリに登録

Clojure でと言う事は leiningen にお世話になるので datomic を登録しておく。
~\bin\maven-install があるのでそれを有難く使う。Windows でも中身の mvn コマンドを ~/ で実行すれば登録可能。

結構色々ダウンロードされるが最終的に BUILD SUCCESS が出れば OK。

leiningen プロジェクト作成

チュートリアルお試し用に leiningen プロジェクトを作る。

>lein new app datomic-tut
Generating a project called datomic-tut based on the 'app' template.
>

project.clj に依存関係追加。

(defproject datomic-tut "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [com.datomic/datomic-free "0.8.4218"]]
  :main datomic-tut.core)

leiningen で依存ライブラリ参照コマンド叩いとく。

>lein deps

環境によるだろうが、結構色々ダウンロードされた。

core.clj 編集

Clojure の datomic API を使えるように use しとく。
~\samples\seattle\getting-started.clj で REPL 確認用のコードが置いてあるのでそこから割と使い回せる。

core.clj 先頭の ns マクロに記述。

(ns datomic-tut.core
  (:use [datomic.api :only [q db] :as d]
        clojure.pprint)
  (:gen-class))

transactor 起動

オンメモリ(mem プロトコル)にすれば transactor 要らずなのだが、前回ローカルファイル(free プロトコル)での transactor 動かしてたのでそいつを起動。

>bin\transactor config\free-transactor.properties
'data-dir' property not set, defaulting to 'data' in current directory
'log-dir' property not set, defaulting to 'log' in current directory
Starting datomic:free://localhost:4334/<DB-NAME>, storing data in: data ...
System started datomic:free://localhost:4334/<DB-NAME>, storing data in: data

以後 REPL 上で確認(lein repl でも emacs 経由 nREPL でもお好きなように)。
自分は emacs + clojure-mode + nrepl.el 環境で以後試す。

transactor 接続

最初は Java shell から確認してたが、今回は emacs nrepl 上となるので一応試しておく。

さっき書いた ns は C-c C-n してーの、C-c M-j して REPL 起動。
Clojure の datomic APIを見つつ試す。
サンプルでは in-ns 後のプロンプトは実際には datomic-tut.core が出てるが省略する。

; nREPL 0.2.0alpha
user> (in-ns 'datomic-tut.core)
#<Namespace datomic-tut.core>
> (def uri "datomic:free://localhost:4334/test")
#'datomic-tut.core/uri
> (d/connect uri)
#<Connection {:db-id "test-7f951986-43bd-492a-bb56-d6e9dc655c78", :unsent-updates-queue 0, :pending-txes 0, :next-t 1000, :basis-t 62, :index-rev 0, :index-root {:rev 0, :key "5268debf-cd9a-47c4-ae7c-a8bcae10b8bc"}, :log-root {:rev 2, :key "5268debf-f5a0-4b2f-90e1-2eb189e2ebbe"}}>
> 

うん、繋がった。

DB 削除

ん、いきなり DB 削除?いや、transactor 接続確認で作った test はチュートリアルで使うものではないので削除しておきたかったのです。
API 探すとあった

> (d/delete-database uri)
true

DB 作成

Java Shell でやってたのと意味上は殆ど変わらんけどチュートリアル用の DB である seattle を作る。

> (def uri "datomic:free://localhost:4334/seattle")
#'datomic-tut.core/uri
> (d/create-database uri)
true

スキーマ追加

サンプルスキーマファイルが datomic 展開フォルダの sample 以下にある *.dtm ファイルみたいなので
leiningen プロジェクトフォルダに resources フォルダを作った上でコピー & project.clj の :resources-path に追加して REPL 再起動。

(defproject datomic-tut "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [com.datomic/datomic-free "0.8.4218"]]
  :resource-paths ["resources"]
  :main datomic-tut.core)

チュートリアルではデータ構造の説明が結構続いてるが、そこは飛ばしてとりあえず読み込ませる。
core.clj の先頭 ns マクロに少し追加して C-c C-n。

(ns datomic-tut.core
  (:use [datomic.api :only [q db] :as d]
        clojure.pprint)
  (:require [clojure.java.io :as io])
  (:import datomic.Util)
  (:gen-class))

実際の読み込ませるコードはこう。ns マクロで Java の datomic.Util を import してるのは Util/readAll メソッドの ClojureAPI が見当たらなかった為。

> (with-open [r (io/reader "resources/seattle-schema.dtm")]
    (def schema-tx (.. (Util/readAll r) (get 0))))
#'datomic-tut.core/schema-tx
> (def cn (d/connect "datomic:free://localhost:4334/seattle"))
#'datomic-tut.core/cn
> (def result (d/transact cn schema-tx))
#'datomic-tut.core/result

REPL 上の確認なので def でいちいち返って来たものを格納してるが、実際に作り出すとどんな感じがいいかはまだ良く分からん。

シードデータの追加

直訳だが、要は実データの追加。
これもサンプルファイルがあるので読む。
コード見るとスキーマの追加と殆ど変わらんのでヘルパー関数定義。

(defn read-dtm [f]
  (with-open [r (io/reader f)]
    (->> (.. (Util/readAll r) (get 0))
         (d/transact cn))))

で、読み込む。

> (read-dtm "resources/seattle-data0.dtm")
#<promise$settable_future$reify__5411@11fe48: ...(省略)...>

データベースへの問い合わせ

始めのクエリ

スキーマ定義もデータ読込も無事完了したら次のクエリ発行してみようとあるので試す。
チュートリアルで出てたのはこう。

[:find ?c :where [?c :community/name]]

REPL で実行。

> (q '[:find ?c :where [?c :community/name]] (db cn))
#{[17592186045520] [17592186045518] [17592186045517] [17592186045516] ...(省略)...}

これ何返ってきてるんだろな、って所から良く分からん。次回すっ飛ばしたデータ構造確認してみるか。

今回はここまで。