Skip to content
Nikita Prokopov edited this page Jul 14, 2015 · 23 revisions

Querying a schema

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]}

EDN serialization

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 by default, just use

(cljs.reader/read-string "#datascript/DB {:schema {} :datoms[]}")

Filtering database entities

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))))))
Clone this wiki locally