Преглед изворни кода

perf: table search

1. cache :block/title calculation
2. sort rows if only sorting changed
3. use string/includes? instead of fuzzy search
Tienson Qin пре 1 година
родитељ
комит
e85b05336d

+ 6 - 6
deps/db/src/logseq/db.cljs

@@ -447,12 +447,12 @@
   [db]
   (->>
    (d/datoms db :avet :block/name)
-   (distinct)
-   (map #(d/entity db (:e %)))
-   (filter page?)
-   (remove hidden?)
-   (remove (fn [page]
-             (common-util/uuid-string? (:block/name page))))))
+   (keep (fn [d]
+           (let [e (d/entity db (:e d))]
+             (when (and (page? e)
+                        (not (hidden? e))
+                        (not (common-util/uuid-string? (:block/name e))))
+               e))))))
 
 (defn built-in?
   "Built-in page or block"

+ 9 - 4
deps/db/src/logseq/db/frontend/entity_plus.cljc

@@ -32,9 +32,9 @@
       (or
        (get (.-kv e) k)
        (let [result (lookup-entity e k default-value)
-             result' (if (string? result)
-                       (db-content/special-id-ref->page-ref result
-                                                            (:block/refs e))
+             refs (:block/refs e)
+             result' (if (and (string? result) refs)
+                       (db-content/special-id-ref->page-ref result refs)
                        result)]
          (or result' default-value))))))
 
@@ -59,8 +59,13 @@
                                  (into {})))
              (lookup-entity e :block/properties nil)))
 
+         ;; cache :block/title
          :block/title
