-
Notifications
You must be signed in to change notification settings - Fork 312
Tips & tricks
As of 0.11.6, DataScript does not support idents (this may change in the future). Instead, if you need to represent enums, use keywords as values. E.g. instead of
;; setting attribute type to reference
@(datomic.api/transact conn [[:db/add <attr> :db/valueType :db.type/ref]])
;; registering enum value
@(datomic.api/transact conn [[:db/add #db/id[:db.part/db] :db/ident :some-enum-value]])
;; transacting data
@(datomic.api/transact conn [[:db/add <eid> <attr> :some-enum-value]])just do
(datascript/transact! conn [[:db/add <eid> <attr> :some-enum-value]])Sometimes you may need idents anyway. Say, you want to transparently stream Datomic data to client without any additional conversion. Or you want to specify :db/ident on some special entities and reference them in transactions later.
At the moment, you can create :db/ident as a regular attribute on your own, and reference special entities with idents via lookup refs:
;; registering in schema
(def schema {:db/ident {:db/unique :db.unique/identity}})
;; specifying ident on an entity
(datascript/transact! conn [[:db/add <eid> :db/ident :unique-key]])
;; looking up entity by ident value
(datascript/entity @conn [:db/ident :unique-key])
(datascript/transact! conn [[:db/add [:db/ident :unique-key] <attr> <value>]])
(datascript/q '[:find ?a ?v
:where [[:db/ident :unique-key] ?a ?v]]
@conn)Unlike Datomic, in DataScript schema is just a map kept separate from database datoms. It means you cannot query it as you would in Datomic, but you can query it just as any other collection. Use [[?attr [[?aprop ?avalue] ...]] ...] destructuring form to convert nested maps into a flat collection of facts.
For example, following query will return all datoms from a DB which attribute has cardinality many:
(def schema
{ :entry/id {:db/unique :db.unique/identity}
:entry/child {:db/cardinality :db.cardinality/many
:db/valueType :db.type/ref}
:entry/first-child {:db/valueType :db.type/ref} })
(def db (-> (datascript/empty-db schema)
(datascript/db-with [[:db/add 1 :entry/id "a"]
[:db/add 1 :entry/child 2]
[:db/add 1 :entry/child 3]
[:db/add 1 :entry/first-child 2]])))
(datascript/q '[:find ?entity ?attr ?value
:in $ [[?attr [[?aprop ?avalue] ...]] ...]
:where [(= ?avalue :db.cardinality/many)]
[?entity ?attr ?value]]
db (:schema db))
=> #{[1 :entry/child 3] [1 :entry/child 2]}DataScript DB support EDN serialization natively:
(pr-str (datascript/empty-db)) => "#datascript/DB {:schema {} :datoms[]}"Do read database from a reader,
In Clojure use datascript/data-readers:
(clojure.edn/read-string
{:readers datascript/data-readers}
"#datascript/DB {:schema {} :datoms[]}")In ClojureScript #datascript/DB handler is installed globally when using DataScript, so read-string just works:
(cljs.reader/read-string "#datascript/DB {:schema {} :datoms[]}")datascript/filter filters individual datoms, but what if you want to keep whole entities? For example, you want to filter a DB leaving out only persons whose name is "Ivan":
(d/filter db
(fn [db datom]
(or
;; leaving all datoms that are not about :person/* as-is
(not= "person" (namespace (:a datom)))
;; for :person/* attributes take entity id
;; and check :person/name on that entity using db value
(let [eid (:e datom)
entity (d/entity db eid)]
(= "Ivan" (:person/name entity))))))Until DataScript supports not clause, use a combination of get-else and nil? predicate to find out entities which do not have some attribute:
(datascript/q '[:find ?e
:where [?e :document/id _]
[(get-else $ ?e :document/users nil) ?u]
[(nil? ?u)]]
db)If you have a big database and want to render 10 first posts on a page, instead of doing a query (which will have to materialize everything you have in a result set), use index directly:
(take 10 (d/datoms :avet :post/timestamp))This will return 10 datoms with the smallest :post/timestamp value. You can then (mapv :e ...) to get their entity ids and pass them down to a query/pull-many call.
Index is already sorted and stored in ascending order. (take 10 ...) will return 10 smallest values very fast.
To get 10 biggest values, reverse the index:
(take 10 (reverse (d/datoms :avet :post/timestamp)))Reverse return a special view on an index that allows walking it in the reverse direction. This operation is allocation free and about as fast as direct index walking.