How I filter org-roam nodes with tags and properties for org-roam-node-find

You only need to pay attention to my/org-roam-node-find-with-tags-and-props. It accepts a list of tags and a list of props with their values in regular expression. Both lists can be empty.

(defun my/org-roam-unique-properties ()
  (let ((properties-for-nodes (mapcar #'car (org-roam-db-query [:select [properties] :from nodes])))
        (uniq-properties '()))
    (dolist (properties properties-for-nodes)
      (dolist (prop-val properties)
        (let ((prop (car prop-val)))
          (when (not (string= prop "ALLTAGS"))
            (add-to-list 'uniq-properties prop)))))
    uniq-properties))

;;;###autoload
(defun my/org-roam-node-find-with-tags-and-props (other-window required-tags prop-val-alist)
  (interactive (list
                (equal current-prefix-arg '(4))
                (completing-read-multiple "Comma-separated tags: " (org-roam-tag-completions) nil t nil nil)
                (let ((prop-val-alist '())
                      (unique-props (my/org-roam-unique-properties)))
                  (while (y-or-n-p "Match a property with a value regex?")
                    (let* ((prop (completing-read "Property: " unique-props nil t))
                           (val (read-string "Value Regex: ")))
                      (add-to-list 'prop-val-alist (cons prop val))))
                  prop-val-alist)))
  (org-roam-node-find other-window
                      nil
                      (lambda (node)
                        (let* ((node-tags (org-roam-node-tags node))
                               (node-props (org-roam-node-properties node)))
                          (and (cl-subsetp required-tags node-tags :test #'string=)
                               (seq-every-p (lambda (prop-val)
                                              (when-let ((val (alist-get (car prop-val) node-props nil nil #'string=)))
                                                (string-match (cdr prop-val) val)))
                                            prop-val-alist))))))

(use-package org-roam
  :commands (org-roam-tag-completions org-roam-db-query)
  :bind (("C-c n f" . org-roam-node-find)
         ("C-c n F" . my/org-roam-node-find-with-tags-and-props)
         ("C-c n i" . org-roam-node-insert)
         ("C-c n l" . org-roam-buffer-toggle)
         ("C-c n t" . org-roam-tag-add)
         ("C-c n T" . org-roam-tag-remove)
         :map org-roam-dailies-map
         ("d" . (lambda ()
                  (interactive)
                  (org-roam-dailies-goto-today "d")))
         ("y" . (lambda ()
                  (interactive)
                  (org-roam-dailies-goto-yesterday 1 "d")))
         ("t" . (lambda ()
                  (interactive)
                  (org-roam-dailies-goto-tomorrow 1 "d"))))
  :bind-keymap ("C-c n d" . org-roam-dailies-map)
  :custom
  (org-roam-directory "...")
  (org-roam-dailies-directory "journal/")
  (org-roam-capture-templates '(...))
  (org-roam-dailies-capture-templates '(...))
  :config
  (require 'org-roam-dailies)
  (org-roam-db-autosync-mode))

Is there a better way to do this? Perhaps, there is a built-in way to do this?

This is a weird question, but … What’s useful about properties and tags? (All I ever use is text search, aliases, links, backlinks and the ordinary org-roam asterisk-denoted tree structure.)

I don’t like unstructured text search. I like structured search with tags and properties.

I can have a type of notes where author property is set.

Unstructured text search would suck for searching for specific authors. Unstructured text search is non-deterministic. I want deterministic search process for deterministic search problems.

I want a database.

Interesting. What I do is have a note for the author, with a headline ‘publications’, under which I put subheadlines for each publication with links to the note corresponding to that publication.

This does not find nodes containing a specific tag, for me.

I have read many posts here and elsewhere, so far I have found no way.

How can I do that?

I don’t know how to make it happen for you. You are going to have to learn org-roam and some emacs lisp.

Ok. This is an imperfect way to find nodes with a single tag:

(defun nodes-with-tag (tag)
   (interactive "sTag: ")
   (let ((fn (lambda (node)
             (cl-subsetp (list tag) (org-roam-node-tags node) :test #'string=))))
     (org-roam-node-find t nil fn)))

It’s one tag, and you have to press tab for node and scroll through the list.

It is ridiculous that this sort of answer is not found by me after all these years, or that org-roam doesn’t have a function to do this, after mentioning tags in the docs.

Your code doesn’t have (org-roam-tag-completions) and completing-read in interactive.

That’s why you won’t get auto completion.

I don’t really remember all the tags I have.