query.cljs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. (ns frontend.components.query
  2. (:require [clojure.string :as string]
  3. [frontend.components.file-based.query-table :as query-table]
  4. [frontend.components.file-based.query :as file-query]
  5. [frontend.components.query.result :as query-result]
  6. [frontend.components.query.view :as query-view]
  7. [frontend.context.i18n :refer [t]]
  8. [frontend.db :as db]
  9. [frontend.db-mixins :as db-mixins]
  10. [frontend.extensions.sci :as sci]
  11. [frontend.handler.editor :as editor-handler]
  12. [frontend.state :as state]
  13. [frontend.ui :as ui]
  14. [frontend.util :as util]
  15. [lambdaisland.glogi :as log]
  16. [rum.core :as rum]
  17. [frontend.config :as config]))
  18. (defn- built-in-custom-query?
  19. [title]
  20. (let [queries (get-in (state/sub-config) [:default-queries :journals])]
  21. (when (seq queries)
  22. (boolean (some #(= % title) (map :title queries))))))
  23. ;; TODO: Split this into file and DB graph versions. DB graph needlessly coupled to file graph args
  24. (rum/defcs custom-query-inner < rum/static
  25. [state {:keys [db-graph? dsl-query?] :as config} {:keys [query breadcrumb-show?]}
  26. {:keys [query-error-atom
  27. current-block
  28. table?
  29. page-list?
  30. view-f
  31. result
  32. group-by-page?]}]
  33. (let [{:keys [->hiccup ->elem inline-text page-cp map-inline]} config
  34. *query-error query-error-atom
  35. only-blocks? (:block/uuid (first result))
  36. blocks-grouped-by-page? (and group-by-page?
  37. (seq result)
  38. (coll? (first result))
  39. (:block/name (ffirst result))
  40. (:block/uuid (first (second (first result))))
  41. true)]
  42. (if @*query-error
  43. (do
  44. (log/error :exception @*query-error)
  45. [:div.warning.my-1 "Query failed: "
  46. [:p (.-message @*query-error)]])
  47. [:div.custom-query-results
  48. (cond
  49. (and (seq result) view-f)
  50. (let [result (try
  51. (sci/call-fn view-f result)
  52. (catch :default error
  53. (log/error :custom-view-failed {:error error
  54. :result result})
  55. [:div "Custom view failed: "
  56. (str error)]))]
  57. (util/hiccup-keywordize result))
  58. (and db-graph? (not (:built-in-query? config)))
  59. (query-view/query-result (assoc config :id (:db/id current-block))
  60. current-block result)
  61. (and (not db-graph?)
  62. (or page-list? table?))
  63. (query-table/result-table config current-block result {:page? page-list?} map-inline page-cp ->elem inline-text)
  64. ;; Normally displays built-in-query results
  65. (and (seq result) (or only-blocks? blocks-grouped-by-page?))
  66. (->hiccup result
  67. (assoc config
  68. :custom-query? true
  69. :current-block (:db/id current-block)
  70. :dsl-query? dsl-query?
  71. :query query
  72. :breadcrumb-show? (if (some? breadcrumb-show?)
  73. breadcrumb-show?
  74. true)
  75. :group-by-page? blocks-grouped-by-page?
  76. :ref? true)
  77. {:style {:margin-top "0.25rem"
  78. :margin-left "0.25rem"}})
  79. (seq result)
  80. (let [result (->>
  81. (for [record result]
  82. (if (map? record)
  83. (str (util/pp-str record) "\n")
  84. record))
  85. (remove nil?))]
  86. (when (seq result)
  87. [:ul
  88. (for [item result]
  89. [:li (str item)])]))
  90. (or (string/blank? query)
  91. (= query "(and)"))
  92. nil
  93. :else
  94. [:div.text-sm.mt-2.opacity-90 (t :search-item/no-result)])])))
  95. (rum/defc query-title
  96. [config title {:keys [result-count]}]
  97. (let [inline-text (:inline-text config)]
  98. [:div.custom-query-title.flex.justify-between.w-full
  99. [:span.title-text (cond
  100. (vector? title) title
  101. (string? title) (inline-text config
  102. (get-in config [:block :block/format] :markdown)
  103. title)
  104. :else title)]
  105. (when result-count
  106. [:span.opacity-60.text-sm.ml-2.results-count
  107. (str result-count (if (> result-count 1) " results" " result"))])]))
  108. (defn- calculate-collapsed?
  109. [current-block current-block-uuid {:keys [collapsed?]}]
  110. (let [temp-collapsed? (state/sub-collapsed current-block-uuid)
  111. collapsed?' (if (some? temp-collapsed?)
  112. temp-collapsed?
  113. (or collapsed?
  114. (:block/collapsed? current-block)))]
  115. collapsed?'))
  116. (rum/defc custom-query* < rum/reactive db-mixins/query
  117. [{:keys [*query-error db-graph? dsl-query? built-in-query? table? current-block] :as config}
  118. {:keys [builder query view collapsed?] :as q}
  119. *result]
  120. (let [collapsed?' (:collapsed? config)
  121. result' (rum/react *result)]
  122. (let [result (when *result (query-result/transform-query-result config q result'))
  123. ;; Args for displaying query header and results
  124. view-fn (if (keyword? view) (get-in (state/sub-config) [:query/views view]) view)
  125. view-f (and view-fn (sci/eval-string (pr-str view-fn)))
  126. page-list? (and (seq result) (some? (:block/name (first result))))
  127. opts {:query-error-atom *query-error
  128. :current-block current-block
  129. :table? table?
  130. :view-f view-f
  131. :page-list? page-list?
  132. :result result
  133. :group-by-page? (query-result/get-group-by-page q {:table? table?})}]
  134. (if (:custom-query? config)
  135. ;; Don't display recursive results when query blocks are a query result
  136. [:code (if dsl-query? (str "Results for " (pr-str query)) "Advanced query results")]
  137. (when-not (and built-in-query? (empty? result))
  138. [:div.custom-query (get config :attr {})
  139. (when (and (not db-graph?) (not built-in-query?))
  140. (file-query/custom-query-header config
  141. q
  142. {:query-error-atom *query-error
  143. :current-block current-block
  144. :table? table?
  145. :view-f view-f
  146. :page-list? page-list?
  147. :result result
  148. :collapsed? collapsed?'}))
  149. (when (and dsl-query? builder) builder)
  150. (if built-in-query?
  151. [:div {:style {:margin-left 2}}
  152. (ui/foldable
  153. (query-title config (:title q) {:result-count (count result)})
  154. (fn []
  155. (custom-query-inner config q opts))
  156. {:default-collapsed? collapsed?
  157. :title-trigger? true})]
  158. (when-not (:table? config)
  159. [:div.bd
  160. (when-not collapsed?'
  161. (custom-query-inner config q opts))]))])))))
  162. (rum/defc trigger-custom-query
  163. [config q]
  164. (let [[result set-result!] (rum/use-state nil)]
  165. (rum/use-effect!
  166. (fn []
  167. (query-result/trigger-custom-query! config q (:*query-error config) set-result!))
  168. [q])
  169. (when (util/atom? result)
  170. (custom-query* config q result))))
  171. (rum/defcs custom-query < rum/static
  172. {:init (fn [state]
  173. (let [db-graph? (config/db-based-graph? (state/get-current-repo))
  174. [{:keys [dsl-query? built-in-query?] :as config}
  175. {:keys [collapsed?]}] (:rum/args state)]
  176. ;; collapsed? not needed for db graphs
  177. (when (not db-graph?)
  178. (when-not (or built-in-query? dsl-query?)
  179. (when collapsed?
  180. (editor-handler/collapse-block! (or (:block/uuid (:block config))
  181. (:block/uuid config)))))))
  182. (assoc state :query-error (atom nil)))}
  183. [state {:keys [built-in-query?] :as config}
  184. {:keys [query collapsed?] :as q}]
  185. (ui/catch-error
  186. (ui/block-error "Query Error:" {:content (:query q)})
  187. (let [*query-error (:query-error state)
  188. db-graph? (config/db-based-graph? (state/get-current-repo))
  189. current-block-uuid (or (:block/uuid (:block config))
  190. (:block/uuid config))
  191. current-block (db/entity [:block/uuid current-block-uuid])
  192. ;; Get query result
  193. collapsed?' (calculate-collapsed? current-block current-block-uuid {:collapsed? (if-not db-graph? collapsed? false)})
  194. built-in-collapsed? (and collapsed? built-in-query?)
  195. table? (when-not db-graph?
  196. (or (get-in current-block [:block/properties :query-table])
  197. (and (string? query) (string/ends-with? (string/trim query) "table"))))
  198. config' (assoc config
  199. :db-graph? db-graph?
  200. :current-block current-block
  201. :current-block-uuid current-block-uuid
  202. :collapsed? collapsed?'
  203. :table? table?
  204. :built-in-query? (built-in-custom-query? (:title q))
  205. :*query-error *query-error)]
  206. (when (or built-in-collapsed? (not collapsed?'))
  207. (trigger-custom-query config' q)))))