Fork me on GitHub
#datomic
<
2023-06-15
>
s-ol16:06:47

I'm surprised that (d/transact) doesn't cast datomic.query.EntityMap to entity ids automatically, wouldn't that just make sense?

s-ol16:06:09

since they are immutable, processing them as normal maps doesn't do any good and you'd only ever use them as references anyway

Keith18:06:47

Hello @U05AL1ZH8TW, can you help me better understand the scenario you're describing here? What are you trying to do that you either can't do, or feel like you have to work around?

s-ol14:06:17

@U424XHTGT I want to make a db transaction related to an entity and some of its "neighbours". The EntityMap is a comfortable way to implement my business logic and allows me to walk the graph as I need to dynamically, and this is a rare action so I'm not concerned at all with the fact that using a very complex to write query could give me this data in a more performant way

s-ol14:06:57

in particular, the EntityMap is convenient because I can do conditional recursion/relationships, whereas with :find (pull) I have to go 'all or nothing', and there is a 'separation of concerns' where I don't have to adjust the query to fetch exactly the right data that my business logic will need, the business logic can just walk the graph and data is streamed in as necessary

s-ol14:06:02

so that's all good, and I can easily and idiomatically build up a new entity to transact into the database using the map-form of (d/transact) . But if I want to transact a relationship to an entity I pulled out of the EntityMap I have to get its :db/id . As soon as I do that, the data of that entity becomes inaccessible, which means I can't use it for further business logic

s-ol14:06:52

to be a bit more concrete: I have a function that is supposed to take a business-logic entity "tx" and "complete" it by fetching some of its relationships, doing some calculations and then, if necessary, adding some more data to it

s-ol14:06:49

I need to call this function with existing "tx" entities that I have fetched from the DB, but also with new "tx" entities prior to insertion into the DB. New "tx"s can contain :tx/move relationships to existing entities, or to plain hashmaps with a tempid that are supposed to be inserted together with the "tx"

s-ol14:06:03

TL;DR: at the most basic, I was expecting something like this to work:

(let [sol (d/entity (d/db conn) [:user/name "s-ol"])]
  (d/transact conn [{:user/name "test" :user/friend sol}])

Keith20:06:58

Thanks for the descriptive response, the context helps a lot. Perhaps your example you provided is simpler than reality, but are you aware that you can https://docs.datomic.com/pro/schema/identity.html#lookup-refs? If you were to pull the entire entity (or some subset of it) prior to transacting, you introduce an inherent race condition wherein you might overwrite updated facts about the entity that were asserted by other callers between the time you called d/entity and the time you called d/transact.

(let [sol (d/entity (d/db conn) [:user/name "s-ol"])]
  (d/transact conn [{:user/name "test" :user/friend sol}]))
would become
(d/transact conn [{:user/name "test" :user/friend [:user/name "s-ol"]}])

s-ol07:06:45

@U424XHTGT yeah, at the moment I have a function that swaps the entities for lookup refs before transacting, but they are nested inside my actual data in the real use case and I can't do it when I'm creating that structure because I need to access attributes on those entities for my application-logic processing prior to insertion

s-ol07:06:49

I wouldn't have expected my example to re-assert the facts sol contains, but I see how one might be lead to believe that because it looks like a regular map

s-ol07:06:29

instead I would've assumed it to transform into either an entity id or a lookup reference automatically on insertion, since it is a readonly reference to data that already exists (or existed*) in the database

s-ol07:06:58

"existed" because of the race condition, but in my mind that race condition is there all the same if you use (d/q) to fetch information for your processing