Search code examples
clojurescriptdatalogdatascript

In Datascript, how can I create a new attribute from the value of another?


Trying to create a new attribute where the value is the result of a transformation on a different attribute for the same entity. So, say I have a database where every entity has attribute :content. I want to have an additional attribute for every entity called :transformed whose value is the result of applying function f to :content. How would I go about this idiomatically and efficiently? Currently trying to do this by performing a transaction and assigning the value of the new attribute to the value of the function applied to a query for the value of the original attribute for that entity.

If it's not obvious, I'm fairly new to Datalog and Datascript

(doseq [included-block-ds-id (vec (ds/q '[:find ?id
                                          :where
                                          [?id :block/included true]]
                                        @conn))]
  (let [content (first (first (vec (ds/q '[:find ?content
                                           :where
                                           [?included-block-ds-id :block/content ?content]]
                                         @conn))))]
    (ds/transact! conn [[:db/add (first included-block-ds-id)
                         :block/hiccup (block-content->hiccup
                                        conn
                                        content)]])))

Solution

  • In general your code is correct, yet not optimal performance-wise. You can use single query to retrieve all id-content pairs. Then build a single transaction out of them using for. Then transact it all at once.

    (let [db @conn
          id+content (ds/q '[:find ?id ?content
                             :where [?id :block/included true]
                                    [?id :block/content ?content]]
                       db)
          tx (for [[id content] id+content]
               [:db/add id :block/hiccup (block-content->hiccup db content)])]
      (ds/transact! conn tx))
    

    Notice that it’s in general a good idea to take an immutable value of your database at some point in time and then do all calculations based on it (e.g. in passing to block-content->hiccup function). Only pass conn when you need function to alter the db.

    You don’t need it in this case, but in general, if you only need to look up attributes on a single entity, it’s much more efficient to use ds/entity instead of query:

    (:block/content (d/entity db id))