I'm having a lot of trouble wrapping my head around reverse-lookups, I saw them in the datomic guides, and a couple SO questions, but don't really understand how/when to use them. Normally when writing a query, I'm thinking either:
"I have thing X, and I want to find what Y(s) its associated with"
OR
"Of all my X's, I want to find ones that have Y qualities"
These (to me) are directly solvable by the 'basic' (non-pull) query syntax. I get the sense I should probably be using pull more, which I'm less practiced with (beyond getting attributes that belong specifically to a target entity).
I feel my biggest difficulty is figuring out the "direction" I'm supposed to go in when writing a reverse-lookup: in a normal query, I find the pivoting on attributes a pretty straight-forward path, I'm starting from my X's, and pivoting down to my Y's, filtering with each step. Reverse-lookups (appropriately) seem to invert this somehow, but without using the same kind of pivots I'm familiar with.
Any good tutorials/guides/advice for more 'interesting' pulls and reverse-lookups?Your data may be totally different then my so ymmv: Take a big piece of paper, like a flipchartblock but use a normal ballpen or pencil, don't write big. Draw example data of your domain as a fishet. Project A, -> company B, -> person C. Also Project A, -> other company -> three other people etc. When you want to pull a project you need an entry point. Then pull, for the exercise at least 5 joins deep. {:project/companies {:company/person {:person/car [*]}}} Mark the lines of the pull, which is a tree on the bidirectional graph fishnet on your paper. Do this until you see the trees that overlay the net. At the beginning I thought about queries a lot. Now, your data may be differen, I only query to find entry points like all persons named John Smith. Then I pull. In 90% I will not need a query, because the user clickt on John Smith, which I know to be :db/id or :person/id 4 so I just pull 4, in extreme cases more than 10 levels deep. When you see your domain as the data with the pull tree overlaing them you will start restructuring your data to make a better web.
Of course follow up by entering on a car and going to its owner, the owners company and the projects.
Eg. you will want to have extra information on the relationship of person to company. So you add empoyment. You want to have status on the company in relation to the project so you add membership. This yields project -> membership -> company -> employment -> person -> car. Now the status of the company in the person is :membership/status, the role of the emloyee is :emloyment/role. Play with it on a table with a big sheat of paper and trace multiple different pulls from different directions on the fishnet of the data.
Very much appreciate the direction @magra, I'll have to internalize this! Can I ask about a concrete example I've constructed? In this example, I'm modeling relationships between different people
(d/transact! conn
[{:db/id 1, :person/name "Joe"}
{:db/id 2, :person/name "Mike"}
{:db/id 3, :person/name "Dave"}
{:db/id 4, :person/name "Larry"}])
(d/transact! conn
[#:relationship {:type :friend :from 2 :to 1}
#:relationship {:type :friend :from 3 :to 1}
#:relationship {:type :boss :from 4 :to 1}])
If I wanted to get the names for each person who's friends with "Joe" (:db/id 1), I figured I would do something like this:
(d/q
'[:find (pull ?e
[:db/id
{:relationship/_to
[{:relationship/from [:person/name]}
:relationship/type]}])
:in $ ?e
:where
[?rel :relationship/to ?e]
[?rel :relationship/type :friend]]
(d/db conn) 1)
but this does not exclude "Larry" (:db/id 4, who's relationship-type is :boss, not :friend)
How should I be referencing those 'nested' items that I'd like filter?You are right. As soon as you pull a tree it does not make sense to filter with datalog. You would run a regular clojure filter on the tree. In this particular example you might use the datalog query to get all friends of Joe's and pull a tree of data on each of them including their car, pet, chat history etc.
(q '[:find [(pull ?f [deep tree of pull including pets and chat history]) ...]
:in $ ?e
:where
[?rel :rel/type :friend]
[?rel :rel/from ?e]
[?rel :rel/to ?f]]
db 1)(q '[:find [(pull ?f [:db/id :person/name]) ...]
:in $ ?e
:where
[?rel :rel/type :friend]
[?rel :rel/from ?e]
[?rel :rel/to ?f]]
db 1)
If you just want the name you do not need a pull
(q '[:find [?f ?n]
:in $ ?e
:where
[?rel :rel/type :friend]
[?rel :rel/from ?e]
[?rel :rel/to ?f]
[?f :person/name ?n]]
db 1)
ahh ok I see, so I really wanna move from pulling from the ?e (my "given"), and pull from the filtered values instead, that makes a lot of sense