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.