Datalog: does every value for a given entity attribute exactly match a given set?


#1

Hi all. I asked this question on the Slack channel, but I thought this might be a better place for it.

As a setup, the model I’m working with has cards and persons. A person can be assigned to one or more cards, so I’ve got a cardinality-many attribute :card/assignees. The sample data I’m working with looks roughly like this (please excuse any syntax errors, this is more like pseudocode):

[{:db/id [:card/id "A"]   :card/assignees [[:person/id "a"]]}
 {:db/id [:card/id "B"]   :card/assignees [[:person/id "b"]]}
 {:db/id [:card/id "C"]   :card/assignees [[:person/id "c"]]}
 {:db/id [:card/id "AB"]  :card/assignees [[:person/id "a"] 
                                           [:person/id "b"]]}
 {:db/id [:card/id "ABC"] :card/assignees [[:person/id "a"] 
                                           [:person/id "b"] 
                                           [:person/id "c"]]}]

My requirement is that I have a filter for viewing cards by their assignees, so I’m getting a set of users from the front-end, and I need to return every card which is assigned to those users. This would be quite trivial if I needed to OR the users together, a la (filter-users #{"a" "b"}) => #{"A" "B" "AB" "ABC"}).

In my case, however, I need to AND the users together, so that (filter-users #{"a" "b"}) should return the cards that are assigned to both "a" and "b": the cards "AB" and "ABC".

I’ve been struggling to figure out how to do this in pure datalog. I did find a potential solution in this previous question, but involves calling out to the entity API to generate a set of values, and while I did manage to make the AND query work this way, it’s been difficult to compose this with other bits of the datalog I need for other requirements.

One of the fundamental problems I’m seeing is that if I pass in the target set into a query using collection binding, I’m only matching on a single member of the set at once, so any particular test to see whether a datom should be included in the result set has no access to the other members.

I have a feeling that there’s something fundamental I’m missing in my approach to the problem. Something about this stack overflow datalog answer seems like it gets close to the problem, but I’ve been unsuccessful translating it into its datomic equivalent.


#2

Can you give an example of “it’s been difficult to compose this with other bits of the datalog I need”? Note we also have clojure.core/set and all of datomic.api including d/q


#3

Well, I suppose it’s more annoying than difficult. As one example, though, I’ve got another filter that should return true if the input set is a subset of all of the assignees plus a different card attribute, :card/author. So now in order to get the set-based stuff to work I need to extract the card author, get the set of assignees, (conj) the author into the assignees, and check whether the input set matches.

It’s not impossible, but doing all the set operations in datalog looks ugly to me, and I still have a feeling that this can be solved in pure datalog somehow without having to break out to the Clojure layer.


#4

Thanks, okay I see and I agree with your summary. Here’s a relevant Slack chat from Rich (that does not answer your question): C-f “query style” http://www.dustingetz.com/:datomic-ion-launch-day-questions/