model.cljs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968
  1. (ns frontend.db.model
  2. "Core db functions."
  3. ;; TODO: Remove this config once how repos are passed to this ns are standardized
  4. {:clj-kondo/config {:linters {:unused-binding {:level :off}}}}
  5. (:require [clojure.set :as set]
  6. [clojure.string :as string]
  7. [clojure.walk :as walk]
  8. [datascript.core :as d]
  9. [frontend.config :as config]
  10. [frontend.date :as date]
  11. [frontend.db.conn :as conn]
  12. [frontend.db.react :as react]
  13. [frontend.db.utils :as db-utils]
  14. [frontend.state :as state]
  15. [frontend.util :as util :refer [react]]
  16. [logseq.common.util :as common-util]
  17. [logseq.common.util.date-time :as date-time-util]
  18. [logseq.db :as ldb]
  19. [logseq.db.frontend.content :as db-content]
  20. [logseq.db.frontend.rules :as rules]
  21. [logseq.graph-parser.db :as gp-db]))
  22. ;; TODO: extract to specific models and move data transform logic to the
  23. ;; corresponding handlers.
  24. (def file-graph-block-attrs
  25. "In file graphs, use it to replace '*' for datalog queries"
  26. '[:db/id
  27. :block/uuid
  28. :block/parent
  29. :block/order
  30. :block/collapsed?
  31. :block/format
  32. :block/refs
  33. :block/_refs
  34. :block/path-refs
  35. :block/tags
  36. :block/link
  37. :block/title
  38. :block/marker
  39. :block/priority
  40. :block/properties
  41. :block/properties-order
  42. :block/properties-text-values
  43. :block/pre-block?
  44. :block/scheduled
  45. :block/deadline
  46. :block/repeated?
  47. :block/created-at
  48. :block/updated-at
  49. ;; TODO: remove this in later releases
  50. :block/heading-level
  51. :block/file
  52. :logseq.property/parent
  53. {:block/page [:db/id :block/name :block/title :block/journal-day]}
  54. {:block/_parent ...}])
  55. (def hidden-page? ldb/hidden?)
  56. (defn get-all-tagged-pages
  57. [repo]
  58. (d/q '[:find ?page ?tag
  59. :where
  60. [?page :block/tags ?tag]]
  61. (conn/get-db repo)))
  62. (defn get-all-pages
  63. [repo]
  64. (when-let [db (conn/get-db repo)]
  65. (ldb/get-all-pages db)))
  66. (defn get-all-page-titles
  67. [repo]
  68. (->> (get-all-pages repo)
  69. (map :block/title)))
  70. (defn get-alias-source-page
  71. "return the source page of an alias"
  72. [repo alias-id]
  73. (when-let [db (conn/get-db repo)]
  74. (ldb/get-alias-source-page db alias-id)))
  75. (defn get-files-blocks
  76. [repo-url paths]
  77. (let [paths (set paths)
  78. pred (fn [_db e]
  79. (contains? paths e))]
  80. (-> (d/q '[:find ?block
  81. :in $ ?pred
  82. :where
  83. [?file :file/path ?path]
  84. [(?pred $ ?path)]
  85. [?p :block/file ?file]
  86. [?block :block/page ?p]]
  87. (conn/get-db repo-url) pred)
  88. db-utils/seq-flatten)))
  89. (defn get-file-last-modified-at
  90. [repo path]
  91. (when (and repo path)
  92. (when-let [db (conn/get-db repo)]
  93. (-> (db-utils/entity db [:file/path path])
  94. :file/last-modified-at))))
  95. (defn file-exists?
  96. [repo path]
  97. (when (and repo path)
  98. (when-let [db (conn/get-db repo)]
  99. (db-utils/entity db [:file/path path]))))
  100. (defn get-files-full
  101. [repo]
  102. (when-let [db (conn/get-db repo)]
  103. (->>
  104. (d/q
  105. '[:find (pull ?file [*])
  106. :where
  107. [?file :file/path]]
  108. db)
  109. (flatten))))
  110. (defn get-file
  111. ([path]
  112. (get-file (state/get-current-repo) path))
  113. ([repo path]
  114. (when (and repo path)
  115. (when-let [db (conn/get-db repo)]
  116. (:file/content (db-utils/entity db [:file/path path]))))))
  117. (defn get-custom-css
  118. []
  119. (when-let [repo (state/get-current-repo)]
  120. (get-file repo "logseq/custom.css")))
  121. (defn get-block-by-uuid
  122. [id]
  123. (db-utils/entity [:block/uuid (if (uuid? id) id (uuid id))]))
  124. (defn query-block-by-uuid
  125. "Return block or page entity, depends on the uuid"
  126. [id]
  127. (db-utils/pull [:block/uuid (if (uuid? id) id (uuid id))]))
  128. (defn heading-content->route-name
  129. "Converts a heading block's content to its route name. This works
  130. independent of format as format specific heading characters are stripped"
  131. [block-content]
  132. (some->> block-content
  133. (re-find #"^#{0,}\s*(.*)(?:\n|$)")
  134. second
  135. string/lower-case))
  136. (defn get-block-by-page-name-and-block-route-name
  137. "Returns first block for given page name and block's route name. Block's route
  138. name must match the content of a page's block header"
  139. [repo page-uuid-str route-name]
  140. (let [db (conn/get-db repo)]
  141. (if (config/db-based-graph? repo)
  142. (->> (d/q '[:find (pull ?b [:block/uuid])
  143. :in $ ?page-uuid ?route-name ?content-matches %
  144. :where
  145. [?page :block/uuid ?page-uuid]
  146. [?b :block/page ?page]
  147. (has-property ?b :logseq.property/heading)
  148. [?b :block/title ?content]
  149. [(?content-matches ?content ?route-name ?b)]]
  150. db
  151. (uuid page-uuid-str)
  152. route-name
  153. (fn content-matches? [block-content external-content block-id]
  154. (let [block (db-utils/entity repo block-id)
  155. ref-tags (distinct (concat (:block/tags block) (:block/refs block)))]
  156. (= (-> block-content
  157. (db-content/id-ref->title-ref ref-tags true)
  158. (db-content/content-id-ref->page ref-tags)
  159. heading-content->route-name)
  160. (string/lower-case external-content))))
  161. (rules/extract-rules rules/db-query-dsl-rules [:has-property]))
  162. ffirst)
  163. (->> (d/q '[:find (pull ?b [:block/uuid])
  164. :in $ ?page-uuid ?route-name ?content-matches
  165. :where
  166. [?page :block/uuid ?page-uuid]
  167. [?b :block/page ?page]
  168. [?b :block/properties ?prop]
  169. [(get ?prop :heading) _]
  170. [?b :block/title ?content]
  171. [(?content-matches ?content ?route-name)]]
  172. db
  173. (uuid page-uuid-str)
  174. route-name
  175. (fn content-matches? [block-content external-content]
  176. (= (heading-content->route-name block-content)
  177. (string/lower-case external-content))))
  178. ffirst))))
  179. (defn get-page-format
  180. [page-name]
  181. {:post [(keyword? %)]}
  182. (if (config/db-based-graph? (state/get-current-repo))
  183. :markdown
  184. (keyword
  185. (or
  186. (let [page (some->> page-name (ldb/get-page (conn/get-db)))]
  187. (or
  188. (get page :block/format :markdown)
  189. (when-let [file (:block/file page)]
  190. (when-let [path (:file/path (db-utils/entity (:db/id file)))]
  191. (common-util/get-format path)))))
  192. (state/get-preferred-format)
  193. :markdown))))
  194. (defn page-alias-set
  195. [repo-url page-id]
  196. (->>
  197. (ldb/get-block-alias (conn/get-db repo-url) page-id)
  198. (set)
  199. (set/union #{page-id})))
  200. (defn get-page-alias-names
  201. [repo page-id]
  202. (let [page (db-utils/entity page-id)
  203. alias-ids (->> (page-alias-set repo page-id)
  204. (remove #{page-id}))]
  205. (when (seq alias-ids)
  206. (map (fn [id] (:block/title (db-utils/entity id))) alias-ids))))
  207. (defn with-pages
  208. [blocks]
  209. (let [pages-ids (->> (map (comp :db/id :block/page) blocks)
  210. (remove nil?))
  211. pages (when (seq pages-ids)
  212. (db-utils/pull-many '[:db/id :block/name :block/title :block/journal-day] pages-ids))
  213. pages-map (reduce (fn [acc p] (assoc acc (:db/id p) p)) {} pages)
  214. blocks (map
  215. (fn [block]
  216. (assoc block :block/page
  217. (get pages-map (:db/id (:block/page block)))))
  218. blocks)]
  219. blocks))
  220. (def sort-by-order ldb/sort-by-order)
  221. (defn sub-block
  222. [id]
  223. (when-let [repo (state/get-current-repo)]
  224. (when id
  225. (let [ref (react/q repo [:frontend.worker.react/block id]
  226. {:query-fn (fn [_]
  227. (let [e (db-utils/entity id)]
  228. [e (:block/tx-id e)]))}
  229. nil)
  230. e (-> ref react first)]
  231. (when-let [id (:db/id e)]
  232. (db-utils/entity id))))))
  233. (defn sort-by-order-recursive
  234. [form]
  235. (walk/postwalk (fn [f]
  236. (if (and (map? f)
  237. (:block/_parent f))
  238. (let [children (:block/_parent f)]
  239. (-> f
  240. (dissoc :block/_parent)
  241. (assoc :block/children (sort-by-order children))))
  242. f))
  243. form))
  244. ;; File-based only
  245. ;; Diverged of get-sorted-page-block-ids
  246. (defn get-sorted-page-block-ids-and-levels
  247. "page-name: the page name, original name
  248. return: a list with elements in:
  249. :id - a list of block ids, sorted by :block/order
  250. :level - the level of the block, 1 for root, 2 for children of root, etc."
  251. [page-name]
  252. {:pre [(string? page-name)]}
  253. (let [root (ldb/get-page (conn/get-db) page-name)]
  254. (loop [result []
  255. children (sort-by-order (:block/_parent root))
  256. ;; BFS log of walking depth
  257. levels (repeat (count children) 1)]
  258. (if (seq children)
  259. (let [child (first children)
  260. cur-level (first levels)
  261. next-children (sort-by-order (:block/_parent child))]
  262. (recur (conj result {:id (:db/id child) :level cur-level})
  263. (concat
  264. next-children
  265. (rest children))
  266. (concat
  267. (repeat (count next-children) (inc cur-level))
  268. (rest levels))))
  269. result))))
  270. (defn has-children?
  271. ([block-id]
  272. (has-children? (conn/get-db) block-id))
  273. ([db block-id]
  274. (ldb/has-children? db block-id)))
  275. (defn top-block?
  276. [block]
  277. (= (:db/id (:block/parent block))
  278. (:db/id (:block/page block))))
  279. (defn get-block-parent
  280. ([block-id]
  281. (get-block-parent (state/get-current-repo) block-id))
  282. ([repo block-id]
  283. (when-let [db (conn/get-db repo)]
  284. (when-let [block (db-utils/entity db [:block/uuid block-id])]
  285. (:block/parent block)))))
  286. (defn get-block-parents
  287. [repo block-id opts]
  288. (when-let [db (conn/get-db repo)]
  289. (ldb/get-block-parents db block-id opts)))
  290. ;; Use built-in recursive
  291. (defn get-block-parents-v2
  292. [repo block-id]
  293. (d/pull (conn/get-db repo)
  294. '[:db/id :block/collapsed? {:block/parent ...}]
  295. [:block/uuid block-id]))
  296. (def get-block-last-direct-child-id ldb/get-block-last-direct-child-id)
  297. (defn get-block-deep-last-open-child-id
  298. [db db-id]
  299. (loop [node (db-utils/entity db db-id)]
  300. (if-let [last-child-id (get-block-last-direct-child-id db (:db/id node) true)]
  301. (let [e (db-utils/entity db last-child-id)]
  302. (if (or (:block/collapsed? e) (empty? (:block/_parent e)))
  303. last-child-id
  304. (recur e)))
  305. nil)))
  306. (def page? ldb/page?)
  307. (defn get-next
  308. "Get next block, either its right sibling, or loop to find its next block."
  309. [db db-id & {:keys [skip-collapsed? init?]
  310. :or {skip-collapsed? true
  311. init? true}
  312. :as opts}]
  313. (when-let [entity (db-utils/entity db db-id)]
  314. (or (when-not (and (:block/collapsed? entity) skip-collapsed? init?)
  315. (ldb/get-right-sibling (d/entity db db-id)))
  316. (let [parent-id (:db/id (:block/parent (db-utils/entity db db-id)))]
  317. (get-next db parent-id (assoc opts :init? false))))))
  318. (defn get-prev
  319. "Get prev block, either its left sibling if the sibling is collapsed or no children,
  320. or get sibling's last deep displayable child (collaspsed parent or non-collapsed child)."
  321. [db db-id]
  322. (when-let [entity (db-utils/entity db db-id)]
  323. (or
  324. (when-let [prev-sibling (ldb/get-left-sibling entity)]
  325. (if (or (:block/collapsed? prev-sibling)
  326. (empty? (:block/_parent prev-sibling)))
  327. prev-sibling
  328. (some->> (get-block-deep-last-open-child-id db (:db/id prev-sibling))
  329. (db-utils/entity db))))
  330. (let [parent (:block/parent entity)]
  331. (when-not (page? parent)
  332. parent)))))
  333. (defn get-page-blocks-no-cache
  334. ([page-id]
  335. (get-page-blocks-no-cache (state/get-current-repo) page-id nil))
  336. ([repo page-id]
  337. (get-page-blocks-no-cache repo page-id nil))
  338. ([repo page-id opts]
  339. (when-let [db (conn/get-db repo)]
  340. (ldb/get-page-blocks db page-id opts))))
  341. (defn get-page-blocks-count
  342. [repo page-id]
  343. (when-let [db (conn/get-db repo)]
  344. (ldb/get-page-blocks-count db page-id)))
  345. (defn page-exists?
  346. "Whether a page exists."
  347. [page-name tags]
  348. (let [repo (state/get-current-repo)]
  349. (when-let [db (conn/get-db repo)]
  350. (ldb/page-exists? db page-name tags))))
  351. (defn page-empty?
  352. "Whether a page is empty. Does it has a non-page block?
  353. `page-id` could be either a string or a db/id."
  354. [repo page-id]
  355. (when-let [db (conn/get-db repo)]
  356. (ldb/page-empty? db page-id)))
  357. (defn parents-collapsed?
  358. [repo block-uuid]
  359. (when-let [block (:block/parent (get-block-parents-v2 repo block-uuid))]
  360. (->> (tree-seq map? (fn [x] [(:block/parent x)]) block)
  361. (some util/collapsed?))))
  362. (defn get-block-page
  363. [repo block-uuid]
  364. (assert (uuid? block-uuid) (str "get-block-page requires block-uuid to be of type uuid but got " block-uuid))
  365. (when-let [block (db-utils/entity repo [:block/uuid block-uuid])]
  366. (db-utils/entity repo (:db/id (:block/page block)))))
  367. (defn get-block-immediate-children
  368. "Doesn't include nested children."
  369. [repo block-uuid]
  370. (when-let [db (conn/get-db repo)]
  371. (ldb/get-children db block-uuid)))
  372. (defn get-block-children
  373. "Including nested children."
  374. [repo block-uuid]
  375. (when-let [db (conn/get-db repo)]
  376. (let [ids (ldb/get-block-children-ids db block-uuid)]
  377. (when (seq ids)
  378. (let [ids' (map (fn [id] [:block/uuid id]) ids)]
  379. (db-utils/pull-many repo '[*] ids'))))))
  380. (defn get-block-and-children
  381. [repo block-uuid]
  382. (let [db (conn/get-db repo)]
  383. (ldb/get-block-and-children db block-uuid)))
  384. (defn get-file-page
  385. ([file-path]
  386. (get-file-page file-path true))
  387. ([file-path title?]
  388. (when-let [repo (state/get-current-repo)]
  389. (when-let [db (conn/get-db repo)]
  390. (some->
  391. (d/q
  392. (if title?
  393. '[:find ?page-name
  394. :in $ ?path
  395. :where
  396. [?file :file/path ?path]
  397. [?page :block/file ?file]
  398. [?page :block/title ?page-name]]
  399. '[:find ?page-name
  400. :in $ ?path
  401. :where
  402. [?file :file/path ?path]
  403. [?page :block/file ?file]
  404. [?page :block/name ?page-name]])
  405. db file-path)
  406. db-utils/seq-flatten
  407. first)))))
  408. (defn get-page-file
  409. ([page-name]
  410. (get-page-file (state/get-current-repo) page-name))
  411. ([repo page-name]
  412. (when-let [db (conn/get-db repo)]
  413. (gp-db/get-page-file db page-name))))
  414. (defn get-block-file-path
  415. [block]
  416. (when-let [page-id (:db/id (:block/page block))]
  417. (:file/path (:block/file (db-utils/entity page-id)))))
  418. (defn get-file-page-id
  419. [file-path]
  420. (when-let [repo (state/get-current-repo)]
  421. (when-let [db (conn/get-db repo)]
  422. (some->
  423. (d/q
  424. '[:find ?page
  425. :in $ ?path
  426. :where
  427. [?file :file/path ?path]
  428. [?page :block/name]
  429. [?page :block/file ?file]]
  430. db file-path)
  431. db-utils/seq-flatten
  432. first))))
  433. (defn get-page
  434. [page-name-or-uuid]
  435. (when page-name-or-uuid
  436. (ldb/get-page (conn/get-db) page-name-or-uuid)))
  437. (defn get-case-page
  438. [page-name-or-uuid]
  439. (when page-name-or-uuid
  440. (ldb/get-case-page (conn/get-db) page-name-or-uuid)))
  441. (defn get-journal-page
  442. [page-title]
  443. (when-let [journal-day (date/journal-title->int page-title)]
  444. (when-let [db (conn/get-db)]
  445. (->
  446. (d/q
  447. '[:find [?page ...]
  448. :in $ ?day
  449. :where
  450. [?page :block/journal-day ?day]]
  451. db
  452. journal-day)
  453. first))))
  454. (defn get-redirect-page-name
  455. "Given any readable page-name, return the exact page-name in db. If page
  456. doesn't exists yet, will return the passed `page-name`. Accepts both
  457. sanitized or unsanitized names.
  458. alias?: if true, alias is allowed to be returned; otherwise, it would be deref."
  459. ([page-name] (get-redirect-page-name page-name false))
  460. ([page-name alias?]
  461. (when page-name
  462. (let [page-entity (ldb/get-page (conn/get-db) page-name)]
  463. (cond
  464. alias?
  465. (or (:block/name page-entity) page-name)
  466. (nil? page-entity)
  467. (if-let [journal-name (date/journal-title->custom-format page-name)]
  468. (util/page-name-sanity-lc journal-name)
  469. page-name)
  470. :else
  471. (let [source-page (get-alias-source-page (state/get-current-repo) (:db/id page-entity))]
  472. (or (:block/name source-page)
  473. (:block/name page-entity)
  474. page-name)))))))
  475. (defn get-journals-length
  476. []
  477. (let [today (date-time-util/date->int (js/Date.))]
  478. (if (config/db-based-graph?)
  479. (d/q '[:find (count ?page) .
  480. :in $ ?today
  481. :where
  482. [?page :block/tags :logseq.class/Journal]
  483. [?page :block/journal-day ?journal-day]
  484. [(<= ?journal-day ?today)]]
  485. (conn/get-db (state/get-current-repo))
  486. today)
  487. (d/q '[:find (count ?page) .
  488. :in $ ?today
  489. :where
  490. [?page :block/type "journal"]
  491. [?page :block/journal-day ?journal-day]
  492. [(<= ?journal-day ?today)]]
  493. (conn/get-db (state/get-current-repo))
  494. today))))
  495. (defn get-latest-journals
  496. ([n]
  497. (get-latest-journals (state/get-current-repo) n))
  498. ([repo-url n]
  499. (when (conn/get-db repo-url)
  500. (let [date (js/Date.)
  501. _ (.setDate date (- (.getDate date) (dec n)))
  502. today (date-time-util/date->int (js/Date.))]
  503. (->>
  504. (react/q repo-url [:frontend.worker.react/journals] {:use-cache? false}
  505. '[:find [(pull ?page [*]) ...]
  506. :in $ ?today
  507. :where
  508. [?page :block/name ?page-name]
  509. [?page :block/journal-day ?journal-day]
  510. [(<= ?journal-day ?today)]]
  511. today)
  512. (react)
  513. (sort-by :block/journal-day)
  514. (reverse)
  515. (take n))))))
  516. ;; get pages that this page referenced
  517. (defn get-page-referenced-pages
  518. [repo page-id]
  519. (when-let [db (conn/get-db repo)]
  520. (let [pages (page-alias-set repo page-id)
  521. ref-pages (d/q
  522. '[:find [?ref-page ...]
  523. :in $ ?pages
  524. :where
  525. [(untuple ?pages) [?page ...]]
  526. [?block :block/page ?page]
  527. [?block :block/refs ?ref-page]]
  528. db
  529. pages)]
  530. ref-pages)))
  531. ;; get pages who mentioned this page
  532. (defn get-pages-that-mentioned-page
  533. [repo page-id include-journals?]
  534. (when (conn/get-db repo)
  535. (let [pages (page-alias-set repo page-id)
  536. mentioned-pages (->>
  537. (mapcat
  538. (fn [id]
  539. (let [page (db-utils/entity repo id)]
  540. (->> (:block/_refs page)
  541. (keep (fn [ref]
  542. (if (ldb/page? ref)
  543. page
  544. (:block/page ref)))))))
  545. pages)
  546. (util/distinct-by :db/id))]
  547. (keep (fn [page]
  548. (when-not (and (not include-journals?) (ldb/journal? page))
  549. (:db/id page)))
  550. mentioned-pages))))
  551. (defn get-page-referenced-blocks-full
  552. ([page-id]
  553. (get-page-referenced-blocks-full (state/get-current-repo) page-id nil))
  554. ([page-id options]
  555. (get-page-referenced-blocks-full (state/get-current-repo) page-id options))
  556. ([repo page-id options]
  557. (when (and repo page-id)
  558. (when-let [db (conn/get-db repo)]
  559. (let [pages (page-alias-set repo page-id)
  560. aliases (set/difference pages #{page-id})]
  561. (->>
  562. (d/q
  563. '[:find [(pull ?block ?block-attrs) ...]
  564. :in $ [?ref-page ...] ?block-attrs
  565. :where
  566. [?block :block/path-refs ?ref-page]]
  567. db
  568. pages
  569. (butlast file-graph-block-attrs))
  570. (remove (fn [block] (= page-id (:db/id (:block/page block)))))
  571. db-utils/group-by-page
  572. (map (fn [[k blocks]]
  573. (let [k (if (contains? aliases (:db/id k))
  574. (assoc k :block/alias? true)
  575. k)]
  576. [k blocks])))))))))
  577. (defn get-referenced-blocks
  578. ([eid]
  579. (get-referenced-blocks (state/get-current-repo) eid nil))
  580. ([eid options]
  581. (get-referenced-blocks (state/get-current-repo) eid options))
  582. ([repo eid options]
  583. (when repo
  584. (when (conn/get-db repo)
  585. (let [entity (db-utils/entity eid)
  586. ids (page-alias-set repo eid)]
  587. (->>
  588. (react/q repo
  589. [:frontend.worker.react/refs eid]
  590. {:query-fn (fn []
  591. (let [entities (mapcat (fn [id]
  592. (:block/_path-refs (db-utils/entity id))) ids)
  593. blocks (map (fn [e]
  594. {:block/parent (:block/parent e)
  595. :block/order (:block/order e)
  596. :block/page (:block/page e)
  597. :block/collapsed? (:block/collapsed? e)}) entities)]
  598. {:entities entities
  599. :blocks blocks}))}
  600. nil)
  601. react
  602. :entities
  603. (remove (fn [block]
  604. (or
  605. (= (:db/id block) eid)
  606. (= eid (:db/id (:block/page block)))
  607. (ldb/hidden? (:block/page block))
  608. (contains? (set (map :db/id (:block/tags block))) (:db/id entity))
  609. (some? (get block (:db/ident entity))))))
  610. (util/distinct-by :db/id)))))))
  611. (defn get-block-referenced-blocks
  612. ([block-id]
  613. (get-block-referenced-blocks block-id {}))
  614. ([block-id options]
  615. (when-let [repo (state/get-current-repo)]
  616. (when (conn/get-db repo)
  617. (->> (get-referenced-blocks repo block-id options)
  618. (sort-by-order-recursive)
  619. db-utils/group-by-page)))))
  620. (defn journal-page?
  621. "sanitized page-name only"
  622. [page-name]
  623. (ldb/journal? (ldb/get-page (conn/get-db) page-name)))
  624. (defn get-classes-with-property
  625. "Get classes which have given property as a class property"
  626. [property-id]
  627. (ldb/get-classes-with-property (conn/get-db) property-id))
  628. (defn get-all-referenced-blocks-uuid
  629. "Get all uuids of blocks with any back link exists."
  630. []
  631. (when-let [db (conn/get-db)]
  632. (d/q '[:find [?refed-uuid ...]
  633. :where
  634. ;; ?referee-b is block with ref towards ?refed-b
  635. [?refed-b :block/uuid ?refed-uuid]
  636. [?referee-b :block/refs ?refed-b]] db)))
  637. (defn delete-blocks
  638. [repo-url files _delete-page?]
  639. (when (seq files)
  640. (let [blocks (->> (get-files-blocks repo-url files)
  641. (remove nil?))]
  642. (mapv (fn [eid] [:db.fn/retractEntity eid]) blocks))))
  643. (defn delete-files
  644. [files]
  645. (mapv (fn [path] [:db.fn/retractEntity [:file/path path]]) files))
  646. ;; file-based only so it's safe to use :block/name lookup refs here
  647. (defn delete-pages-by-files
  648. [files]
  649. (let [pages (->> (mapv get-file-page files)
  650. (remove nil?))]
  651. (when (seq pages)
  652. (mapv (fn [page] [:db.fn/retractEntity [:block/name page]]) (map util/page-name-sanity-lc pages)))))
  653. ;; TODO: check whether this works when adding pdf back on Web
  654. (defn get-pre-block
  655. [repo page-id]
  656. (-> (d/q '[:find (pull ?b [*])
  657. :in $ ?page
  658. :where
  659. [?b :block/page ?page]
  660. [?b :block/pre-block? true]]
  661. (conn/get-db repo)
  662. page-id)
  663. ffirst))
  664. (defn whiteboard-page?
  665. "Given a page entity, page object or page name, check if it is a whiteboard page"
  666. [page]
  667. (let [page (if (string? page)
  668. (get-page page)
  669. page)]
  670. (ldb/whiteboard? page)))
  671. (comment
  672. (defn get-orphaned-pages
  673. [opts]
  674. (let [db (conn/get-db)]
  675. (ldb/get-orphaned-pages db
  676. (merge opts
  677. {:built-in-pages-names
  678. (if (config/db-based-graph? (state/get-current-repo))
  679. sqlite-create-graph/built-in-pages-names
  680. gp-db/built-in-pages-names)})))))
  681. ;; FIXME: use `Untitled` instead of UUID for db based graphs
  682. (defn untitled-page?
  683. [page-name]
  684. (when (some->> page-name (ldb/get-page (conn/get-db)))
  685. (some? (parse-uuid page-name))))
  686. (defn get-all-whiteboards
  687. [repo]
  688. (if (config/db-based-graph?)
  689. (d/q
  690. '[:find [(pull ?page [:db/id
  691. :block/uuid
  692. :block/name
  693. :block/title
  694. :block/created-at
  695. :block/updated-at]) ...]
  696. :where
  697. [?page :block/name]
  698. [?page :block/tags :logseq.class/Whiteboard]]
  699. (conn/get-db repo))
  700. (d/q
  701. '[:find [(pull ?page [:db/id
  702. :block/uuid
  703. :block/name
  704. :block/title
  705. :block/created-at
  706. :block/updated-at]) ...]
  707. :where
  708. [?page :block/name]
  709. [?page :block/type "whiteboard"]]
  710. (conn/get-db repo))))
  711. (defn get-whiteboard-id-nonces
  712. [repo page-id]
  713. (let [db-based? (config/db-based-graph? repo)
  714. key (if db-based?
  715. :logseq.property.tldraw/shape
  716. :logseq.tldraw.shape)
  717. page (db-utils/entity page-id)]
  718. (->> (:block/_page page)
  719. (keep (fn [{:block/keys [uuid] :as b}]
  720. (when-let [shape (if db-based?
  721. (get b key)
  722. (get (:block/properties b) key))]
  723. {:id (str uuid)
  724. :nonce (:nonce shape)}))))))
  725. (defn get-all-classes
  726. [repo & {:keys [except-root-class? except-private-tags?]
  727. :or {except-root-class? false
  728. except-private-tags? true}}]
  729. (let [db (conn/get-db repo)
  730. classes (->> (d/datoms db :avet :block/tags :logseq.class/Tag)
  731. (map (fn [d]
  732. (db-utils/entity db (:e d))))
  733. (remove (fn [d]
  734. (and except-private-tags?
  735. (contains? ldb/private-tags (:db/ident d))))))]
  736. (if except-root-class?
  737. (keep (fn [e] (when-not (= :logseq.class/Root (:db/ident e)) e)) classes)
  738. classes)))
  739. (defn get-all-readable-classes
  740. "Gets all classes that are used in a read only context e.g. querying or used
  741. for property value selection. This should _not_ be used in a write context e.g.
  742. adding a tag to a node or creating a new node with a tag"
  743. [repo opts]
  744. (get-all-classes repo (merge opts {:except-private-tags? false})))
  745. (defn get-structured-children
  746. [repo eid]
  747. (->>
  748. (d/q '[:find [?children ...]
  749. :in $ ?parent %
  750. :where
  751. (parent ?parent ?children)]
  752. (conn/get-db repo)
  753. eid
  754. (:parent rules/rules))
  755. (remove #{eid})))
  756. (defn get-class-objects
  757. [repo class-id]
  758. (when-let [class (db-utils/entity repo class-id)]
  759. (->>
  760. (if (first (:logseq.property/_parent class)) ; has children classes
  761. (let [all-classes (conj (->> (get-structured-children repo class-id)
  762. (map #(db-utils/entity repo %)))
  763. class)]
  764. (->> (mapcat :block/_tags all-classes)
  765. distinct))
  766. (:block/_tags class))
  767. (remove ldb/hidden?))))
  768. (defn sub-class-objects
  769. [repo class-id]
  770. (when class-id
  771. (-> (react/q repo [:frontend.worker.react/objects class-id]
  772. {:query-fn (fn [_] (get-class-objects repo class-id))}
  773. nil)
  774. react)))
  775. (defn get-property-related-objects
  776. [repo property-id]
  777. (when-let [property (db-utils/entity repo property-id)]
  778. (->> (d/q '[:find [?b ...]
  779. :in $ % ?prop
  780. :where
  781. (has-property-or-default-value? ?b ?prop)]
  782. (conn/get-db repo)
  783. (rules/extract-rules rules/db-query-dsl-rules [:has-property-or-default-value]
  784. {:deps rules/rules-dependencies})
  785. (:db/ident property))
  786. (map #(db-utils/entity repo %))
  787. (remove ldb/hidden?))))
  788. (defn get-all-namespace-relation
  789. [repo]
  790. (d/q '[:find ?page ?parent
  791. :where
  792. [?page :block/namespace ?parent]]
  793. (conn/get-db repo)))
  794. (defn get-all-namespace-parents
  795. [repo]
  796. (let [db (conn/get-db repo)]
  797. (->> (get-all-namespace-relation repo)
  798. (map (fn [[_ ?parent]]
  799. (db-utils/entity db ?parent))))))
  800. ;; Ignore files with empty blocks for now
  801. (defn get-pages-relation
  802. [repo with-journal?]
  803. (when-let [db (conn/get-db repo)]
  804. (if (config/db-based-graph?)
  805. (let [q (if with-journal?
  806. '[:find ?p ?ref-page
  807. :where
  808. [?block :block/page ?p]
  809. [?block :block/refs ?ref-page]]
  810. '[:find ?p ?ref-page
  811. :where
  812. [?block :block/page ?p]
  813. [?p :block/tags]
  814. (not [?p :block/tags :logseq.class/Journal])
  815. [?block :block/refs ?ref-page]])]
  816. (d/q q db))
  817. (let [q (if with-journal?
  818. '[:find ?p ?ref-page
  819. :where
  820. [?block :block/page ?p]
  821. [?block :block/refs ?ref-page]]
  822. '[:find ?p ?ref-page
  823. :where
  824. [?block :block/page ?p]
  825. (not [?p :block/type "journal"])
  826. [?block :block/refs ?ref-page]])]
  827. (d/q q db)))))
  828. (defn get-namespace-pages
  829. "Accepts both sanitized and unsanitized namespaces"
  830. [repo namespace]
  831. (ldb/get-namespace-pages (conn/get-db repo) namespace {:db-graph? (config/db-based-graph? repo)}))
  832. (defn- tree [flat-col root]
  833. (let [sort-fn #(sort-by :block/name %)
  834. children (group-by :block/namespace flat-col)
  835. namespace-children (fn namespace-children [parent-id]
  836. (map (fn [m]
  837. (assoc m :namespace/children
  838. (sort-fn (namespace-children {:db/id (:db/id m)}))))
  839. (sort-fn (get children parent-id))))]
  840. (namespace-children root)))
  841. (defn get-namespace-hierarchy
  842. "Unsanitized namespaces"
  843. [repo namespace]
  844. (let [children (get-namespace-pages repo namespace)
  845. namespace-id (:db/id (db-utils/entity [:block/name (util/page-name-sanity-lc namespace)]))
  846. root {:db/id namespace-id}
  847. col (conj children root)]
  848. (tree col root)))
  849. (defn get-page-namespace
  850. [repo page]
  851. (:block/namespace (db-utils/entity repo [:block/name (util/page-name-sanity-lc page)])))
  852. (defn get-page-namespace-routes
  853. [repo page]
  854. (assert (string? page))
  855. (when-let [db (conn/get-db repo)]
  856. (when-not (string/blank? page)
  857. (let [page (util/page-name-sanity-lc (string/trim page))
  858. page-exist? (db-utils/entity repo [:block/name page])
  859. ids (if page-exist?
  860. '()
  861. (->> (d/datoms db :aevt :block/name)
  862. (filter (fn [datom]
  863. (string/ends-with? (:v datom) (str "/" page))))
  864. (map :e)))]
  865. (when (seq ids)
  866. (db-utils/pull-many repo
  867. '[:db/id :block/name :block/title
  868. {:block/file [:db/id :file/path]}]
  869. ids))))))
  870. (comment
  871. ;; For debugging
  872. (defn get-all-blocks
  873. []
  874. (let [repo (state/get-current-repo)]
  875. (d/q
  876. '[:find [(pull ?b [*]) ...]
  877. :where
  878. [?b :block/uuid]]
  879. (conn/get-db repo)))))