Comments (7)
@huahaiy adapted Entity to allow assoc
, dissoc
, add
and retract
. This works by adding a immutable "stage" to Entity that does nothing until the Entity is transacted. For example:
(-> (entity @conn [:user/handle "ava"])
(assoc :user/age 42))
; => {:db/id 1, :<STAGED> #:user{:age [{:op :assoc} 42]}}
I like this interface because it is fully backwards compatible. Entity still works read-only but can optionally act as an immutable stage until transacted.
A lot of the code has been copied from datalevin.impl.entity
to match the new Entity type. However, there are maybe only 50 lines of code that are actually new.
Here's a thorough API example:
(def db-path "data/lab/entity-db")
(def schema
{:user/handle #:db {:valueType :db.type/string
:unique :db.unique/identity}
:user/address #:db{:valueType :db.type/ref
:cardinality :db.cardinality/one}
:address/street #:db{:valueType :db.type/string}
:user/friends #:db{:valueType :db.type/ref
:cardinality :db.cardinality/many}})
(def conn
(d/create-conn db-path schema))
(transact! conn [{:user/handle "ava"
:user/friends [{:user/handle "fred"}
{:user/handle "jane"}]}])
;; *** Simple example
(let [ava-with-age (-> (entity @conn [:user/handle "ava"])
(assoc :user/age 42))]
#spy/c (entities->txs [ava-with-age])
;; => [[:db/add 1 :user/age 42]]
(transact! conn [ava-with-age])
)
;; *** Nested entities must be transacted separately
(let [{:keys [user/friends] :as ava}
(update (entity @conn [:user/handle "ava"]) :user/age inc)
fred (some
#(when (= (:user/handle %) "fred") %)
friends)
bestie (assoc fred :bestie? true)]
#spy/c (entities->txs [ava bestie])
;; => [[:db/add 1 :user/age 43] [:db/add 2 :bestie? true]]
(transact! conn [ava bestie]))
;; *** `add` and `retract` are directly defined on entity
;; they differ from assoc/dissoc in that they do not overwrite
;; the attr's values
(let [ava (entity @conn [:user/handle "ava"])
fred (some
#(when (= (:user/handle %) "fred") %)
(:user/friends ava))]
#spy/c (entities->txs [(retract ava :user/friends fred)])
;; => [[:db/retract 1 :user/friends 2]]
(transact! conn [(retract ava :user/friends fred)])
)
All the code is here: https://github.com/den1k/stuffs/blob/main/src/stuffs/datalevin/entity.clj
Thoughts?
from datalevin.
I am all for programmer convenience (that's the whole point of this project), so I think this could be a nice addition. Would you mind sending a PR with some tests as well. Thanks a lot!
from datalevin.
This also possibly unlocks use of libraries like specter or meander to update entities.
from datalevin.
Sounds like a good idea. Could you elaborate a bit more on how specter or meander could be used to update entities if the change is made?
from datalevin.
Could you elaborate a bit more on how specter or meander could be used to update entities if the change is made?
Here's a basic example with specter based on the entities above:
(use 'com.rpl.specter)
(def ent-as-map-recursive
{:db/id 1
:user/email "[email protected]"
:user/friends #{{:db/id 2
:user/email "[email protected]"} {:db/id 3}}})
(setval [:user/friends ALL :user/email (pred #(= % "[email protected]"))]
"[email protected]"
ent-as-map-recursive)
; =>
{:db/id 1,
:user/email "[email protected]",
:user/friends #{{:db/id 2, :user/email "[email protected]"} {:db/id 3, :user/email nil}}}
(setval [:user/friends ALL :user/email (pred #(= % "[email protected]"))]
"[email protected]"
(d/entity @conn 1)))
; =>
; Execution error (UnsupportedOperationException) at datalevin.impl.entity.Entity/assoc (entity.cljc:47).
; null
from datalevin.
I wonder how to deal with deeply nested, or even recursively nested entities?
from datalevin.
Yes, I started pondering that as well. I actually think that turning the Entity into a map is a bad idea. But we could possible extend the Entity type to hold a change-set of updates to the entity similar to the cache of touched
.
The key is that the, sic, key
is known, e.g. for
(update (d/entity @conn 1) :user/email str/upper-case)
we know that only the value for :user/email
changed, so we can translate that into [:db/add 1 :user/email "[email protected]]
or {:db/id 1 :user/email "[email protected]}
for the transaction, while ignoring unchanged keys and values.
This would be similar for nested values. Non-updated entities are treated as eids and updated ones contain a minimal change-set derived from assoc/update calls.
from datalevin.
Related Issues (20)
- `:xform` not being called for `:cardinality/one` ref attributes in pull expression HOT 1
- Error transacting db.type/bigdec
- :validate-data? does not recognise homogeneous tuple :db/tupleType HOT 1
- DB cannot be started HOT 2
- deal with client calls when server search engine is timed out
- Datalevin doesn't play well with sci (through malli) HOT 6
- value unification in function expression result
- [QUESTION]: separate the client into a small lib? HOT 23
- Allow indexing of arbitrary data
- Bump taoensso/encore is breaking datalevin HOT 4
- Options not recovered after automatic reconnection HOT 2
- Return maps
- Staged entity transactions broken for refs HOT 2
- Contribution Instructions out of date? HOT 3
- TTL for key-value store
- Materialized views
- orm.lmdbjava.Dbi$MapResizedException on query after reimport 0.8.16 → 0.9.5 HOT 1
- Hard fail when upgrading to 0.9.0 from 0.8.29 HOT 6
- Port Jepsen Test for Datomic
- Use CVM algorithm for faster cardinality estimation
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from datalevin.