Overthunk

RTFM and another PR bites the dust

Jul 22, 2020


Contents

Expectations

After finishing Brave Clojure yesterday, I wanted to focus on the Athens issue - https://github.com/athensresearch/athens/issues/292 today and potentially open up a PR for it.

What I learned

Adrien, Michael and I were able to successfully open the PR today. Let’s look at the process that led us to submitting the PR and the travails along the way.

I wrote a helper function that performs a DataScript query to get the database id from a uid -

(defn get-id
  [uid]
  (-> (d/q '[:find ?id
             :in $ ?uid
             :where [?id :block/uid ?uid]]
           @dsdb
           uid)
      ffirst))

We can use this function inside the get-children-recursively.

(defn get-children-recursively
  "Get list of children UIDs for given block ID (including the root block's UID)"
  [uid]
  (let [document (->> @(pull dsdb '[:block/order :block/uid {:block/children ...}] (get-id uid)))]
    (->> (tree-seq :block/children :block/children document)
         (map :block/uid))))

Now in events.cljs, Adrien had worked on creating an event handler to delete a page. We can use the list of children uids from get-children-recursively inside this event handler to retract each child.

(reg-event-fx
  :page/delete
  (fn [_ [_ uid]]
    {:transact! (vec (map (fn [uid] [:db/retract [:block/uid uid] :block/uid]) (get-children-recursively uid)))}))

DataScript’s transact! requires a vector of vector to work on. So even if we wanted to retract a single entity, we’d have to nest it within a vector. In this case we’re working on many items so we create a nested vector to batch the retract operation.

However, using retract on an entity and an attr only retracts that specific attribute and not the whole entity.

To retract an entity, we have to use the aptly named retractEntity function present in DataScript. Now we ran into an interesting issue where after reading the datomic docs on retractEntity, we concluded that it would also recursively retract all child entities. But this didn’t turn out to be the case.

The solution was to use the retractEntity function inside each sub-vector in :page/delete.

(reg-event-fx
  :page/delete
  (fn [_ [_ uid]]
    {:transact! (vec (map (fn [uid] [:db/retractEntity [:block/uid uid]]) (get-children-recursively uid)))}))

Takeaways

The clear and absolute takeaway from the last two days is to RTFM. At least twice, we’ve run into issues where we spent time trying to code up a solution only to find an out of the box solution waiting for us in either the core lib or the documentation.

Besides that, it does feel great to work closely with Adrien and Michael in solving these issues, making PRs and doing our part in making Athens into a fully functional application.