├── .gitignore ├── README.adoc ├── datomic-guide ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── doc │ └── intro.md ├── project.clj ├── resources │ ├── movie-data.edn │ └── movie-schema.edn ├── src │ └── datomic_guide │ │ ├── api │ │ ├── basic.clj │ │ ├── db.clj │ │ ├── log.clj │ │ └── tempid.clj │ │ ├── movie.clj │ │ ├── schema_example.clj │ │ └── time_travel.clj └── test │ └── datomic_guide │ └── core_test.clj ├── img └── datomic-architecture.png └── installation.adoc /.gitignore: -------------------------------------------------------------------------------- 1 | /datomic-guide/target 2 | /datomic-guide/.lein-repl-history 3 | 4 | *~ 5 | *# 6 | .#* 7 | 8 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = Datomic: 함수형 데이터베이스 2 | :sectnums: 3 | :source-language: clojure 4 | :source-highlighter: coderay 5 | :coderay-css: class 6 | :imagesdir: img/ 7 | :latexmath: 8 | 9 | == 강사 소개 10 | 11 | * 이름: 김영태 12 | * e-mail: philos99@gmail.com 13 | * link:http://www.kyobobook.co.kr/product/detailViewKor.laf?ejkGb=KOR&mallGb=KOR&barcode=9788966261802&orderClick=LAG&Kc=[클로저 시작하기] 공역, 인사이트 (원서: link:https://www.amazon.com/Living-Clojure-Introduction-Training-Developers/dp/1491909048/ref=sr_1_1?ie=UTF8&qid=1509447512&sr=8-1&keywords=living+clojure[Living Clojure], Carin Meier, O'Reilly) 14 | * link:https://github.com/philoskim/debux[debux] (Clojure/ClojureScript 디버깅 라이브러리) 제작 15 | ** 예제: link:https://github.com/philoskim/debux#simple-example[] 16 | 17 | 18 | == Datomic 소개 19 | 20 | * Clojure 언어의 창시자 Rich Hickey가 만든 함수형 데이터베이스 21 | * Clojure 언어로 쓰여져 있다. 22 | * "Immutable" Database: 개념상 git과 유사 23 | * 2012년에 최초 버전(0.8.3335) 발표 24 | * 현재 최신 버전: 0.9.5561.62 25 | 26 | === 참고: link:https://github.com/tonsky/datascript[DataScript] 27 | 28 | * Immutable memory database and Datalog query engine for Clojure, ClojureScript and JavaScript 29 | * 서버에서 사용되는 Datomic을 모방해 만들었다. 30 | * 주로 Web Browser Client용으로 사용된다. 31 | * Datomic과 거의 동일한 API를 제공한다. 32 | 33 | 34 | == Datomic 도입 사례 35 | 36 | * link:http://blog.cognitect.com/blog/2015/6/30/walmart-runs-clojure-at-scale[Walmart runs Clojure and Datomic at scale] 37 | * link:http://blog.datomic.com/2015/09/nubank-chooses-datomic.html[Nubank chooses Datomic] 38 | 39 | * link:http://www.datomic.com/customers.html[기타 Customers Story] 40 | 41 | 42 | == Datomic의 특징 43 | 44 | * Transactional store, Analytics store, Distributed database, Graph database, Logic 45 | database 46 | 47 | * 관계형 데이터베이스 사용 예의 96% 대체 가능. 나머지 4%는 쓰기 작업이 많은(high 48 | write-volume) 경우. 49 | 50 | * 응답 속도가 빠르다 (거의 memory DB 수준) 51 | ** ex) 1.5 ms per query, compared to the 10-20 ms in SQL query 52 | 53 | * Storage backend로 다양한 DB를 사용할 수 있다. 54 | ** DynamoDB(AWS), Cassandra, Riak, Couchbase, Infinispan, or SQL database등 55 | 56 | 57 | == Datomic architecture 58 | 59 | * Reads are separated from writes and decentralized. 60 | 61 | * Writes don't block reads, reads don't block one another. 62 | 63 | * Read scaling horizontally by adding more "peer" systems. 64 | 65 | image::datomic-architecture.png[] 66 | 67 | [listing] 68 | .Datomic DB vs. SQL DB 69 | ---- 70 | Datomic DB SQL DB 71 | ============================================= 72 | |-- Storage 73 | | 74 | |-- Transactor 75 | | |-- Writing 76 | | `-- Indexing SQL Server 77 | | 78 | `-- Peer Library 79 | |-- Live index 80 | |-- Cache 81 | ====|======================================== 82 | `-- Query SQL Client Library 83 | ============================================= 84 | ---- 85 | 86 | 87 | == Keyword data type in Clojure 88 | 89 | === keyword data type 형식 90 | 91 | [listing] 92 | ---- 93 | :namespace-part/name-part 94 | 95 | :table-name/field-name 96 | ---- 97 | 98 | === keyword examples 99 | 100 | [source] 101 | .... 102 | :employee/address 103 | 104 | :address 105 | 106 | :user.type/superuser 107 | 108 | :user.type.v2/superuser 109 | 110 | :user.type/superuser.boss 111 | :user.type/superuser.manager 112 | .... 113 | 114 | 115 | === keyword data type의 평가(evaluation) 116 | 117 | * Keyowrd data type is a self-evaluated value. 118 | 119 | 120 | [listing] 121 | ---- 122 | :employee/address ;=> :employee/address 123 | ---- 124 | 125 | 126 | == Datom 127 | 128 | * datom은 Datomic이 데이터를 처리할 때의 기본 단위이다. 129 | 130 | === datom의 구성요소 131 | 132 | * 하나의 datom은 1 개의 field를 표현한다. 133 | * 하나의 datom은 5 개의 구성요소로 이루어져 있다. 134 | + 135 | [listing] 136 | ---- 137 | [entity-id attribute value transaction-id added?] 138 | ---- 139 | 140 | === datom의 구성요소 설명 141 | 142 | [width="75%",cols="1,3"] 143 | |=== 144 | 145 | | `entity-id` 146 | a| 147 | * SQL DB의 record id (primary key 역할)에 해당. 148 | * 개발자가 지정해 줄 수 없으며, Datomic이 자동으로 생성. 동일 db 내에서 유일한 값. 149 | * Java의 long 타입 (64 비트 정수형) 150 | 151 | | `attribute` | SQL DB의 field name 152 | | `value` | SQL DB의 field value 153 | | `transaction-id` | 동일한 transaction(쓰기)일 때, 동일한 `transaction-id` 값을 부여 받는다. 154 | | `added?` 155 | a| 156 | * `true`: assert or accumulate 157 | * `false`: retract 158 | 159 | |=== 160 | 161 | [listing,subs="macros,quotes"] 162 | .datom들의 실례 163 | ---- 164 | +[ ]+ 165 | ... 166 | [ 42 :person/firstName "James" 102 true ] 167 | [ 42 :person/lastName "Clark" 102 true ] 168 | [ 42 :person/address #43# 102 true ] 169 | 170 | [ #43# :address/state "Oregon" 102 true ] 171 | [ #43# :address/city "Portland" 102 true ] 172 | 173 | [ 1077 :person/fitstName "Viola" 103 true ] 174 | [ 1077 :person/lastName "Davis" 103 true ] 175 | [ 1077 :person/friends #{42 89} 103 true ] 176 | ... 177 | ---- 178 | 179 | * (참고) `:db.type/ref` <> 180 | 181 | ==== SQL table의 record: 2차원 배열 182 | 183 | [listing] 184 | ---- 185 | [ 42 :person/firstName "James" 102 true ] 186 | [ 42 :person/lastName "Clark" 102 true ] 187 | [ 42 :person/gender "male" 102 true ] 188 | ---- 189 | 190 | ==== NoSQL의 document: tree 구조 191 | 192 | [listing] 193 | ---- 194 | ... 195 | [ 42 :person/firstName "James" 102 true ] 196 | [ 42 :person/lastName "Clark" 102 true ] 197 | 198 | [ 52 :person/firstName "Alice" 102 true ] 199 | [ 52 :person/lastName "Parker" 102 true ] 200 | 201 | [ 1077 :person/fitstName "Viola" 103 true ] 202 | [ 1077 :person/lastName "Davis" 103 true ] 203 | [ 1077 :person/friends #{42 52} 103 true ] 204 | ... 205 | ---- 206 | 207 | ==== Graph DB도 구현 가능 208 | 209 | 210 | 211 | === SQL CRUD와의 용어 비교 212 | 213 | * CRUD vs. ARAR 214 | + 215 | [%header,width="75%",cols="3*^"] 216 | |=== 217 | 218 | | SQL | Datomic | Datomic 담당 함수 219 | | Create | Assert | `transact` 220 | | Read | Read | `q` 221 | | Update | Accumulate | `transact` 222 | | Delete | Retract | `transact` 223 | |=== 224 | 225 | 226 | 227 | 228 | == Schema 229 | 230 | === DB별 비교 231 | 232 | * SQL DB 233 | ** schema를 table 단위로 작성한다. 234 | ** record 구조가 고정되어 있다. 235 | 236 | * Datomic DB 237 | ** schema를 field 단위로 작성한다. 238 | ** record 구조가 고정되어 있지 않다. 239 | + 240 | 따라서 동적으로 field를 조합하여 record를 만들 수 있다. 241 | ** schema 자체도 datom으로 이루어져 있다. 242 | + 243 | 따라서 프로그램 실행 중에 동적으로 schema 생성 및 변경이 가능하다. 244 | 245 | * NoSQL DB 246 | ** schema가 아예 없다. 247 | 248 | === Datomic Schema의 구성 249 | 250 | [source] 251 | .Datomic Schema의 예 252 | .... 253 | (def schema 254 | [{:db/ident :user/id 255 | :db/valueType :db.type/string 256 | :db/unique :db.unique/value 257 | :db/cardinality :db.cardinality/one} 258 | 259 | {:db/ident :user/name 260 | :db/valueType :db.type/string 261 | :db/cardinality :db.cardinality/one} 262 | 263 | {:db/ident :user/e-mail 264 | :db/valueType :db.type/string 265 | :db/cardinality :db.cardinality/one}]) 266 | .... 267 | 268 | [%header,cols="1,3a"] 269 | |=== 270 | 271 | | Schema Attributes | Values 272 | |`:db/ident` | field name을 지정한다. 반드시 keyword 자료형이어야 한다. 273 | 274 | |`:db/valueType` [[db-type-ref]] 275 | | field value의 자료형을 지정힌다. 276 | 277 | * `:db.type/string` 278 | * `:db.type/long :db.type/bigint` 279 | * `:db.type/float :db.type/double :db.type/bigdec` 280 | * `:db.type/boolean` 281 | * `:db.type/keyword` 282 | * `:db.type/ref` 283 | * `:db.type/instant` 284 | * `:db.type/uuid` 285 | * `:db.type/url` 286 | * `:db.type/bytes` 287 | 288 | | `:db/cardinality` 289 | | 290 | * `:db.cardinality/one` - 일대일 대응 291 | * `:db.cardinality/many` - 일대다 대응 292 | 293 | | `:db/noHistory` 294 | | 295 | * `true` - field value를 누적(accumulate)시키지 않고, 기존의 값을 덮어 쓴다(overwrite). 296 | * `false` - default 297 | 298 | | pass:q[......] 299 | | pass:q[......] 300 | 301 | |=== 302 | 303 | === Schema 예제 304 | 305 | [source] 306 | .datomic-guide/schema-example.clj 307 | .... 308 | (ns datomic-guide.schema-example 309 | (:require [datomic.api :as d])) 310 | 311 | ;;; database 생성 312 | (def db-uri "datomic:mem://schema-example") 313 | 314 | (d/create-database db-uri) 315 | 316 | ;;; conn 얻기 317 | (def conn (d/connect db-uri)) 318 | 319 | ;;; 320 | ;;; schema definitions 321 | ;;; 322 | (def schema-1 323 | [{:db/ident :user/id 324 | :db/valueType :db.type/string 325 | :db/unique :db.unique/value 326 | :db/cardinality :db.cardinality/one} 327 | 328 | {:db/ident :user/name 329 | :db/valueType :db.type/string 330 | :db/cardinality :db.cardinality/one} 331 | 332 | {:db/ident :user/e-mail 333 | :db/valueType :db.type/string 334 | :db/cardinality :db.cardinality/one}]) 335 | 336 | ;;; schema-1 install 337 | @(d/transact conn schema-1) 338 | 339 | 340 | ;;; 341 | ;;; data definitions 342 | ;;; 343 | (def user-data 344 | [{:db/id #db/id[:db.part/user] 345 | :user/id "alice" 346 | :user/name "Alice Parker" 347 | :user/e-mail "alice@gmail.com"} 348 | 349 | {:db/id #db/id[:db.part/user] 350 | :user/id "jack" 351 | :user/name "Jack Hinton" 352 | :user/e-mail "jack@gmail.com"}]) 353 | 354 | ;;; data install 355 | @(d/transact conn user-data) 356 | 357 | 358 | ;;; 359 | ;;; "alice" 관련 정보 확인 360 | ;;; 361 | (defn find-user [id] 362 | (d/q '[:find ?e . 363 | :in $ ?id 364 | :where [?e :user/id ?id]] 365 | (d/db conn) id)) 366 | 367 | (def alice-ent-id (find-user "alice")) 368 | 369 | alice-ent-id ; => 17592186045418 370 | 371 | (d/pull (d/db conn) '[*] alice-ent-id) 372 | ; => {:db/id 17592186045418, 373 | ; :user/id "alice", 374 | ; :user/name "Alice Parker", 375 | ; :user/e-mail "alice@gmail.com"} 376 | 377 | 378 | ;;; 379 | ;;; field name :user/alias 추가 380 | ;;; 381 | (def schema-2 382 | [{:db/ident :user/alias 383 | :db/valueType :db.type/string 384 | :db/cardinality :db.cardinality/one}]) 385 | 386 | @(d/transact conn schema-2) 387 | 388 | 389 | ;; "alice"의 :user/alias 필드값 추가 390 | @(d/transact conn [[:db/add alice-ent-id :user/alias "wonderland"]]) 391 | 392 | ;; 추가된 :user/alias 필드값 확인 393 | (d/pull (d/db conn) '[*] alice-ent-id) 394 | ; => {:db/id 17592186045418, 395 | ; :user/id "alice", 396 | ; :user/name "Alice Parker", 397 | ; :user/e-mail "alice@gmail.com", 398 | ; :user/alias "wonderland"} 399 | 400 | 401 | ;;; 402 | ;;; field name :user/alias --> :user/nickname 으로 변경 403 | ;;; 404 | (def alias-ent-id (d/entid (d/db conn) :user/alias)) 405 | 406 | @(d/transact conn [[:db/add alias-ent-id :db/ident :user/nickname]]) 407 | 408 | (d/pull (d/db conn) '[*] alice-end-id) 409 | ; => {:db/id 17592186045418, 410 | ; :user/id "alice", 411 | ; :user/name "Alice Parker", 412 | ; :user/e-mail "alice@gmail.com", 413 | ; :user/nickname "wonderland"} 414 | .... 415 | 416 | 417 | === schema도 datom이다 418 | 419 | [listing,subs="macros,quotes"] 420 | ---- 421 | +[ ]+ 422 | ... 423 | [ #33# :db/ident #:user/alias# 102 true ] 424 | [ #33# :db/type :db.type/string 102 true ] 425 | [ #33# :db/cardinality :db.cardinality/one 102 true ] 426 | 427 | [ 42 :user/id "alice" 95 true ] 428 | [ 42 :user/name "Alice Parker" 95 true ] 429 | [ 42 :user/e-mail "alice@gmail.com" 95 true ] 430 | 431 | [ 42 #:user/alias# "wonderland" 205 true ] 432 | (실제값은 #33#) 433 | ... 434 | ---- 435 | 436 | 437 | == Query 438 | 439 | === Query language 440 | 441 | * Datomic은 datalog라는 query language를 사용한다. 즉, SQL query 문과 다르다. 442 | * Datalog = Data + Prolog(Logic programming) 443 | 444 | [%header,cols="1,2,2"] 445 | |=== 446 | 447 | | ^| SQL ^| Datomc 448 | | query | SQL query (문자열 pass:q[-->] 조작이 쉽지 않다) | Datalog query (Clojure 자료형 pass:q[-->] 조작이 쉽다) 449 | | join 방식 | 명시적 | 묵시적 450 | 451 | |=== 452 | 453 | 454 | === Query examples 455 | 456 | [source] 457 | .schema 요약 458 | .... 459 | :movie/title :db.type/string :db.cardinality/one 460 | :movie/year :db.type/long :db.cardinality/one 461 | :movie/cast :db.type/ref :db.cardinality/many 462 | 463 | :person/name :db.type/string :db.cardinality/one 464 | :person/born :db.type/instan :db.cardinality/one 465 | .... 466 | 467 | 468 | [source,subs="macros,quotes"] 469 | .query 예제 470 | .... 471 | 472 | ;; SQL query 473 | ;; 474 | ;; SELECT m.title 475 | ;; FROM movies m 476 | ;; WHERE m.year = 1987; 477 | 478 | (d/q '[:find ?title 479 | :where [?e :movie/year 1987] 480 | [?e :movie/title ?title]] 481 | (d/db conn)) 482 | ; => +#+{["RoboCop"] ["Lethal Weapon"] ["Predator"]} 483 | 484 | 485 | ;; query문에서 parameter의 사용 486 | (d/q '[:find ?title 487 | :in #$ ?year# 488 | :where [?e :movie/year ?year] 489 | [?e :movie/title ?title]] 490 | #(d/db conn) 1987#) 491 | ; => +#+{["RoboCop"] ["Lethal Weapon"] ["Predator"]} 492 | 493 | 494 | ;; query문에서 함수의 사용 495 | (d/q '[:find ?title ?year 496 | :where [?m :movie/title ?title] 497 | [?m :movie/year ?year] 498 | [#(< ?year 1984)#]] 499 | (d/db conn)) 500 | ; => +#+{["Alien" 1979] ["Mad Max" 1979] ["First Blood" 1982] ["Mad Max 2" 1981]} 501 | .... 502 | 503 | 504 | === Join example 505 | 506 | ==== SQL explicit join 507 | 508 | [listing] 509 | ---- 510 | SELECT a.account_id, c.gender, e.fname, e.lname 511 | FROM account a INNER JOIN customer c 512 | ON a.cust_id = c.cust_id 513 | INNER JOIN employee e 514 | ON a.emp_id = e.emp_id 515 | WHERE c.cust_type = 'B'; 516 | ---- 517 | 518 | [listing,subs="macros,quotes"] 519 | ---- 520 | c (customer) 521 | |-- #cust_id# (PK) <-- 522 | |-- cust_type | 523 | `-- *gender* | 524 | | 525 | a (account) | 526 | |-- *account_id* (PK) | 527 | |-- #cust_id# (FK) <-- 528 | `-- #emp_id# (FK) <-- 529 | | 530 | e (employee) | 531 | |-- #emp_id# (PK) <-- 532 | |-- *fname* 533 | `-- *lname* 534 | ---- 535 | 536 | 537 | ==== Datomic implicit join 538 | 539 | [source,subs="macros,quotes"] 540 | .... 541 | (d/q '[:find ?account-id ?gender ?fname ?lname 542 | :where [?cust-id :cust/type "B"] 543 | [#?cust-id# :cust/gender *?gender*] 544 | [*?account-id* :account/cust-id #?cust-id#] 545 | [?account-id :account/emp-id #?emp-id#] 546 | [#?emp-id# :emp/fname *?fname*] 547 | [?emp-id :emp/lname *?lname*]]) 548 | .... 549 | 550 | ==== query시 주의할 점 551 | 552 | * query문의 ``:where``절 순서가 중요하다. 553 | 554 | [source] 555 | .다음 두 개의 쿼리 중 어느 쪽이 더 효율적일까? 556 | .... 557 | (d/q '[:find ?cust-id 558 | :where [?cust-id :cust/gender "female"] 559 | [?cust-id :cust/type "B"]]) 560 | 561 | (d/q '[:find ?cust-id 562 | :where [?cust-id :cust/type "B"] 563 | [?cust-id :cust/gender "female"]]) 564 | .... 565 | 566 | 567 | [%header,width="75%",cols="2,^.^1"] 568 | |=== 569 | 570 | ^| 처리량 ^| 메모리 사용량 571 | 572 | | 100만명의 고객 * 1/2 * 1/10 = 5만명의 고객 573 | | 50만명 + 5만명 = 55만명 574 | 575 | | 100만명의 고객 * 1/10 * 1/2 = 5만명의 고객 576 | | 10만명 + 5만명 = 15만명 577 | 578 | |=== 579 | 580 | 581 | 582 | == DB as a Value & History 583 | 584 | === Atom data type in Clojure 585 | 586 | [%header,cols="1a,1a"] 587 | |=== 588 | 589 | ^| Code ^| Diagram 590 | 591 | | [source] 592 | .... 593 | (def a (atom 10)) 594 | 595 | a ;=> #atom[10 0x274b9960] 596 | @a ; => 10 597 | 598 | (def b @a) 599 | 600 | b ; => 10 601 | .... 602 | 603 | | [listing] 604 | ---- 605 | var atom value 606 | ============================================= 607 | a --> #atom[10 0x274b9960] --> 10 (100 번지) 608 | ^ 609 | \| 610 | b -------------------------------- 611 | ---- 612 | 613 | | 614 | [source] 615 | .... 616 | (reset! a 20) 617 | 618 | @a ; => 20 619 | b ; => 10 620 | .... 621 | 622 | | [listing] 623 | ---- 624 | var atom value 625 | ============================================ 626 | a --> #atom[20 0x274b9960] --> 20 (200 번지) 627 | 628 | b ----------------------------> 10 (100 번지) 629 | ---- 630 | 631 | |=== 632 | 633 | === DB as A Value 634 | 635 | [source] 636 | .... 637 | (ns datomic-guide.time-travel 638 | (:require [datomic.api :as d])) 639 | 640 | ;;; db connection 641 | (def db-uri "datomic:mem://time-travel") 642 | (d/create-database db-uri) 643 | (def conn (d/connect db-uri)) 644 | 645 | 646 | (def db-1(d/db conn)) 647 | 648 | db-1 ; => datomic.db.Db@29475cf8 649 | 650 | 651 | ;; schema install 652 | (def schema 653 | [{:db/ident :user/id 654 | :db/valueType :db.type/string 655 | :db/unique :db.unique/value 656 | :db/cardinality :db.cardinality/one} 657 | 658 | {:db/ident :user/name 659 | :db/valueType :db.type/string 660 | :db/cardinality :db.cardinality/one} 661 | 662 | {:db/ident :user/e-mail 663 | :db/valueType :db.type/string 664 | :db/unique :db.unique/identity 665 | :db/cardinality :db.cardinality/one}]) 666 | 667 | @(d/transact conn schema) 668 | ; => {:db-before datomic.db.Db@29475cf8, 669 | ; :db-after datomic.db.Db@5ba75b5c, 670 | ; :tx-data [#datom[13194139534312 50 #inst "2017-11-09T06:03:29.648-00:00" 13194139534312 true] 671 | ; #datom[63 10 :user/id 13194139534312 true] 672 | ; #datom[63 40 23 13194139534312 true] 673 | ; #datom[63 42 37 13194139534312 true] 674 | ; #datom[63 41 35 13194139534312 true] 675 | ; #datom[64 10 :user/name 13194139534312 true] 676 | ; #datom[64 40 23 13194139534312 true] 677 | ; #datom[64 41 35 13194139534312 true] 678 | ; #datom[65 10 :user/e-mail 13194139534312 true] 679 | ; #datom[65 40 23 13194139534312 true] 680 | ; #datom[65 42 38 13194139534312 true] 681 | ; #datom[65 41 35 13194139534312 true] 682 | ; #datom[0 13 65 13194139534312 true] 683 | ; #datom[0 13 64 13194139534312 true] 684 | ; #datom[0 13 63 13194139534312 true]], 685 | ; :tempids {-9223301668109598143 63, -9223301668109598142 64, -9223301668109598141 65}} 686 | 687 | (def db-2 (d/db conn)) 688 | 689 | db-2 ; => datomic.db.Db@5ba75b5c 690 | 691 | 692 | ;; data install 693 | (def user-data 694 | [{:db/id #db/id[:db.part/user] 695 | :user/id "alice" 696 | :user/name "Alice Parker" 697 | :user/e-mail "alice@gmail.com"} 698 | 699 | {:db/id #db/id[:db.part/user] 700 | :user/id "jack" 701 | :user/name "Jack Hinton" 702 | :user/e-mail "jack@gmail.com"}]) 703 | 704 | @(d/transact conn user-data) 705 | 706 | (def db-3 (d/db conn)) 707 | 708 | db-3 ; => datomic.db.Db@d34f836c 709 | 710 | 711 | (def alice-ent-id 712 | (d/q '[:find ?e . 713 | :where [?e :user/id "alice"]] 714 | db-3)) 715 | 716 | (d/pull db-3 '[*] alice-ent-id) 717 | ; => {:db/id 17592186045418, 718 | ; :user/id "alice", 719 | ; :user/name "Alice Parker", 720 | ; :user/e-mail "alice@gmail.com"} 721 | 722 | 723 | 724 | @(d/transact conn [[:db/add alice-ent-id :user/e-mail "alice@facebook.com"]]) 725 | 726 | (def db-4 (d/db conn)) 727 | 728 | (d/pull db-4 '[*] alice-ent-id) 729 | ; => {:db/id 17592186045418, 730 | ; :user/id "alice", 731 | ; :user/name "Alice Parker", 732 | ; :user/e-mail "alice@facebook.com"} 733 | .... 734 | 735 | 736 | === History DB 737 | 738 | [source] 739 | .... 740 | (def hist-db (d/history db-4)) 741 | 742 | (d/q '[:find ?e ?e-mail ?tx 743 | :in $hist 744 | :where [$hist ?e :user/id "alice"] 745 | [$hist ?e :user/e-mail ?e-mail]] 746 | hist-db) 747 | ; => #{[17592186045418 "alice@gmail.com"] 748 | ; [17592186045418 "alice@facebook.com"]} 749 | .... 750 | 751 | 752 | == 한계 753 | 754 | * not open-source, free-as-free-beer. 755 | * memory 사용량이 많다. 756 | 757 | 758 | == 참고 자료 759 | 760 | . link:http://www.learndatalogtoday.org[] 761 | ** Datomic에서 사용하고 있는 Datalog 방식의 query를 간단히 소개 762 | 763 | . link:https://www.amazon.com/Professional-Clojure-Jeremy-Anderson/dp/1119267277/ref=sr_1_1?ie=UTF8&qid=1510200830&sr=8-1&keywords=professional+clojure[Professional Clojure], Chapter 6 764 | ** 현재까지 나온 Clojure 관련 책들 중에서 Datomic에 대해 상대적으로 가장 자세하게 소개 765 | 766 | . link:http://docs.datomic.com/index.html[] 767 | ** Datomic 공식 문서 site 768 | + 769 | . link:https://github.com/Datomic/day-of-datomic[] 770 | ** 다양하고 풍부한 예제 제공 771 | ** 특히 link:https://github.com/Datomic/day-of-datomic/tree/master/tutorial[tutorial] 폴더에 Datomic이 제공하는 거의 모든 API에 대한 예제가 담겨 있다. 772 | -------------------------------------------------------------------------------- /datomic-guide/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | .hgignore 11 | .hg/ 12 | -------------------------------------------------------------------------------- /datomic-guide/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/). 3 | 4 | ## [Unreleased] 5 | ### Changed 6 | - Add a new arity to `make-widget-async` to provide a different widget shape. 7 | 8 | ## [0.1.1] - 2017-10-31 9 | ### Changed 10 | - Documentation on how to make the widgets. 11 | 12 | ### Removed 13 | - `make-widget-sync` - we're all async, all the time. 14 | 15 | ### Fixed 16 | - Fixed widget maker to keep working when daylight savings switches over. 17 | 18 | ## 0.1.0 - 2017-10-31 19 | ### Added 20 | - Files from the new template. 21 | - Widget maker public API - `make-widget-sync`. 22 | 23 | [Unreleased]: https://github.com/your-name/datomic-guide/compare/0.1.1...HEAD 24 | [0.1.1]: https://github.com/your-name/datomic-guide/compare/0.1.0...0.1.1 25 | -------------------------------------------------------------------------------- /datomic-guide/LICENSE: -------------------------------------------------------------------------------- 1 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 2 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 3 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 4 | 5 | 1. DEFINITIONS 6 | 7 | "Contribution" means: 8 | 9 | a) in the case of the initial Contributor, the initial code and 10 | documentation distributed under this Agreement, and 11 | 12 | b) in the case of each subsequent Contributor: 13 | 14 | i) changes to the Program, and 15 | 16 | ii) additions to the Program; 17 | 18 | where such changes and/or additions to the Program originate from and are 19 | distributed by that particular Contributor. A Contribution 'originates' from 20 | a Contributor if it was added to the Program by such Contributor itself or 21 | anyone acting on such Contributor's behalf. Contributions do not include 22 | additions to the Program which: (i) are separate modules of software 23 | distributed in conjunction with the Program under their own license 24 | agreement, and (ii) are not derivative works of the Program. 25 | 26 | "Contributor" means any person or entity that distributes the Program. 27 | 28 | "Licensed Patents" mean patent claims licensable by a Contributor which are 29 | necessarily infringed by the use or sale of its Contribution alone or when 30 | combined with the Program. 31 | 32 | "Program" means the Contributions distributed in accordance with this 33 | Agreement. 34 | 35 | "Recipient" means anyone who receives the Program under this Agreement, 36 | including all Contributors. 37 | 38 | 2. GRANT OF RIGHTS 39 | 40 | a) Subject to the terms of this Agreement, each Contributor hereby grants 41 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 42 | reproduce, prepare derivative works of, publicly display, publicly perform, 43 | distribute and sublicense the Contribution of such Contributor, if any, and 44 | such derivative works, in source code and object code form. 45 | 46 | b) Subject to the terms of this Agreement, each Contributor hereby grants 47 | Recipient a non-exclusive, worldwide, royalty-free patent license under 48 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 49 | transfer the Contribution of such Contributor, if any, in source code and 50 | object code form. This patent license shall apply to the combination of the 51 | Contribution and the Program if, at the time the Contribution is added by the 52 | Contributor, such addition of the Contribution causes such combination to be 53 | covered by the Licensed Patents. The patent license shall not apply to any 54 | other combinations which include the Contribution. No hardware per se is 55 | licensed hereunder. 56 | 57 | c) Recipient understands that although each Contributor grants the licenses 58 | to its Contributions set forth herein, no assurances are provided by any 59 | Contributor that the Program does not infringe the patent or other 60 | intellectual property rights of any other entity. Each Contributor disclaims 61 | any liability to Recipient for claims brought by any other entity based on 62 | infringement of intellectual property rights or otherwise. As a condition to 63 | exercising the rights and licenses granted hereunder, each Recipient hereby 64 | assumes sole responsibility to secure any other intellectual property rights 65 | needed, if any. For example, if a third party patent license is required to 66 | allow Recipient to distribute the Program, it is Recipient's responsibility 67 | to acquire that license before distributing the Program. 68 | 69 | d) Each Contributor represents that to its knowledge it has sufficient 70 | copyright rights in its Contribution, if any, to grant the copyright license 71 | set forth in this Agreement. 72 | 73 | 3. REQUIREMENTS 74 | 75 | A Contributor may choose to distribute the Program in object code form under 76 | its own license agreement, provided that: 77 | 78 | a) it complies with the terms and conditions of this Agreement; and 79 | 80 | b) its license agreement: 81 | 82 | i) effectively disclaims on behalf of all Contributors all warranties and 83 | conditions, express and implied, including warranties or conditions of title 84 | and non-infringement, and implied warranties or conditions of merchantability 85 | and fitness for a particular purpose; 86 | 87 | ii) effectively excludes on behalf of all Contributors all liability for 88 | damages, including direct, indirect, special, incidental and consequential 89 | damages, such as lost profits; 90 | 91 | iii) states that any provisions which differ from this Agreement are offered 92 | by that Contributor alone and not by any other party; and 93 | 94 | iv) states that source code for the Program is available from such 95 | Contributor, and informs licensees how to obtain it in a reasonable manner on 96 | or through a medium customarily used for software exchange. 97 | 98 | When the Program is made available in source code form: 99 | 100 | a) it must be made available under this Agreement; and 101 | 102 | b) a copy of this Agreement must be included with each copy of the Program. 103 | 104 | Contributors may not remove or alter any copyright notices contained within 105 | the Program. 106 | 107 | Each Contributor must identify itself as the originator of its Contribution, 108 | if any, in a manner that reasonably allows subsequent Recipients to identify 109 | the originator of the Contribution. 110 | 111 | 4. COMMERCIAL DISTRIBUTION 112 | 113 | Commercial distributors of software may accept certain responsibilities with 114 | respect to end users, business partners and the like. While this license is 115 | intended to facilitate the commercial use of the Program, the Contributor who 116 | includes the Program in a commercial product offering should do so in a 117 | manner which does not create potential liability for other Contributors. 118 | Therefore, if a Contributor includes the Program in a commercial product 119 | offering, such Contributor ("Commercial Contributor") hereby agrees to defend 120 | and indemnify every other Contributor ("Indemnified Contributor") against any 121 | losses, damages and costs (collectively "Losses") arising from claims, 122 | lawsuits and other legal actions brought by a third party against the 123 | Indemnified Contributor to the extent caused by the acts or omissions of such 124 | Commercial Contributor in connection with its distribution of the Program in 125 | a commercial product offering. The obligations in this section do not apply 126 | to any claims or Losses relating to any actual or alleged intellectual 127 | property infringement. In order to qualify, an Indemnified Contributor must: 128 | a) promptly notify the Commercial Contributor in writing of such claim, and 129 | b) allow the Commercial Contributor to control, and cooperate with the 130 | Commercial Contributor in, the defense and any related settlement 131 | negotiations. The Indemnified Contributor may participate in any such claim 132 | at its own expense. 133 | 134 | For example, a Contributor might include the Program in a commercial product 135 | offering, Product X. That Contributor is then a Commercial Contributor. If 136 | that Commercial Contributor then makes performance claims, or offers 137 | warranties related to Product X, those performance claims and warranties are 138 | such Commercial Contributor's responsibility alone. Under this section, the 139 | Commercial Contributor would have to defend claims against the other 140 | Contributors related to those performance claims and warranties, and if a 141 | court requires any other Contributor to pay any damages as a result, the 142 | Commercial Contributor must pay those damages. 143 | 144 | 5. NO WARRANTY 145 | 146 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON 147 | AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER 148 | EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR 149 | CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A 150 | PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the 151 | appropriateness of using and distributing the Program and assumes all risks 152 | associated with its exercise of rights under this Agreement , including but 153 | not limited to the risks and costs of program errors, compliance with 154 | applicable laws, damage to or loss of data, programs or equipment, and 155 | unavailability or interruption of operations. 156 | 157 | 6. DISCLAIMER OF LIABILITY 158 | 159 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 160 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 161 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 162 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 163 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 164 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 165 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 166 | OF SUCH DAMAGES. 167 | 168 | 7. GENERAL 169 | 170 | If any provision of this Agreement is invalid or unenforceable under 171 | applicable law, it shall not affect the validity or enforceability of the 172 | remainder of the terms of this Agreement, and without further action by the 173 | parties hereto, such provision shall be reformed to the minimum extent 174 | necessary to make such provision valid and enforceable. 175 | 176 | If Recipient institutes patent litigation against any entity (including a 177 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 178 | (excluding combinations of the Program with other software or hardware) 179 | infringes such Recipient's patent(s), then such Recipient's rights granted 180 | under Section 2(b) shall terminate as of the date such litigation is filed. 181 | 182 | All Recipient's rights under this Agreement shall terminate if it fails to 183 | comply with any of the material terms or conditions of this Agreement and 184 | does not cure such failure in a reasonable period of time after becoming 185 | aware of such noncompliance. If all Recipient's rights under this Agreement 186 | terminate, Recipient agrees to cease use and distribution of the Program as 187 | soon as reasonably practicable. However, Recipient's obligations under this 188 | Agreement and any licenses granted by Recipient relating to the Program shall 189 | continue and survive. 190 | 191 | Everyone is permitted to copy and distribute copies of this Agreement, but in 192 | order to avoid inconsistency the Agreement is copyrighted and may only be 193 | modified in the following manner. The Agreement Steward reserves the right to 194 | publish new versions (including revisions) of this Agreement from time to 195 | time. No one other than the Agreement Steward has the right to modify this 196 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 197 | Eclipse Foundation may assign the responsibility to serve as the Agreement 198 | Steward to a suitable separate entity. Each new version of the Agreement will 199 | be given a distinguishing version number. The Program (including 200 | Contributions) may always be distributed subject to the version of the 201 | Agreement under which it was received. In addition, after a new version of 202 | the Agreement is published, Contributor may elect to distribute the Program 203 | (including its Contributions) under the new version. Except as expressly 204 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 205 | licenses to the intellectual property of any Contributor under this 206 | Agreement, whether expressly, by implication, estoppel or otherwise. All 207 | rights in the Program not expressly granted under this Agreement are 208 | reserved. 209 | 210 | This Agreement is governed by the laws of the State of New York and the 211 | intellectual property laws of the United States of America. No party to this 212 | Agreement will bring a legal action under this Agreement more than one year 213 | after the cause of action arose. Each party waives its rights to a jury trial 214 | in any resulting litigation. 215 | -------------------------------------------------------------------------------- /datomic-guide/README.md: -------------------------------------------------------------------------------- 1 | # datomic-guide 2 | 3 | A Clojure library designed to ... well, that part is up to you. 4 | 5 | ## Usage 6 | 7 | FIXME 8 | 9 | ## License 10 | 11 | Copyright © 2017 FIXME 12 | 13 | Distributed under the Eclipse Public License either version 1.0 or (at 14 | your option) any later version. 15 | -------------------------------------------------------------------------------- /datomic-guide/doc/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to datomic-guide 2 | 3 | TODO: write [great documentation](http://jacobian.org/writing/what-to-write/) 4 | -------------------------------------------------------------------------------- /datomic-guide/project.clj: -------------------------------------------------------------------------------- 1 | (defproject datomic-guide "0.1.0" 2 | :dependencies [[org.clojure/clojure "1.8.0"] 3 | [com.datomic/datomic-pro "0.9.5561.62"] 4 | [philoskim/debux "0.3.12"]] 5 | :plugins [[refactor-nrepl "2.3.1"]]) 6 | -------------------------------------------------------------------------------- /datomic-guide/resources/movie-data.edn: -------------------------------------------------------------------------------- 1 | [ 2 | {:db/id #db/id [:db.part/user -100] 3 | :person/name "James Cameron" 4 | :person/born #inst "1954-08-16"} 5 | 6 | {:db/id #db/id [:db.part/user -101] 7 | :person/name "Arnold Schwarzenegger" 8 | :person/born #inst "1947-07-30"} 9 | 10 | {:db/id #db/id [:db.part/user -102] 11 | :person/name "Linda Hamilton" 12 | :person/born #inst "1956-09-26"} 13 | 14 | {:db/id #db/id [:db.part/user -103] 15 | :person/name "Michael Biehn" 16 | :person/born #inst "1956-07-31"} 17 | 18 | {:db/id #db/id [:db.part/user -104] 19 | :person/name "Ted Kotcheff" 20 | :person/born #inst "1931-04-07"} 21 | 22 | {:db/id #db/id [:db.part/user -105] 23 | :person/name "Sylvester Stallone" 24 | :person/born #inst "1946-07-06"} 25 | 26 | {:db/id #db/id [:db.part/user -106] 27 | :person/name "Richard Crenna" 28 | :person/born #inst "1926-11-30" 29 | :person/death #inst "2003-01-17"} 30 | 31 | {:db/id #db/id [:db.part/user -107] 32 | :person/name "Brian Dennehy" 33 | :person/born #inst "1938-07-09"} 34 | 35 | {:db/id #db/id [:db.part/user -108] 36 | :person/name "John McTiernan" 37 | :person/born #inst "1951-01-08"} 38 | 39 | {:db/id #db/id [:db.part/user -109] 40 | :person/name "Elpidia Carrillo" 41 | :person/born #inst "1961-08-16"} 42 | 43 | {:db/id #db/id [:db.part/user -110] 44 | :person/name "Carl Weathers" 45 | :person/born #inst "1948-01-14"} 46 | 47 | {:db/id #db/id [:db.part/user -111] 48 | :person/name "Richard Donner" 49 | :person/born #inst "1930-04-24"} 50 | 51 | {:db/id #db/id [:db.part/user -112] 52 | :person/name "Mel Gibson" 53 | :person/born #inst "1956-01-03"} 54 | 55 | {:db/id #db/id [:db.part/user -113] 56 | :person/name "Danny Glover" 57 | :person/born #inst "1946-07-22"} 58 | 59 | {:db/id #db/id [:db.part/user -114] 60 | :person/name "Gary Busey" 61 | :person/born #inst "1944-07-29"} 62 | 63 | {:db/id #db/id [:db.part/user -115] 64 | :person/name "Paul Verhoeven" 65 | :person/born #inst "1938-07-18"} 66 | 67 | {:db/id #db/id [:db.part/user -116] 68 | :person/name "Peter Weller" 69 | :person/born #inst "1947-06-24"} 70 | 71 | {:db/id #db/id [:db.part/user -117] 72 | :person/name "Nancy Allen" 73 | :person/born #inst "1950-06-24"} 74 | 75 | {:db/id #db/id [:db.part/user -118] 76 | :person/name "Ronny Cox" 77 | :person/born #inst "1938-07-23"} 78 | 79 | {:db/id #db/id [:db.part/user -119] 80 | :person/name "Mark L. Lester" 81 | :person/born #inst "1946-11-26"} 82 | 83 | {:db/id #db/id [:db.part/user -120] 84 | :person/name "Rae Dawn Chong" 85 | :person/born #inst "1961-02-28"} 86 | 87 | {:db/id #db/id [:db.part/user -121] 88 | :person/name "Alyssa Milano" 89 | :person/born #inst "1972-12-19"} 90 | 91 | {:db/id #db/id [:db.part/user -122] 92 | :person/name "Bruce Willis" 93 | :person/born #inst "1955-03-19"} 94 | 95 | {:db/id #db/id [:db.part/user -123] 96 | :person/name "Alan Rickman" 97 | :person/born #inst "1946-02-21"} 98 | 99 | {:db/id #db/id [:db.part/user -124] 100 | :person/name "Alexander Godunov" 101 | :person/born #inst "1949-11-28" 102 | :person/death #inst "1995-05-18"} 103 | 104 | {:db/id #db/id [:db.part/user -125] 105 | :person/name "Robert Patrick" 106 | :person/born #inst "1958-11-05"} 107 | 108 | {:db/id #db/id [:db.part/user -126] 109 | :person/name "Edward Furlong" 110 | :person/born #inst "1977-08-02"} 111 | 112 | {:db/id #db/id [:db.part/user -127] 113 | :person/name "Jonathan Mostow" 114 | :person/born #inst "1961-11-28"} 115 | 116 | {:db/id #db/id [:db.part/user -128] 117 | :person/name "Nick Stahl" 118 | :person/born #inst "1979-12-05"} 119 | 120 | {:db/id #db/id [:db.part/user -129] 121 | :person/name "Claire Danes" 122 | :person/born #inst "1979-04-12"} 123 | 124 | {:db/id #db/id [:db.part/user -130] 125 | :person/name "George P. Cosmatos" 126 | :person/born #inst "1941-01-04" 127 | :person/death #inst "2005-04-19"} 128 | 129 | {:db/id #db/id [:db.part/user -131] 130 | :person/name "Charles Napier" 131 | :person/born #inst "1936-04-12" 132 | :person/death #inst "2011-10-05"} 133 | 134 | {:db/id #db/id [:db.part/user -132] 135 | :person/name "Peter MacDonald"} 136 | 137 | {:db/id #db/id [:db.part/user -133] 138 | :person/name "Marc de Jonge" 139 | :person/born #inst "1949-02-16" 140 | :person/death #inst "1996-06-06"} 141 | 142 | {:db/id #db/id [:db.part/user -134] 143 | :person/name "Stephen Hopkins"} 144 | 145 | {:db/id #db/id [:db.part/user -135] 146 | :person/name "Ruben Blades" 147 | :person/born #inst "1948-07-16"} 148 | 149 | {:db/id #db/id [:db.part/user -136] 150 | :person/name "Joe Pesci" 151 | :person/born #inst "1943-02-09"} 152 | 153 | {:db/id #db/id [:db.part/user -137] 154 | :person/name "Ridley Scott" 155 | :person/born #inst "1937-11-30"} 156 | 157 | {:db/id #db/id [:db.part/user -138] 158 | :person/name "Tom Skerritt" 159 | :person/born #inst "1933-08-25"} 160 | 161 | {:db/id #db/id [:db.part/user -139] 162 | :person/name "Sigourney Weaver" 163 | :person/born #inst "1949-10-08"} 164 | 165 | {:db/id #db/id [:db.part/user -140] 166 | :person/name "Veronica Cartwright" 167 | :person/born #inst "1949-04-20"} 168 | 169 | {:db/id #db/id [:db.part/user -141] 170 | :person/name "Carrie Henn"} 171 | 172 | {:db/id #db/id [:db.part/user -142] 173 | :person/name "George Miller" 174 | :person/born #inst "1945-03-03"} 175 | 176 | {:db/id #db/id [:db.part/user -143] 177 | :person/name "Steve Bisley" 178 | :person/born #inst "1951-12-26"} 179 | 180 | {:db/id #db/id [:db.part/user -144] 181 | :person/name "Joanne Samuel"} 182 | 183 | {:db/id #db/id [:db.part/user -145] 184 | :person/name "Michael Preston" 185 | :person/born #inst "1938-05-14"} 186 | 187 | {:db/id #db/id [:db.part/user -146] 188 | :person/name "Bruce Spence" 189 | :person/born #inst "1945-09-17"} 190 | 191 | {:db/id #db/id [:db.part/user -147] 192 | :person/name "George Ogilvie" 193 | :person/born #inst "1931-03-05"} 194 | 195 | {:db/id #db/id [:db.part/user -148] 196 | :person/name "Tina Turner" 197 | :person/born #inst "1939-11-26"} 198 | 199 | {:db/id #db/id [:db.part/user -149] 200 | :person/name "Sophie Marceau" 201 | :person/born #inst "1966-11-17"} 202 | 203 | {:db/id #db/id [:db.part/user -200] 204 | :movie/title "The Terminator" 205 | :movie/year 1984 206 | :movie/director #db/id [:db.part/user -100] 207 | :movie/cast [#db/id [:db.part/user -101] 208 | #db/id [:db.part/user -102] 209 | #db/id [:db.part/user -103]] 210 | :movie/sequel #db/id [:db.part/user -207]} 211 | 212 | {:db/id #db/id [:db.part/user -201] 213 | :movie/title "First Blood" 214 | :movie/year 1982 215 | :movie/director #db/id [:db.part/user -104] 216 | :movie/cast [#db/id [:db.part/user -105] 217 | #db/id [:db.part/user -106] 218 | #db/id [:db.part/user -107]] 219 | :movie/sequel #db/id [:db.part/user -209]} 220 | 221 | {:db/id #db/id [:db.part/user -202] 222 | :movie/title "Predator" 223 | :movie/year 1987 224 | :movie/director #db/id [:db.part/user -108] 225 | :movie/cast [#db/id [:db.part/user -101] 226 | #db/id [:db.part/user -109] 227 | #db/id [:db.part/user -110]] 228 | :movie/sequel #db/id [:db.part/user -211]} 229 | 230 | {:db/id #db/id [:db.part/user -203] 231 | :movie/title "Lethal Weapon" 232 | :movie/year 1987 233 | :movie/director #db/id [:db.part/user -111] 234 | :movie/cast [#db/id [:db.part/user -112] 235 | #db/id [:db.part/user -113] 236 | #db/id [:db.part/user -114]] 237 | :movie/sequel #db/id [:db.part/user -212]} 238 | 239 | {:db/id #db/id [:db.part/user -204] 240 | :movie/title "RoboCop" 241 | :movie/year 1987 242 | :movie/director #db/id [:db.part/user -115] 243 | :movie/cast [#db/id [:db.part/user -116] 244 | #db/id [:db.part/user -117] 245 | #db/id [:db.part/user -118]]} 246 | 247 | {:db/id #db/id [:db.part/user -205] 248 | :movie/title "Commando" 249 | :movie/year 1985 250 | :movie/director #db/id [:db.part/user -119] 251 | :movie/cast [#db/id [:db.part/user -101] 252 | #db/id [:db.part/user -120] 253 | #db/id [:db.part/user -121]]} 254 | 255 | {:db/id #db/id [:db.part/user -206] 256 | :movie/title "Die Hard" 257 | :movie/year 1988 258 | :movie/director #db/id [:db.part/user -108] 259 | :movie/cast [#db/id [:db.part/user -122] 260 | #db/id [:db.part/user -123] 261 | #db/id [:db.part/user -124]]} 262 | 263 | {:db/id #db/id [:db.part/user -207] 264 | :movie/title "Terminator 2: Judgment Day" 265 | :movie/year 1991 266 | :movie/director #db/id [:db.part/user -100] 267 | :movie/cast [#db/id [:db.part/user -101] 268 | #db/id [:db.part/user -102] 269 | #db/id [:db.part/user -125] 270 | #db/id [:db.part/user -126]] 271 | :movie/sequel #db/id [:db.part/user -208]} 272 | 273 | {:db/id #db/id [:db.part/user -208] 274 | :movie/title "Terminator 3: Rise of the Machines" 275 | :movie/year 2003 276 | :movie/director #db/id [:db.part/user -127] 277 | :movie/cast [#db/id [:db.part/user -101] 278 | #db/id [:db.part/user -128] 279 | #db/id [:db.part/user -129]]} 280 | 281 | {:db/id #db/id [:db.part/user -209] 282 | :movie/title "Rambo: First Blood Part II" 283 | :movie/year 1985 284 | :movie/director #db/id [:db.part/user -130] 285 | :movie/cast [#db/id [:db.part/user -105] 286 | #db/id [:db.part/user -106] 287 | #db/id [:db.part/user -131]] 288 | :movie/sequel #db/id [:db.part/user -210]} 289 | 290 | {:db/id #db/id [:db.part/user -210] 291 | :movie/title "Rambo III" 292 | :movie/year 1988 293 | :movie/director #db/id [:db.part/user -132] 294 | :movie/cast [#db/id [:db.part/user -105] 295 | #db/id [:db.part/user -106] 296 | #db/id [:db.part/user -133]]} 297 | 298 | {:db/id #db/id [:db.part/user -211] 299 | :movie/title "Predator 2" 300 | :movie/year 1990 301 | :movie/director #db/id [:db.part/user -134] 302 | :movie/cast [#db/id [:db.part/user -113] 303 | #db/id [:db.part/user -114] 304 | #db/id [:db.part/user -135]]} 305 | 306 | {:db/id #db/id [:db.part/user -212] 307 | :movie/title "Lethal Weapon 2" 308 | :movie/year 1989 309 | :movie/director #db/id [:db.part/user -111] 310 | :movie/cast [#db/id [:db.part/user -112] 311 | #db/id [:db.part/user -113] 312 | #db/id [:db.part/user -136]] 313 | :movie/sequel #db/id [:db.part/user -213]} 314 | 315 | {:db/id #db/id [:db.part/user -213] 316 | :movie/title "Lethal Weapon 3" 317 | :movie/year 1992 318 | :movie/director #db/id [:db.part/user -111] 319 | :movie/cast [#db/id [:db.part/user -112] 320 | #db/id [:db.part/user -113] 321 | #db/id [:db.part/user -136]]} 322 | 323 | {:db/id #db/id [:db.part/user -214] 324 | :movie/title "Alien" 325 | :movie/year 1979 326 | :movie/director #db/id [:db.part/user -137] 327 | :movie/cast [#db/id [:db.part/user -138] 328 | #db/id [:db.part/user -139] 329 | #db/id [:db.part/user -140]] 330 | :movie/sequel #db/id [:db.part/user -215]} 331 | 332 | {:db/id #db/id [:db.part/user -215] 333 | :movie/title "Aliens" 334 | :movie/year 1986 335 | :movie/director #db/id [:db.part/user -100] 336 | :movie/cast [#db/id [:db.part/user -139] 337 | #db/id [:db.part/user -141] 338 | #db/id [:db.part/user -103]]} 339 | 340 | {:db/id #db/id [:db.part/user -216] 341 | :movie/title "Mad Max" 342 | :movie/year 1979 343 | :movie/director #db/id [:db.part/user -142] 344 | :movie/cast [#db/id [:db.part/user -112] 345 | #db/id [:db.part/user -143] 346 | #db/id [:db.part/user -144]] 347 | :movie/sequel #db/id [:db.part/user -217]} 348 | 349 | {:db/id #db/id [:db.part/user -217] 350 | :movie/title "Mad Max 2" 351 | :movie/year 1981 352 | :movie/director #db/id [:db.part/user -142] 353 | :movie/cast [#db/id [:db.part/user -112] 354 | #db/id [:db.part/user -145] 355 | #db/id [:db.part/user -146]] 356 | :movie/sequel #db/id [:db.part/user -218]} 357 | 358 | {:db/id #db/id [:db.part/user -218] 359 | :movie/title "Mad Max Beyond Thunderdome" 360 | :movie/year 1985 361 | :movie/director [#db/id [:db.part/user -142] 362 | #db/id [:db.part/user -147]] 363 | :movie/cast [#db/id [:db.part/user -112] 364 | #db/id [:db.part/user -148]]} 365 | 366 | {:db/id #db/id [:db.part/user -219] 367 | :movie/title "Braveheart" 368 | :movie/year 1995 369 | :movie/director [#db/id [:db.part/user -112]] 370 | :movie/cast [#db/id [:db.part/user -112] 371 | #db/id [:db.part/user -149]]} 372 | ] 373 | -------------------------------------------------------------------------------- /datomic-guide/resources/movie-schema.edn: -------------------------------------------------------------------------------- 1 | [ 2 | {:db/ident :movie/title 3 | :db/valueType :db.type/string 4 | :db/cardinality :db.cardinality/one} 5 | 6 | {:db/ident :movie/year 7 | :db/valueType :db.type/long 8 | :db/cardinality :db.cardinality/one} 9 | 10 | {:db/ident :movie/director 11 | :db/valueType :db.type/ref 12 | :db/cardinality :db.cardinality/many} 13 | 14 | {:db/ident :movie/sequel 15 | :db/valueType :db.type/ref 16 | :db/cardinality :db.cardinality/one} 17 | 18 | {:db/ident :movie/cast 19 | :db/valueType :db.type/ref 20 | :db/cardinality :db.cardinality/many} 21 | 22 | {:db/ident :person/name 23 | :db/valueType :db.type/string 24 | :db/cardinality :db.cardinality/one} 25 | 26 | {:db/ident :person/born 27 | :db/valueType :db.type/instant 28 | :db/cardinality :db.cardinality/one} 29 | 30 | {:db/ident :person/death 31 | :db/valueType :db.type/instant 32 | :db/cardinality :db.cardinality/one} 33 | ] 34 | -------------------------------------------------------------------------------- /datomic-guide/src/datomic_guide/api/basic.clj: -------------------------------------------------------------------------------- 1 | (ns datomic-guide.api.basic 2 | (:require [datomic.api :as d])) 3 | 4 | (use 'debux.core) 5 | 6 | 7 | ;;; db connection 8 | (def db-uri "datomic:mem://api-basic") 9 | 10 | (d/create-database db-uri) 11 | 12 | (def conn (d/connect db-uri)) 13 | 14 | 15 | ;; schema install 16 | (def schema 17 | [{:db/ident :user/id 18 | :db/valueType :db.type/string 19 | :db/unique :db.unique/value 20 | :db/cardinality :db.cardinality/one} 21 | 22 | {:db/ident :user/name 23 | :db/valueType :db.type/string 24 | :db/cardinality :db.cardinality/one} 25 | 26 | {:db/ident :user/e-mail 27 | :db/valueType :db.type/string 28 | :db/unique :db.unique/identity 29 | :db/cardinality :db.cardinality/one}]) 30 | 31 | (def tx1 @(d/transact conn schema)) 32 | 33 | tx1 34 | ; => {:db-before datomic.db.Db@31cdbf64, 35 | ; :db-after datomic.db.Db@c8dd9f68, 36 | ; :tx-data [#datom[13194139534312 50 #inst "2017-11-07T10:45:54.700-00:00" 13194139534312 true] 37 | ; #datom[63 10 :user/id 13194139534312 true] 38 | ; #datom[63 40 23 13194139534312 true] 39 | ; #datom[63 42 37 13194139534312 true] 40 | ; #datom[63 41 35 13194139534312 true] 41 | ; #datom[64 10 :user/name 13194139534312 true] 42 | ; #datom[64 40 23 13194139534312 true] 43 | ; #datom[64 41 35 13194139534312 true] 44 | ; #datom[65 10 :user/e-mail 13194139534312 true] 45 | ; #datom[65 40 23 13194139534312 true] 46 | ; #datom[65 42 38 13194139534312 true] 47 | ; #datom[65 41 35 13194139534312 true] 48 | ; #datom[0 13 65 13194139534312 true] 49 | ; #datom[0 13 64 13194139534312 true] 50 | ; #datom[0 13 63 13194139534312 true]], 51 | ; :tempids {-9223301668109598140 63, -9223301668109598139 64, -9223301668109598138 65}} 52 | 53 | (d/attribute (d/db conn) :user/e-mail) 54 | ; => #AttrInfo{:id 65 55 | ; :ident :user/e-mail 56 | ; :value-type :db.type/string 57 | ; :cardinality :db.cardinality/one 58 | ; :indexed false 59 | ; :has-avet true 60 | ; :unique :db.unique/identity 61 | ; :is-component false 62 | ; :no-history false 63 | ; :fulltext false} 64 | 65 | (def ent-id (d/entid (d/db conn) :user/e-mail)) 66 | ; => 65 67 | 68 | (d/ident (d/db conn) ent-id) 69 | ; => :user/e-mail 70 | 71 | 72 | (def entity (d/entity (d/db conn) ent-id)) 73 | ; => {:db/id 65} 74 | 75 | (d/touch entity) 76 | ; => {:db/id 65, 77 | ; :db/ident :user/e-mail, 78 | ; :db/valueType :db.type/string, 79 | ; :db/cardinality :db.cardinality/one, 80 | ; :db/unique :db.unique/identity} 81 | 82 | (d/entity-db entity) 83 | ; => datomic.db.Db@c8dd9f68 84 | 85 | (d/pull (d/db conn) '[*] ent-id) 86 | ; => {:db/id 65, 87 | ; :db/ident :user/e-mail, 88 | ; :db/valueType {:db/id 23}, 89 | ; :db/cardinality {:db/id 35}, 90 | ; :db/unique {:db/id 38}} 91 | 92 | 93 | -------------------------------------------------------------------------------- /datomic-guide/src/datomic_guide/api/db.clj: -------------------------------------------------------------------------------- 1 | (ns datomic-guide.api.db 2 | (:require [datomic.api :as d])) 3 | 4 | (use 'debux.core) 5 | 6 | 7 | (def db-uri "datomic:mem://api-db") 8 | 9 | (d/create-database db-uri) 10 | ; => true 11 | 12 | (d/get-database-names "datomic:mem://*") 13 | ; => ("api-db") 14 | 15 | 16 | (d/rename-database db-uri "api-db2") 17 | ; => true 18 | 19 | (d/get-database-names "datomic:mem://*") 20 | ; => ("api-db2") 21 | 22 | 23 | (d/delete-database "datomic:mem://api-db2") 24 | ; => true 25 | 26 | (d/get-database-names "datomic:mem://*") 27 | ; => nil 28 | 29 | -------------------------------------------------------------------------------- /datomic-guide/src/datomic_guide/api/log.clj: -------------------------------------------------------------------------------- 1 | (ns datomic-guide.api.log 2 | (:require [datomic.api :as d])) 3 | 4 | (use 'debux.core) 5 | 6 | 7 | ;;; db connection 8 | (def db-uri "datomic:mem://api-log") 9 | 10 | (d/create-database db-uri) 11 | 12 | (def conn (d/connect db-uri)) 13 | 14 | 15 | ;; schema install 16 | (def schema 17 | [{:db/ident :user/id 18 | :db/valueType :db.type/string 19 | :db/unique :db.unique/value 20 | :db/cardinality :db.cardinality/one} 21 | 22 | {:db/ident :user/name 23 | :db/valueType :db.type/string 24 | :db/cardinality :db.cardinality/one} 25 | 26 | {:db/ident :user/e-mail 27 | :db/valueType :db.type/string 28 | :db/unique :db.unique/identity 29 | :db/cardinality :db.cardinality/one}]) 30 | 31 | (def tx1 @(d/transact conn schema)) 32 | 33 | 34 | ;; data install 35 | (def user-data 36 | [{:db/id #db/id[:db.part/user -100] 37 | :user/id "wonderland" 38 | :user/name "alice" 39 | :user/e-mail "wonderland@gmail.com"} 40 | 41 | {:db/id #db/id[:db.part/user -101] 42 | :user/id "mississippi" 43 | :user/name "huckleberry" 44 | :user/e-mail "mississippi@gmail.com"}]) 45 | 46 | (def tx2 @(d/transact conn user-data)) 47 | tx2 48 | ; => {:db-before datomic.db.Db@37888178, 49 | ; :db-after datomic.db.Db@aa62d884, 50 | ; :tx-data [#datom[13194139534313 50 #inst "2017-11-07T05:28:02.689-00:00" 13194139534313 true] 51 | ; #datom[17592186045418 63 "wonderland" 13194139534313 true] 52 | ; #datom[17592186045418 64 "alice" 13194139534313 true] 53 | ; #datom[17592186045418 65 "wonderland@gmail.com" 13194139534313 true] 54 | ; #datom[17592186045419 63 "mississippi" 13194139534313 true] 55 | ; #datom[17592186045419 64 "huckleberry" 13194139534313 true] 56 | ; #datom[17592186045419 65 "mississippi@gmail.com" 13194139534313 true]], 57 | ; :tempids {-9223350046622220388 17592186045418, -9223350046622220389 17592186045419}} 58 | 59 | @(d/transact conn [[:db/add 17592186045418 :user/e-mail "wonderland@facebook.com"]]) 60 | 61 | ;;; log 함수는 현재의 connection 값을 인수로 받아, 62 | ;;; transacrion 시간순으로 정렬된 새로운 connection 객체를 반환한다. 63 | (def log (d/log conn)) 64 | log 65 | ; => #object[datomic.peer.LocalConnection$reify__8756 0x261fca19 "datomic.peer.LocalConnection$reify__8756@261fca19"] 66 | 67 | (def tx-range (d/tx-range log nil nil)) 68 | 69 | tx-range 70 | ; => #object[datomic.log$tx_range$reify__8128 0x63e4bf4d "datomic.log$tx_range$reify__8128@63e4bf4d"] 71 | 72 | (mapv (fn [tx] tx) tx-range) 73 | ; => [{:t 1000, 74 | ; :data [#datom[13194139534312 50 #inst "2017-11-07T05:45:49.134-00:00" 13194139534312 true] 75 | ; #datom[63 10 :user/id 13194139534312 true] 76 | ; #datom[63 40 23 13194139534312 true] 77 | ; #datom[63 42 37 13194139534312 true] 78 | ; #datom[63 41 35 13194139534312 true] 79 | ; #datom[64 10 :user/name 13194139534312 true] 80 | ; #datom[64 40 23 13194139534312 true] 81 | ; #datom[64 41 35 13194139534312 true] 82 | ; #datom[65 10 :user/e-mail 13194139534312 true] 83 | ; #datom[65 40 23 13194139534312 true] 84 | ; #datom[65 42 38 13194139534312 true] 85 | ; #datom[65 41 35 13194139534312 true] 86 | ; #datom[0 13 65 13194139534312 true] 87 | ; #datom[0 13 64 13194139534312 true] 88 | ; #datom[0 13 63 13194139534312 true]]} 89 | ; {:t 1001, 90 | ; :data [#datom[13194139534313 50 #inst "2017-11-07T05:45:49.138-00:00" 13194139534313 true] 91 | ; #datom[17592186045418 63 "wonderland" 13194139534313 true] 92 | ; #datom[17592186045418 64 "alice" 13194139534313 true] 93 | ; #datom[17592186045418 65 "wonderland@gmail.com" 13194139534313 true] 94 | ; #datom[17592186045419 63 "mississippi" 13194139534313 true] 95 | ; #datom[17592186045419 64 "huckleberry" 13194139534313 true] 96 | ; #datom[17592186045419 65 "mississippi@gmail.com" 13194139534313 true]]} 97 | ; {:t 1004, :data [#datom[13194139534316 50 #inst "2017-11-07T05:45:49.139-00:00" 13194139534316 true] 98 | ; #datom[17592186045418 65 "wonderland@facebook.com" 13194139534316 true] 99 | ; #datom[17592186045418 65 "wonderland@gmail.com" 13194139534316 false]]}] 100 | 101 | 102 | ;;; history 함수는 db 값을 인수로 받아, 모든 datom을 담은 새로운 db 값을 반환한다. 103 | ;;; 이 db 값은 질의(d/q 함수)에 사용할 수 있다. 104 | (def hist-db (d/history (d/db conn))) 105 | 106 | (d/q '[:find ?e ?e-mail ?tx ?added 107 | :in $hist 108 | :where [$hist ?e :user/id "wonderland"] 109 | [$hist ?e :user/e-mail ?e-mail ?tx ?added]] 110 | hist-db) 111 | ; => #{[17592186045418 "wonderland@facebook.com" 13194139534316 true] 112 | ; [17592186045418 "wonderland@gmail.com" 13194139534313 true] 113 | ; [17592186045418 "wonderland@gmail.com" 13194139534316 false]} 114 | 115 | ;; added?를 명시적으로 지정하지 않으면, true에 해당하는 datom만을 반환한다. 116 | (d/q '[:find ?e ?e-mail 117 | :in $hist 118 | :where [$hist ?e :user/id "wonderland"] 119 | [$hist ?e :user/e-mail ?e-mail]] 120 | hist-db) 121 | ; => #{[17592186045418 "wonderland@facebook.com"] 122 | ; [17592186045418 "wonderland@gmail.com"]} 123 | 124 | 125 | ;;; history 함수는 모든 datom을 반환한다. 즉, 어떤 datom도 필터링하지 않는 함수이다. 126 | ;;; 이에 반해, as-of 함수는 db와 시점 정보를 인수로 받아, 시점을 포함한 이전의 모든 datom을 127 | ;;; 반환하는 필터링 함수이고, 128 | ;;; since 함수는 db와 시점 정보를 인수로 받아, 주어진 시점을 제외한 이후의 모든 datom을 반환하는 필터링 함수이다. 129 | ;;; filter 함수는 db와 pred을 인수로 받아, pred을 통과하는 datom만을 반환하는 필터링 함수이다. 130 | 131 | 132 | (d/basis-t (d/db conn)) 133 | ; => 1004 134 | 135 | (def db-1001 (d/as-of (d/db conn) 1001)) 136 | db-1001 137 | ; => datomic.db.Db@827e9c49 138 | 139 | (d/as-of-t db-1001) 140 | ; => 1001 141 | 142 | 143 | ;; 이 함수의 인수로 주어진 db 값에 상관없이, 이 db의 연쇄를 통해 도달할 수 있는 144 | ;; 현재의 가장 높은 t값보다 더 큰 값을 반환한다. 그래서 1004가 아니라 1005를 반환한다. 145 | (d/next-t db-1001) 146 | ; => 1005 147 | 148 | 149 | 150 | (def db-since (d/since (d/db conn) 1001)) 151 | db-since 152 | ; => datomic.db.Db@827e9c49 153 | 154 | ;; d/since 함수의 인수로 주어진 t값을 반환한다. 155 | (d/since-t db-since) 156 | ; => 1001 157 | 158 | (def t->tx (d/t->tx 1001)) 159 | t->tx 160 | ; => 13194139534313 161 | 162 | (d/tx->t t->tx) 163 | ; => 1001 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /datomic-guide/src/datomic_guide/api/tempid.clj: -------------------------------------------------------------------------------- 1 | (ns datomic-guide.api.tempid 2 | (:require [datomic.api :as d])) 3 | 4 | (use 'debux.core) 5 | 6 | ; tag literal 방식이나 tempid 함수 호출 방식 모두 같은 일을 한다. 다만, tag literal 방식은 7 | ; literal source 입력 시에 많이 사용하고, tempid 함수 호출 방식은 프로그래밍을 통해 임시 id를 8 | ; 동적으로 만들어야 할 때 주로 사용한다. 9 | 10 | ;; tag literal 방식으로 tempid 생성 11 | #db/id[:db.part/user] 12 | ; => #db/id[:db.part/user -1000004] 13 | 14 | #db/id[:db.part/user -100] 15 | ; => #db/id[:db.part/user -100] 16 | 17 | 18 | ;; tempid 함수 호출 방식으로 tempid 생성 19 | (d/tempid :db/part/user) 20 | ; => #db/id[:db/part/user -1000005] 21 | 22 | (d/tempid :db/part/user -100) 23 | ; => #db/id[:db/part/user -100] 24 | 25 | 26 | ;;; db connection 27 | (def db-uri "datomic:mem://api-tempid") 28 | 29 | (d/create-database db-uri) 30 | ; => true 31 | 32 | (def conn (d/connect db-uri)) 33 | conn 34 | ; => #object[datomic.peer.LocalConnection 0x11ea2217 "datomic.peer.LocalConnection@11ea2217"] 35 | 36 | 37 | ;; schema install 38 | (def schema 39 | [{:db/id (d/tempid :db.part/db -1000) 40 | :db/ident :user/id 41 | :db/valueType :db.type/string 42 | :db/unique :db.unique/value 43 | :db/cardinality :db.cardinality/one} 44 | 45 | {:db/id (d/tempid :db.part/db -2000) 46 | :db/ident :user/e-mail 47 | :db/valueType :db.type/string 48 | :db/unique :db.unique/identity 49 | :db/cardinality :db.cardinality/one}]) 50 | 51 | (def tx @(d/transact conn schema)) 52 | (dbg tx) 53 | ; => {:db-before datomic.db.Db@8edbccf1, 54 | ; :db-after datomic.db.Db@a349dc06, 55 | ; :tx-data [#datom[13194139534312 50 #inst "2017-11-07T04:55:10.246-00:00" 13194139534312 true] 56 | ; #datom[63 10 :user/id 13194139534312 true] 57 | ; #datom[63 40 23 13194139534312 true] 58 | ; #datom[63 42 37 13194139534312 true] 59 | ; #datom[63 41 35 13194139534312 true] 60 | ; #datom[64 10 :user/e-mail 13194139534312 true] 61 | ; #datom[64 40 23 13194139534312 true] 62 | ; #datom[64 42 38 13194139534312 true] 63 | ; #datom[64 41 35 13194139534312 true] 64 | ; #datom[0 13 64 13194139534312 true] 65 | ; #datom[0 13 63 13194139534312 true]], 66 | ; :tempids {-9223301668109598143 63, -9223301668109598142 64}} 67 | 68 | 69 | (d/db conn) 70 | ; => datomic.db.Db@a349dc06 71 | 72 | 73 | ;; resolve-tempid는 transact할 때 이미 사용한 tempid를 대상으로 actual id를 반환한다. 74 | (d/resolve-tempid (d/db conn) (:tempids tx) -9223301668109598143) 75 | ; => 63 76 | 77 | 78 | -------------------------------------------------------------------------------- /datomic-guide/src/datomic_guide/movie.clj: -------------------------------------------------------------------------------- 1 | (ns datomic-guide.movie 2 | (:require [clojure.java.io :as io] 3 | [datomic.api :as d])) 4 | 5 | (use 'debux.core) 6 | 7 | ;; db creation 8 | (def db-uri "datomic:mem://movies") 9 | 10 | (d/create-database db-uri) 11 | 12 | 13 | ;; db connection 14 | (def conn (d/connect db-uri)) 15 | 16 | 17 | ;; schema install 18 | (def movie-schema 19 | (-> (io/resource "movie-schema.edn") 20 | slurp 21 | read-string)) 22 | 23 | @(d/transact conn movie-schema) 24 | 25 | 26 | ;; data install 27 | (def movie-data 28 | (-> (io/resource "movie-data.edn") 29 | slurp 30 | read-string)) 31 | 32 | @(d/transact conn movie-data) 33 | 34 | 35 | ;;; 36 | ;;; query examples 37 | ;;; 38 | 39 | (d/q '[:find ?title 40 | :where [?e :movie/year 1987] 41 | [?e :movie/title ?title]] 42 | (d/db conn)) 43 | ; => #{["RoboCop"] ["Lethal Weapon"] ["Predator"]} 44 | 45 | 46 | (d/q '[:find ?title 47 | :in $ ?year 48 | :where [?e :movie/year ?year] 49 | [?e :movie/title ?title]] 50 | (d/db conn) 1987) 51 | ; => #{["RoboCop"] ["Lethal Weapon"] ["Predator"]} 52 | 53 | 54 | (d/q '[:find ?title ?year 55 | :where [?m :movie/title ?title] 56 | [?m :movie/year ?year] 57 | [(< ?year 1984)]] 58 | (d/db conn)) 59 | ; => #{["Alien" 1979] ["Mad Max" 1979] ["First Blood" 1982] ["Mad Max 2" 1981]} 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /datomic-guide/src/datomic_guide/schema_example.clj: -------------------------------------------------------------------------------- 1 | (ns datomic-guide.schema-example 2 | (:require [datomic.api :as d])) 3 | 4 | (def db-uri "datomic:mem://schema-example") 5 | 6 | (d/create-database db-uri) 7 | 8 | (def conn (d/connect db-uri)) 9 | 10 | 11 | ;; schema definition 12 | (def schema-1 13 | [{:db/ident :user/id 14 | :db/valueType :db.type/string 15 | :db/unique :db.unique/value 16 | :db/cardinality :db.cardinality/one} 17 | 18 | {:db/ident :user/name 19 | :db/valueType :db.type/string 20 | :db/cardinality :db.cardinality/one} 21 | 22 | {:db/ident :user/e-mail 23 | :db/valueType :db.type/string 24 | :db/cardinality :db.cardinality/one}]) 25 | 26 | @(d/transact conn schema-1) 27 | 28 | 29 | ;;; data install 30 | (def user-data 31 | [{:db/id #db/id[:db.part/user] 32 | :user/id "alice" 33 | :user/name "Alice Parker" 34 | :user/e-mail "alice@gmail.com"} 35 | 36 | {:db/id #db/id[:db.part/user] 37 | :user/id "jack" 38 | :user/name "Jack Hinton" 39 | :user/e-mail "jack@gmail.com"}]) 40 | 41 | @(d/transact conn user-data) 42 | 43 | 44 | (defn find-user [id] 45 | (d/q '[:find ?e . 46 | :in $ ?id 47 | :where [?e :user/id ?id]] 48 | (d/db conn) id)) 49 | 50 | (def alice-ent-id (find-user "alice")) 51 | alice-ent-id ; => 17592186045418 52 | 53 | (d/pull (d/db conn) '[*] alice-ent-id) 54 | ; => {:db/id 17592186045418, 55 | ; :user/id "alice", 56 | ; :user/name "Alice Parker", 57 | ; :user/e-mail "alice@gmail.com"} 58 | 59 | 60 | ;;; field name :user/alias 추가 61 | (def schema-2 62 | [{:db/ident :user/alias 63 | :db/valueType :db.type/string 64 | :db/cardinality :db.cardinality/one}]) 65 | 66 | @(d/transact conn schema-2) 67 | @(d/transact conn [[:db/add alice :user/alias "wonderland"]]) 68 | 69 | (d/pull (d/db conn) '[*] alice-ent-id) 70 | ; => {:db/id 17592186045418, 71 | ; :user/id "alice", 72 | ; :user/name "Alice Parker", 73 | ; :user/e-mail "alice@gmail.com", 74 | ; :user/alias "wonderland"} 75 | 76 | 77 | ;;; field name :user/alias --> :user/nickname 으로 변경 78 | (def alias-ent-id (d/entid (d/db conn) :user/alias)) 79 | 80 | @(d/transact conn [[:db/add alias-ent-id :db/ident :user/nickname]]) 81 | (d/pull (d/db conn) '[*] alice-end-id) 82 | ; => {:db/id 17592186045418, 83 | ; :user/id "alice", 84 | ; :user/name "Alice Parker", 85 | ; :user/e-mail "alice@gmail.com", 86 | ; :user/nickname "wonderland"} 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /datomic-guide/src/datomic_guide/time_travel.clj: -------------------------------------------------------------------------------- 1 | (ns datomic-guide.time-travel 2 | (:require [datomic.api :as d])) 3 | 4 | ;;; db connection 5 | (def db-uri "datomic:mem://time-travel") 6 | (d/create-database db-uri) 7 | (def conn (d/connect db-uri)) 8 | 9 | 10 | (d/db conn) ; => datomic.db.Db@29475cf8 11 | 12 | 13 | ;; schema install 14 | (def schema 15 | [{:db/ident :user/id 16 | :db/valueType :db.type/string 17 | :db/unique :db.unique/value 18 | :db/cardinality :db.cardinality/one} 19 | 20 | {:db/ident :user/name 21 | :db/valueType :db.type/string 22 | :db/cardinality :db.cardinality/one} 23 | 24 | {:db/ident :user/e-mail 25 | :db/valueType :db.type/string 26 | :db/unique :db.unique/identity 27 | :db/cardinality :db.cardinality/one}]) 28 | 29 | @(d/transact conn schema) 30 | ; => {:db-before datomic.db.Db@29475cf8, 31 | ; :db-after datomic.db.Db@5ba75b5c, 32 | ; :tx-data [#datom[13194139534312 50 #inst "2017-11-09T06:03:29.648-00:00" 13194139534312 true] 33 | ; #datom[63 10 :user/id 13194139534312 true] 34 | ; #datom[63 40 23 13194139534312 true] 35 | ; #datom[63 42 37 13194139534312 true] 36 | ; #datom[63 41 35 13194139534312 true] 37 | ; #datom[64 10 :user/name 13194139534312 true] 38 | ; #datom[64 40 23 13194139534312 true] 39 | ; #datom[64 41 35 13194139534312 true] 40 | ; #datom[65 10 :user/e-mail 13194139534312 true] 41 | ; #datom[65 40 23 13194139534312 true] 42 | ; #datom[65 42 38 13194139534312 true] 43 | ; #datom[65 41 35 13194139534312 true] 44 | ; #datom[0 13 65 13194139534312 true] 45 | ; #datom[0 13 64 13194139534312 true] 46 | ; #datom[0 13 63 13194139534312 true]], 47 | ; :tempids {-9223301668109598143 63, -9223301668109598142 64, -9223301668109598141 65}} 48 | 49 | (d/db conn) ; => datomic.db.Db@5ba75b5c 50 | 51 | 52 | ;; data install 53 | (def user-data 54 | [{:db/id #db/id[:db.part/user] 55 | :user/id "alice" 56 | :user/name "Alice Parker" 57 | :user/e-mail "alice@gmail.com"} 58 | 59 | {:db/id #db/id[:db.part/user] 60 | :user/id "jack" 61 | :user/name "Jack Hinton" 62 | :user/e-mail "jack@gmail.com"}]) 63 | 64 | @(d/transact conn user-data) 65 | 66 | (d/db conn) ; => datomic.db.Db@d34f836c 67 | 68 | 69 | (def alice-ent-id 70 | (d/q '[:find ?e . 71 | :where [?e :user/id "alice"]] 72 | (d/db conn))) 73 | 74 | (d/pull (d/db conn) '[*] alice-ent-id) 75 | ; => {:db/id 17592186045418, 76 | ; :user/id "alice", 77 | ; :user/name "Alice Parker", 78 | ; :user/e-mail "alice@gmail.com"} 79 | 80 | 81 | 82 | @(d/transact conn [[:db/add alice-ent-id :user/e-mail "alice@facebook.com"]]) 83 | 84 | (d/pull (d/db conn) '[*] alice-ent-id) 85 | ; => {:db/id 17592186045418, 86 | ; :user/id "alice", 87 | ; :user/name "Alice Parker", 88 | ; :user/e-mail "alice@facebook.com"} 89 | 90 | 91 | (def hist-db (d/history (d/db conn))) 92 | 93 | (d/q '[:find ?e ?e-mail ?tx 94 | :in $hist 95 | :where [$hist ?e :user/id "alice"] 96 | [$hist ?e :user/e-mail ?e-mail]] 97 | hist-db) 98 | ; => #{[17592186045418 "alice@gmail.com"] 99 | ; [17592186045418 "alice@facebook.com"]} 100 | 101 | -------------------------------------------------------------------------------- /datomic-guide/test/datomic_guide/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns datomic-guide.core-test 2 | (:require [clojure.test :refer :all] 3 | [datomic-guide.core :refer :all])) 4 | 5 | (deftest a-test 6 | (testing "FIXME, I fail." 7 | (is (= 0 1)))) 8 | -------------------------------------------------------------------------------- /img/datomic-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philoskim/datomic-intro/85f12edfd49ff9b648bc7b0407dd5938aa56f6b7/img/datomic-architecture.png -------------------------------------------------------------------------------- /installation.adoc: -------------------------------------------------------------------------------- 1 | = Installation 2 | :sectnums: 3 | :source-language: clojure 4 | 5 | 6 | == Datomic Pro Starter 설치 7 | 8 | === 계정 등록 9 | 10 | * link:https://my.datomic.com/account[]에서 자신의 계정을 등록한다. 11 | 12 | * 참고: 위의 계정으로 설치한 datomic-pro의 expiry date: 2017/11/30 13 | + 14 | 이 기간이 지나면 새로운 계정으로 등록한 후, 다음의 과정을 다시 진행해야 한다. 그렇지 15 | 않으면 아래의 transactor 실행시 에러가 나며 실행되지 않는다. 16 | 17 | 18 | === datomic 다운로드 19 | 20 | Datomic 다운로드 방식에는 두 가지가 있다. 21 | 22 | . link:https://my.datomic.com/account[]에 로그인한 후, Datomic 최신 버전을 직접 다운로드 받는다. 23 | . 또는 로그인하지 않은 상태에서 아래와 같이 다운로드 받는다. (버전 번호는 다운받는 버전의 번호로 바꾸어 준다.) 24 | + 25 | [listing] 26 | ---- 27 | $ wget --http-user=philos99.family@gmail.com \ 28 | --http-password=8c880dca-0e18-4fe9-9108-9b85df24ed79 \ 29 | https://my.datomic.com/repo/com/datomic/datomic-pro/0.9.5530/datomic-pro-0.9.5530.zip \ 30 | -O datomic-pro-0.9.5530.zip 31 | ---- 32 | 33 | 34 | 35 | === Datomic 설치하기 36 | 37 | ==== maven 설치 38 | 39 | [listing] 40 | ---- 41 | $ sudo apt install maven 42 | ---- 43 | 44 | ==== Datomic 설치 45 | 46 | 아래의 명령을 실행하면 ~/.m2/repository/com/datomic/datomic-pro 디렉토리에 datomic-pro 47 | peer 라이브러리가 설치된다. 48 | 49 | [source] 50 | .... 51 | $ unzip datomic-pro-0.9.5530.zip 52 | $ cd datomic-pro-0.9.5530 53 | $ bin/maven-install 54 | .... 55 | 56 | 57 | == Running the transactor with the dev storage protocol 58 | 59 | === license-key 설정하기 60 | 61 | 먼저 다음과 같이 파일을 복사한다. 62 | 63 | [listing] 64 | ---- 65 | $ cp config/samples/dev-transactor-templates.properties config/dev-transactor.properties 66 | ---- 67 | 68 | link:https://my.datomic.com/account[]에서 본인의 e-mail 주소로 송신한 것을 69 | `config/dev-transactor.properties` 파일에 다음과 같이 붙여 놓는다. 70 | 71 | [listing] 72 | .config/dev-transactor.properties 73 | ---- 74 | license-key=ddL1TkMUyi45BMzggkqZvt0WXAL9E6ziNYcKRG1IJp5B5vHTge\ 75 | 8GdpqKFeV9u5fIvF9A0PpawnNC9RCuaDylc2C1Y4oYCUJz14v8w8jSseLomMb2\ 76 | 2tjFgb79xywzAXw72f9O1dzaAvVHyyEaKwkVS1cuW2dse1AF5rRQLILVcqunb8\ 77 | X5z3Dpt9GK88JBK4b+MxokYIbPsl3uB1FKkEPG0zk/BEbHgMju/XHnTYQa3+/T\ 78 | tIqWF6H2/vIKWFSNM1Qn52jmkYfyJ7/6SQTKa4TEY3xGlj6vWHlHf2MVuOrfFX\ 79 | Eh7KP9LQpjAtRBeGPAR/i1xXGKbsHB2rDJOOmxYVNu6Q== 80 | ---- 81 | 82 | 83 | === transactor 실행하기 84 | 85 | [listing] 86 | ---- 87 | $ pwd 88 | datomic-pro-0.9.5530 89 | 90 | $ bin/transactor config/dev-transactor.properties 91 | launching with Java options -server -Xms1g -Xmx1g -XX:+UseG1GC -XX:MaxGCPauseMillis=50 92 | Starting datomic:dev://localhost:4334/, storing data in: data ... 93 | System started datomic:dev://localhost:4334/, storing data in: data 94 | ---- 95 | 96 | 97 | == project.clj :dependencies 지정하기 98 | 99 | [source] 100 | .project.clj 101 | ---- 102 | (defproject datomic-test "0.1.0-SNAPSHOT" 103 | :dependencies [[com.datomic/datomic-pro "0.9.5530"]]) 104 | ---- 105 | 106 | 107 | == Sample db `datomic-mbrainz-1968-1973-backup-2015-02-11.tar`를 datomic에 설치하기 108 | 109 | [listing] 110 | ---- 111 | $ wget http://s3.amazonaws.com/mbrainz/datomic-mbrainz-1968-1973-backup-2015-02-11.tar 112 | $ tar -xvf datomic-mbrainz-1968-1973-backup-2015-02-11.tar 113 | 114 | $ bin/datomic restore-db file:///path/to/backup/mbrainz-1968-1973 \ 115 | datomic:dev://localhost:4334/mbrainz-1968-1973 116 | ---- 117 | 118 | --------------------------------------------------------------------------------