-         (get-block-title e k default-value)
+         (or (:block.temp/cached-title @(.-cache e))
+             (let [title (get-block-title e k default-value)]
+               (vreset! (.-cache e) (assoc @(.-cache e)
+                                           :block.temp/cached-title title))
+               title))
 
          :block/_parent
          (->> (lookup-entity e k default-value)

+ 96 - 94
deps/shui/src/logseq/shui/table/core.cljc

@@ -112,116 +112,116 @@
   [& prop-and-children]
   (let [[prop children] (get-prop-and-children prop-and-children)]
     [:div (merge {:class "ls-table w-full caption-bottom text-sm table-fixed"}
-                   prop)
+                 prop)
      children]))
 
 ;; FIXME: ux
 (defn- use-sticky-element!
   [^js/HTMLElement container target-ref]
   (rum/use-effect!
-    (fn []
-      (let [^js el (rum/deref target-ref)
-            ^js cls (.-classList el)
-            *ticking? (volatile! false)
-            el-top (-> el (.getBoundingClientRect) (.-top))
-            head-top (-> (get-head-container) (js/getComputedStyle) (.-height) (js/parseInt))
-            translate (fn [offset]
-                        (set! (. (.-style el) -transform) (str "translate3d(0, " offset "px , 0)"))
-                        (if (zero? offset)
-                          (.remove cls "translated")
-                          (.add cls "translated")))
-            *last-offset (volatile! 0)
-            handle (fn []
-                     (let [scroll-top (js/parseInt (.-scrollTop container))
-                           offset (if (> (+ scroll-top head-top) el-top)
-                                    (+ (- scroll-top el-top) head-top 1) 0)
-                           offset (js/parseInt offset)
-                           last-offset @*last-offset]
-                       (if (and (not (zero? last-offset))
-                             (not= offset last-offset))
-                         (let [dir (if (neg? (- offset last-offset)) -1 1)]
-                           (loop [offset' (+ last-offset dir)]
-                             (translate offset')
-                             (if (and (not= offset offset')
-                                   (< (abs (- offset offset')) 100))
-                               (recur (+ offset' dir))
-                               (translate offset))))
-                         (translate offset))
-                       (vreset! *last-offset offset)))
-            handler (fn [^js e]
-                      (when (not @*ticking?)
-                        (js/window.requestAnimationFrame
-                          #(do (handle) (vreset! *ticking? false)))
-                        (vreset! *ticking? true)))]
-        (.addEventListener container "scroll" handler)
-        #(.removeEventListener container "scroll" handler)))
-    []))
+   (fn []
+     (let [^js el (rum/deref target-ref)
+           ^js cls (.-classList el)
+           *ticking? (volatile! false)
+           el-top (-> el (.getBoundingClientRect) (.-top))
+           head-top (-> (get-head-container) (js/getComputedStyle) (.-height) (js/parseInt))
+           translate (fn [offset]
+                       (set! (. (.-style el) -transform) (str "translate3d(0, " offset "px , 0)"))
+                       (if (zero? offset)
+                         (.remove cls "translated")
+                         (.add cls "translated")))
+           *last-offset (volatile! 0)
+           handle (fn []
+                    (let [scroll-top (js/parseInt (.-scrollTop container))
+                          offset (if (> (+ scroll-top head-top) el-top)
+                                   (+ (- scroll-top el-top) head-top 1) 0)
+                          offset (js/parseInt offset)
+                          last-offset @*last-offset]
+                      (if (and (not (zero? last-offset))
+                               (not= offset last-offset))
+                        (let [dir (if (neg? (- offset last-offset)) -1 1)]
+                          (loop [offset' (+ last-offset dir)]
+                            (translate offset')
+                            (if (and (not= offset offset')
+                                     (< (abs (- offset offset')) 100))
+                              (recur (+ offset' dir))
+                              (translate offset))))
+                        (translate offset))
+                      (vreset! *last-offset offset)))
+           handler (fn [^js e]
+                     (when (not @*ticking?)
+                       (js/window.requestAnimationFrame
+                        #(do (handle) (vreset! *ticking? false)))
+                       (vreset! *ticking? true)))]
+       (.addEventListener container "scroll" handler)
+       #(.removeEventListener container "scroll" handler)))
+   []))
 
 ;; FIXME: another solution for the sticky header
 (defn- use-sticky-element2!
   [^js/HTMLDivElement target-ref]
   (rum/use-effect!
-    (fn []
-      (let [^js target (rum/deref target-ref)
-            ^js container (or (.closest target ".sidebar-item-list") (get-main-scroll-container))
-            ^js target-cls (.-classList target)
-            ^js table (.closest target ".ls-table-rows")
-            ^js table-footer (some-> table (.querySelector ".ls-table-footer"))
-            ^js page-el (.closest target ".page-inner")
-            *ticking? (volatile! false)
-            *el-top (volatile! (-> target (.getBoundingClientRect) (.-top)))
-            head-top (-> (get-head-container) (js/getComputedStyle) (.-height) (js/parseInt))
-            update-target-top! (fn []
-                                 (when (not (.contains target-cls "ls-fixed"))
-                                   (vreset! *el-top (+ (-> target (.getBoundingClientRect) (.-top))
+   (fn []
+     (let [^js target (rum/deref target-ref)
+           ^js container (or (.closest target ".sidebar-item-list") (get-main-scroll-container))
+           ^js target-cls (.-classList target)
+           ^js table (.closest target ".ls-table-rows")
+           ^js table-footer (some-> table (.querySelector ".ls-table-footer"))
+           ^js page-el (.closest target ".page-inner")
+           *ticking? (volatile! false)
+           *el-top (volatile! (-> target (.getBoundingClientRect) (.-top)))
+           head-top (-> (get-head-container) (js/getComputedStyle) (.-height) (js/parseInt))
+           update-target-top! (fn []
+                                (when (not (.contains target-cls "ls-fixed"))
+                                  (vreset! *el-top (+ (-> target (.getBoundingClientRect) (.-top))
                                                       (.-scrollTop container)))))
-            update-footer! (fn []
-                             (when table-footer
-                               (set! (. (.-style table-footer) -width) (str (.-scrollWidth table) "px"))))
-            update-target! (fn []
-                             (if (.contains target-cls "ls-fixed")
-                               (let [^js rect (-> table (.getBoundingClientRect))
-                                     width (.-clientWidth table)
-                                     left (.-left rect)]
-                                 (set! (. (.-style target) -width) (str width "px"))
-                                 (set! (. (.-style target) -left) (str left "px")))
-                               (do
-                                 (set! (. (.-style target) -width) "auto")
-                                 (set! (. (.-style target) -left) "0px")))
+           update-footer! (fn []
+                            (when table-footer
+                              (set! (. (.-style table-footer) -width) (str (.-scrollWidth table) "px"))))
+           update-target! (fn []
+                            (if (.contains target-cls "ls-fixed")
+                              (let [^js rect (-> table (.getBoundingClientRect))
+                                    width (.-clientWidth table)
+                                    left (.-left rect)]
+                                (set! (. (.-style target) -width) (str width "px"))
+                                (set! (. (.-style target) -left) (str left "px")))
+                              (do
+                                (set! (. (.-style target) -width) "auto")
+                                (set! (. (.-style target) -left) "0px")))
                              ;; update scroll
-                             (set! (. target -scrollLeft) (.-scrollLeft table)))
+                            (set! (. target -scrollLeft) (.-scrollLeft table)))
             ;; target observer
-            target-observe! (fn []
-                              (let [scroll-top (js/parseInt (.-scrollTop container))
-                                    table-in-top (+ scroll-top head-top)
-                                    table-bottom (.-bottom (.getBoundingClientRect table))
-                                    fixed? (and (> table-bottom (+ head-top 90))
-                                             (> table-in-top @*el-top))]
-                                (if fixed?
-                                  (.add target-cls "ls-fixed")
-                                  (.remove target-cls "ls-fixed"))
-                                (update-target!)))
-            target-observe-handle! (fn [^js _e]
-                                     (when (not @*ticking?)
-                                       (js/window.requestAnimationFrame
-                                         #(do (target-observe!) (vreset! *ticking? false)))
-                                       (vreset! *ticking? true)))
-            resize-observer (js/ResizeObserver. update-target!)
-            page-resize-observer (js/ResizeObserver. (fn [] (update-target-top!)))]
+           target-observe! (fn []
+                             (let [scroll-top (js/parseInt (.-scrollTop container))
+                                   table-in-top (+ scroll-top head-top)
+                                   table-bottom (.-bottom (.getBoundingClientRect table))
+                                   fixed? (and (> table-bottom (+ head-top 90))
+                                               (> table-in-top @*el-top))]
+                               (if fixed?
+                                 (.add target-cls "ls-fixed")
+                                 (.remove target-cls "ls-fixed"))
+                               (update-target!)))
+           target-observe-handle! (fn [^js _e]
+                                    (when (not @*ticking?)
+                                      (js/window.requestAnimationFrame
+                                       #(do (target-observe!) (vreset! *ticking? false)))
+                                      (vreset! *ticking? true)))
+           resize-observer (js/ResizeObserver. update-target!)
+           page-resize-observer (js/ResizeObserver. (fn [] (update-target-top!)))]
         ;; events
-        (.observe resize-observer container)
-        (.observe resize-observer table)
-        (some->> page-el (.observe page-resize-observer))
-        (.addEventListener container "scroll" target-observe-handle!)
-        (.addEventListener table "scroll" update-target!)
-        (.addEventListener table "resize" update-target!)
-        (update-footer!)
+       (.observe resize-observer container)
+       (.observe resize-observer table)
+       (some->> page-el (.observe page-resize-observer))
+       (.addEventListener container "scroll" target-observe-handle!)
+       (.addEventListener table "scroll" update-target!)
+       (.addEventListener table "resize" update-target!)
+       (update-footer!)
 
         ;; teardown
-        #(do (.removeEventListener container "scroll" target-observe!)
-           (.disconnect resize-observer)
-           (.disconnect page-resize-observer))))
-    []))
+       #(do (.removeEventListener container "scroll" target-observe!)
+            (.disconnect resize-observer)
+            (.disconnect page-resize-observer))))
+   []))
 
 (rum/defc table-header < rum/static
   [& prop-and-children]
@@ -244,7 +244,7 @@
   [& prop-and-children]
   (let [[prop children] (get-prop-and-children prop-and-children)]
     [:div.ls-table-row.flex.flex-row.items-center (merge {:class "border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted bg-gray-01 items-stretch"}
-                               prop)
+                                                         prop)
      children]))
 
 (rum/defc table-cell < rum/static
@@ -272,3 +272,5 @@
              :style {:z-index 101}}
             prop)
      children]))
+
+(def table-sort-rows impl/sort-rows)

+ 3 - 5
deps/shui/src/logseq/shui/table/impl.cljc

@@ -58,9 +58,7 @@
   (sort-rows rows sorting columns))
 
 (defn rows
-  [{:keys [columns sorting row-filter]
+  [{:keys [row-filter]
     :as opts}]
-  (let [rows' (:rows opts)
-        rows' (if row-filter (filter row-filter rows') rows')]
-    (cond-> rows'
-      (seq sorting) (sort-rows sorting columns))))
+  (let [rows' (:rows opts)]
+    (if row-filter (filter row-filter rows') rows')))

+ 25 - 17
src/main/frontend/components/all_pages.cljs

@@ -4,13 +4,13 @@
             [frontend.components.page :as component-page]
             [frontend.components.views :as views]
             [frontend.context.i18n :refer [t]]
+            [frontend.db :as db]
             [frontend.handler.page :as page-handler]
             [frontend.state :as state]
             [logseq.db :as ldb]
-            [frontend.db :as db]
+            [logseq.shui.ui :as shui]
             [promesa.core :as p]
-            [rum.core :as rum]
-            [logseq.shui.ui :as shui]))
+            [rum.core :as rum]))
 
 (defn- columns
   []
@@ -41,7 +41,8 @@
 (rum/defc all-pages < rum/static
   []
   (let [db (db/get-db)
-        [data set-data!] (rum/use-state (get-all-pages))
+        [data set-data!] (rum/use-state nil)
+        [loading? set-loading!] (rum/use-state true)
         columns' (views/build-columns {} (columns)
                                       {:with-object-name? false})
         view-entity (first (ldb/get-all-pages-views db))]
@@ -50,19 +51,26 @@
        (when-let [^js worker @state/*db-worker]
          (p/let [result-str (.get-page-refs-count worker (state/get-current-repo))
                  result (ldb/read-transit-str result-str)
+                 data (get-all-pages)
                  data (map (fn [row] (assoc row :block.temp/refs-count (get result (:db/id row) 0))) data)]
-           (set-data! data))))
+           (set-data! data)
+           (set-loading! false))))
      [])
     [:div.ls-all-pages.w-full.mx-auto
-     (views/view view-entity {:data data
-                              :set-data! set-data!
-                              :title-key :all-pages/table-title
-                              :columns columns'
-                              :on-delete-rows (fn [table selected-rows]
-                                                (shui/dialog-open!
-                                                 (component-page/batch-delete-dialog
-                                                  selected-rows false
-                                                  (fn []
-                                                    (when-let [f (get-in table [:data-fns :set-row-selection!])]
-                                                      (f {}))
-                                                    (set-data! (get-all-pages))))))})]))
+     (if loading?
+       [:div.space-y-2
+        (shui/skeleton {:class "h-8 w-1/3 mb-8"})
+        (shui/skeleton {:class "h-6 w-full"})
+        (shui/skeleton {:class "h-6 w-full"})]
+       (views/view view-entity {:data data
+                                :set-data! set-data!
+                                :title-key :all-pages/table-title
+                                :columns columns'
+                                :on-delete-rows (fn [table selected-rows]
+                                                  (shui/dialog-open!
+                                                   (component-page/batch-delete-dialog
+                                                    selected-rows false
+                                                    (fn []
+                                                      (when-let [f (get-in table [:data-fns :set-row-selection!])]
+                                                        (f {}))
+                                                      (set-data! (get-all-pages))))))}))]))

+ 22 - 28
src/main/frontend/components/views.cljs

@@ -13,18 +13,17 @@
             [frontend.date :as date]
             [frontend.db :as db]
             [frontend.handler.property :as property-handler]
-            [frontend.common.search-fuzzy :as fuzzy]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [goog.dom :as gdom]
             [dommy.core :as dom]
-            [goog.functions :refer [debounce]]
             [logseq.db.frontend.property :as db-property]
             [logseq.db.frontend.property.type :as db-property-type]
             [logseq.shui.ui :as shui]
             [rum.core :as rum]
-            [frontend.mixins :as mixins]))
+            [frontend.mixins :as mixins]
+            [logseq.shui.table.core :as table-core]))
 
 (rum/defc header-checkbox < rum/static
   [{:keys [selected-all? selected-some? toggle-selected-all!]}]
@@ -865,11 +864,6 @@
               (ui/icon "x"))]))
         filters)])))
 
-(defn- fuzzy-matched?
-  [input s]
-  (pos? (fuzzy/score (string/lower-case (str input))
-                     (string/lower-case (str s)))))
-
 (defn- row-matched?
   [row input filters]
   (and
@@ -877,7 +871,8 @@
    (if (string/blank? input)
      true
      (when row
-       (fuzzy-matched? input (:block/title row))))
+       ;; fuzzy search is too slow
+       (string/includes? (string/lower-case (:block/title row)) (string/lower-case input))))
    ;; filters check
    (every?
     (fn [[property-ident operator match]]
@@ -916,7 +911,7 @@
                     (boolean (empty? (set/intersection (set value') match))))))
 
               :text-contains
-              (some #(fuzzy-matched? match (get-property-value-content %)) value')
+              (some #(string/includes? (string/lower-case (get-property-value-content %)) (string/lower-case match)) value')
 
               :text-not-contains
               (not-any? #(string/includes? (str (get-property-value-content %)) match) value')
@@ -1025,13 +1020,12 @@
        (property-handler/set-block-property! repo (:db/id entity) :logseq.property.table/sized-columns sized-columns))}))
 
 (rum/defc table-view < rum/static
-  [table option row-selection add-new-object! ready?]
+  [table option row-selection add-new-object!]
   (let [selected-rows (shui/table-get-selection-rows row-selection (:rows table))]
     (shui/table
      (let [columns' (:columns table)
            rows (:rows table)]
        [:div.ls-table-rows.content.overflow-x-auto.force-visible-scrollbar
-        {:class (when (not ready?) "invisible")}
         [:div.relative
          (table-header table columns' option selected-rows)
 
@@ -1100,7 +1094,7 @@
                         (fn [row]
                           (row-matched? row input filters)))
         [row-filter set-row-filter!] (rum/use-state row-filter-fn)
-        debounced-set-row-filter! (debounce set-row-filter! 200)
+        [input-filters set-input-filters!] (rum/use-state [input filters])
         [row-selection set-row-selection!] (rum/use-state {})
         columns (sort-columns columns ordered-columns)
         table (shui/table-option {:data data
@@ -1120,19 +1114,27 @@
                                              :set-sized-columns! set-sized-columns!
                                              :set-row-selection! set-row-selection!
                                              :add-new-object! add-new-object!}})
-
-        [ready?, set-ready!] (rum/use-state false)
         *view-ref (rum/use-ref nil)
         display-type (or (:db/ident (get view-entity :logseq.property.view/type))
                          :logseq.property.view/type.table)]
 
     (rum/use-effect!
-     (fn [] (debounced-set-row-filter!
-             (fn []
-               (fn [row]
-                 (row-matched? row input filters)))))
+     (fn []
+       (let [new-input-filters [input filters]]
+         (when-not (= input-filters new-input-filters)
+           (set-input-filters! [input filters])
+           (set-row-filter!
+            (fn []
+              (fn [row]
+                (row-matched? row input filters)))))))
      [input filters])
 
+    (rum/use-effect!
+     (fn []
+       (let [data' (table-core/table-sort-rows data sorting columns)]
+         (set-data! data')))
+     [sorting])
+
     [:div.flex.flex-col.gap-2.grid
      {:ref *view-ref}
      [:div.flex.flex-wrap.items-center.justify-between.gap-1
@@ -1160,14 +1162,6 @@
 
      (filters-row table)
 
-     (rum/use-effect! #(js/setTimeout (fn [] (set-ready! true)) 16) [])
-     (rum/use-effect!
-      (fn []
-        (when-let [^js cnt (and ready? (some-> (rum/deref *view-ref) (.closest ".is-node-page")))]
-          (.setAttribute cnt "data-ready" true)
-          #(.removeAttribute cnt "data-ready")))
-      [ready?])
-
      (case display-type
        :logseq.property.view/type.list
        (list-view (:config option) view-entity (:rows table))
@@ -1175,7 +1169,7 @@
        :logseq.property.view/type.card
        (card-view (:config option) view-entity (:rows table))
 
-       (table-view table option row-selection add-new-object! ready?))]))
+       (table-view table option row-selection add-new-object!))]))
 
 (rum/defc view
   "Provides a view for data like query results and tagged objects, multiple