28

The 'Embedded' section of the Datomic Queries and Rules document says:

Query languages like SQL are oriented around a client-server model where, in a single conversaton, you are going to have to both:

  • Answer your fundamental question, e.g. who bought socks this month.
  • Recover any additional information required for reporting and processing, e.g. what are their names and email addresses.

The latter is not really a query, it is just a mechanical navigation to related information.

While I appreciate how the orthogonality of the two different mentioned aspects is honored, I think I'll often need to retrieve a whole entity, whichever its attributes are.

As far as I know, queries typically have this form:

(datomic.api/q '[:find ?name ?age ?email
                 :where
                 [?e :myapp/name ?name]
                 [?e :myapp/age ?age]
                 [?e :myapp/email ?email]]
               (db conn))

If I wanted to retrieve entities that have N attributes, I'd have them to list them all in each query, which seems tedious and error-prone to me.

How to tell Datomic to retrieve the entities with all the fields they were persisted with, without having to specify them explicitly?

deprecated
  • 5,142
  • 3
  • 41
  • 62

5 Answers5

20

Having the entity (id) from query like:

=> (def eid (d/q '[:find ?e :where [?e :myapp/name "Fred"]] (db conn)))

you can get the EntityMap:

=> (def ent (d/entity (db conn) (ffirst eid)))

so you can access fields/attributes without making additional query:

=> (seq ent)
;; ([:myapp/name "Fred"] [:myapp/age 16] [:myapp/email "fred@email.com"])

however it may be easier to get the keys first:

=> (keys ent)
;; (:myapp/name :myapp/age :myapp/email)

You can even get reverse keys ("foreign" ref attributes that point to this entity) using the following trick:

=> (.touch ent)
=> (keys (.cache ent))
Grzegorz Luczywo
  • 9,962
  • 1
  • 33
  • 22
15

You can use pull to get all fields from an entity, or even just a selection. Using '[*] as the pattern for pull will retrieve all fields

See the pull documentation for more information.

To get all fields from an entity with id eid use:

(d/pull (db conn) '[*] eid)

Pull can also be used in queries:

(datomic.api/q '[:find (pull ?e [*])
                 :where
                 [?e :myapp/name]
               (db conn))
ChrisBlom
  • 1,262
  • 12
  • 17
  • You don't need to use the `_` in final position in the `:where` clause - that position can simply be elided. I.e., `[?e :myapp/name]`. – Ben Kamphaus Sep 10 '15 at 01:48
10

datomic.api/touch function

There is a specific datomic.api/touch function to just "Touch" all the attributes of the entity. Entity returned by the entity function are lazy and the attribute value is returned only when accessed, the touch function eagerly retrieve all the entity attributes.

Example:

(let [entity (d/entity db-val (ffirst (d/q '[:find ?e :in $ ?email
                                             :where [?e :user/email ?email]]
                                            db-val email))]
    ;;then just d/touch the entity returned by the d/entity fn
    (d/touch entity))
=> {:user/username "gretchen", :user/email "gretchen@user.com", :user/password "xxxxxx", :db/id 17592186046433}
James Conkling
  • 3,235
  • 2
  • 25
  • 37
Jérémie
  • 360
  • 5
  • 6
7

datomic.api/entity provides such functionality.

It just has the gotcha that the returned map has a custom representation which hides all fields but :db/id. One can access those fields, but printing them requires merging the map into a regular Clojure map.

deprecated
  • 5,142
  • 3
  • 41
  • 62
  • 3
    A couple notes: (1) entities don't "hide" attributes, they're a lazy interface to fetching entity attributes and related entities. This is important to understand from a performance perspective, and it explains why the attributes aren't all immediately visible—it would have to fetch the datoms from the DB in order to display the attributes, defeating the advantages of laziness. (2) It's not necessary to merge an entity into a map—simply call the [touch](http://docs.datomic.com/clojure/#datomic.api/touch) function to get all of the attributes. – camdez Mar 17 '17 at 17:32
0

I haven't tested it, but if I remember correctly you can just put a variable as the attribute name

(datomic.api/q '[:find ?key ?value
             :where
             [?e ?key ?value]]
           (db conn))
Joe Lehmann
  • 1,087
  • 5
  • 10
  • 1
    Not sure if your query was just an stub but it throws an IllegalArgumentException as it would cause a full database scan – deprecated Jan 07 '13 at 19:25
  • Yes, usually you would restrict the entity by adding some meaningful where clauses, e.g., [?e :user/name "Joe"] – Joe Lehmann Jan 09 '13 at 03:49