-
-
Notifications
You must be signed in to change notification settings - Fork 311
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Transactable entities #389
Comments
Have you considered using |
Sure, I'm familiar with pull but it's slower and not dynamic in the sense that relationships can be traversed downstream. |
The main problem with transacting entities is exactly dynamic relationships. How far to transfer? If you want to transact entities as refs, just As to turning entities to map, this is not exactly a great pattern. That way you’ll be transacting way more than you need, e.g. to change one attribute you’ll be transacting the whole map. This is usually not a great idea. Much better idea is to control exactly each one of the attributes you transact, not “I’ve got some map, figure out what’s different and do what you need to do”. Datomic, as far as I understand (and I agree with it here), makes a point of separating reads and writes. Read as much as you like, transact only what’s needed. |
I see what you mean here. For example, a deep
True, and this has been what I've been doing but it gets hairy for nested entities.
|
Deep assoc-in would be very confusing and error-prone |
Hmm, okay the following is simpler and would work better:
(assoc (d/entity @conn 1) :foo "bar")
; =>
{:db/id 1 :foo "bar"} ; not transacting the whole maps with extra key values and nested relationships
(update-in (d/entity @conn 1) [:user/friends 2] :user/email clojure.string/upper-case)
; returns hash-map
{:db/id 1
:user/friends [{:db/id 2} ; <- Entity
{:db/id 3 :user/email "[email protected]"} ; <- hash-map
]} However, the examples above break down on multiple updates, e.g.
It would be possible to do this however, with a special data-structure that keeps a reference to the original Entity as well as the set of changes. This is made possible through specific implementations of assoc on this new Type |
Sorry, but it feels like a lot of special casing and non-standard behavior of standard clojure methods. I’ve worked with DataScript quite a bit and never felt the urge to transact entities for any reason. I trust you that you have your pains with it, but I just can’t see them (I’m trying) |
All good. I might be able to implement this myself. Will keep you posted. Thank you for discussing the idea. |
FYI @tonsky implemented this for Datalevin. It preserves all Entity functionality and is fully backwards compatible: juji-io/datalevin#48 (comment) What do you think? |
Sound idea, but I still don’t see why this is needed? Building a proper transactions seems much more obvious and straightforward than transacting an opaque map that only transacts part of its attributes. |
In my experience it eliminates a lot of code. For example, in a UI that uses Entity and passes down its ref entities through a component tree it's nice to be able to immutably assoc/dissoc/add/retract and finally transact! instead of building up verbose transaction vectors. Especially removing an attributes or a ref under an attribute is verbose since one can't transact a map without a key but must use the vector form instead. |
This is a backwards compatible change that would vastly improve DX in my opinion
I've always wondered: why are entities not transactable? I find myself converting entities to maps all the time solely to transact them. This still causes problems when entities nest other entities. So here are a few simple ideas on how entities could be treated in transactions:
1. Entities could be treated as refs in transactions
Now I convert it to a map
I try to transact it
So I can either dissoc the
:user/friends
map-entry or convert contained entities to refsWe could spare ourselves from this by treating entities as refs in transactions. The database already walks nested data-structures to resolve refs so why not resolve entities as refs, also?
2. Entities to return maps on update
datascript.impl.entity/Entity
implementsclojure.lang.Associative
which currently only throws errors:Instead
assoc
could return a hashmapThis would also make
update
possible. Together this means that the change of email toent
from above, could look like this:I would've already implemented this for my own projects but unfortunately Clojure (unlike ClojureScript) doesn't allow to overwrite a Type's methods. To achieve this one would have to for DataScript and change the code of
datascript.impl.entity/Entity
so I wanted to raise the issue here first and see what @tonsky's thoughts are.This would also unlock a more straightforward use of libraries like meander or specter to walk and update entities.
The text was updated successfully, but these errors were encountered: