Fork me on GitHub
#datascript
<
2020-08-26
>
oly11:08:56

is there anything built in to flatten nested data, so when looking up refs in a pull query it returns the refs as sub hashmaps when i have a single key value can i flatten or should I do that afterwards ?

pithyless08:08:51

datomic has :xform option in pull-api, but I don't think that has been ported to datascript. Even so, you would need to apply that to each ref individually. One way is to clojure.walk/postwalk over the result and replacing all the known refs with their idents. You can either explicitly pass in the refs you need replaced or you can find all the refs automatically by querying the schema (or in the case of datascript - the initial schema map).

pithyless08:08:04

Fulcro RAD has some code that could serve as a good starting point (it considers both multiple and single cardinality): https://github.com/fulcrologic/fulcro-rad-datomic/blob/develop/src/main/com/fulcrologic/rad/database_adapters/datomic.clj#L80-L102

pithyless08:08:05

I prefer the postwalk approach, because it makes the conversion explicit and doesn't confuse data querying with data mangling 🙂

Casey08:08:42

Woah, I joined this channel with the intent of asking this exact question..

Casey08:08:43

The latter says I should be able to (d/pull-many db '[*] (d/q '[:find ?e :where [?e]]))

Casey08:08:41

But there seem to be multiple things wrong with this: 1. You need to pass the db to the query function 2. The result of the query is a set of 1 element vectors that pull-many choke son

Casey08:08:12

There's got to be something simple going wrong here?

Casey08:08:39

This works, but seems like it should be unnecessary (d/pull-many @conn '[*] (flatten (vec (d/q '[:find ?e :where [?e]] @conn))))

pithyless09:08:08

1. Yes, you want to learn to pass an immutable db around to queries, not derefing an unkown state conn. 2. You're right about the set of tuples, but there is more sugar syntax: [:find ?e] vs [:find ?e .] vs [:find [?e ...]] (what you're looking for) 3. Fetching all data like this seems like an unusual use-case; and if you want all eids, you can probably just take that straight out of the indices.

Casey09:08:59

😮 I always assumed the ... in examples was just the author eliding stuff that wasn't important, not that it was literal syntax.

Casey09:08:21

agreed regarding #3, I'm just finding my way around datalog, and after putting stuff in a natural question is how do I get it all out?

Casey09:08:57

thanks @U05476190 your "domain modeling with datalog" talk got me here 🙂

metal 3
oly09:08:03

thanks for that, not liking these nested replies I missed the replies till now 😕

oly09:08:42

but you have given me a few options to look into, think i need to make notes as I go and make a post about these things 🙂

pithyless09:08:04

ad 3. that's why it unusual - asking for "all" the data hardly ever comes up in practice; unless you want to serialize/backup/etc, and then it's probably more practical to use d/datoms d/seek-datoms d/history etc.

oly09:08:56

currently I am making a query to grab data and make a csv files from the results

oly09:08:20

I ended up specifying each entity in the query instead of using pull inside d/q with the [*] wild card

Casey09:08:25

@U05476190 regarding #1, makes sense and is something I'm already used to when working with state maps, however is it possible to transact in datascript without involving the atom? that is I want a function to take db value, and return an updated db value. datacript's transact! only operates on a connection.

pithyless09:08:48

Does the function also need to query? If not, you're looking at a function signature ala:

(defn update-foo [conn new-foo]
  (let [tx-result (d/transact! ...)]
    ;; return new DB for system to use
    (:db-after tx-result))
If the function needs to query the DB before making the update, which version of the DB? You need to make that decision. But if you're interested in the "newest" version (e.g. to increment a value), then you need to move that query logic into a transactor function to avoid race conditions. So the above pattern still applies. If you're working in DataScript in single-threaded JS and you don't care about race conditions, then you're update-foo could just take a conn as an arg, deref the conn, do the query, then transact!, and return :db-after as before.

cjsauer13:08:06

@U70QFSCG2 have a look at d/with and d/db-with. Those both allow you to incorporate new datoms into a database as a pure function of db->db

pithyless09:08:48

Does the function also need to query? If not, you're looking at a function signature ala:

(defn update-foo [conn new-foo]
  (let [tx-result (d/transact! ...)]
    ;; return new DB for system to use
    (:db-after tx-result))
If the function needs to query the DB before making the update, which version of the DB? You need to make that decision. But if you're interested in the "newest" version (e.g. to increment a value), then you need to move that query logic into a transactor function to avoid race conditions. So the above pattern still applies. If you're working in DataScript in single-threaded JS and you don't care about race conditions, then you're update-foo could just take a conn as an arg, deref the conn, do the query, then transact!, and return :db-after as before.