Recursively filtering bases

Hi all,
I have a couple of use cases for recursively filtering bases.

First, I’d like to list all notes that link directly to a given note. Here I’d like to also show notes that link to the notes that link to the parent note (this should be done recursively). I’ve been able to do this in dataviewjs, but I’d like to make use of base’s better performance and in-place YAML editing. Can this be done in bases?

Second, assuming this can be done, I’d like to take that recursively generated list of notes and filter another set of notes with that list. I.e., a note is only displayed in that base, if a specific property contains a link to at least one of the above generated list of notes.

The Obsidian bases documentation mentions recursive filtering but I can’t seem to find an example.

I hope this is understandable.
In any case, I’d appreciate any input.

Best,
M

Which of these do you want the rows to be?

  1. Each row is a note that has a first- or second-degree link to one specified note, such as the current note.
  2. The rows contain various notes, with columns that show their first- and second-degree backlinks.

For the first one, and presuming you’re talking about the currently active note, use these two filters:

filters:
  or:
    - file.hasLink(this)
    - file.links.filter(value.asFile().hasLink(this))


For the second one, click Properties and select file backlinks, and then click Add formula and add this formula:

file.backlinks.map(value.asFile().backlinks)

thanks for the input.

I meant the first option. Is it possible to do higher degree connections, too?

Yes. Continue the pattern to extend to an arbitrary number of degrees.

If you plan to go far, you could change tactics to use the repeat() and reduce() functions. But note that hasLink() seems to not refresh swiftly (this is not documented; it’s just from observation), so you’ll probably end up having to manually close and reopen notes/bases to force refreshes.

Almost forgot, I meant to address the other part once you said which version you wanted:

Add these two filters to your OR group:

note["your property"].filter(file(value).hasLink(this))

note["your property"].filter(file(value).links.filter(file(value).hasLink(this)))

Replace your property with your property key.

This too can be expanded to an arbitrary degree of your choosing.

1 Like

@dawni thanks a lot and sorry for the late response

I can’t quite seem to figure it out yet.

Could you give me an example how to filter to include also second degree backlink neighbors to a given note?
I tried file.links.filter(value.asFile().hasLink(file.links.filter(value.asFile().hasLink(this)))) but that passes a list when hasLink expects a single link. Playing around with .some didn’t get me much further either, unfortunately.

Filter for notes that have a…

Link to the current file:

file.hasLink(this)

Link to a link to the current file:

file.links.filter(value.asFile().hasLink(this))

Third-degree link to the current file:

file.links.filter(value.asFile().links.filter(value.asFile().hasLink(this)))

Fourth-degree link to the current file:

file.links.filter(value.asFile().links.filter(value.asFile().links.filter(value.asFile().hasLink(this))))

Awesome thanks, it works perfectly!

@dawni thanks again for your earlier help!

I’m continuing to play around with it and came across a related question: The recursive filtering is now considering all links in a given note (value.asFile().links.filter). Is it possible to consider only links found in a given property? I’m envisioning something along the lines of `value.asFile().properties[“my property”].filter’, but can’t seem to get it to work.

To clarify maybe again: I have a list of notes all in a given folder, they all have a property “Associated project” that contains one or more links to another set of “project notes”. Each project note has a “Parent projects” property. In each project note, I want to have a base listing all notes that have “associated project” linking to the project note directly and all notes that link to any of the children project notes. I’d like to look only at links in that property to avoid any circularity from appearing once I start linking to projects and other notes inside the note body.

Curious to hear if that’d be possible.

Best,

M

Your idea to filter all in-links for the ones that are in specified properties would work. Another probably more efficient way is to begin with only the values in Associated project and Parent projects, and then filtering out if they don’t lead to this project.

To begin with, this filter returns notes that are direct children of this note:

childOfHere: note["Parent projects"].contains(this)

Now for the “manual” method, where you would extend the pattern to however many degrees of descendants you want.

associatesHere: note["Associated project"].contains(this)
parentAssociatesHere: note["Associated project"].map(value.asFile().properties["Parent projects"].contains(this))
grandparentSssociatesHere: note["Associated project"].map(value.asFile().properties["Parent projects"].map(value.asFile().properties["Parent projects"].contains(this)))

Filters for thought

I suspect that the above would be good for your setup. But here’s more info if you want to play with other options.

Were your link-sequence represented in a text-type property, returning n + 1 generations of descendants would be quite straightforward, where you merely enter a number for n into this filter’s repeat(n):

" ".repeat(n).split("").reduce(
  if(
    acc == this || acc.isEmpty(),
    acc,
    acc.asFile().properties["your property"]
  ), note["your property"]
) == this

It becomes more complex because…

  • You have two propertie. So you would OR the filter with another version that connects to a map for the final Parent project connection.
  • You’re using lists. You would need to change == evaluations to contains and, I think, use a nested repeat. Double-emphasis on I think because I’m thinking as I’m explaining and haven’t planned it out entirely.

If you were willing to adapt either your structure or the above filter, you might have a method where you wouldn’t have to add a bunch of additional, increasingly longer filters. Just one or two filters ought to capture a nice long chain of links.

1 Like

Thanks a lot @dawni, this clarifies things!

I had to take the .contains() statement out of the mapping function to make it work, though. It also only worked when putting the .map()output into a list again:

- note["Associated project"].contains(this)
- note["Associated projects"].map(file(value).properties["Parent projects"]).contains(this.file.name)
- note["Associated projects"].map(list(file(value).properties["Parent projects"]).map(file(value).properties["Parent projects"])).contains(this.file.name)

You’re showing “Associated project” (singular) for the child and then “Associated projects” (plural) for deeper descendants. I didn’t realize you had two properties (unless that’s just a typo), but that could account for the issue.

And sounds like you might have some of your list properties formatted as strings. But if you’re getting the results you want and don’t care about the YAML format, then :tada: I’m glad you got it to where you want it to be!

It’s supposed to say "Associated projects” (i.e., it’s a typo).

I double-checked but as far as I can tell all my properties are defined as lists. Not sure what’s going on here.

You could do a quick check by making a new base with this filter:

!note["Associated projects"].isType("list") && note["Associated projects"]

If that returns any files, then check their YAML in source mode to make sure they’re lists instead of strings.

If that returns nothing, then you’re all good there.

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.