{"id":13722660,"url":"https://github.com/philoskim/datomic-intro","last_synced_at":"2025-12-26T12:32:08.415Z","repository":{"id":95177369,"uuid":"110481983","full_name":"philoskim/datomic-intro","owner":"philoskim","description":"Datomic introduction (in Korean)","archived":false,"fork":false,"pushed_at":"2017-11-13T00:59:11.000Z","size":215,"stargazers_count":12,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-08-04T01:17:12.247Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/philoskim.png","metadata":{"files":{"readme":"README.adoc","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-11-13T00:39:28.000Z","updated_at":"2024-05-28T07:51:14.000Z","dependencies_parsed_at":"2023-06-12T07:15:24.180Z","dependency_job_id":null,"html_url":"https://github.com/philoskim/datomic-intro","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/philoskim%2Fdatomic-intro","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/philoskim%2Fdatomic-intro/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/philoskim%2Fdatomic-intro/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/philoskim%2Fdatomic-intro/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/philoskim","download_url":"https://codeload.github.com/philoskim/datomic-intro/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224620739,"owners_count":17341984,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T01:01:31.424Z","updated_at":"2025-12-26T12:32:08.405Z","avatar_url":"https://github.com/philoskim.png","language":"Clojure","funding_links":[],"categories":["Clojure"],"sub_categories":[],"readme":"= Datomic: 함수형 데이터베이스\n:sectnums:\n:source-language: clojure\n:source-highlighter: coderay\n:coderay-css: class\n:imagesdir: img/\n:latexmath:\n\n== 강사 소개\n\n* 이름: 김영태\n* e-mail: philos99@gmail.com\n* link:http://www.kyobobook.co.kr/product/detailViewKor.laf?ejkGb=KOR\u0026mallGb=KOR\u0026barcode=9788966261802\u0026orderClick=LAG\u0026Kc=[클로저 시작하기] 공역, 인사이트 (원서: link:https://www.amazon.com/Living-Clojure-Introduction-Training-Developers/dp/1491909048/ref=sr_1_1?ie=UTF8\u0026qid=1509447512\u0026sr=8-1\u0026keywords=living+clojure[Living Clojure], Carin Meier, O'Reilly)\n* link:https://github.com/philoskim/debux[debux] (Clojure/ClojureScript 디버깅 라이브러리) 제작\n** 예제: link:https://github.com/philoskim/debux#simple-example[]\n\n\n== Datomic 소개\n\n* Clojure 언어의 창시자 Rich Hickey가 만든 함수형 데이터베이스\n* Clojure 언어로 쓰여져 있다.\n* \"Immutable\" Database: 개념상 git과 유사\n* 2012년에 최초 버전(0.8.3335) 발표\n* 현재 최신 버전: 0.9.5561.62\n\n=== 참고: link:https://github.com/tonsky/datascript[DataScript]\n\n* Immutable memory database and Datalog query engine for Clojure, ClojureScript and JavaScript\n* 서버에서 사용되는 Datomic을 모방해 만들었다.\n* 주로 Web Browser Client용으로 사용된다.\n* Datomic과 거의 동일한 API를 제공한다.\n\n\n== Datomic 도입 사례\n\n* link:http://blog.cognitect.com/blog/2015/6/30/walmart-runs-clojure-at-scale[Walmart runs Clojure and Datomic at scale]\n* link:http://blog.datomic.com/2015/09/nubank-chooses-datomic.html[Nubank chooses Datomic]\n \n* link:http://www.datomic.com/customers.html[기타 Customers Story]\n\n\n== Datomic의 특징\n\n* Transactional store, Analytics store, Distributed database, Graph database, Logic\n  database\n\n* 관계형 데이터베이스 사용 예의 96% 대체 가능. 나머지 4%는 쓰기 작업이 많은(high\n  write-volume) 경우.\n\n* 응답 속도가 빠르다 (거의 memory DB 수준)\n** ex) 1.5 ms per query, compared to the 10-20 ms in SQL query\n\n* Storage backend로 다양한 DB를 사용할 수 있다.\n** DynamoDB(AWS), Cassandra, Riak, Couchbase, Infinispan, or SQL database등 \n\n\n== Datomic architecture\n\n* Reads are separated from writes and decentralized.\n\n* Writes don't block reads, reads don't block one another.\n\n* Read scaling horizontally by adding more \"peer\" systems.\n\nimage::datomic-architecture.png[]\n\n[listing]\n.Datomic DB vs. SQL DB\n----\n   Datomic DB               SQL DB\n=============================================\n|-- Storage\n|\n|-- Transactor\n|   |-- Writing\n|   `-- Indexing          SQL Server\n|\n`-- Peer Library\n    |-- Live index\n    |-- Cache\n====|========================================\n    `-- Query             SQL Client Library\n=============================================\n----\n\n\n== Keyword data type in Clojure\n\n=== keyword data type 형식\n\n[listing]\n----\n:namespace-part/name-part\n\n:table-name/field-name\n----\n\n=== keyword examples\n\n[source]\n....\n:employee/address\n\n:address\n\n:user.type/superuser\n\n:user.type.v2/superuser\n\n:user.type/superuser.boss\n:user.type/superuser.manager\n....\n\n\n=== keyword data type의 평가(evaluation)\n\n* Keyowrd data type is a self-evaluated value.\n\n\n[listing]\n----\n:employee/address   ;=\u003e :employee/address\n----\n\n\n== Datom\n\n* datom은 Datomic이 데이터를 처리할 때의 기본 단위이다.\n\n=== datom의 구성요소\n\n* 하나의 datom은 1 개의 field를 표현한다.\n* 하나의 datom은 5 개의 구성요소로 이루어져 있다.\n+\n[listing]\n----\n[entity-id attribute value transaction-id added?]\n----\n\n=== datom의 구성요소 설명\n\n[width=\"75%\",cols=\"1,3\"]\n|===\n\n| `entity-id`\na|\n* SQL DB의 record id (primary key 역할)에 해당.\n* 개발자가 지정해 줄 수 없으며, Datomic이 자동으로 생성. 동일 db 내에서 유일한 값.\n* Java의 long 타입 (64 비트 정수형)\n\n| `attribute` | SQL DB의 field name\n| `value`     | SQL DB의 field value\n| `transaction-id` | 동일한 transaction(쓰기)일 때, 동일한 `transaction-id` 값을 부여 받는다.\n| `added?` \na|\n* `true`: assert or accumulate\n* `false`: retract \n\n|===\n\n[listing,subs=\"macros,quotes\"]\n.datom들의 실례\n----\n+[\u003ce-id\u003e   \u003cattribute\u003e         \u003cvalue\u003e     \u003ctx-id\u003e  \u003cadded?\u003e]+\n...\n[   42    :person/firstName   \"James\"      102     true    ]\n[   42    :person/lastName    \"Clark\"      102     true    ]\n[   42    :person/address     #43#           102     true    ]\n\n[   #43#    :address/state      \"Oregon\"     102     true    ]\n[   #43#    :address/city       \"Portland\"   102     true    ]\n\n[ 1077    :person/fitstName   \"Viola\"      103     true    ]\n[ 1077    :person/lastName    \"Davis\"      103     true    ]\n[ 1077    :person/friends     #{42 89}     103     true    ]\n...\n----\n\n* (참고) `:db.type/ref` \u003c\u003cdb-type-ref\u003e\u003e\n\n==== SQL table의 record: 2차원 배열\n\n[listing]\n----\n[   42    :person/firstName   \"James\"      102     true    ]\n[   42    :person/lastName    \"Clark\"      102     true    ]\n[   42    :person/gender      \"male\"       102     true    ]\n----\n\n==== NoSQL의 document: tree 구조 \n\n[listing]\n----\n...\n[   42    :person/firstName   \"James\"      102     true    ]\n[   42    :person/lastName    \"Clark\"      102     true    ]\n\n[   52    :person/firstName   \"Alice\"      102     true    ]\n[   52    :person/lastName    \"Parker\"     102     true    ]\n\n[ 1077    :person/fitstName   \"Viola\"      103     true    ]\n[ 1077    :person/lastName    \"Davis\"      103     true    ]\n[ 1077    :person/friends     #{42 52}     103     true    ]\n...\n----\n\n==== Graph DB도 구현 가능\n\n\n\n=== SQL CRUD와의 용어 비교\n\n* CRUD vs. ARAR\n+\n[%header,width=\"75%\",cols=\"3*^\"]\n|===\n\n| SQL    | Datomic     | Datomic 담당 함수\n| Create | Assert      | `transact`\n| Read   | Read        | `q`\n| Update | Accumulate  | `transact`\n| Delete | Retract     | `transact`\n|===\n\n\n\n\n== Schema\n\n=== DB별 비교\n \n* SQL DB\n** schema를 table 단위로 작성한다.\n** record 구조가 고정되어 있다.\n\n* Datomic DB\n** schema를 field 단위로 작성한다.\n** record 구조가 고정되어 있지 않다.\n+\n따라서 동적으로 field를 조합하여 record를 만들 수 있다.\n** schema 자체도 datom으로 이루어져 있다.\n+\n따라서 프로그램 실행 중에 동적으로 schema 생성 및 변경이 가능하다.\n\n* NoSQL DB\n** schema가 아예 없다.\n\n=== Datomic Schema의 구성\n\n[source]\n.Datomic Schema의 예\n....\n(def schema\n  [{:db/ident :user/id\n    :db/valueType :db.type/string\n    :db/unique :db.unique/value\n    :db/cardinality :db.cardinality/one}\n\n   {:db/ident :user/name\n    :db/valueType :db.type/string\n    :db/cardinality :db.cardinality/one}\n\n   {:db/ident :user/e-mail\n    :db/valueType :db.type/string\n    :db/cardinality :db.cardinality/one}])\n....\n\n[%header,cols=\"1,3a\"]\n|===\n\n| Schema Attributes   | Values\n|`:db/ident`   | field name을 지정한다. 반드시 keyword 자료형이어야 한다.\n\n|`:db/valueType` [[db-type-ref]]\n| field value의 자료형을 지정힌다.\n\n* `:db.type/string`\n* `:db.type/long :db.type/bigint`\n* `:db.type/float :db.type/double :db.type/bigdec`\n* `:db.type/boolean`\n* `:db.type/keyword`\n* `:db.type/ref` \n* `:db.type/instant`\n* `:db.type/uuid`\n* `:db.type/url`\n* `:db.type/bytes`\n\n| `:db/cardinality`\n|\n* `:db.cardinality/one` - 일대일 대응\n* `:db.cardinality/many` - 일대다 대응\n\n| `:db/noHistory`\n| \n* `true` - field value를 누적(accumulate)시키지 않고, 기존의 값을 덮어 쓴다(overwrite).\n* `false` - default\n\n| pass:q[......]\n| pass:q[......]\n\n|===\n\n=== Schema 예제\n\n[source]\n.datomic-guide/schema-example.clj\n....\n(ns datomic-guide.schema-example\n  (:require [datomic.api :as d]))\n\n;;; database 생성\n(def db-uri \"datomic:mem://schema-example\")\n\n(d/create-database db-uri)\n\n;;; conn 얻기\n(def conn (d/connect db-uri))\n\n;;;\n;;; schema definitions\n;;;\n(def schema-1\n  [{:db/ident :user/id\n    :db/valueType :db.type/string\n    :db/unique :db.unique/value\n    :db/cardinality :db.cardinality/one}\n\n   {:db/ident :user/name\n    :db/valueType :db.type/string\n    :db/cardinality :db.cardinality/one}\n\n   {:db/ident :user/e-mail\n    :db/valueType :db.type/string\n    :db/cardinality :db.cardinality/one}])\n\n;;; schema-1 install\n@(d/transact conn schema-1)\n\n\n;;;\n;;; data definitions\n;;;\n(def user-data\n  [{:db/id #db/id[:db.part/user]\n    :user/id \"alice\"\n    :user/name \"Alice Parker\"\n    :user/e-mail \"alice@gmail.com\"}\n\n   {:db/id #db/id[:db.part/user]\n    :user/id \"jack\"\n    :user/name \"Jack Hinton\"\n    :user/e-mail \"jack@gmail.com\"}])\n   \n;;; data install\n@(d/transact conn user-data)\n\n\n;;;\n;;; \"alice\" 관련 정보 확인 \n;;;\n(defn find-user [id]\n  (d/q '[:find ?e .\n         :in $ ?id\n         :where [?e :user/id ?id]]\n        (d/db conn) id))\n\n(def alice-ent-id (find-user \"alice\"))\n\nalice-ent-id ; =\u003e 17592186045418\n\n(d/pull (d/db conn) '[*] alice-ent-id)\n; =\u003e {:db/id 17592186045418,\n;     :user/id \"alice\",\n;     :user/name \"Alice Parker\",\n;     :user/e-mail \"alice@gmail.com\"}\n\n\n;;;\n;;; field name :user/alias 추가\n;;;\n(def schema-2\n  [{:db/ident :user/alias\n    :db/valueType :db.type/string\n    :db/cardinality :db.cardinality/one}])\n\n@(d/transact conn schema-2)\n\n\n;; \"alice\"의 :user/alias 필드값 추가\n@(d/transact conn [[:db/add alice-ent-id :user/alias \"wonderland\"]])\n\n;; 추가된 :user/alias 필드값 확인\n(d/pull (d/db conn) '[*] alice-ent-id)\n; =\u003e {:db/id 17592186045418,\n;     :user/id \"alice\",\n;     :user/name \"Alice Parker\",\n;     :user/e-mail \"alice@gmail.com\",\n;     :user/alias \"wonderland\"}\n\n\n;;;\n;;; field name :user/alias --\u003e :user/nickname 으로 변경\n;;;\n(def alias-ent-id (d/entid (d/db conn) :user/alias))\n\n@(d/transact conn [[:db/add alias-ent-id :db/ident :user/nickname]])\n\n(d/pull (d/db conn) '[*] alice-end-id)\n; =\u003e {:db/id 17592186045418,\n;     :user/id \"alice\",\n;     :user/name \"Alice Parker\",\n;     :user/e-mail \"alice@gmail.com\",\n;     :user/nickname \"wonderland\"}\n....\n\n\n=== schema도 datom이다\n\n[listing,subs=\"macros,quotes\"]\n----\n+[\u003ce-id\u003e   \u003cattribute\u003e       \u003cvalue\u003e              \u003ctx-id\u003e   \u003cadded?\u003e]+\n...\n[   #33#    :db/ident         #:user/alias#          102       true    ]\n[   #33#    :db/type          :db.type/string      102       true    ]\n[   #33#    :db/cardinality   :db.cardinality/one  102       true    ]\n\n[   42    :user/id          \"alice\"              95        true    ]\n[   42    :user/name        \"Alice Parker\"       95        true    ]\n[   42    :user/e-mail      \"alice@gmail.com\"    95        true    ]\n\n[   42    #:user/alias#       \"wonderland\"         205       true    ]\n          (실제값은 #33#)\n...\n----\n\n\n== Query\n\n=== Query language\n\n* Datomic은 datalog라는 query language를 사용한다. 즉, SQL query 문과 다르다.\n* Datalog = Data + Prolog(Logic programming)\n\n[%header,cols=\"1,2,2\"]\n|===\n\n|             ^| SQL                   ^| Datomc\n| query        | SQL query (문자열 pass:q[--\u003e] 조작이 쉽지 않다)     | Datalog query (Clojure 자료형  pass:q[--\u003e] 조작이 쉽다) \n| join 방식    | 명시적                 | 묵시적\n\n|===\n\n\n=== Query examples\n\n[source]\n.schema 요약\n....\n:movie/title       :db.type/string   :db.cardinality/one\n:movie/year        :db.type/long     :db.cardinality/one\n:movie/cast        :db.type/ref      :db.cardinality/many\n\n:person/name       :db.type/string   :db.cardinality/one\n:person/born       :db.type/instan   :db.cardinality/one\n....\n\n\n[source,subs=\"macros,quotes\"]\n.query 예제\n....\n\n;; SQL query\n;;\n;; SELECT m.title\n;; FROM movies m\n;; WHERE m.year = 1987;\n \n(d/q '[:find ?title\n       :where [?e :movie/year 1987]\n              [?e :movie/title ?title]]\n     (d/db conn))\n; =\u003e +#+{[\"RoboCop\"] [\"Lethal Weapon\"] [\"Predator\"]}\n\n\n;; query문에서 parameter의 사용\n(d/q '[:find ?title\n       :in #$ ?year#\n       :where [?e :movie/year ?year]\n              [?e :movie/title ?title]]\n     #(d/db conn) 1987#)\n; =\u003e +#+{[\"RoboCop\"] [\"Lethal Weapon\"] [\"Predator\"]}\n\n\n;; query문에서 함수의 사용\n(d/q '[:find ?title ?year\n       :where [?m :movie/title ?title]\n              [?m :movie/year ?year]\n              [#(\u003c ?year 1984)#]]\n     (d/db conn))\n; =\u003e +#+{[\"Alien\" 1979] [\"Mad Max\" 1979] [\"First Blood\" 1982] [\"Mad Max 2\" 1981]}\n....\n\n \n=== Join example\n\n==== SQL explicit join\n\n[listing]\n----\nSELECT a.account_id, c.gender, e.fname, e.lname\nFROM account a INNER JOIN customer c\n       ON a.cust_id = c.cust_id\n     INNER JOIN employee e\n       ON a.emp_id = e.emp_id\nWHERE c.cust_type = 'B';\n----\n\n[listing,subs=\"macros,quotes\"]\n----\nc (customer)\n|-- #cust_id#     (PK)  \u003c--\n|-- cust_type           |\n`-- *gender*              |     \n                        |\na (account)             |\n|-- *account_id*  (PK)    |\n|-- #cust_id#     (FK)  \u003c--\n`-- #emp_id#      (FK)      \u003c--\n                            |  \ne (employee)                |\n|-- #emp_id#      (PK)      \u003c--\n|-- *fname*\n`-- *lname*\n----\n\n\n==== Datomic implicit join\n\n[source,subs=\"macros,quotes\"]\n....\n(d/q '[:find ?account-id ?gender ?fname ?lname\n       :where [?cust-id     :cust/type        \"B\"]\n              [#?cust-id#     :cust/gender      *?gender*]\n              [*?account-id*  :account/cust-id  #?cust-id#]\n              [?account-id  :account/emp-id   #?emp-id#]\n              [#?emp-id#      :emp/fname        *?fname*]\n              [?emp-id      :emp/lname        *?lname*]])\n....\n\n==== query시 주의할 점\n\n* query문의 ``:where``절 순서가 중요하다.\n\n[source]\n.다음 두 개의 쿼리 중 어느 쪽이 더 효율적일까?\n....\n(d/q '[:find ?cust-id\n       :where [?cust-id :cust/gender \"female\"]\n              [?cust-id :cust/type   \"B\"]])\n\n(d/q '[:find ?cust-id\n       :where [?cust-id :cust/type   \"B\"]\n              [?cust-id :cust/gender \"female\"]])\n....\n\n\n[%header,width=\"75%\",cols=\"2,^.^1\"]\n|===\n\n^| 처리량  ^| 메모리 사용량\n\n| 100만명의 고객 * 1/2 * 1/10 = 5만명의 고객\n| 50만명 + 5만명 = 55만명\n\n| 100만명의 고객 * 1/10 * 1/2 = 5만명의 고객\n| 10만명 + 5만명 = 15만명\n\n|===\n\n\n\n== DB as a Value \u0026 History\n\n=== Atom data type in Clojure\n\n[%header,cols=\"1a,1a\"]\n|===\n\n^| Code ^| Diagram\n\n| [source]\n....\n(def a (atom 10))\n\na   ;=\u003e #atom[10 0x274b9960]\n@a  ; =\u003e 10\n\n(def b @a)\n\nb   ; =\u003e 10\n....\n\n| [listing]\n----\n var          atom            value\n=============================================\na --\u003e  #atom[10 0x274b9960] --\u003e 10 (100 번지)\n                                 ^\n                                 \\|\nb --------------------------------\n----\n\n|\n[source]\n....\n(reset! a 20)\n\n@a   ; =\u003e 20\nb    ; =\u003e 10\n....\n\n| [listing]\n----\n var          atom            value\n============================================\na --\u003e  #atom[20 0x274b9960] --\u003e 20 (200 번지)\n\nb ----------------------------\u003e 10 (100 번지)\n----\n\n|===\n\n=== DB as A Value\n\n[source]\n....\n(ns datomic-guide.time-travel\n  (:require [datomic.api :as d]))\n\n;;; db connection\n(def db-uri \"datomic:mem://time-travel\")\n(d/create-database db-uri)\n(def conn (d/connect db-uri))\n\n\n(def db-1(d/db conn))\n\ndb-1   ; =\u003e datomic.db.Db@29475cf8\n\n\n;; schema install\n(def schema\n  [{:db/ident :user/id\n    :db/valueType :db.type/string\n    :db/unique :db.unique/value\n    :db/cardinality :db.cardinality/one}\n\n   {:db/ident :user/name\n    :db/valueType :db.type/string\n    :db/cardinality :db.cardinality/one}\n\n   {:db/ident :user/e-mail\n    :db/valueType :db.type/string\n    :db/unique :db.unique/identity\n    :db/cardinality :db.cardinality/one}])\n\n@(d/transact conn schema)\n; =\u003e {:db-before datomic.db.Db@29475cf8,\n;     :db-after datomic.db.Db@5ba75b5c,\n;     :tx-data [#datom[13194139534312 50 #inst \"2017-11-09T06:03:29.648-00:00\" 13194139534312 true]\n;               #datom[63 10 :user/id 13194139534312 true]\n;               #datom[63 40 23 13194139534312 true]\n;               #datom[63 42 37 13194139534312 true]\n;               #datom[63 41 35 13194139534312 true]\n;               #datom[64 10 :user/name 13194139534312 true]\n;               #datom[64 40 23 13194139534312 true]\n;               #datom[64 41 35 13194139534312 true]\n;               #datom[65 10 :user/e-mail 13194139534312 true]\n;               #datom[65 40 23 13194139534312 true]\n;               #datom[65 42 38 13194139534312 true]\n;               #datom[65 41 35 13194139534312 true]\n;               #datom[0 13 65 13194139534312 true]\n;               #datom[0 13 64 13194139534312 true]\n;               #datom[0 13 63 13194139534312 true]],\n;     :tempids {-9223301668109598143 63, -9223301668109598142 64, -9223301668109598141 65}}\n\n(def db-2 (d/db conn))\n\ndb-2   ; =\u003e datomic.db.Db@5ba75b5c\n\n\n;; data install\n(def user-data\n  [{:db/id #db/id[:db.part/user]\n    :user/id \"alice\"\n    :user/name \"Alice Parker\"\n    :user/e-mail \"alice@gmail.com\"}\n\n   {:db/id #db/id[:db.part/user]\n    :user/id \"jack\"\n    :user/name \"Jack Hinton\"\n    :user/e-mail \"jack@gmail.com\"}])\n   \n@(d/transact conn user-data)\n\n(def db-3 (d/db conn))\n\ndb-3   ; =\u003e datomic.db.Db@d34f836c\n\n\n(def alice-ent-id\n  (d/q '[:find ?e .\n         :where [?e :user/id \"alice\"]]\n       db-3))\n\n(d/pull db-3 '[*] alice-ent-id)\n; =\u003e {:db/id 17592186045418,\n;     :user/id \"alice\",\n;     :user/name \"Alice Parker\",\n;     :user/e-mail \"alice@gmail.com\"}\n\n\n\n@(d/transact conn [[:db/add alice-ent-id :user/e-mail \"alice@facebook.com\"]])\n\n(def db-4 (d/db conn))\n\n(d/pull db-4 '[*] alice-ent-id)\n; =\u003e {:db/id 17592186045418,\n;     :user/id \"alice\",\n;     :user/name \"Alice Parker\",\n;     :user/e-mail \"alice@facebook.com\"}\n....\n\n\n=== History DB\n\n[source]\n....\n(def hist-db (d/history db-4))\n\n(d/q '[:find ?e ?e-mail ?tx\n       :in $hist\n       :where [$hist ?e :user/id \"alice\"]\n              [$hist ?e :user/e-mail ?e-mail]]\n     hist-db)\n; =\u003e #{[17592186045418 \"alice@gmail.com\"]\n;      [17592186045418 \"alice@facebook.com\"]}\n....\n\n\n== 한계\n\n* not open-source, free-as-free-beer. \n* memory 사용량이 많다.\n  \n\n== 참고 자료\n\n. link:http://www.learndatalogtoday.org[]\n** Datomic에서 사용하고 있는 Datalog 방식의 query를 간단히 소개\n\n. link:https://www.amazon.com/Professional-Clojure-Jeremy-Anderson/dp/1119267277/ref=sr_1_1?ie=UTF8\u0026qid=1510200830\u0026sr=8-1\u0026keywords=professional+clojure[Professional Clojure], Chapter 6\n** 현재까지 나온 Clojure 관련 책들 중에서 Datomic에 대해 상대적으로 가장 자세하게 소개\n\n. link:http://docs.datomic.com/index.html[]\n** Datomic 공식 문서 site\n+\n. link:https://github.com/Datomic/day-of-datomic[]\n** 다양하고 풍부한 예제 제공\n** 특히 link:https://github.com/Datomic/day-of-datomic/tree/master/tutorial[tutorial] 폴더에 Datomic이 제공하는 거의 모든 API에 대한 예제가 담겨 있다.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphiloskim%2Fdatomic-intro","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fphiloskim%2Fdatomic-intro","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphiloskim%2Fdatomic-intro/lists"}