xtdb / learn-xtdb-datalog-today

Learn xtdb Datalog syntax
MIT License
6 stars 4 forks source link

friends rule is incorrect #16

Closed refset closed 3 years ago

refset commented 3 years ago

the query is actually not doing friends of friends, but is actually just returning all people in the database.

this query however works - this is probably the way it should have been written, without the self-recursion:

(q '{:find [friend]
:in [name]
:where [[p1 :person/name name]
(myfriends p1 p2)
[p2 :person/name friend]]
:rules [[(friends ?p1 ?p2)
[?m :movie/cast ?p1]
[?m :movie/cast ?p2]
[(not= ?p1 ?p2)]]
[(friends ?p1 ?p2)
[?m :movie/cast ?p1]
[?m :movie/director ?p2]
[(not= ?p1 ?p2)]]
[(myfriends ?p1 ?p2)
(friends ?p1 ?p2)]
[(myfriends ?p1 ?p2)
(friends ?p2 ?p1)]]}
"Mel Gibson")

On first glance, I think the original solution only works because the semantics of that Datalog implementation are order-sensitive (unlike XT's more declarative style), which means ?p2 is already constrained by the time it gets to evaluating [?p2 :person/name ?friend]

original solution is from http://www.learndatalogtoday.org/chapter/8 Question "1"

Alternatively, this might be a victim of an unresolved bug https://github.com/xtdb/xtdb/issues/1569

refset commented 3 years ago

there is a known open Crux unification bug which this solution seems to be hitting. If you add a couple of unification clauses a workaround though, as suggested, then it works as expected:

[p1 :person/name name] (myfriends p1 p2) [p2 :person/name friend]

;; becomes

[p1 :person/name name] (myfriends p1 p2) ;; intermediate variables [p2 :person/name friend] [(== p1 p1)] ;; added this [(== p2 p2)]] ;; added this

However, there is a simpler/better answer available by simply removing the "inverse" rule anyway, which doesn't require those workaround clauses:

[(friends ?p1 ?p2) [?m :movie/cast ?p1] [?m :movie/cast ?p2] [(not= ?p1 ?p2)]] [(friends ?p1 ?p2) [?m :movie/cast ?p1] [?m :movie/director ?p2]] [(friends ?p1 ?p2) (friends ?p2 ?p1)]

;; becomes

[(friends ?p1 ?p2) [?m :movie/cast ?p1] [?m :movie/cast ?p2] [(not= ?p1 ?p2)]] [(friends ?p1 ?p2) [?m :movie/cast ?p1] [?m :movie/director ?p2] [(not= ?p1 ?p2)]] ;; also added this as per https://github.com/jonase/learndatalogtoday/pull/33

I've now merged this as the official solution.

refset commented 3 years ago

The merged solution did not resolve the inverse director<->cast case.

refset commented 3 years ago

Note there's an implicit assumption that a film cannot have more than one director!