db.cljs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. (ns logseq.db
  2. "Main namespace for public db fns. For DB and file graphs.
  3. For shared file graph only fns, use logseq.graph-parser.db"
  4. (:require [clojure.string :as string]
  5. [datascript.core :as d]
  6. [datascript.impl.entity :as de]
  7. [logseq.common.config :as common-config]
  8. [logseq.common.util :as common-util]
  9. [logseq.common.uuid :as common-uuid]
  10. [logseq.db.frontend.class :as db-class]
  11. [logseq.db.frontend.delete-blocks :as delete-blocks] ;; Load entity extensions
  12. [logseq.db.frontend.entity-plus]
  13. [logseq.db.frontend.entity-util :as entity-util]
  14. [logseq.db.frontend.order :as db-order]
  15. [logseq.db.frontend.rules :as rules]
  16. [logseq.db.sqlite.common-db :as sqlite-common-db]
  17. [logseq.db.sqlite.util :as sqlite-util]
  18. [logseq.db.frontend.content :as db-content]
  19. [logseq.db.frontend.property :as db-property]))
  20. ;; Use it as an input argument for datalog queries
  21. (def block-attrs
  22. '[:db/id
  23. :block/uuid
  24. :block/parent
  25. :block/order
  26. :block/collapsed?
  27. :block/format
  28. :block/refs
  29. :block/_refs
  30. :block/path-refs
  31. :block/tags
  32. :block/link
  33. :block/title
  34. :block/marker
  35. :block/priority
  36. :block/properties
  37. :block/properties-order
  38. :block/properties-text-values
  39. :block/pre-block?
  40. :block/scheduled
  41. :block/deadline
  42. :block/repeated?
  43. :block/created-at
  44. :block/updated-at
  45. ;; TODO: remove this in later releases
  46. :block/heading-level
  47. :block/file
  48. :logseq.property/parent
  49. {:block/page [:db/id :block/name :block/title :block/journal-day]}
  50. {:block/_parent ...}])
  51. (defonce *transact-fn (atom nil))
  52. (defn register-transact-fn!
  53. [f]
  54. (when f (reset! *transact-fn f)))
  55. (defn transact!
  56. "`repo-or-conn`: repo for UI thread and conn for worker/node"
  57. ([repo-or-conn tx-data]
  58. (transact! repo-or-conn tx-data nil))
  59. ([repo-or-conn tx-data tx-meta]
  60. (let [tx-data (map (fn [m]
  61. (if (map? m)
  62. (dissoc m :block/children :block/meta :block/top? :block/bottom? :block/anchor
  63. :block.temp/ast-title :block.temp/ast-body :block/level :block/container :db/other-tx
  64. :block/unordered)
  65. m)) tx-data)
  66. tx-data (->> (common-util/fast-remove-nils tx-data)
  67. (remove empty?))
  68. delete-blocks-tx (when-not (string? repo-or-conn)
  69. (delete-blocks/update-refs-and-macros @repo-or-conn tx-data tx-meta))
  70. tx-data (concat tx-data delete-blocks-tx)]
  71. ;; Ensure worker can handle the request sequentially (one by one)
  72. ;; Because UI assumes that the in-memory db has all the data except the last one transaction
  73. (when (seq tx-data)
  74. ;; (prn :debug :transact :sync? (= d/transact! (or @*transact-fn d/transact!)) :tx-meta tx-meta)
  75. ;; (cljs.pprint/pprint tx-data)
  76. ;; (js/console.trace)
  77. (let [f (or @*transact-fn d/transact!)]
  78. (try
  79. (f repo-or-conn tx-data tx-meta)
  80. (catch :default e
  81. (js/console.trace)
  82. (prn :debug-tx-data tx-data)
  83. (throw e))))))))
  84. (def page? entity-util/page?)
  85. (def class? entity-util/class?)
  86. (def property? entity-util/property?)
  87. (def closed-value? entity-util/closed-value?)
  88. (def whiteboard? entity-util/whiteboard?)
  89. (def journal? entity-util/journal?)
  90. (def hidden? entity-util/hidden?)
  91. (def public-built-in-property? db-property/public-built-in-property?)
  92. (defn sort-by-order
  93. [blocks]
  94. (sort-by :block/order blocks))
  95. (defn- get-block-and-children-aux
  96. [entity & {:keys [include-property-block?]
  97. :or {include-property-block? false}
  98. :as opts}]
  99. (if-let [children (sort-by-order
  100. (if include-property-block?
  101. (:block/_raw-parent entity)
  102. (:block/_parent entity)))]
  103. (cons entity (mapcat #(get-block-and-children-aux % opts) children))
  104. [entity]))
  105. (defn get-block-and-children
  106. [db block-uuid & {:as opts}]
  107. (when-let [e (d/entity db [:block/uuid block-uuid])]
  108. (get-block-and-children-aux e opts)))
  109. (defn get-page-blocks
  110. "Return blocks of the designated page, without using cache.
  111. page-id - eid"
  112. [db page-id & {:keys [pull-keys]
  113. :or {pull-keys '[*]}}]
  114. (when page-id
  115. (let [datoms (d/datoms db :avet :block/page page-id)
  116. block-eids (mapv :e datoms)]
  117. (d/pull-many db pull-keys block-eids))))
  118. (defn get-page-blocks-count
  119. [db page-id]
  120. (count (d/datoms db :avet :block/page page-id)))
  121. (defn- get-block-children-or-property-children
  122. [block parent]
  123. (let [from-property (:logseq.property/created-from-property block)
  124. closed-property (:block/closed-value-property block)]
  125. (sort-by-order (cond
  126. closed-property
  127. (:property/closed-values closed-property)
  128. from-property
  129. (filter (fn [e]
  130. (= (:db/id (:logseq.property/created-from-property e))
  131. (:db/id from-property)))
  132. (:block/_raw-parent parent))
  133. :else
  134. (:block/_parent parent)))))
  135. (defn get-right-sibling
  136. [block]
  137. (assert (or (de/entity? block) (nil? block)))
  138. (when-let [parent (:block/parent block)]
  139. (let [children (get-block-children-or-property-children block parent)
  140. right (some (fn [child] (when (> (compare (:block/order child) (:block/order block)) 0) child)) children)]
  141. (when (not= (:db/id right) (:db/id block))
  142. right))))
  143. (defn get-left-sibling
  144. [block]
  145. (assert (or (de/entity? block) (nil? block)))
  146. (when-let [parent (:block/parent block)]
  147. (let [children (reverse (get-block-children-or-property-children block parent))
  148. left (some (fn [child] (when (< (compare (:block/order child) (:block/order block)) 0) child)) children)]
  149. (when (not= (:db/id left) (:db/id block))
  150. left))))
  151. (defn get-down
  152. [block]
  153. (assert (or (de/entity? block) (nil? block)))
  154. (first (sort-by-order (:block/_parent block))))
  155. (defn get-pages
  156. [db]
  157. (->> (d/q
  158. '[:find ?page-title
  159. :where
  160. [?page :block/name ?page-name]
  161. [(get-else $ ?page :block/title ?page-name) ?page-title]]
  162. db)
  163. (map first)
  164. (remove hidden?)))
  165. (def get-first-page-by-name sqlite-common-db/get-first-page-by-name)
  166. (def db-based-graph? entity-util/db-based-graph?)
  167. (defn page-exists?
  168. "Whether a page exists with the `type`."
  169. [db page-name type']
  170. (when page-name
  171. (if (db-based-graph? db)
  172. ;; Classes and properties are case sensitive
  173. (if (#{"class" "property"} type')
  174. (seq
  175. (d/q
  176. '[:find [?p ...]
  177. :in $ ?name ?type
  178. :where
  179. [?p :block/title ?name]
  180. [?p :block/type ?type]]
  181. db
  182. page-name
  183. type'))
  184. ;; TODO: Decouple db graphs from file specific :block/name
  185. (seq
  186. (d/q
  187. '[:find [?p ...]
  188. :in $ ?name ?type
  189. :where
  190. [?p :block/name ?name]
  191. [?p :block/type ?type]]
  192. db
  193. (common-util/page-name-sanity-lc page-name)
  194. type')))
  195. (d/entity db [:block/name (common-util/page-name-sanity-lc page-name)]))))
  196. (defn get-page
  197. "Get a page given its unsanitized name"
  198. [db page-name-or-uuid]
  199. (when db
  200. (if-let [id (if (uuid? page-name-or-uuid) page-name-or-uuid
  201. (parse-uuid page-name-or-uuid))]
  202. (d/entity db [:block/uuid id])
  203. (d/entity db (get-first-page-by-name db (name page-name-or-uuid))))))
  204. (defn get-case-page
  205. "Case sensitive version of get-page. For use with DB graphs"
  206. [db page-name-or-uuid]
  207. (when db
  208. (if-let [id (if (uuid? page-name-or-uuid) page-name-or-uuid
  209. (parse-uuid page-name-or-uuid))]
  210. (d/entity db [:block/uuid id])
  211. (d/entity db (sqlite-common-db/get-first-page-by-title db page-name-or-uuid)))))
  212. (defn page-empty?
  213. "Whether a page is empty. Does it has a non-page block?
  214. `page-id` could be either a string or a db/id."
  215. [db page-id]
  216. (let [page-id (if (string? page-id)
  217. (get-first-page-by-name db page-id)
  218. page-id)
  219. page (d/entity db page-id)]
  220. (empty? (:block/_parent page))))
  221. (defn get-first-child
  222. [db id]
  223. (first (sort-by-order (:block/_parent (d/entity db id)))))
  224. (defn get-orphaned-pages
  225. [db {:keys [pages empty-ref-f built-in-pages-names]
  226. :or {empty-ref-f (fn [page] (zero? (count (:block/_refs page))))
  227. built-in-pages-names #{}}}]
  228. (let [pages (->> (or pages (get-pages db))
  229. (remove nil?))
  230. built-in-pages (set (map string/lower-case built-in-pages-names))
  231. orphaned-pages (->>
  232. (map
  233. (fn [page]
  234. (when-let [page (get-page db page)]
  235. (let [name' (:block/name page)]
  236. (and
  237. (empty-ref-f page)
  238. (or
  239. (page-empty? db (:db/id page))
  240. (let [first-child (get-first-child db (:db/id page))
  241. children (:block/_page page)]
  242. (and
  243. first-child
  244. (= 1 (count children))
  245. (contains? #{"" "-" "*"} (string/trim (:block/title first-child))))))
  246. (not (contains? built-in-pages name'))
  247. (not (whiteboard? page))
  248. (not (:block/_namespace page))
  249. (not (property? page))
  250. ;; a/b/c might be deleted but a/b/c/d still exists (for backward compatibility)
  251. (not (and (string/includes? name' "/")
  252. (not (journal? page))))
  253. (not (:block/properties page))
  254. page))))
  255. pages)
  256. (remove false?)
  257. (remove nil?)
  258. (remove hidden?))]
  259. orphaned-pages))
  260. (defn has-children?
  261. [db block-id]
  262. (some? (:block/_parent (d/entity db [:block/uuid block-id]))))
  263. (defn- collapsed-and-has-children?
  264. [db block]
  265. (and (:block/collapsed? block) (has-children? db (:block/uuid block))))
  266. (defn get-block-last-direct-child-id
  267. "Notice: if `not-collapsed?` is true, will skip searching for any collapsed block."
  268. ([db db-id]
  269. (get-block-last-direct-child-id db db-id false))
  270. ([db db-id not-collapsed?]
  271. (when-let [block (d/entity db db-id)]
  272. (when (if not-collapsed?
  273. (not (collapsed-and-has-children? db block))
  274. true)
  275. (let [children (sort-by :block/order (:block/_parent block))]
  276. (:db/id (last children)))))))
  277. (defn get-children
  278. "Doesn't include nested children."
  279. ([block-entity]
  280. (get-children nil block-entity))
  281. ([db block-entity-or-eid]
  282. (when-let [parent (cond
  283. (number? block-entity-or-eid)
  284. (d/entity db block-entity-or-eid)
  285. (uuid? block-entity-or-eid)
  286. (d/entity db [:block/uuid block-entity-or-eid])
  287. :else
  288. block-entity-or-eid)]
  289. (sort-by-order (:block/_parent parent)))))
  290. (defn get-block-parents
  291. [db block-id {:keys [depth] :or {depth 100}}]
  292. (loop [block-id block-id
  293. parents' (list)
  294. d 1]
  295. (if (> d depth)
  296. parents'
  297. (if-let [parent (:block/parent (d/entity db [:block/uuid block-id]))]
  298. (recur (:block/uuid parent) (conj parents' parent) (inc d))
  299. parents'))))
  300. (def get-block-children-ids sqlite-common-db/get-block-children-ids)
  301. (def get-block-children sqlite-common-db/get-block-children)
  302. (defn- get-sorted-page-block-ids
  303. [db page-id]
  304. (let [root (d/entity db page-id)]
  305. (loop [result []
  306. children (sort-by-order (:block/_parent root))]
  307. (if (seq children)
  308. (let [child (first children)]
  309. (recur (conj result (:db/id child))
  310. (concat
  311. (sort-by-order (:block/_parent child))
  312. (rest children))))
  313. result))))
  314. (defn sort-page-random-blocks
  315. "Blocks could be non consecutive."
  316. [db blocks]
  317. (assert (every? #(= (:block/page %) (:block/page (first blocks))) blocks) "Blocks must to be in a same page.")
  318. (let [page-id (:db/id (:block/page (first blocks)))
  319. ;; TODO: there's no need to sort all the blocks
  320. sorted-ids (get-sorted-page-block-ids db page-id)
  321. blocks-map (zipmap (map :db/id blocks) blocks)]
  322. (keep blocks-map sorted-ids)))
  323. (defn last-child-block?
  324. "The child block could be collapsed."
  325. [db parent-id child-id]
  326. (when-let [child (d/entity db child-id)]
  327. (cond
  328. (= parent-id child-id)
  329. true
  330. (get-right-sibling child)
  331. false
  332. :else
  333. (last-child-block? db parent-id (:db/id (:block/parent child))))))
  334. (defn- consecutive-block?
  335. [db block-1 block-2]
  336. (let [aux-fn (fn [block-1 block-2]
  337. (and (= (:block/page block-1) (:block/page block-2))
  338. (or
  339. ;; sibling or child
  340. (= (:db/id (get-left-sibling block-2)) (:db/id block-1))
  341. (when-let [prev-sibling (get-left-sibling (d/entity db (:db/id block-2)))]
  342. (last-child-block? db (:db/id prev-sibling) (:db/id block-1))))))]
  343. (or (aux-fn block-1 block-2) (aux-fn block-2 block-1))))
  344. (defn get-non-consecutive-blocks
  345. [db blocks]
  346. (vec
  347. (keep-indexed
  348. (fn [i _block]
  349. (when (< (inc i) (count blocks))
  350. (when-not (consecutive-block? db (nth blocks i) (nth blocks (inc i)))
  351. (nth blocks i))))
  352. blocks)))
  353. (defn new-block-id
  354. []
  355. (common-uuid/gen-uuid))
  356. (defn get-classes-with-property
  357. "Get classes which have given property as a class property"
  358. [db property-id]
  359. (:logseq.property.class/_properties (d/entity db property-id)))
  360. (defn get-alias-source-page
  361. "return the source page (page-name) of an alias"
  362. [db alias-id]
  363. (when alias-id
  364. ;; may be a case that a user added same alias into multiple pages.
  365. ;; only return the first result for idiot-proof
  366. (first (:block/_alias (d/entity db alias-id)))))
  367. (defn get-block-alias
  368. [db eid]
  369. (->>
  370. (d/q
  371. '[:find [?e ...]
  372. :in $ ?eid %
  373. :where
  374. (alias ?eid ?e)]
  375. db
  376. eid
  377. (:alias rules/rules))
  378. distinct))
  379. (defn get-block-refs
  380. [db id]
  381. (let [alias (->> (get-block-alias db id)
  382. (cons id)
  383. distinct)
  384. refs (->> (mapcat (fn [id] (:block/_path-refs (d/entity db id))) alias)
  385. distinct)]
  386. (when (seq refs)
  387. (d/pull-many db '[*] (map :db/id refs)))))
  388. (defn get-block-refs-count
  389. [db id]
  390. (some-> (d/entity db id)
  391. :block/_refs
  392. count))
  393. (defn get-page-unlinked-refs
  394. "Get unlinked refs from search result"
  395. [db page-id search-result-eids]
  396. (let [alias (->> (get-block-alias db page-id)
  397. (cons page-id)
  398. set)
  399. eids (remove
  400. (fn [eid]
  401. (when-let [e (d/entity db eid)]
  402. (or (some alias (map :db/id (:block/refs e)))
  403. (:block/link e)
  404. (nil? (:block/title e)))))
  405. search-result-eids)]
  406. (when (seq eids)
  407. (d/pull-many db '[*] eids))))
  408. (defn get-all-pages
  409. [db]
  410. (->>
  411. (d/datoms db :avet :block/name)
  412. (distinct)
  413. (map #(d/entity db (:e %)))
  414. (filter page?)
  415. (remove hidden?)
  416. (remove (fn [page]
  417. (common-util/uuid-string? (:block/name page))))))
  418. (defn built-in?
  419. "Built-in page or block"
  420. [entity]
  421. (:logseq.property/built-in? entity))
  422. (defn built-in-class-property?
  423. "Whether property a built-in property for the specific class"
  424. [class-entity property-entity]
  425. (and (built-in? class-entity)
  426. (class? class-entity)
  427. (built-in? property-entity)
  428. (contains? (set (get-in (db-class/built-in-classes (:db/ident class-entity)) [:schema :properties]))
  429. (:db/ident property-entity))))
  430. (def write-transit-str sqlite-util/write-transit-str)
  431. (def read-transit-str sqlite-util/read-transit-str)
  432. (defn create-favorites-page!
  433. "Creates hidden favorites page for storing favorites"
  434. [repo]
  435. (transact!
  436. repo
  437. [(sqlite-util/block-with-timestamps
  438. {:block/uuid (common-uuid/gen-uuid)
  439. :block/name common-config/favorites-page-name
  440. :block/title common-config/favorites-page-name
  441. :block/type "hidden"
  442. :block/format :markdown})]))
  443. (defn build-favorite-tx
  444. "Builds tx for a favorite block in favorite page"
  445. [favorite-uuid]
  446. {:block/link [:block/uuid favorite-uuid]
  447. :block/title ""
  448. :block/format :markdown})
  449. (defn create-views-page!
  450. "Creates hidden all pages for storing views"
  451. [conn]
  452. (let [page-id (common-uuid/gen-uuid)]
  453. (transact!
  454. conn
  455. [(sqlite-util/block-with-timestamps
  456. {:block/uuid page-id
  457. :block/name common-config/views-page-name
  458. :block/title common-config/views-page-name
  459. :block/type "hidden"
  460. :block/format :markdown})
  461. (sqlite-util/block-with-timestamps
  462. {:block/uuid (common-uuid/gen-uuid)
  463. :block/title ""
  464. :block/format :markdown
  465. :block/parent [:block/uuid page-id]
  466. :block/order (db-order/gen-key nil)
  467. :block/page [:block/uuid page-id]
  468. :logseq.property/view-for :all-pages})])))
  469. ;; TODO: why not generate a UUID for all local graphs?
  470. ;; And prefer this local graph UUID when picking an ID for new rtc graph?
  471. (defn get-graph-rtc-uuid
  472. [db]
  473. (when db (:kv/value (d/entity db :logseq.kv/graph-uuid))))
  474. ;; File based fns
  475. (defn get-namespace-pages
  476. "Accepts both sanitized and unsanitized namespaces"
  477. [db namespace' {:keys [db-graph?]}]
  478. (assert (string? namespace'))
  479. (let [namespace'' (common-util/page-name-sanity-lc namespace')
  480. pull-attrs (cond-> [:db/id :block/name :block/title :block/namespace]
  481. (not db-graph?)
  482. (conj {:block/file [:db/id :file/path]}))]
  483. (d/q
  484. [:find [(list 'pull '?c pull-attrs) '...]
  485. :in '$ '% '?namespace
  486. :where
  487. ['?p :block/name '?namespace]
  488. (list 'namespace '?p '?c)]
  489. db
  490. (:namespace rules/rules)
  491. namespace'')))
  492. (defn get-pages-by-name-partition
  493. [db partition']
  494. (when-not (string/blank? partition')
  495. (let [partition'' (common-util/page-name-sanity-lc (string/trim partition'))
  496. ids (->> (d/datoms db :aevt :block/name)
  497. (filter (fn [datom]
  498. (let [page (:v datom)]
  499. (string/includes? page partition''))))
  500. (map :e))]
  501. (when (seq ids)
  502. (d/pull-many db
  503. '[:db/id :block/name :block/title]
  504. ids)))))
  505. (defn get-all-properties
  506. [db]
  507. (->> (d/datoms db :avet :block/type "property")
  508. (map (fn [d]
  509. (d/entity db (:e d))))))
  510. (defn get-class-parents
  511. [class]
  512. (let [*classes (atom #{})]
  513. (when-let [parent (:logseq.property/parent class)]
  514. (loop [current-parent parent]
  515. (when (and
  516. current-parent
  517. (class? parent)
  518. (not (contains? @*classes (:db/id parent))))
  519. (swap! *classes conj (:db/id current-parent))
  520. (recur (:logseq.property/parent current-parent)))))
  521. @*classes))
  522. (defn get-all-pages-views
  523. [db]
  524. (when (db-based-graph? db)
  525. (when-let [page (get-page db common-config/views-page-name)]
  526. (->> (:block/_parent page)
  527. (filter (fn [b] (= :all-pages (:logseq.property/view-for b))))))))
  528. (defn inline-tag?
  529. [block-raw-title tag]
  530. (assert (string? block-raw-title) "block-raw-title should be a string")
  531. (or (string/includes? block-raw-title (str "#" (db-content/block-id->special-id-ref (:block/uuid tag))))
  532. (string/includes? block-raw-title (str "#" db-content/page-ref-special-chars (:block/uuid tag)))))