async.cljs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. (ns frontend.db.async
  2. "Async queries"
  3. (:require [cljs-time.coerce :as tc]
  4. [cljs-time.core :as t]
  5. [cljs-time.format :as tf]
  6. [datascript.core :as d]
  7. [frontend.config :as config]
  8. [frontend.date :as date]
  9. [frontend.db :as db]
  10. [frontend.db.async.util :as db-async-util]
  11. [frontend.db.file-based.async :as file-async]
  12. [frontend.db.model :as db-model]
  13. [frontend.db.react :as react]
  14. [frontend.db.utils :as db-utils]
  15. [frontend.handler.file-based.property.util :as property-util]
  16. [frontend.persist-db.browser :as db-browser]
  17. [frontend.state :as state]
  18. [frontend.util :as util]
  19. [logseq.db :as ldb]
  20. [logseq.db.frontend.property :as db-property]
  21. [promesa.core :as p]))
  22. (def <q db-async-util/<q)
  23. (def <pull db-async-util/<pull)
  24. (comment
  25. (def <pull-many db-async-util/<pull-many))
  26. (defn <get-files
  27. [graph]
  28. (p/let [result (<q graph
  29. {:transact-db? false}
  30. '[:find [(pull ?file [:file/path :file/last-modified-at]) ...]
  31. :where
  32. [?file :file/path ?path]])]
  33. (->> result seq reverse (map #(vector (:file/path %) (or (:file/last-modified-at %) 0))))))
  34. (defn <get-all-templates
  35. [graph]
  36. (p/let [result (<q graph
  37. {:transact-db? true}
  38. '[:find ?t (pull ?b [*])
  39. :where
  40. [?b :block/properties ?p]
  41. [(get ?p :template) ?t]])]
  42. (into {} result)))
  43. (defn <get-template-by-name
  44. [name]
  45. (let [repo (state/get-current-repo)]
  46. (p/let [templates (<get-all-templates repo)]
  47. (get templates name))))
  48. (defn <db-based-get-all-properties
  49. "Return seq of all property names except for private built-in properties."
  50. [graph & {:keys [remove-built-in-property? remove-non-queryable-built-in-property?]
  51. :or {remove-built-in-property? true
  52. remove-non-queryable-built-in-property? false}}]
  53. (let [result (->> (d/datoms (db/get-db graph) :avet :block/tags :logseq.class/Property)
  54. (map (fn [datom] (db/entity (:e datom))))
  55. (sort-by (juxt ldb/built-in? :block/title)))]
  56. (cond->> result
  57. remove-built-in-property?
  58. ;; remove private built-in properties
  59. (remove (fn [p]
  60. (let [ident (:db/ident p)]
  61. (and (ldb/built-in? p)
  62. (not (ldb/public-built-in-property? p))
  63. (not= ident :logseq.property/icon)))))
  64. remove-non-queryable-built-in-property?
  65. (remove (fn [p]
  66. (let [ident (:db/ident p)]
  67. (and (ldb/built-in? p)
  68. (not (:queryable? (db-property/built-in-properties ident))))))))))
  69. (defn <get-all-properties
  70. "Returns all public properties as property maps including their
  71. :block/title and :db/ident. For file graphs the map only contains
  72. :block/title"
  73. [& {:as opts}]
  74. (when-let [graph (state/get-current-repo)]
  75. (if (config/db-based-graph? graph)
  76. (<db-based-get-all-properties graph opts)
  77. (p/let [properties (file-async/<file-based-get-all-properties graph)
  78. hidden-properties (set (map name (property-util/hidden-properties)))]
  79. (remove #(hidden-properties (:block/title %)) properties)))))
  80. (defn <file-get-property-values
  81. "For file graphs, returns property value names for given property name"
  82. [graph property]
  83. (when-not (config/db-based-graph? graph)
  84. (file-async/<get-file-based-property-values graph property)))
  85. (defn <get-block-property-values
  86. "For db graphs, returns property value ids for given property db-ident.
  87. Separate from file version because values are lazy loaded"
  88. [graph property-id]
  89. (let [default-value-id (:db/id (:logseq.property/default-value (db/entity property-id)))
  90. empty-id (:db/id (db/entity :logseq.property/empty-placeholder))]
  91. (p/let [result (<q graph {:transact-db? false}
  92. '[:find [?v ...]
  93. :in $ ?property-id ?empty-id
  94. :where
  95. [?b ?property-id ?v]
  96. [(not= ?v ?empty-id)]]
  97. property-id
  98. empty-id)]
  99. (if default-value-id
  100. ;; put default value the first
  101. (concat [default-value-id] result)
  102. result))))
  103. ;; TODO: batch queries for better performance and UX
  104. (defn <get-block
  105. [graph name-or-uuid & {:keys [children? nested-children?]
  106. :or {children? true
  107. nested-children? false}
  108. :as opts}]
  109. (let [name' (str name-or-uuid)
  110. *async-queries (:db/async-queries @state/state)
  111. async-requested? (get @*async-queries [name' opts])
  112. e (cond
  113. (number? name-or-uuid)
  114. (db/entity name-or-uuid)
  115. (util/uuid-string? name')
  116. (db/entity [:block/uuid (uuid name')])
  117. :else
  118. (db/get-page name'))
  119. id (or (and (:block/uuid e) (str (:block/uuid e)))
  120. (and (util/uuid-string? name') name')
  121. name-or-uuid)]
  122. (if (or (:block.temp/fully-loaded? e) async-requested?)
  123. e
  124. (when-let [^Object sqlite @db-browser/*worker]
  125. (swap! *async-queries assoc [name' opts] true)
  126. (state/update-state! :db/async-query-loading (fn [s] (conj s name')))
  127. (p/let [result (.get-block-and-children sqlite graph id (ldb/write-transit-str
  128. {:children? children?
  129. :nested-children? nested-children?}))
  130. {:keys [properties block children] :as result'} (ldb/read-transit-str result)
  131. conn (db/get-db graph false)
  132. block-and-children (concat properties [block] children)
  133. _ (d/transact! conn block-and-children)
  134. affected-keys (->> (keep :db/id block-and-children)
  135. (map #(vector :frontend.worker.react/block %)))]
  136. (react/refresh-affected-queries! graph affected-keys)
  137. (state/update-state! :db/async-query-loading (fn [s] (disj s name')))
  138. (if children?
  139. block
  140. result'))))))
  141. (defn <get-block-parents
  142. [graph id depth]
  143. (assert (integer? id))
  144. (when-let [^Object worker @db-browser/*worker]
  145. (when-let [block-id (:block/uuid (db/entity graph id))]
  146. (state/update-state! :db/async-query-loading (fn [s] (conj s (str block-id "-parents"))))
  147. (p/let [result-str (.get-block-parents worker graph id depth)
  148. result (ldb/read-transit-str result-str)
  149. conn (db/get-db graph false)
  150. _ (d/transact! conn result)]
  151. (state/update-state! :db/async-query-loading (fn [s] (disj s (str block-id "-parents"))))
  152. result))))
  153. (defn <get-page-all-blocks
  154. [page-name]
  155. (when-let [page (some-> page-name (db-model/get-page))]
  156. (when-let [^Object worker @db-browser/*worker]
  157. (p/let [result (.get-block-and-children worker
  158. (state/get-current-repo)
  159. (str (:block/uuid page))
  160. (ldb/write-transit-str
  161. {:children? true
  162. :nested-children? false}))]
  163. (some-> result (ldb/read-transit-str) (:children))))))
  164. (defn <get-block-refs
  165. [graph eid]
  166. (assert (integer? eid))
  167. (when-let [^Object worker @db-browser/*worker]
  168. (state/update-state! :db/async-query-loading (fn [s] (conj s (str eid "-refs"))))
  169. (p/let [result-str (.get-block-refs worker graph eid)
  170. result (ldb/read-transit-str result-str)
  171. conn (db/get-db graph false)
  172. _ (d/transact! conn result)]
  173. (state/update-state! :db/async-query-loading (fn [s] (disj s (str eid "-refs"))))
  174. result)))
  175. (defn <get-block-refs-count
  176. [graph eid]
  177. (assert (integer? eid))
  178. (when-let [^Object worker @db-browser/*worker]
  179. (.get-block-refs-count worker graph eid)))
  180. (defn <get-all-referenced-blocks-uuid
  181. "Get all uuids of blocks with any back link exists."
  182. [graph]
  183. (<q graph {:transact-db? false}
  184. '[:find [?refed-uuid ...]
  185. :where
  186. ;; ?referee-b is block with ref towards ?refed-b
  187. [?refed-b :block/uuid ?refed-uuid]
  188. [?referee-b :block/refs ?refed-b]]))
  189. (defn <get-file
  190. [graph path]
  191. (when (and graph path)
  192. (p/let [result (<pull graph [:file/path path])]
  193. (:file/content result))))
  194. (defn <get-date-scheduled-or-deadlines
  195. [journal-title]
  196. (when-let [date (date/journal-title->int journal-title)]
  197. (let [future-days (state/get-scheduled-future-days)
  198. date-format (tf/formatter "yyyyMMdd")
  199. current-day (tf/parse date-format (str date))
  200. future-date (t/plus current-day (t/days future-days))
  201. future-day (some->> future-date
  202. (tf/unparse date-format)
  203. (parse-long))
  204. start-time (date/journal-day->utc-ms date)
  205. future-time (tc/to-long future-date)]
  206. (when-let [repo (and future-day (state/get-current-repo))]
  207. (p/let [result
  208. (if (config/db-based-graph? repo)
  209. (<q repo {}
  210. '[:find [(pull ?block ?block-attrs) ...]
  211. :in $ ?start-time ?end-time ?block-attrs
  212. :where
  213. (or [?block :logseq.task/scheduled ?n]
  214. [?block :logseq.task/deadline ?n])
  215. [(>= ?n ?start-time)]
  216. [(<= ?n ?end-time)]
  217. [?block :logseq.task/status ?status]
  218. [?status :db/ident ?status-ident]
  219. [(not= ?status-ident :logseq.task/status.done)]
  220. [(not= ?status-ident :logseq.task/status.canceled)]]
  221. start-time
  222. future-time
  223. '[*])
  224. (<q repo {}
  225. '[:find [(pull ?block ?block-attrs) ...]
  226. :in $ ?day ?future ?block-attrs
  227. :where
  228. (or
  229. [?block :block/scheduled ?d]
  230. [?block :block/deadline ?d])
  231. [(get-else $ ?block :block/repeated? false) ?repeated]
  232. [(get-else $ ?block :block/marker "NIL") ?marker]
  233. [(not= ?marker "DONE")]
  234. [(not= ?marker "CANCELED")]
  235. [(not= ?marker "CANCELLED")]
  236. [(<= ?d ?future)]
  237. (or-join [?repeated ?d ?day]
  238. [(true? ?repeated)]
  239. [(>= ?d ?day)])]
  240. date
  241. future-day
  242. db-model/file-graph-block-attrs))]
  243. (->> result
  244. db-model/sort-by-order-recursive
  245. db-utils/group-by-page))))))
  246. (defn <get-tag-pages
  247. [graph tag-id]
  248. (<q graph {:transact-db? true}
  249. '[:find [(pull ?page [:db/id :block/uuid :block/name :block/title :block/created-at :block/updated-at])]
  250. :in $ ?tag-id
  251. :where
  252. [?page :block/tags ?tag-id]
  253. [?page :block/name]]
  254. tag-id))
  255. (defn <get-tag-objects
  256. [graph class-id]
  257. (let [class-children (db-model/get-structured-children graph class-id)
  258. class-ids (distinct (conj class-children class-id))]
  259. (<q graph {:transact-db? true}
  260. '[:find [(pull ?b [*]) ...]
  261. :in $ [?class-id ...]
  262. :where
  263. [?b :block/tags ?class-id]]
  264. class-ids)))
  265. (defn <get-views
  266. [graph class-id view-feature-type]
  267. (<q graph {:transact-db? true}
  268. '[:find [(pull ?b [*]) ...]
  269. :in $ ?class-id ?view-feature-type
  270. :where
  271. [?b :logseq.property/view-for ?class-id]
  272. [?b :logseq.property.view/feature-type ?view-feature-type]]
  273. class-id
  274. view-feature-type))
  275. (defn <get-asset-with-checksum
  276. [graph checksum]
  277. (p/let [result (<q graph {:transact-db? true}
  278. '[:find [(pull ?b [*]) ...]
  279. :in $ ?checksum
  280. :where
  281. [?b :logseq.property.asset/checksum ?checksum]]
  282. checksum)]
  283. (some-> (first result)
  284. :db/id
  285. db/entity)))
  286. (defn <get-pdf-annotations
  287. [graph pdf-id]
  288. (p/let [result (<q graph {:transact-db? true}
  289. '[:find [(pull ?b [*]) ...]
  290. :in $ ?pdf-id
  291. :where
  292. [?b :logseq.property/asset ?pdf-id]]
  293. pdf-id)]
  294. result))
  295. (defn <get-block-properties-history
  296. [graph block-id]
  297. (p/let [result (<q graph {:transact-db? true}
  298. '[:find [(pull ?b [*]) ...]
  299. :in $ ?block-id
  300. :where
  301. [?b :logseq.property.history/block ?block-id]]
  302. block-id)]
  303. (->> (sort-by :block/created-at result)
  304. (map (fn [b] (db/entity (:db/id b)))))))
  305. (defn <task-spent-time
  306. [graph block-id]
  307. (p/let [history (<get-block-properties-history graph block-id)
  308. status-history (filter
  309. (fn [b] (= :logseq.task/status (:db/ident (:logseq.property.history/property b))))
  310. history)]
  311. (when (seq status-history)
  312. (let [time (loop [[last-item item & others] status-history
  313. time 0]
  314. (if item
  315. (let [last-status (:db/ident (:logseq.property.history/ref-value last-item))
  316. this-status (:db/ident (:logseq.property.history/ref-value item))]
  317. (if (and (= this-status :logseq.task/status.doing)
  318. (empty? others))
  319. (-> (+ time (- (tc/to-long (t/now)) (:block/created-at item)))
  320. (quot 1000))
  321. (let [time' (if (or
  322. (= last-status :logseq.task/status.doing)
  323. (and
  324. (not (contains? #{:logseq.task/status.canceled
  325. :logseq.task/status.backlog
  326. :logseq.task/status.done} last-status))
  327. (= this-status :logseq.task/status.done)))
  328. (+ time (- (:block/created-at item) (:block/created-at last-item)))
  329. time)]
  330. (recur (cons item others) time'))))
  331. (quot time 1000)))]
  332. [status-history time]))))