deadghost
deadghost

Reputation: 5217

Datomic Pull API only retrieves one entity

I'm having some trouble figuring out the pull API. I have two location entities. When I use pull I only receive one.

(ns some-ns.core
  (:require [datomic.api :as d]))

(d/q '[:find ?e 
       :where [?e :location/name]]
     db)
=> #{[17592186045535] [17592186045420]} ; two results

(d/q '[:find [(pull ?e [:db/id
                        :location/name])]
       :where [?e :location/name]]
     db)
=> [{:db/id 17592186045535, :location/name "Some Other Location"}] ; one result

I suspect I might be using the incorrect pull expression but I see nothing glaringly wrong.

Upvotes: 3

Views: 1009

Answers (3)

Alan Thompson
Alan Thompson

Reputation: 29958

To avoid subtle errors like this one, you may wish to try the Tupelo Datomic library.

Instead of using confusing symbols like "...", "[]", etc, Tupelo Datomic splits up the query syntax into four distinct functions. Here is an example usage:

  ; If you want just a single attribute as output, you can get a set of values (rather than a set of
  ; tuples) using td/query-set.  As usual, any duplicate values will be discarded.
  (let [names     (td/query-set :let    [$ (live-db)]
                                :find   [?name] ; <- a single attr-val output allows use of td/query-set
                                :where  [ [?eid :person/name ?name] ] )
        cities    (td/query-set :let    [$ (live-db)]
                                :find   [?loc]  ; <- a single attr-val output allows use of td/query-set
                                :where  [ [?eid :location ?loc] ] )

  ]
    (is (= names    #{"Dr No" "James Bond" "M"} ))  ; all names are present, since unique
    (is (= cities   #{"Caribbean" "London"} )))     ; duplicate "London" discarded

The pull API is also supported if that format is better for your problem. Enjoy!

Upvotes: 1

Ben Kamphaus
Ben Kamphaus

Reputation: 1665

In the example you provide, you're using the "single tuple" find specification around the pull expression, which only returns a single tuple regardless of the number of entities matched by the query. You would run into the same problem if you specified a scalar return in find, i.e. with ..

(1) The most straight forward way to correct this is to drop the find specification (this matches the form of your original query):

(d/q '[:find (pull ?e [:db/id :location/name])
       :where [?e :location/name]]
     db)

(2) You could also, as in your own answer, specify a collection in find:

(d/q '[:find [(pull ?e [:db/id :location/name]) ...]
   :where [?e :location/name]]
 db)

The primary difference is that (1) will return a set of nested maps, whereas (2) will return a vectors of maps.

Upvotes: 3

deadghost
deadghost

Reputation: 5217

Looks like I was missing ....

(d/q '[:find [(pull ?e [:db/id
                        :location/name]) ...]
       :where [?e :location/name]]
     db)
=> [{:db/id 17592186045535, :location/name "Some Other Location"} {:db/id 17592186045420, :location/name "White House"}]

Upvotes: 4

Related Questions