Skip to content

Instantly share code, notes, and snippets.

@martintrojer
Created July 16, 2012 17:32
Show Gist options
  • Save martintrojer/3123920 to your computer and use it in GitHub Desktop.
Save martintrojer/3123920 to your computer and use it in GitHub Desktop.
core.logic datomic query blog2
(do
(defrel id-first p1 p2)
(defrel id-email p1 p2)
(defrel id2-email p1 p2)
(defrel id2-height p1 p2)
(fact id-first 1 "John")
(fact id-email 1 "[email protected]")
(fact id-first 2 "Jane")
(fact id-email 2 "[email protected]")
(fact id2-email 100 "[email protected]")
(fact id2-height 100 73)
(fact id2-email 101 "[email protected]")
(fact id2-height 101 71)
(run* [q]
(fresh [id id2 fst email height]
(id-first id fst)
(id-email id email)
(id2-email id2 email)
(id2-height id2 height)
(== q [fst height]))))
;; (["John" 71] ["Jane" 73])
(do
(defquery join [:first :height] [[:last :first :email] [:email :height]])
(fact join-last-first-email "Doe" "John" "[email protected]")
(fact join-last-first-email "Doe" "Jane" "[email protected]")
(fact join-email-height "[email protected]" 73)
(fact join-email-height "[email protected]" 71)
(join-run))
;; (["Jane" 73] ["John" 71])
(do
(defquery join2 [:firstName :height] [[:e1 :firstName] [:e1 :email] [:e2 :email] [:e2 :height]])
(fact join2-e1-firstName 1 "John")
(fact join2-e1-email 1 "[email protected]")
(fact join2-e1-firstName 2 "Jane")
(fact join2-e1-email 2 "[email protected]")
(fact join2-e2-email 100 "[email protected]")
(fact join2-e2-height 100 73)
(fact join2-e2-email 101 "[email protected]")
(fact join2-e2-height 101 71)
(join2-run))
;; (["John" 71] ["Jane" 73])
(defmacro defquery [relname find rels]
(let [idx-syms (->> (repeatedly gensym) (map #(with-meta % {:index :t})))
relname (fn [r] (symbol (str relname "-" (->> r (map name) (interpose "-") (reduce str)))))
lvars (fn [r] (->> r (map name) (map symbol)))
defrels (for [r rels] `(defrel ~(relname r) ~@(take (count r) idx-syms)))
joins (for [r rels] `(~(relname r) ~@(lvars r)))]
`(do
~@defrels
(defn ~(relname [:run]) []
(run* [q#]
(fresh [~@(set (mapcat lvars rels))]
~@joins
(== q# [~@(lvars find)])))))))
(defrel last-first-email p1 p2 p3)
(defrel email-height p1 p2)
(fact last-first-email "Doe" "John" "[email protected]")
(fact last-first-email "Doe" "Jane" "[email protected]")
(fact email-height "[email protected]" 73)
(fact email-height "[email protected]" 71)
(run* [q]
(fresh [fst email height]
(last-first-email (lvar) fst email)
(email-height email height)
(== q [fst height])))
;; (["Jane" 73] ["John" 71])
(defn join-test2 [xs ys]
;; setup the relations
(defquery join [:first :height] [[:last :first :email] [:email :height]])
;; load the facts
(time
(do
(doseq [x xs] (apply fact join-last-first-email x))
(doseq [y ys] (apply fact join-email-height y))))
;; run the query
(time
(join-run)))
(defn bench [n f]
(let [rand-str #(str (java.util.UUID/randomUUID))
emails (repeatedly n rand-str)
name-email (reduce (fn [res em]
(conj res (vector (rand-str) (rand-str) em)))
[] emails)
email-height (reduce (fn [res em]
(conj res (vector em (rand-int 100))))
[] emails)]
(time (count (f name-email email-height)))))
(bench 5000 join-test2)
;; "Elapsed time: 287.275 msecs" (loading the data)
;; "Elapsed time: 127.188 msecs" (running the query)
;; "Elapsed time: 415.466 msecs" (total)
;; 5000
;; ===================================================
;; Results from previous post
(bench 5000 (partial q '[:find ?first ?height
:in [[?last ?first ?email]] [[?email ?height]]]))
;; "Elapsed time: 14757.248824 msecs"
;; 5000
(bench 5000 (partial q '[:find ?first ?height
:in $a $b
:where [$a ?last ?first ?email] [$b ?email ?height]]))
;; "Elapsed time: 10.869 msecs"
;; 5000
(bench 5000 join-test)
;; "Elapsed time: 185.604 msecs"
;; 5000
;; simple in-memory join, two database bindings
(q '[:find ?first ?height
:in $db1 $db2
:where [$db1 ?e1 :firstName ?first]
[$db1 ?e1 :email ?email]
[$db2 ?e2 :email ?email]
[$db2 ?e2 :height ?height]]
[[1 :firstName "John"]
[1 :email "[email protected]"]
[2 :firstName "Jane"]
[2 :email "[email protected]"]]
[[100 :email "[email protected]"]
[100 :height 73]
[101 :email "[email protected]"]
[101 :height 71]])
;; #<HashSet [["Jane" 73], ["John" 71]]>
@swannodette
Copy link

Putting defrel in join-test2 doesn't make much sense. In reality you would not load the facts in this way every time, but I guess that makes sense if you want to compare with Datomic's neato functionality.

@martintrojer
Copy link
Author

Sure, you can move the defrels out, doesn't impact the the speed much. I do need to load the facts in the "query function" tho, like you said, that's the use-case I'm going for here.

@swannodette
Copy link

Why do you need to? Many improvements to core.logic are necessary for this to work well for larger amounts of data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment