Files
net-deertopia/src/net/deertopia/doerg/roam.clj
Madeleine Sydney Ślaga 6e9531f944
Some checks failed
build / build (push) Failing after 36s
refactor: doerg는 publisher와 결합
2026-04-03 13:21:00 -06:00

184 lines
5.0 KiB
Clojure
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
(ns net.deertopia.doerg.roam
(:require [babashka.fs :as fs]
[net.deertopia.doerg.config :as cfg]
[net.deertopia.doerg.elisp :as elisp]
[net.deertopia.doerg.slug :as slug]
[next.jdbc :as sql])
(:import (java.util UUID)))
;;; Global database
(defonce ^:dynamic *use-db-cache?* true)
(defn ds []
(sql/get-datasource
{:dbtype "sqlite"
:dbname (-> cfg/*cfg* ::cfg/org-roam-db-path str)}))
;;; Elisp sexp (de)serialisation
(defn id [node]
(-> node :id))
(defn slug [node]
(-> node :id slug/from-uuid))
(defn- print-id [node]
(-> node id elisp/print))
;;; Node
(defrecord Node [id cache])
(defn uuid-exists? [uuid]
(sql/execute-one! (ds)
["select 1 from nodes where id = ? limit 1"
(-> uuid str elisp/print)]))
(defn make-node
([uuid] (make-node uuid {}))
([uuid props]
(and (uuid-exists? uuid)
(->Node uuid (atom props)))))
(defn- fetch-with-cache [node field fetch]
(if *use-db-cache?*
(-> (:cache node)
(swap! (fn [cache]
(update cache field #(or % (fetch node)))))
(get field))
(fetch node)))
(defn org-file [node]
(fetch-with-cache
node :org-file
(fn [node]
(when-some [r (sql/execute-one!
(ds)
["select file from nodes where id = ?"
(-> node :id str elisp/print)])]
(-> r :nodes/file elisp/read-string)))))
(defn title [node]
(fetch-with-cache
node :title
#(when-some [r (sql/execute-one!
(ds)
["select title from nodes where id = ?"
(print-id %)])]
(-> r :nodes/title elisp/read-string))))
(defprotocol GetNode
(get-node [this]
"Return the node associated with `this` or nil."))
(extend-protocol GetNode
String
(get-node [this]
(or (some-> this slug/from-string get-node)
(some-> this parse-uuid get-node)
(throw (IllegalArgumentException.
"Give `get-node` a UUID or slug string plz. }:)"))))
java.util.UUID
(get-node [this]
(make-node this))
net.deertopia.doerg.slug.Slug
(get-node [this]
(-> this slug/to-uuid make-node))
Node
(get-node [this]
this))
(comment
(def node (get-node "68XqhHerTWCbE--RYLEdHw"))
(fetch-with-cache
node :title
#(do (println "fetch")
(sql/execute-one! (ds) ["select title from nodes where id = ?"
(elisp/print (:id %))]))))
;;; Node operations
(defn level [node]
(fetch-with-cache
node :level
#(-> (sql/execute-one!
(ds) ["select level from nodes where id = ?"
(print-id %)])
:nodes/level)))
(defn top-level? [node]
(zero? (level node)))
(defn file [node]
(fetch-with-cache
node :file
#(-> (sql/execute-one!
(ds) ["select file from nodes where id = ?"
(print-id %)])
:nodes/file
elisp/read-string)))
(defn properties [node]
(fetch-with-cache
node :properties
#(-> (sql/execute-one!
(ds) ["select properties from nodes where id = ?"
(print-id %)])
:nodes/properties
elisp/read-alist)))
(defn public? [node]
(-> node properties (get "DEERTOPIAVISIBILITY") (= "public")))
(defn graph-visible? [node]
(#{"public" "graphonly"}
(-> node properties (get "DEERTOPIAVISIBILITY"))))
(defn backlinks
"Returns a collection of nodes linking to `node`."
[node]
(for [{id :nodes/id title :nodes/title}
(sql/execute! (ds) ["select distinct nodes.id, nodes.title from links
inner join nodes
on nodes.id = links.source
where links.dest = ?"
(elisp/print (str (:id node)))])
:let [id' (elisp/read-string id)]
:when (-> id' parse-uuid get-node public?)]
(make-node id' {:title (elisp/read-string title)})))
;;; Graph support
(defn- read-string-field [n field]
(-> n (get field) elisp/read-string))
(defn- uuid-graph-visible? [uuid]
(-> uuid parse-uuid get-node graph-visible?))
(defn get-graph []
(let [nodes (sql/execute! (ds) ["select id, title from nodes"])
links (sql/execute!
(ds)
["select n1.id as source, nodes.id as target from
((nodes as n1) join links on n1.id = links.source)
join (nodes as n2) on links.dest = nodes.id
where links.type = '\"id\"'"])]
{:nodes (for [n nodes
:let [id (read-string-field n :nodes/id)]
:when (uuid-graph-visible? id)]
{:id id
:title (read-string-field n :nodes/title)})
:links (for [l links
:let [source (read-string-field l :nodes/source)
target (read-string-field l :nodes/target)]
:when (and (uuid-graph-visible? source)
(uuid-graph-visible? target))]
{:source source
:target target})}))