Skip to content

Commit

Permalink
perf: debounce search
Browse files Browse the repository at this point in the history
  • Loading branch information
tiensonqin committed Sep 21, 2024
1 parent 2738f2c commit c392a9c
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 104 deletions.
15 changes: 9 additions & 6 deletions src/main/frontend/common/search_fuzzy.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

(defn clean-str
[s]
(string/replace (string/lower-case s) #"[\[ \\/_\]\(\)]+" ""))
(string/lower-case (string/replace (string/lower-case s) #"[\[ \\/_\]\(\)]+" "")))

(defn char-array
[s]
Expand All @@ -31,22 +31,25 @@
(defn score
[oquery ostr]
(let [query (clean-str oquery)
s (clean-str ostr)]
original-s (clean-str ostr)]
(loop [q (seq (char-array query))
s (seq (char-array s))
s (seq (char-array original-s))
mult 1
idx MAX-STRING-LENGTH
score' 0]
(cond
;; add str-len-distance to score, so strings with matches in same position get sorted by length
;; boost score if we have an exact match including punctuation
(empty? q) (+ score'
(str-len-distance query s)
(str-len-distance query original-s)
(cond
(<= 0 (.indexOf ostr oquery))
(string/starts-with? original-s query)
(+ MAX-STRING-LENGTH 10)

(<= 0 (.indexOf original-s query))
MAX-STRING-LENGTH

(<= 0 (.indexOf (string/lower-case ostr) (string/lower-case oquery)))
(<= 0 (.indexOf original-s query))
(- MAX-STRING-LENGTH 0.1)

:else
Expand Down
184 changes: 95 additions & 89 deletions src/main/frontend/components/cmdk/core.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@
(take 5 items))))
node-exists? (let [blocks-result (keep :source-block (get-in results [:nodes :items]))]
(when-not (string/blank? input)
(or (db/get-page (string/trim (last (string/split input "/"))))
(or (some-> (last (string/split input "/"))
string/trim
db/get-page)
(some (fn [block]
(and
(:block/tags block)
Expand Down Expand Up @@ -223,7 +225,7 @@
new-result)))]))

(defn- page-item
[repo page q]
[repo page]
(let [entity (db/entity [:block/uuid (:block/uuid page)])
source-page (model/get-alias-source-page repo (:db/id entity))
icon (cond
Expand All @@ -235,7 +237,7 @@
"whiteboard"
:else
"page")
title (highlight-content-query (title/block-unique-title page) q)
title (title/block-unique-title page)
title' (if source-page (str title " -> alias: " (:block/title source-page)) title)]
(hash-map :icon icon
:icon-theme :gray
Expand Down Expand Up @@ -267,9 +269,11 @@
(swap! !results assoc-in [:current-page :status] :loading)
(p/let [blocks (search/block-search repo @!input opts)
blocks (remove nil? blocks)
blocks (search/fuzzy-search blocks @!input {:limit 100
:extract-fn :block/title})
items (keep (fn [block]
(if (:page? block)
(page-item repo block !input)
(page-item repo block)
(block-item repo block current-page !input))) blocks)]
(if (= group :current-page)
(let [items-on-current-page (filter :current-page? items)]
Expand Down Expand Up @@ -656,23 +660,17 @@
e-type (gobj/getValueByKeys e "type")
composing-end? (= e-type "compositionend")
!input (::input state)
input-ref @(::input-ref state)
!load-results-throttled (::load-results-throttled state)]
input-ref @(::input-ref state)]

;; update the input value in the UI
(reset! !input input)
(set! (.-value input-ref) input)

(reset! (::input-changed? state) true)

;; ensure that there is a throttled version of the load-results function
(when-not @!load-results-throttled
(reset! !load-results-throttled (gfun/throttle load-results 50)))

;; retrieve the load-results function and update all the results
(when (or (not composing?) composing-end?)
(when-let [load-results-throttled @!load-results-throttled]
(load-results-throttled :default state))))))
(load-results :default state)))))

(defn- open-current-item-link
"Opens a link for the current item if a page or block. For pages, opens the
Expand Down Expand Up @@ -777,14 +775,23 @@
[state all-items opts]
(let [highlighted-item @(::highlighted-item state)
input @(::input state)
input-ref (::input-ref state)]
input-ref (::input-ref state)
debounced-on-change (rum/use-callback
(gfun/debounce
(fn [e]
(let [new-value (.-value (.-target e))]
(handle-input-change state e)
(when-let [on-change (:on-input-change opts)]
(on-change new-value))))
150)
[])]
;; use-effect [results-ordered input] to check whether the highlighted item is still in the results,
;; if not then clear that puppy out!
;; This was moved to a functional component
(rum/use-effect! (fn []
(when (and highlighted-item (= -1 (.indexOf all-items (dissoc highlighted-item :mouse-enter-triggered-highlight))))
(reset! (::highlighted-item state) nil)))
[all-items])
[all-items])
(rum/use-effect! (fn [] (load-results :default state)) [])
[:div {:class "bg-gray-02 border-b border-1 border-gray-07"}
[:input.cp__cmdk-search-input
Expand All @@ -793,30 +800,29 @@
:autoComplete "off"
:placeholder (input-placeholder false)
:ref #(when-not @input-ref (reset! input-ref %))
:on-change (fn [e]
(let [new-value (.-value (.-target e))]
(handle-input-change state e)
(when-let [on-change (:on-input-change opts)]
(on-change new-value))))
:on-change debounced-on-change
:on-blur (fn [_e]
(when-let [on-blur (:on-input-blur opts)]
(on-blur input)))
:on-composition-end (fn [e] (handle-input-change state e))
:on-key-down (fn [e]
(p/let [value (.-value @input-ref)
last-char (last value)
backspace? (= (util/ekey e) "Backspace")
filter-group (:group @(::filter state))
slash? (= (util/ekey e) "/")
namespace-pages (when (and slash? (contains? #{:whiteboards} filter-group))
(search/block-search (state/get-current-repo) (str value "/") {}))
namespace-page-matched? (some #(string/includes? % "/") namespace-pages)]
(when (and filter-group
(or (and slash? (not namespace-page-matched?))
(and backspace? (= last-char "/"))
(and backspace? (= input ""))))
(reset! (::filter state) nil)
(load-results :default state))))
:on-composition-end (gfun/debounce (fn [e] (handle-input-change state e)) 150)
:on-key-down (gfun/debounce
(fn [e]
(p/let [value (.-value @input-ref)
last-char (last value)
backspace? (= (util/ekey e) "Backspace")
filter-group (:group @(::filter state))
slash? (= (util/ekey e) "/")
namespace-pages (when (and slash? (contains? #{:whiteboards} filter-group))
(search/block-search (state/get-current-repo) (str value "/") {}))
namespace-page-matched? (some #(string/includes? % "/") namespace-pages)]
(when (and filter-group
(or (and slash? (not namespace-page-matched?))
(and backspace? (= last-char "/"))
(and backspace? (= input ""))))
(reset! (::filter state) nil)
;; (load-results :default state)
)))
150)
:default-value input}]]))

(defn rand-tip
Expand Down Expand Up @@ -921,49 +927,48 @@

(rum/defcs cmdk
< rum/static
rum/reactive
{:will-mount
(fn [state]
(when-not (:sidebar? (last (:rum/args state)))
(shortcut/unlisten-all!))
state)

:will-unmount
(fn [state]
(when-not (:sidebar? (last (:rum/args state)))
(shortcut/listen-all!))
state)}
{:init (fn [state]
(let [search-mode (:search/mode @state/state)
opts (last (:rum/args state))]
(assoc state
::ref (atom nil)
::filter (if (and search-mode
(not (contains? #{:global :graph} search-mode))
(not (:sidebar? opts)))
(atom {:group search-mode})
(atom nil))
::input (atom (or (:initial-input opts) "")))))
:will-unmount (fn [state]
(state/set-state! :search/mode nil)
state)}
(mixins/event-mixin
(fn [state]
(let [ref @(::ref state)]
(mixins/on-key-down state {}
{:target ref
:all-handler (fn [e _key] (keydown-handler state e))})
(mixins/on-key-up state {} (fn [e _key]
(keyup-handler state e))))))
(rum/local false ::shift?)
(rum/local false ::meta?)
(rum/local nil ::highlighted-group)
(rum/local nil ::highlighted-item)
(rum/local default-results ::results)
(rum/local nil ::load-results-throttled)
(rum/local nil ::scroll-container-ref)
(rum/local nil ::input-ref)
(rum/local false ::input-changed?)
rum/reactive
{:will-mount
(fn [state]
(when-not (:sidebar? (last (:rum/args state)))
(shortcut/unlisten-all!))
state)

:will-unmount
(fn [state]
(when-not (:sidebar? (last (:rum/args state)))
(shortcut/listen-all!))
state)}
{:init (fn [state]
(let [search-mode (:search/mode @state/state)
opts (last (:rum/args state))]
(assoc state
::ref (atom nil)
::filter (if (and search-mode
(not (contains? #{:global :graph} search-mode))
(not (:sidebar? opts)))
(atom {:group search-mode})
(atom nil))
::input (atom (or (:initial-input opts) "")))))
:will-unmount (fn [state]
(state/set-state! :search/mode nil)
state)}
(mixins/event-mixin
(fn [state]
(let [ref @(::ref state)]
(mixins/on-key-down state {}
{:target ref
:all-handler (fn [e _key] (keydown-handler state e))})
(mixins/on-key-up state {} (fn [e _key]
(keyup-handler state e))))))
(rum/local false ::shift?)
(rum/local false ::meta?)
(rum/local nil ::highlighted-group)
(rum/local nil ::highlighted-item)
(rum/local default-results ::results)
(rum/local nil ::scroll-container-ref)
(rum/local nil ::input-ref)
(rum/local false ::input-changed?)
[state {:keys [sidebar?] :as opts}]
(let [*input (::input state)
search-mode (:search/mode @state/state)
Expand All @@ -987,22 +992,23 @@
(search-only state (string/capitalize (name group-filter)))])

(let [items (filter
(fn [[_group-name group-key group-count _group-items]]
(and (not= 0 group-count)
(if-not group-filter true
(or (= group-filter group-key)
(and (= group-filter :nodes)
(= group-key :current-page))
(and (contains? #{:create} group-filter)
(= group-key :create))))))
results-ordered)]
(if (seq items)
(fn [[_group-name group-key group-count _group-items]]
(and (not= 0 group-count)
(if-not group-filter true
(or (= group-filter group-key)
(and (= group-filter :nodes)
(= group-key :current-page))
(and (contains? #{:create} group-filter)
(= group-key :create))))))
results-ordered)]
(when-not (= ["Filters"] (map first items))
(if (seq items)
(for [[group-name group-key _group-count group-items] items]
(let [title (string/capitalize group-name)]
(result-group state title group-key group-items first-item sidebar?)))
[:div.flex.flex-col.p-4.opacity-50
(when-not (string/blank? @*input)
"No matched results")]))]
"No matched results")])))]
(when-not sidebar? (hints state))]))

(rum/defc cmdk-modal [props]
Expand Down
16 changes: 8 additions & 8 deletions src/main/frontend/components/editor.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,14 @@
[id format embed? db-tag? q current-pos input pos]
(let [db? (config/db-based-graph? (state/get-current-repo))
q (string/trim q)
[matched-pages set-matched-pages!] (rum/use-state nil)]
(rum/use-effect! (fn []
(when-not (string/blank? q)
(p/let [result (if db-tag?
(editor-handler/get-matched-classes q)
(editor-handler/<get-matched-blocks q {:nlp-pages? true}))]
(set-matched-pages! result))))
[q])
[matched-pages set-matched-pages!] (rum/use-state nil)
search-f (fn []
(when-not (string/blank? q)
(p/let [result (if db-tag?
(editor-handler/get-matched-classes q)
(editor-handler/<get-matched-blocks q {:nlp-pages? true}))]
(set-matched-pages! result))))]
(rum/use-effect! search-f [(mixins/use-debounce 50 q)])
(let [matched-pages (if (string/blank? q)
(->> (map (fn [title] {:block/title title
:nlp-date? true})
Expand Down
11 changes: 10 additions & 1 deletion src/main/frontend/mixins.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
(:require [rum.core :as rum]
[goog.dom :as dom]
[frontend.util :refer [profile] :as util]
[frontend.state :as state])
[frontend.state :as state]
[goog.functions :as gfun])
(:import [goog.events EventHandler]))

(defn detach
Expand Down Expand Up @@ -156,3 +157,11 @@
(profile
(str "Render " desc)
(render-fn state))))})

(defn use-debounce
"A rumext custom hook that debounces the value changes"
[ms value]
(let [[state update-fn] (rum/use-state value)
update-fn (rum/use-callback (gfun/debounce update-fn ms) [])]
(rum/use-effect! #(update-fn value) #js [value])
state))

0 comments on commit c392a9c

Please sign in to comment.