thi.ng.fabric.facts.test.core
(deftest test-query-basic3
(let [g (ff/fact-graph )
q1 (ff/add-query! g '[nil friend nil ] {})
q2 (ff/add-param-query! g '[alice ?p ?o] {})
q3 (ff/add-query! g '[nil friend nil ] {:limit 1 })
q4 (ff/add-query! g '[nil friend nil ] {:filter (fn [[s]] (= 'bob s))})
q5 (ff/add-param-query! g '[alice ?p ?o] {:limit 1 :filter #(= 'friend (% '?p))})
q6 (ff/add-param-query! g '[alice ?p ?o] {:limit 1 :filter #(= 'friend (% '?p)) :select '?o})]
(ff/add-fact! g '[alice friend bob])
(ff/add-fact! g '[bob friend charlie])
(ff/add-fact! g '[alice friend dora])
(ff/add-fact! g '[alice email " [email protected] " ])
(f/execute! (f/sync-execution-context {:graph g}))
(is (= :basic-query (f/component-type q1)))
(is (= :param-query (f/component-type q2)))
(is (every? set? [@q1 @q2 @q3 @q4]))
(is (= '#{[alice friend dora] [bob friend charlie] [alice friend bob]} @q1))
(is (= '#{{?p email ?o " [email protected] " } {?p friend ?o bob} {?p friend ?o dora}} @q2))
(is (== 1 (count @q3)))
(is (= '#{[bob friend charlie]} @q4))
(is (== 1 (count @q5)))
(is ('#{bob dora} ((first @q5) '?o)))
(is ('#{{?o bob} {?o dora}} (first @q6)))
(run! #(f/remove-from-graph! % g) [q1 q2 q3 q4 q5 q6])
(is (== (+ 3 4 ) (count (f/vertices g))))))
(deftest test-query-join3
(let [g (ff/fact-graph )
q1 (ff/add-param-query! g '[?a friend ?b] {})
q2 (ff/add-param-query! g '[?b friend ?c] {})
q3 (ff/add-param-query! g '[?b nick ?n] {})
jq1 (ff/add-join! g q1 q2 {})
jq2 (ff/add-query-join! g '[[?a friend ?b] [?b friend ?c]] {})
jq3 (ff/add-join! g ff/join-optional q1 q3 {})
jq4 (ff/add-query-join-optional! g '[[?a friend ?b] [?b nick ?n] [?n name ?nn]] {})
jq5 (ff/add-join! g q2 q1 {:limit 1 :filter #(= 'alice (% '?a))})
jq6 (ff/add-query-join! g '[[?a friend ?b] [?b friend ?c]]
{:limit 1 :filter #(= 'alice (% '?a))})
jq7 (ff/add-query-join-optional! g '[[?a friend ?b] [?b nick ?n] [?n name ?nn]]
{:limit 1 :filter #(= 'alice (% '?a))})]
(ff/add-fact! g '[alice friend bob])
(ff/add-fact! g '[bob friend charlie])
(ff/add-fact! g '[alice friend dora])
(ff/add-fact! g '[bob nick bobby])
(f/execute! (f/sync-execution-context {:graph g}))
(is (= :join-query (f/component-type jq1)))
(is (= '#{{?a alice ?b dora} {?a bob ?b charlie} {?a alice ?b bob}} @q1))
(is (= '#{{?b alice ?c dora} {?b bob ?c charlie} {?b alice ?c bob}} @q2))
(is (= '#{{?a alice ?b bob ?c charlie}} @jq1))
(is (= @jq1 @jq2))
(is (= '#{{?a alice ?b dora} {?a bob ?b charlie} {?a alice ?b bob ?n bobby}} @jq3))
(is (= @jq3 @jq4))
(is (= '#{{?a alice ?b bob ?c charlie}} @jq5))
(is (= @jq5 @jq6))
(is (and (every? #(= 'alice (% '?a)) @jq7) (every? #('#{dora bob} (% '?b)) @jq7)))
(run! #(f/remove-from-graph! % g) [jq1 jq2 jq3 jq4 jq5 jq6 jq7])
(is (== (+ 3 4 ) (count (f/vertices g))))))
(deftest test-paths
(let [g (ff/fact-graph )
ctx (f/sync-execution-context {:graph g})
pq1 (ff/add-path-query! g '[?s [p1] ?o] {:min 3 :max 3 })
pq2 (ff/add-path-query! g '[?s [p1] ?o] {:min 1 :max 3 })
pq3 (ff/add-path-query! g '[?s [p1 p2] ?o] {:min 2 :max 3 })
pq4 (ff/add-path-query! g '[a [p1 p2] ?o] {:min 2 :max 3 })
pq5 (ff/add-path-query! g '[?s [p1 p2] e] {:min 2 :max 3 })
pq6 (ff/add-path-query! g '[?s [nil ] ?o] {:min 1 :max 5 })]
(run! #(ff/add-fact! g %)
'[[a p1 b]
[b p1 c]
[b p2 c]
[c p1 d]
[c p2 e]])
(f/execute! ctx)
(is (= :path-query (f/component-type pq1)))
(is (= '#{{?s a ?o d}} @pq1))
(is (= '#{{?s a ?o b} {?s a ?o c} {?s a ?o d}
{?s b ?o c} {?s b ?o d}
{?s c ?o d}}
@pq2))
(is (= '#{{?s a ?o c} {?s a ?o d} {?s b ?o e}} @pq3))
(is (= '#{{?o c} {?o d}} @pq4))
(is (= '#{{?s a} {?s b}} @pq5))
(is (= '#{{?s a ?o b} {?s a ?o c} {?s a ?o d} {?s a ?o e}
{?s b ?o c} {?s b ?o d} {?s b ?o e}
{?s c ?o d} {?s c ?o e}}
@pq6))
(warn :pq1 @pq1)
(warn :pq2 @pq2)
(warn :pq3 @pq3)
(warn :pq4 @pq4)
(warn :pq5 @pq5)
(warn :pq6 @pq6)
(run! #(f/remove-from-graph! % g) [pq1 pq2 pq3 pq4 pq5 pq6])
(is (== (+ 3 5 ) (count (f/vertices g))))))
DSL queries & result transformations
(deftest test-qspec
(let [g (ff/fact-graph )
q1 (dsl/add-query-from-spec!
g '{:q [{:where [[?a p ?b] [?b p ?c]]}
{:optional [[?a q ?aq]]}
{:optional [[?b q ?bq]]}]})
q2 (dsl/add-query-from-spec!
g '{:q [{:where [[?a p ?b] [?b p ?c]]}
{:optional [[?a q ?aq]]}
{:optional [[?b q ?bq]]}]
:filter (= c ?a)})
q3 (dsl/add-query-from-spec!
g '{:q [{:where [[?a p ?b] [?b p ?c]] :filter (= a ?a)}
{:optional [[?a q ?aq]]}
{:optional [[?b q ?bq]]}]
:limit 1 })
q4 (dsl/add-query-from-spec!
g '{:q [{:where [[?a p ?b] [?b p ?c]]}
{:optional [[?a q ?aq]]}
{:optional [[?b q ?bq]]}]
:filter (= a ?a)
:order ?c
:select ?c})
q5 (dsl/add-query-from-spec!
g '{:q [{:where [[?a p ?b] [?b p ?c]]}]
:order ?c
:group-by [?a ?b]
:select :* })
q6 (dsl/add-query-from-spec!
g '{:q [{:where [[?a p ?b] [?b p ?c]]}]
:order ?c
:group-by [?a ?b]
:select ?c})
q7 (dsl/add-query-from-spec!
g '{:q [{:where [[?a p ?b] [?b p ?c]]}
{:optional [[?a q ?aq]]}
{:optional [[?b q ?bq]]}]
:filter (in-set? ?a b c)})
q8 (dsl/add-query-from-spec!
g '{:q [{:where [[?a p ?b]] :bind {?c (str ?a " ->" ?b)}}]
:filter (= ?b b)})
q9 (dsl/add-query-from-spec!
g '{:q [{:where [[?a p ?b]]}]
:filter (= ?b b)
:bind {?c (str ?a " ->" ?b)}})]
(run! #(ff/add-fact! g %)
'[[a p b]
[a p c]
[b p d]
[b p e]
[c p e]
[e p f]
[a q aa]
[c q cc]])
(f/execute! (f/sync-execution-context {:graph g}))
(is (= :query-result (f/component-type q1)))
(is (= '#{{?a a ?b b ?c e ?aq aa}
{?a a ?b c ?c e ?aq aa ?bq cc}
{?a a ?b b ?c d ?aq aa}
{?a c ?b e ?c f ?aq cc}
{?a b ?b e ?c f}}
@q1))
(is (= '#{{?a c ?b e ?c f ?aq cc}} @q2))
(is (== 1 (count @q3)))
(is ('#{{?a a ?b b ?c e ?aq aa}
{?a a ?b c ?c e ?aq aa ?bq cc}
{?a a ?b b ?c d ?aq aa}}
(first @q3)))
(is (== 3 (count @q4)))
(is (= '[{?c d} {?c e} {?c e}] @q4))
(is (= '{[a b] [{?a a ?b b ?c d} {?a a ?b b ?c e}]
[a c] [{?a a ?b c ?c e}]
[b e] [{?a b ?b e ?c f}]
[c e] [{?a c ?b e ?c f}]}
@q5))
(is (= '{[a b] [{?c d} {?c e}]
[a c] [{?c e}]
[b e] [{?c f}]
[c e] [{?c f}]}
@q6))
(is (= '#{{?a b ?b e ?c f}
{?a c ?b e ?c f ?aq cc}}
@q7))
(is (= '#{{?a b ?b d ?c " b->d" } {?a b ?b e ?c " b->e" }}) @q7)
(is (= @q8 @q9))
(run! #(f/remove-from-graph! % g) [q1 q2 q3 q4 q5 q6 q7 q8 q9])
(is (== (+ 3 8 ) (count (f/vertices g))))))
(deftest test-path-dsl
(let [g (ff/fact-graph )
q1 (dsl/add-query-from-spec!
g '{:q [{:path [?a [p] ?b] :min 1 :max 5 }]})
q2 (dsl/add-query-from-spec!
g '{:q [{:path [?a [p p] ?b]}]})
q3 (dsl/add-query-from-spec!
g '{:q [{:path [?a [p p] ?b]
:min 2 :max 3
:filter (= a ?a)
:select ?b}]})]
(run! #(ff/add-fact! g %)
'[[a p b]
[a p c]
[b p d]
[b p e]
[c p e]
[e p f]])
(f/execute! (f/sync-execution-context {:graph g}))
(is (= '#{{?a a ?b b} {?a a ?b c} {?a a ?b d} {?a a ?b e} {?a a ?b f}
{?a b ?b d} {?a b ?b e} {?a b ?b f}
{?a c ?b e} {?a c ?b f}
{?a e ?b f}}
@q1))
(is (= '#{{?a a ?b d} {?a a ?b e} {?a b ?b f} {?a c ?b f}} @q2))
(is (= '#{{?b d} {?b e} {?b f}} @q3))))
(deftest test-negation
(let [g (ff/fact-graph )
q1 (dsl/add-query-from-spec!
g '{:q [{:where [[?a type animal]]}
{:minus [[?a type ?type]] :filter (in-set? ?type reptile insect)}]})
q2 (dsl/add-query-from-spec!
g '{:q [{:where [[?a type animal]]}
{:minus [[?a type reptile]]}
{:minus [[?a type insect]]}]})]
(run! #(ff/add-fact! g %)
'[[lf1 type mammal]
[lf2 type reptile]
[lf3 type insect]
[lf1 type animal]
[lf2 type animal]
[lf3 type animal]])
(f/execute! (f/sync-execution-context {:graph g}))
(is (= '#{{?a lf1}} @q1))
(is (= @q1 @q2))))
(deftest test-qspec-aggregation
(let [g (ff/fact-graph )
spec '{:q [{:where [[?b price ?p]]}]
:aggregate {?total (agg-sum ?p)
?avg (round (agg-avg ?p))
?mean (agg-mean ?p)
?num (agg-count )
?min (agg-min ?p)
?max (agg-max ?p)}
:order [?b ?p]}
q1 (dsl/add-query-from-spec! g spec)
q2 (dsl/add-query-from-spec! g (assoc spec :group-by '?b))
q3 (dsl/add-query-from-spec! g (assoc spec :select '[?total ?avg]))
q4 (dsl/add-query-from-spec! g (assoc spec :select '[?total ?avg] :group-by '?b))
q5 (dsl/add-query-from-spec! g (assoc spec :group-by '(group-bins-of ?p 50 )))
q6 (dsl/add-query-from-spec! g (-> spec
(assoc-in [:aggregate '?pc] '(agg-collect ?p))
(assoc :select '[?b ?pc] :group-by '?b)
(dissoc :order )))]
(run! #(ff/add-fact! g %)
'[[b1 price 10 ] [b2 price 20 ] [b1 price 70 ] [b1 price 20 ]])
(f/execute! (f/sync-execution-context {:graph g}))
(is (= '[{?b b1 ?p 10 ?total 120 ?avg 30 ?mean 20 ?num 4 ?min 10 ?max 70 }
{?b b1 ?p 20 ?total 120 ?avg 30 ?mean 20 ?num 4 ?min 10 ?max 70 }
{?b b1 ?p 70 ?total 120 ?avg 30 ?mean 20 ?num 4 ?min 10 ?max 70 }
{?b b2 ?p 20 ?total 120 ?avg 30 ?mean 20 ?num 4 ?min 10 ?max 70 }]
@q1))
(is (= '{b1 [{?b b1 ?p 10 ?total 100 ?avg 33 ?mean 20 ?num 3 ?min 10 ?max 70 }
{?b b1 ?p 20 ?total 100 ?avg 33 ?mean 20 ?num 3 ?min 10 ?max 70 }
{?b b1 ?p 70 ?total 100 ?avg 33 ?mean 20 ?num 3 ?min 10 ?max 70 }]
b2 [{?b b2 ?p 20 ?total 20 ?avg 20 ?mean 20 ?num 1 ?min 20 ?max 20 }]}
@q2))
(is (= '[{?total 120 ?avg 30 }] @q3))
(is (= '{b1 {?total 100 ?avg 33 } b2 {?total 20 ?avg 20 }} @q4))
(is (= '{0.0 [{?b b1 ?p 10 ?total 50 ?avg 17 ?mean 20 ?num 3 ?min 10 ?max 20 }
{?b b1 ?p 20 ?total 50 ?avg 17 ?mean 20 ?num 3 ?min 10 ?max 20 }
{?b b2 ?p 20 ?total 50 ?avg 17 ?mean 20 ?num 3 ?min 10 ?max 20 }]
50.0 [{?b b1 ?p 70 ?total 70 ?avg 70 ?mean 70 ?num 1 ?min 70 ?max 70 }]}
@q5))
(is (= '{b1 #{{?b b1 ?pc #{70 20 10 }}} b2 #{{?b b2 ?pc #{20 }}}} @q6))))
(deftest test-expressions
(is (== 23 ((dsl/compile-expr '?a) '{?a 23 })))
(is (== 133 ((dsl/compile-expr '(+ ?a ?b 100 )) '{?a 23 ?b 10 })))
(is (== -87 ((dsl/compile-expr '(- ?a ?b 100 )) '{?a 23 ?b 10 })))
(is (== 23000 ((dsl/compile-expr '(* ?a ?b 100 )) '{?a 23 ?b 10 })))
(is (== 0.023 ((dsl/compile-expr '(float (/ ?a ?b 100 ))) '{?a 23 ?b 10 })))
(is (== 0 ((dsl/compile-expr '(int (/ ?a ?b 100 ))) '{?a 23 ?b 10 })))
(is (== 1023 ((dsl/compile-expr '(+ ?a (* ?b 100 ))) '{?a 23 ?b 10 })))
(is (== 13 ((dsl/compile-expr '(abs (- ?b ?a))) '{?a 23 ?b 10 })))
(is (== 2 ((dsl/compile-expr '(floor (/ ?a ?b))) '{?a 23 ?b 10 })))
(is (== 3 ((dsl/compile-expr '(ceil (/ ?a ?b))) '{?a 23 ?b 10 })))
(is (== (Math/sqrt 500 ) ((dsl/compile-expr '(sqrt (+ (* ?a ?a) (* ?b ?b)))) '{?a 20 ?b 10 })))
(is (== 1024 ((dsl/compile-expr '(int (pow ?a ?b))) '{?a 2 ?b 10 })))
(is (== 10 ((dsl/compile-expr '(int (logn ?a ?b))) '{?a 1024 ?b 2 })))
(is ((dsl/compile-expr '(match #"\w +" ?a)) '{?a " hello123" }))
(is ((dsl/compile-expr '(match " \\ w+" ?a)) '{?a " hello123" }))
(is (not ((dsl/compile-expr '(match #"\w +" ?a)) {})))
(is ((dsl/compile-expr '(and ?a ?b)) '{?a 1024 ?b 2 }))
(is (not ((dsl/compile-expr '(and ?a ?b ?c)) '{?a 1024 ?b 2 })))
(is ((dsl/compile-expr '(or ?a ?b)) '{?a 1024 }))
(is ((dsl/compile-expr '(or ?a ?b)) '{?b 1024 }))
(is (not ((dsl/compile-expr '(or ?a ?b)) {})))
(is ((dsl/compile-expr '(not ?a)) {}))
(is (not ((dsl/compile-expr '(not ?a)) '{?a 23 })))
(is ((dsl/compile-expr '(= 23 ?a)) '{?a 23 }))
(is ((dsl/compile-expr '(not= 22 ?a)) '{?a 23 }))
(is ((dsl/compile-expr '(< ?a 30 )) '{?a 23 }))
(is ((dsl/compile-expr ('< '?a #inst " 2015-08-28T21:28:59Z" )) {'?a #inst " 2015-08-28T21:28:00Z" }))
(is ((dsl/compile-expr '(< ?a " 2015-08-28" )) '{?a " 2015-08-27" }))
(is ((dsl/compile-expr '(> ?a 20 )) '{?a 23 }))
(is ((dsl/compile-expr ('> '?a #inst " 2015-08-28T21:28:59Z" )) {'?a #inst " 2015-08-28T21:29:00Z" }))
(is ((dsl/compile-expr '(> ?a " 2015-08-27" )) '{?a " 2015-08-28" }))
(is ((dsl/compile-expr '(= ?a 30 )) '{?a 30 }))
(is ((dsl/compile-expr ('= '?a #inst " 2015-08-28T21:28:59Z" )) {'?a #inst " 2015-08-28T21:28:59Z" }))
(is ((dsl/compile-expr '(= ?a " 2015-08-28" )) '{?a " 2015-08-28" }))
(is ((dsl/compile-expr '(<= ?a 30 )) '{?a 30 }))
(is ((dsl/compile-expr '(<= ?a 30 )) '{?a 23 }))
(is ((dsl/compile-expr ('<= '?a #inst " 2015-08-28T21:28:59Z" )) {'?a #inst " 2015-08-28T21:28:59Z" }))
(is ((dsl/compile-expr ('<= '?a #inst " 2015-08-28T21:28:59Z" )) {'?a #inst " 2015-08-28T21:28:58Z" }))
(is ((dsl/compile-expr '(<= ?a " 2015-08-28" )) '{?a " 2015-08-28" }))
(is ((dsl/compile-expr '(<= ?a " 2015-08-28" )) '{?a " 2015-08-27" }))
(is ((dsl/compile-expr '(>= ?a 30 )) '{?a 30 }))
(is ((dsl/compile-expr '(>= ?a 30 )) '{?a 42 }))
(is ((dsl/compile-expr ('>= '?a #inst " 2015-08-28T21:28:59Z" )) {'?a #inst " 2015-08-28T21:28:59Z" }))
(is ((dsl/compile-expr ('>= '?a #inst " 2015-08-28T21:28:59Z" )) {'?a #inst " 2015-08-28T21:29:00Z" }))
(is ((dsl/compile-expr '(>= ?a " 2015-08-28" )) '{?a " 2015-08-28" }))
(is ((dsl/compile-expr '(>= ?a " 2015-08-28" )) '{?a " 2015-08-29" }))
(is (= " hello fabric (42)" ((dsl/compile-expr '(str " hello " ?a " (" ?b " )" )) '{?a " fabric" ?b 42 })))
(is (= " hello fabric ()" ((dsl/compile-expr '(str " hello " ?a " (" ?b " )" )) '{?a " fabric" })))
(is ((dsl/compile-expr '(in-set? ?a 1 2 3 )) '{?a 3 }))
(is (not ((dsl/compile-expr '(in-set? ?a 1 2 3 )) '{?a 0 })))
(is (not ((dsl/compile-expr '(in-set? ?a 1 2 3 )) {})))
(is (== 60 ((dsl/compile-expr '(agg-sum ?a)) '[{?a 50 } {?b 25 } {?a 10 } {?b 5 }])))
(is (== 30 ((dsl/compile-expr '(agg-sum ?b)) '[{?a 50 } {?b 25 } {?a 10 } {?b 5 }])))
(is (not ((dsl/compile-expr '(agg-sum ?a)) [])))
(is (== 30 ((dsl/compile-expr '(agg-avg ?a)) '[{?a 50 } {?b 25 } {?a 10 } {?b 5 }])))
(is (not ((dsl/compile-expr '(agg-avg ?a)) '[{} {} {}])))
(is (not ((dsl/compile-expr '(agg-avg ?a)) '[])))
(is (== 10 ((dsl/compile-expr '(agg-min ?a)) '[{?a 50 } {?b 25 } {?a 10 } {?b 5 }])))
(is (== 50 ((dsl/compile-expr '(agg-max ?a)) '[{?a 50 } {?b 25 } {?a 10 } {?b 5 }])))
(is (== 3 ((dsl/compile-expr '(agg-count)) '[{} {} {}])))
(is (== 0 ((dsl/compile-expr '(agg-count)) nil )))
(is (= #{50 10 } ((dsl/compile-expr '(agg-collect ?a)) '[{?a 50 } {?b 25 } {?a 10 } {?b 5 }])))
)
(deftest test-alias-index
(let [g (ff/fact-graph {:index (ff/alias-index-vertex #{'same-as})})
ctx (f/sync-execution-context {:graph g})
q1 (dsl/add-query-from-spec!
g '{:q [{:where [[?s ?p ?o]]}]})
q2 (dsl/add-query-from-spec!
g '{:q [{:where [[a ?p ?o]]}
{:optional [[?o name ?n]]}]})
q3 (dsl/add-query-from-spec!
g '{:q [{:where [[aa ?p ?o]]}
{:optional [[?o name ?n]]}]})
q4 (dsl/add-query-from-spec!
g '{:q [{:where [[?s ?p b]]}]})
q5 (dsl/add-query-from-spec!
g '{:q [{:where [[c ?p ?o]]}]})
facts '#{[a p1 b]
[b name bob]
[a same-as aa]
[p1 same-as p2]
[b equiv bb]
[aa p2 bb]}]
(run! #(ff/add-fact! g %) facts)
(f/execute! ctx)
; ;(doseq [i @(ff/fact-indices g)] (warn :index @i))
(is (= '#{{?s a ?p p1 ?o b}
{?s a ?p same-as ?o aa}
{?s aa ?p p2 ?o bb}
{?s b ?p equiv ?o bb}
{?s b ?p name ?o bob}
{?s p1 ?p same-as ?o p2}}
@q1))
(is (= '#{{?p p1 ?o b ?n bob}
{?p p2 ?o bb}
{?p same-as ?o aa}}
@q2))
(is (= @q2 @q3))
(is (= '#{{?s a ?p p1}} @q4))
(ff/add-fact! g '[equiv same-as same-as])
(f/execute! ctx)
; ;(doseq [i @(ff/fact-indices g)] (warn :index @i))
(is (= '#{{?s a ?p p1 ?o b}
{?s a ?p same-as ?o aa}
{?s aa ?p p2 ?o bb}
{?s b ?p equiv ?o bb}
{?s b ?p name ?o bob}
{?s equiv ?p same-as ?o same-as}
{?s p1 ?p same-as ?o p2}}
@q1))
(is (= '#{{?p p1 ?o b ?n bob}
{?p p2 ?o bb}
{?p same-as ?o aa}}
@q2))
(is (= @q2 @q3))
(is (= '#{{?s a ?p p1} {?s aa ?p p2} {?s b, ?p equiv}} @q4))
(f/signal! (ff/remove-fact! g '[a same-as aa]) f/sync-vertex-signal)
(f/execute! ctx)
; ;(doseq [i @(ff/fact-indices g)] (warn :index @i))
(is (== 6 (count @q1)))
(is (= '#{{?p p1 ?o b ?n bob}} @q2))
(is (= '#{{?p p2 ?o bb}} @q3))
(run! #(ff/add-fact! g %) '[[a alias c] [alias same-as equiv]])
(f/execute! ctx)
; ;(doseq [i @(ff/fact-indices g)] (warn :index @i))
(debug :all (ff/facts g))
(is (= '#{{?p p1, ?o b, ?n bob} {?p alias, ?o c}} @q2))
(is (= '#{{?p p2, ?o bb}} @q3))
(is (= '#{{?s b, ?p equiv} {?s aa, ?p p2} {?s a, ?p p1}} @q4))
(is (= '#{{?p p1, ?o b} {?p alias, ?o c}} @q5))))
(deftest test-map->facts
(let [facts (ff/map->facts
{:s1 {:p1 {:p2 :o1
:p3 [{:p4 :o2 } {:p5 :o3 }]}
:p6 [:o4 :o5 ]}
:s2 {:p7 :o6 }})
ids (zipmap
(into [] (comp (mapcat identity) (filter string?) (distinct )) facts)
[:bnode1 :bnode2 :bnode3 ])
facts (mapv (fn [f] (mapv #(ids % %) f)) facts)]
(is (== 9 (count facts)))
(is (= [[:s1 :p1 :bnode1 ]
[:bnode1 :p2 :o1 ]
[:bnode1 :p3 :bnode2 ]
[:bnode2 :p4 :o2 ]
[:bnode1 :p3 :bnode3 ]
[:bnode3 :p5 :o3 ]
[:s1 :p6 :o4 ]
[:s1 :p6 :o5 ]
[:s2 :p7 :o6 ]]
facts))))
Combined example w/ rule based inferencing
(def logger
#? (:clj
#(spit " fact-log.edn" (str (pr-str %) " \n " ) :append true )
:cljs
#(.log js/console (pr-str %))))
(def default-facts
'#{[knows type symmetric-prop]
[knows domain person]
[author domain person]
[author range creative-work]
[parent sub-prop-of ancestor]
[ancestor type transitive-prop]
[ancestor domain person]
[ancestor range person]
[toxi author fabric]
[fabric type project]
[toxi modified 23 ]
[toxi modified 42 ]})
(def default-and-inferred-facts-1
(-> default-facts
(conj '[toxi type person] '[fabric type creative-work])
(disj '[toxi modified 23 ])))
(def default-and-inferred-facts-2
(into
default-and-inferred-facts-1
'#{[noah type person]
[fabric tag clojure]
[toxi author geom]
[noah knows toxi]
[ingo type person]
[ingo ancestor noah]
[toxi ancestor noah]
[ingo ancestor toxi]
[ingo parent toxi]
[geom type project]
[geom type creative-work]
[toxi knows noah]
[geom tag clojure]
[toxi parent noah]}))
(def inference-rules
{:symmetric ['[[?a ?prop ?b] [?prop type symmetric-prop]]
(fn [g _ {:syms [?a ?prop ?b]}] (ff/add-fact! g [?b ?prop ?a]))]
:domain ['[[?a ?prop nil ] [?prop domain ?d]]
(fn [g _ {:syms [?a ?prop ?d]}] (ff/add-fact! g [?a 'type ?d]))]
:range ['[[nil ?prop ?a] [?prop range ?r]]
(fn [g _ {:syms [?a ?prop ?r]}] (ff/add-fact! g [?a 'type ?r]))]
:transitive ['[[?a ?prop ?b] [?b ?prop ?c] [?prop type transitive-prop]]
(fn [g _ {:syms [?a ?prop ?c]}] (ff/add-fact! g [?a ?prop ?c]))]
:sub-prop ['[[?a ?prop ?b] [?prop sub-prop-of ?super]]
(fn [g _ {:syms [?a ?super ?b]}] (ff/add-fact! g [?a ?super ?b]))]
:modified ['[[?a modified ?t1] [?a modified ?t2]]
(fn [g _ {:syms [?a ?t1 ?t2]}]
(when-not (= ?t1 ?t2)
(ff/remove-fact! g [?a 'modified (min ?t1 ?t2)])))]})
(defn test-graph3
[opts]
(let [g (ff/fact-graph opts)
; ;log (ff/add-fact-graph-logger g logger)
toxi (ff/add-query! g ['toxi nil nil ] {})
types (ff/add-query! g [nil 'type nil ] {})
projects (ff/add-query! g [nil 'type 'project] {})
knows (ff/add-query! g [nil 'knows nil ] {})
all (ff/add-query! g [nil nil nil ] {})
knows-pq (ff/add-param-query! g '[?s knows ?o] {})
jq (ff/add-query-join! g '[[?p author ?prj] [?prj type project] [?p type person]] {})
tq (ff/add-query-join! g '[[?p author ?prj] [?prj tag ?t]] {})]
(run!
(fn [[id [q prod]]] (ff/add-rule! g {:id id :patterns q :production prod})) inference-rules)
(run!
#(ff/add-fact! g %) default-facts )
{:g g
; ;:log log
:all all
:toxi toxi
:types types
:projects projects
:knows knows
:knows-pq knows-pq
:jq jq
:tq tq}))
(defn test-sync*
[opts]
(let [{:keys [g log all knows-pq jq tq] :as spec} (test-graph3 opts)
ctx (f/sync-execution-context {:graph g})
res (f/execute! ctx)]
(warn :result res)
(is (= default-and-inferred-facts-1 @all))
(run!
#(ff/add-fact! g %)
'[[toxi parent noah]
[ingo parent toxi]
[geom type project]
[toxi author geom]
[toxi knows noah]
[geom tag clojure]
[fabric tag clojure]])
(warn :result2 (f/execute! ctx))
(is (= default-and-inferred-facts-2 @all))
(is (= '#{{?s noah ?o toxi} {?s toxi ?o noah}} @knows-pq))
(is (= '#{{?p toxi ?prj fabric} {?p toxi ?prj geom}} @jq))
(is (= '#{{?p toxi ?prj fabric ?t clojure} {?p toxi ?prj geom ?t clojure}} @tq))
(f/signal! (ff/remove-fact! g '[geom tag clojure]) f/sync-vertex-signal)
(f/signal! (ff/remove-fact! g '[fabric tag clojure]) f/sync-vertex-signal)
(warn :result3 (f/execute! ctx))
(is (= (disj default-and-inferred-facts-2 '[geom tag clojure] '[fabric tag clojure]) @all))
(is (= '#{{?p toxi ?prj fabric} {?p toxi ?prj geom}} @jq))
(is (= #{} @tq))
; ;(ff/remove-fact-graph-logger log)
(assoc spec :ctx ctx)))
(deftest test-sync-default
(test-sync* {}))
(deftest test-sync-gi-ftx
(test-sync* {:transform (ff/combine-transforms (ff/global-index-transform ) 3 )}))
(defn test-async*
[opts]
(let [{:keys [g log all knows-pq jq tq] :as spec} (test-graph3 opts)
ctx (f/async-execution-context {:graph g})
ctx-chan (f/execute! ctx)
notify (chan )]
(go
(let [res (<! ctx-chan)]
(warn :result res)
(is (= default-and-inferred-facts-1 @all))
(run!
#(ff/add-fact! g %)
'[[toxi parent noah]
[ingo parent toxi]
[geom type project]
[toxi author geom]
[toxi knows noah]
[geom tag clojure]
[fabric tag clojure]])
(f/notify! ctx)
(let [res (<! ctx-chan)]
(warn :result2 res)
(is (= default-and-inferred-facts-2 @all))
(is (= '#{{?s noah ?o toxi} {?s toxi ?o noah}} @knows-pq))
(is (= '#{{?p toxi ?prj fabric} {?p toxi ?prj geom}} @jq))
(is (= '#{{?p toxi ?prj fabric ?t clojure} {?p toxi ?prj geom ?t clojure}} @tq))
(ff/remove-fact! g '[geom tag clojure])
(ff/remove-fact! g '[fabric tag clojure])
(f/notify! ctx)
(let [res (<! ctx-chan)]
(warn :result3 res)
(is (= (disj default-and-inferred-facts-2 '[geom tag clojure] '[fabric tag clojure]) @all))
(is (= '#{{?p toxi ?prj fabric} {?p toxi ?prj geom}} @jq))
(is (= #{} @tq))
(f/stop! ctx)
; ;(ff/remove-fact-graph-logger log)
(warn :done )
(>! notify :ok )))))
#?(:clj (<!! notify) :cljs (take! notify (fn [_] (done ))))
(assoc spec :ctx ctx)))
#?(:clj (deftest test-async (test-async* {})))
(ns thi.ng.fabric.facts.test.core
#? (:cljs
(:require-macros
[cljs.core.async.macros :refer [go go-loop]]
[cljs-log.core :refer [debug info warn]]))
(:require
[thi.ng.fabric.core :as f]
[thi.ng.fabric.facts.core :as ff]
[thi.ng.fabric.facts.dsl :as dsl]
[thi.ng.fabric.core.utils :as fu]
#?@(:clj
[[clojure.test :refer :all ]
[clojure.core.async :refer [go go-loop chan close! <! <!! >!]]
[taoensso.timbre :refer [debug info warn]]]
:cljs
[[cemerick.cljs.test :refer-macros [is deftest with-test testing done]]
[cljs.core.async :refer [chan close! <! >! take!]]])))
#?(:clj (taoensso.timbre/set-level! :warn ))
<<const>>
<<helpers>>
<<tests>>