Hello.
I think I’m starting to run into performance problems of org-roam-node-find
and the other completing-read
functions. They freeze up Emacs for a second or two each time I call them. I am pretty sure this is because they call org-roam-node-list
which gets all the nodes from the database, and then filter them from Emacs. I have around 2500 nodes which seems to be enough to make Emacs struggle on my computer. Is there any better way to do this?
I’ve thrown together a quick proof-of-concept that does some initial filtering in the query to the database so that it can do the search. One immediate problem with this is that it completely ignores aliases.
From ba60b5a4bfd4e32ebd074563ee0cdbe25e45a4e3 Mon Sep 17 00:00:00 2001
From: Leo <git@relevant-information.com>
Date: Thu, 21 Dec 2023 11:35:28 +0100
Subject: [PATCH] filter initial-input in sqlite
---
org-roam-node.el | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/org-roam-node.el b/org-roam-node.el
index 720c3a0..9a0d149 100644
--- a/org-roam-node.el
+++ b/org-roam-node.el
@@ -343,10 +343,11 @@ (cl-defmethod org-roam-populate ((node org-roam-node))
(org-roam-node-aliases node) alias-info)))
node)
-(defun org-roam-node-list ()
+(defun org-roam-node-list (&optional initial-input)
"Return all nodes stored in the database as a list of `org-roam-node's."
(let ((rows (org-roam-db-query
- "SELECT
+ (format
+ "SELECT
id,
file,
filetitle,
@@ -411,7 +412,9 @@ (defun org-roam-node-list ()
LEFT JOIN refs ON refs.node_id = nodes.id
GROUP BY nodes.id, tags.tag, aliases.alias )
GROUP BY id, tags )
-GROUP BY id")))
+ where title like '%%%%%s%%%%'
+
+GROUP BY id" initial-input))))
(cl-loop for row in rows
append (pcase-let* ((`(,id ,file ,file-title ,level ,todo ,pos ,priority ,scheduled ,deadline
,title ,properties ,olp ,atime ,mtime ,tags ,aliases ,refs)
@@ -529,7 +532,7 @@ (defun org-roam-node-read (&optional initial-input filter-fn sort-fn require-mat
for an example sort function.
If REQUIRE-MATCH, the minibuffer prompt will require a match.
PROMPT is a string to show at the beginning of the mini-buffer, defaulting to \"Node: \""
- (let* ((nodes (org-roam-node-read--completions filter-fn sort-fn))
+ (let* ((nodes (org-roam-node-read--completions filter-fn sort-fn (or initial-input "")))
(prompt (or prompt "Node: "))
(node (completing-read
prompt
@@ -550,7 +553,7 @@ (defun org-roam-node-read (&optional initial-input filter-fn sort-fn require-mat
(or (cdr (assoc node nodes))
(org-roam-node-create :title node))))
-(defun org-roam-node-read--completions (&optional filter-fn sort-fn)
+(defun org-roam-node-read--completions (&optional filter-fn sort-fn initial-input)
"Return an alist for node completion.
The car is the displayed title or alias for the node, and the cdr
is the `org-roam-node'.
@@ -560,7 +563,7 @@ (defun org-roam-node-read--completions (&optional filter-fn sort-fn)
for an example sort function.
The displayed title is formatted according to `org-roam-node-display-template'."
(let* ((template (org-roam-node--process-display-format org-roam-node-display-template))
- (nodes (org-roam-node-list))
+ (nodes (org-roam-node-list initial-input))
(nodes (if filter-fn
(cl-remove-if-not
(lambda (n) (funcall filter-fn n))
@@ -675,7 +678,7 @@ (defun org-roam-node-read--annotation (_node)
;;;; Linkage
;;;;; [id:] link
;;;###autoload
-(cl-defun org-roam-node-insert (&optional filter-fn &key templates info)
+(cl-defun org-roam-node-insert (&optional filter-fn initial-input &key templates info)
"Find an Org-roam node and insert (where the point is) an \"id:\" link to it.
FILTER-FN is a function to filter out nodes: it takes an `org-roam-node',
and when nil is returned the node will be filtered out.
@@ -692,7 +695,7 @@ (cl-defun org-roam-node-insert (&optional filter-fn &key templates info)
(setq beg (set-marker (make-marker) (region-beginning)))
(setq end (set-marker (make-marker) (region-end)))
(setq region-text (org-link-display-format (buffer-substring-no-properties beg end)))))
- (node (org-roam-node-read region-text filter-fn))
+ (node (org-roam-node-read (or region-text initial-input) filter-fn))
(description (or region-text
(org-roam-node-formatted node))))
(if (org-roam-node-id node)
base-commit: 5c06471c3a11348342719fd9011486455adeb701
--
2.43.0
Edit: you could then use it like this:
(defun my/org-roam-node-find ()
(interactive)
(org-roam-node-find nil (read-from-minibuffer "Nodes: " nil
(let ((km (make-keymap)))
(bind-key "SPC" #'exit-minibuffer)
km))))
The effect is that you call my/org-roam-node-find
with a key binding, type some initial query (without any results shown) and when you hit space you get the normal completing-read
interface filtered for your initial input. This is much faster for me.
Edit 2: Updated the patch to also include org-roam-node-insert