datascript 2022-08-21

[{:db/id 1, :co-ordinate {:x 0, :y 1, :z 2}}, {:db/id 2, :co-ordinate {:x 1, :y 1, :z 2}}]
How would I find all entities with a y co-ordinate of 1? Is there a way I can bind the internals of :co-ordinate as a part of a query?

[:find ?e
 :where [?e :co-ordinate ?x] [(= 1 (:y ?x))]]
doesn't work, obviously

The issue is that (:y ?x) is not part of your predicate, and not evaluated. One way to solve this is like so:

(let [conn (d/create-conn)]
    (d/transact! conn [{:db/id 1, :co-ordinate {:x 0, :y 1, :z 2}}, {:db/id 2, :co-ordinate {:x 1, :y 1, :z 2}}])
    (prn (d/q '{:find [?e ?c]
                :where [[?e :co-ordinate ?c]
                        [(?pred ?c)]]
                :in [$ ?pred]}
              @conn
              #(= 1 (:y %)))))
Though there are other ways to express it which are probably better... the point is just that the arguments to the predicate are not evaluated.

=> #{[2 {:x 1, :y 1, :z 2}] [1 {:x 0, :y 1, :z 2}]}

The nicer way to express it would be:

'{:find [?e ?c]
  :where [[?e :co-ordinate ?c]
          [(get ?c :y) ?y]
          [(= 1 ?y)]]}

^^ This evaluates the y lookup separately and binds it to ?y which can be used with the predicate = directly. https://docs.datomic.com/on-prem/query/query.html#function-expressions

Thanks @timothypratley

😄 1

I guess I'll find a way to flatten the map into the entity instead

Yup well something like {:db/id 1, :x 0, :y 1, :z 2} isn't much different, and is easier to query 🙂

or you can make :co-ordinate a ref instead of a value, either will work.