|
;; Imagine you have Persons and Dogs entities in you database, with a :dog/master relationship from Dogs to Persons |
|
|
|
;; you want a report of how many dog each person in you database has, as a set of [<person-name> <number-of-dogs>], e.g: |
|
|
|
#{["John Doe" 3] |
|
["Foo bar" 0] |
|
["Chuck Norris" 1]} |
|
|
|
;; in particular, you want person who do not have a dog to appear in this report. |
|
|
|
;; The following query would NOT work |
|
|
|
'[:find ?name (count ?dog) :with ?person |
|
:where |
|
[?person :person/name ?name] |
|
[?dog :dog/master ?person]] |
|
|
|
;; the reason it would not work is persons with no dog will never appear in a satisfying clause. |
|
|
|
;; However, the following DOES work: |
|
|
|
'[:find ?name (sum ?weight) :with ?data-point |
|
:where |
|
[?person :person/name ?name] |
|
(or-join [?person ?data-point ?weight] |
|
;; case 1: the data point is the dog, the weight is 1 |
|
(and [?dog :dog/master ?person] |
|
[(identity ?dog) ?data-point] |
|
[(ground 1) ?weight]) |
|
;; case 2: the data point is the person, the weight is 0 |
|
(and [(identity ?person) ?data-point] |
|
[(ground 0) ?weight]))] |
|
|
|
;; Here we have used a trick to make a person always appear in at least one satisfying clause: |
|
;; each dog counts for 1, and each person counts for 0. |
|
|
|
;; this is a bit tedious and not very clear, but note that you can make a rule for the second clause : |
|
|
|
(def rules_sums |
|
'[[(point-with-zero-weight ?e ?data-point ?weight) |
|
[(identity ?e) ?data-point] |
|
[(ground 0) ?weight]]]) |
|
|
|
;; now our query becomes |
|
|
|
'[:find ?name (sum ?weight) :with ?data-point |
|
:in % $ |
|
:where |
|
[?person :person/name ?name] |
|
(or-join [?person ?data-point ?weight] |
|
(and [?dog :dog/master ?person] |
|
[(identity ?dog) ?data-point] |
|
[(ground 1) ?weight]) |
|
(point-with-zero-sum ?person ?data-point ?weight))] |
|
|