graph.cljs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. (ns frontend.handler.graph
  2. (:require [clojure.set :as set]
  3. [clojure.string :as string]
  4. [frontend.db :as db]
  5. [logseq.graph-parser.db.default :as default-db]
  6. [frontend.state :as state]
  7. [frontend.util :as util]))
  8. (defn- build-links
  9. [links]
  10. (map (fn [[from to]]
  11. {:source from
  12. :target to})
  13. links))
  14. (defn- build-nodes
  15. [dark? current-page page-links tags nodes namespaces]
  16. (let [parents (set (map last namespaces))
  17. current-page (or current-page "")
  18. pages (set (flatten nodes))]
  19. (->>
  20. pages
  21. (remove nil?)
  22. (mapv (fn [p]
  23. (let [p (str p)
  24. current-page? (= p current-page)
  25. color (case [dark? current-page?] ; FIXME: Put it into CSS
  26. [false false] "#999"
  27. [false true] "#045591"
  28. [true false] "#93a1a1"
  29. [true true] "#ffffff")
  30. color (if (contains? tags p)
  31. (if dark? "orange" "green")
  32. color)
  33. n (get page-links p 1)
  34. size (int (* 8 (max 1.0 (js/Math.cbrt n))))]
  35. (cond->
  36. {:id p
  37. :label p
  38. :size size
  39. :color color}
  40. (contains? parents p)
  41. (assoc :parent true))))))))
  42. ;; slow
  43. (defn- uuid-or-asset?
  44. [id]
  45. (or (util/uuid-string? id)
  46. (string/starts-with? id "../assets/")
  47. (= id "..")
  48. (string/starts-with? id "assets/")
  49. (string/ends-with? id ".gif")
  50. (string/ends-with? id ".jpg")
  51. (string/ends-with? id ".png")))
  52. (defn- remove-uuids-and-files!
  53. [nodes]
  54. (remove
  55. (fn [node] (uuid-or-asset? (:id node)))
  56. nodes))
  57. (defn- normalize-page-name
  58. [{:keys [nodes links page-name->original-name]}]
  59. (let [links (->>
  60. (map
  61. (fn [{:keys [source target]}]
  62. (let [source (get page-name->original-name source)
  63. target (get page-name->original-name target)]
  64. (when (and source target)
  65. {:source source :target target})))
  66. links)
  67. (remove nil?))
  68. nodes (->> (remove-uuids-and-files! nodes)
  69. (util/distinct-by (fn [node] (:id node)))
  70. (map (fn [node]
  71. (if-let [original-name (get page-name->original-name (:id node))]
  72. (assoc node :id original-name :label original-name)
  73. nil)))
  74. (remove nil?))]
  75. {:nodes nodes
  76. :links links}))
  77. (defn build-global-graph
  78. [theme {:keys [journal? orphan-pages? builtin-pages?]}]
  79. (let [dark? (= "dark" theme)
  80. current-page (or (:block/name (db/get-current-page)) "")]
  81. (when-let [repo (state/get-current-repo)]
  82. (let [relation (db/get-pages-relation repo journal?)
  83. tagged-pages (db/get-all-tagged-pages repo)
  84. namespaces (db/get-all-namespace-relation repo)
  85. tags (set (map second tagged-pages))
  86. full-pages (db/get-all-pages repo)
  87. get-original-name (fn [p] (or (:block/original-name p) (:block/name p)))
  88. all-pages (map get-original-name full-pages)
  89. page-name->original-name (zipmap (map :block/name full-pages) all-pages)
  90. pages-after-journal-filter (if-not journal?
  91. (remove :block/journal? full-pages)
  92. full-pages)
  93. links (concat (seq relation)
  94. (seq tagged-pages)
  95. (seq namespaces))
  96. linked (set (flatten links))
  97. build-in-pages (set (map string/lower-case default-db/built-in-pages-names))
  98. nodes (cond->> (map :block/name pages-after-journal-filter)
  99. (not builtin-pages?)
  100. (remove (fn [p] (contains? build-in-pages (string/lower-case p))))
  101. (not orphan-pages?)
  102. (filter #(contains? linked (string/lower-case %))))
  103. page-links (reduce (fn [m [k v]] (-> (update m k inc)
  104. (update v inc))) {} links)
  105. links (build-links (remove (fn [[_ to]] (nil? to)) links))
  106. nodes (build-nodes dark? (string/lower-case current-page) page-links tags nodes namespaces)]
  107. (normalize-page-name
  108. {:nodes nodes
  109. :links links
  110. :page-name->original-name page-name->original-name})))))
  111. (defn build-page-graph
  112. [page theme]
  113. (let [dark? (= "dark" theme)]
  114. (when-let [repo (state/get-current-repo)]
  115. (let [page (util/page-name-sanity-lc page)
  116. page-entity (db/entity [:block/name page])
  117. tags (:tags (:block/properties page-entity))
  118. tags (remove #(= page %) tags)
  119. ref-pages (db/get-page-referenced-pages repo page)
  120. mentioned-pages (db/get-pages-that-mentioned-page repo page)
  121. namespaces (db/get-all-namespace-relation repo)
  122. links (concat
  123. namespaces
  124. (map (fn [[p _aliases]]
  125. [page p]) ref-pages)
  126. (map (fn [[p _aliases]]
  127. [p page]) mentioned-pages)
  128. (map (fn [tag]
  129. [page tag])
  130. tags))
  131. other-pages (->> (concat (map first ref-pages)
  132. (map first mentioned-pages))
  133. (remove nil?)
  134. (set))
  135. other-pages-links (mapcat
  136. (fn [page]
  137. (let [ref-pages (-> (map first (db/get-page-referenced-pages repo page))
  138. (set)
  139. (set/intersection other-pages))
  140. mentioned-pages (-> (map first (db/get-pages-that-mentioned-page repo page))
  141. (set)
  142. (set/intersection other-pages))]
  143. (concat
  144. (map (fn [p] [page p]) ref-pages)
  145. (map (fn [p] [p page]) mentioned-pages))))
  146. other-pages)
  147. links (->> (concat links other-pages-links)
  148. (remove nil?)
  149. (distinct)
  150. (build-links))
  151. nodes (->> (concat
  152. [page]
  153. (map first ref-pages)
  154. (map first mentioned-pages)
  155. tags)
  156. (remove nil?)
  157. (distinct))
  158. nodes (build-nodes dark? page links (set tags) nodes namespaces)
  159. full-pages (db/get-all-pages repo)
  160. get-original-name (fn [p] (or (:block/original-name p)
  161. (:block/name p)))
  162. all-pages (map get-original-name full-pages)
  163. page-name->original-name (zipmap (map :block/name full-pages) all-pages)]
  164. (normalize-page-name
  165. {:nodes nodes
  166. :links links
  167. :page-name->original-name page-name->original-name})))))
  168. (defn build-block-graph
  169. "Builds a citation/reference graph for a given block uuid."
  170. [block theme]
  171. (let [dark? (= "dark" theme)]
  172. (when-let [repo (state/get-current-repo)]
  173. (let [ref-blocks (db/get-block-referenced-blocks block)
  174. namespaces (db/get-all-namespace-relation repo)
  175. links (concat
  176. (map (fn [[p _aliases]]
  177. [block p]) ref-blocks)
  178. namespaces)
  179. other-blocks (->> (concat (map first ref-blocks))
  180. (remove nil?)
  181. (set))
  182. other-blocks-links (mapcat
  183. (fn [block]
  184. (let [ref-blocks (-> (map first (db/get-block-referenced-blocks block))
  185. (set)
  186. (set/intersection other-blocks))]
  187. (concat
  188. (map (fn [p] [block p]) ref-blocks))))
  189. other-blocks)
  190. links (->> (concat links other-blocks-links)
  191. (remove nil?)
  192. (distinct)
  193. (build-links))
  194. nodes (->> (concat
  195. [block]
  196. (map first ref-blocks))
  197. (remove nil?)
  198. (distinct)
  199. ;; FIXME: get block tags
  200. )
  201. nodes (build-nodes dark? block links #{} nodes namespaces)]
  202. (normalize-page-name
  203. {:nodes nodes
  204. :links links})))))
  205. (defn n-hops
  206. "Get all nodes that are n hops from nodes (a collection of node ids)"
  207. [{:keys [links] :as graph} nodes level]
  208. (let [search-nodes (fn [forward?]
  209. (let [links (group-by (if forward? :source :target) links)]
  210. (loop [nodes nodes
  211. level level]
  212. (if (zero? level)
  213. nodes
  214. (recur (distinct (apply concat nodes
  215. (map
  216. (fn [id]
  217. (->> (get links id) (map (if forward? :target :source))))
  218. nodes)))
  219. (dec level))))))
  220. nodes (concat (search-nodes true) (search-nodes false))
  221. nodes (set nodes)]
  222. (update graph :nodes
  223. (fn [full-nodes]
  224. (filter (fn [node] (contains? nodes (:id node)))
  225. full-nodes)))))