Extract all level 1 headlines as title

Hello,

I want to extract all level one headlines of notes as a title. My plan is to have that only for daily notes, because I use them for meeting notes, where the level one headline names the meeting. For all other notes, the “normal” title extraction is fine. But as a first try, I just try to add for all notes:

(defun org-roam--extract-titles-allheadlines ()
  (org-map-entries (lambda ()
                     (org-element-property :title (org-element-at-point)) )
                   "LEVEL=1")
  )

which works fine, e.g. I eval (M-: (org-roam--extract-titles-allheadlines) on on org file gives me

("A" "B")

which is correct.

After that, I do

(setq org-roam-title-sources '((title allheadlines) alias))

and (org-roam-db-clear) and (org-roam-db-build-cache) which runs fine and prints the status

(org-roam) total: Δ19, files-modified: Δ19, ids: Δ1, links: Δ43, tags: Δ13, titles: Δ19, refs: Δ0, deleted: Δ0

However, no new titles are extracted and shown in org-roam-find-file.

What can still be missing here?

Best Thanks!

I would put a break point with using edebug-defun on the org-roam--extract-titles function and see how your user option behaves in the actual runtime step-by-step.

If this was not something you would opt to do, then I would need to ask someone more knowledgeable to advise.

Ok, I got it. Completely me to blame:

has to be read as (title OR allheadlines) AND alias. Therefore allheadlines was never evaluated as title returned non-nil.

This is what I actually want:

(setq org-roam-title-sources '(title allheadlines alias))
1 Like

My final configuration is

(defun org-roam--extract-titles-allheadlines-for-daillies ()
  (when (string-prefix-p (concat (expand-file-name org-roam-directory) org-roam-dailies-directory) org-roam-file-name)
    (org-map-entries (lambda ()
                       (concat
                        (car (org-roam--extract-titles-title))
                        ": "
                        (org-element-property :title (org-element-at-point))))
                        "LEVEL=1")))

(setq org-roam-title-sources '((title headline) alias allheadlines-for-daillies))

which, only for the daillies gives me titles like

(Meeting, someproject) 2021-05-17: Zusammenarbeit Team1 Team2

which is what I want. Still, open for any ideas or optimizations!

1 Like

Hello,

recently, I have migrated to roam v2, which worked flawless for me. However, it seems that the customization of title extraction is not available anymore. There is no mention of it in Org-roam User Manual and the variable org-roam-title-sources seems to be gone.

Is that correct? If yes, is there a workaround or a plan when to re-introduce this feature?

Thanks!

I think perhaps org-roam-node-display-template will let you do this for the titles shown in org-roam-node-find.

You can see an example (in this case, adding backlinks) for this here in the wiki.

Not exactly what I am looking for. I think it might not really be possible anymore with v2, as now anything with an id is considered a headline, regardless of where it is located.

Would mind reminding us what the variable uses to do and telling us what you did with it?

The variable may be no longer available but you would be able to do most of the things you could do with v1; just differently.

Quoting an older version of the manual from Org-roam User Manual (link to wayback machine)

To control how Org-roam extracts titles, customize org-roam-title-sources. If all methods of title extraction return no results, the file-name is used as the note’s title.

User Option: org-roam-title-sources

The list of sources from which to retrieve a note title. Each element in the list is either:

  • a symbol – this symbol corresponds to a title retrieval function, which returns the list of titles for the current buffer
  • a list of symbols – symbols in the list are treated as with (1). The return value of this list is the first symbol in the list returning a non-nil value.

The return results of the root list are concatenated.

For example the setting: ’((title headline) alias) means the following:
Return the ’title + ’alias, if the title of current buffer is non-empty; Or return ’headline + ’alias otherwise.

[…]

Adding your own title extraction method requires two steps. First, define a method (defun org-roam--extract-titles-foo () ...), where foo a self-prescribed name for the title extraction method. This method takes no arguments, and returns a list of strings (titles). Finally, push the symbol foo into org-roam-title-sources. You may need to rebuild the cache from scratch to re-process all files to pick up the new titles.

So basically, it controls how to get a list of notes (which you can select using org-roam-find-note from a file.

Thank you. I remember now; I used to use it, too :slight_smile:

This means:

  1. First, look for the note’s title; if not found, look for the first headline (find title or headline)
  2. Second, look for aliases

You can also define your own function and use it.


Then… I agree with @doubleloop, and find his suggestion spot on.
What do you think is missing?

If you look at the docstring of org-roam-node-display-template, you get the following.

I agree that extracting the first headline when the title is not found may not make sense (although, you could do it if you code a new method). This new convention gives you the same level of freedom – I think it’s easier to use.

The wiki @doubleloop points to above contains collection of different methods that the community has come up with.

You might want to give a try and see if you can replicate what you use to do with the V1 method.

Each "field-name" is replaced with the return value of each
corresponding accessor function for ‘org-roam-node’, e.g.
"${title}" will be interpolated by the result of
‘org-roam-node-title’. You can also define custom accessors using
‘cl-defmethod’. For example, you can define:

  (cl-defmethod org-roam-node-my-title ((node org-roam-node))
    (concat "My " (org-roam-node-title node)))

and then reference it here or in the capture templates as
"${my-title}".

Sorry, just read the whole thread; so this is what you wanted to achieve all along?

Would you still want to do this ? I missed the context completely. Apologies.

But you should be able to do it. Node contains the path/filename, so from there you can get the first headline.

EDIT: I put this together below. I did a casual testing and ti works. But… I think it’s better to change the DB update (replace the value of title) for performance. The first-heading-or-title method gets executed for every candidate when you call org-roam-node-find. Not the best approach if you have a large number of nodes.

Anyway, take it as a quick proof of concept.

(cl-defmethod org-roam-node-first-heading-or-title ((node org-roam-node))
  (let ((title (org-roam-node-title node)))
    (when (= 0 (org-roam-node-level node))
      (with-current-buffer (find-file-noselect (org-roam-node-file node))
        (org-with-point-at 1
          (when (re-search-forward org-outline-regexp-bol nil t)
            (setq title (nth 4 (org-heading-components)))))))
    title))

(setq org-roam-node-display-template
      "${first-heading-or-title} ${tags}")
1 Like