Fork me on GitHub
#datascript
<
2024-05-16
>
Braden Shepherdson17:05:03

I have need to put UUIDs (as strings) on more or less every entity in my DataScript DB. they'll either be supplied by upstream data getting ingested into DataScript (easy enough) or for newly minted entities will get generated locally and included. I'd be happy to use the UUID strings as entity IDs straight in DataScript but I https://github.com/tonsky/datascript/issues/168 that it's not currently possible to do that. (I don't care about storage, if that makes a difference.) is there a way to auto-fill these at transaction time? I often transact big complicated maps with a mix of upserts and new entities nested all inside them, so it's not practical to know where all the new entities are. I can do (d/q '[:find ?e :in $ :where [?e _ _] [(missing $ ?e :my.ns/uuid)]] db) after each transaction, I suppose.

Niki19:05:34

You can totally store uuids, either as strings or as uuid type, just store them as an attributte

Braden Shepherdson19:05:27

is there a slicker way to enforce them than editing all the code that inserts new entities, and adding a post-test fixture that checks for anything without one?

Niki19:05:59

no, I don’t think so

Braden Shepherdson19:05:37

fair enough. thanks!

Sam Ferrell20:05:44

ive definitely had this need and i think niki talked about using uuids in a hypothetical datascript 2 https://tonsky.me/blog/datascript-2/

Sam Ferrell20:05:06

but yeah i just added them manually in all my transaction fns

Filipe Silva13:05:52

What's the case where you're not sure if you should add a new uuid though? It sounds like you either have it provided from your data source, or you should add one.

Braden Shepherdson13:05:21

It's that I'm sometimes not so clear in my code to build transactions whether this is an update or a new value.

Braden Shepherdson13:05:01

I ended up putting in the necessary conditions by hand. I'm not thrilled about that, but there's not really a good alternative, and the set of functions adding and updating things is limited.

Filipe Silva13:05:24

If you make the uuid unique on the schema, the entity will be up setter

Filipe Silva13:05:27

So you can just transact your nested objects with uids in them directly: if the entity exists it will be a upsert, if it doesn't exist it will be an insert

Filipe Silva13:05:56

(require '[datascript.core :as d])
;; :db/ident is already unique
(def schema {:children {:db/cardinality :db.cardinality/many
                        :db/valueType   :db.type/ref}})
(def conn (d/create-conn schema))

(d/transact! conn [{:db/ident "uuid1"
                    :prop1 1}])
(d/touch (d/entity @conn [:db/ident "uuid1"]))
;; => {:prop1 1, :db/ident "uuid1", :db/id 1}

(d/transact! conn [{:db/ident "uuid1"
                    :prop2 2}])
(d/touch (d/entity @conn [:db/ident "uuid1"]))
;; => {:prop1 1, :prop2 2, :db/ident "uuid1", :db/id 1}


(d/transact! conn [{:db/ident "uuid1"
                    :children [{:db/ident "uuid2"
                                :prop1 1}
                               {:db/ident "uuid3"
                                :prop2 2}]}])
(d/touch (d/entity @conn [:db/ident "uuid1"]))
;; => {:children #{#:db{:id 2} #:db{:id 3}}, :prop1 1, :prop2 2, :db/ident "uuid1", :db/id 1}

(d/touch (d/entity @conn [:db/ident "uuid2"]))
;; => {:prop1 1, :db/ident "uuid2", :db/id 2}

(d/touch (d/entity @conn [:db/ident "uuid3"]))
;; => {:prop2 2, :db/ident "uuid3", :db/id 3}

Filipe Silva13:05:43

I used to do this with a custom :block/uid prop but I've since learned that :db/ident seems to already be unique

Filipe Silva13:05:16

you can also query directly for the unique prop with the ref lookup [:db/ident "whatever"] on pull/entity/etc

Braden Shepherdson14:05:17

yes, I'm familiar with that approach. it's working well, I was just wondering if there was a slick way to hook in to attach a UUID to every entity, or nearly every entity. it's working fine, so no worries now.