I wasn’t satisfied with not making the DQL version do what I intended for it to do, so I went about and made a dataviewjs version instead which delivers the result I was aiming for.
This version uses dv.view()
to trigger a script, so given that the script below is stored within the vault in the file _js/backlintable.js
, you can write the following to get the backlinks table for your current file:
`$= await dv.view("_js/backlinktable") `
And it could produce output like the following:
Other startup variants
You can also choose where to start or how many levels you want, by doing either of the following combinations:
dv.view("_js/backlinktable", { levels: 4} ) `
dv.view("_js/backlinktable", { start: dv.fileLink("Startpage")} ) `
dv.view("_js/backlinktable", { levels: 2, start: dv.fileLink("Startpage")} ) `
A few comments on the script
Basically this scripts loops through all the inlinks to the start file and for each of them it goes recursively down one level and repeats the same for all inlinks to that file. If none are found, it returns none for all those levels. The way to read this table is that a link in the table links to the page in its left column.
So for the first row we can deduce that “J → C” has a link to “C → A” which has a link to “A → none” which doesn’t link to anything besides the current file. Sorry for confusing names, but I needed to have something to help me keep track of which note linked where… 
The script does not handle if you make stuff loop around itself. Not in the current version at least. It could be handled by doing a lookup table to see if a given file has been seen before, and if so bail out possibly with a note on stating which loop is detected.
Installing the script
To make a dv.view()
script work it needs to be stored in a .js
file. These needs to be created outside of Obsidian (unless you’ve got some plugin allowing for these files to be created, like the Plaintext plugin). And it’s preferable to gather these script in some dedicated folder like _js
.
Below I’ve provided both the textual version here in the forum post, and a downloadable zipped version of the same script, which should be ready to be inserted into the aforementioned folder.
The source script used by the dv.view()
// Set debug to 0 for no debug output
// 1 for some debug related to the stack
// 2 for row information too
// 3 for Page information as well
const debug = 0 // Set to 0 for no debug output
let allRows = [] // Holds all rows of backlinks
let stack = [] // Intermediate stack for building the row
// Lift some information from the input
let start = input?.start ?? dv.fileLink(dv.current().file.name)
let levels = input?.levels ?? 3
/**** Start of main run ****/
if (debug) console.log("\n\nNew run")
// Initiate the backlink table build
traverseBacklinks(stack, levels, start, 0, allRows)
if (debug>1)console.log(allRows)
// Build column headers
let headers = []
for (let i = 1; i <= levels; i++) {
headers.push("Level " + i)
}
// Output the resulting table
dv.table(headers, allRows)
/**** End of main run ***/
/* traverseBacklinks
* When entered it pushes the current page (or "none") to the
* stack, and checks which level we're on. If we're at bottom
* level we push the entire row to allRows and bails out after
* pushing the last value on the stack.
* Otherwise we handle three different cases:
* 1) Previous level was "none" so no more inlinks
* 2) Current level doesn't have any inlinks
* 3) Loop through all inlinks
* In each of the cases we call ourselves with a suitable current
* and start over again. At the end of each call, make sure to
* pop the last value of the stack.
*/
function traverseBacklinks(stack, lev, current, colorIdx, allRows) {
if (debug) console.log("-->", lev, typeof current == "string" ? current: current.path)
stack.push(current)
if ( lev == 0 ) {
// Reached the required depth, so add current stack to
// the allRows array, and return
if (debug>1) console.log("Adding row: ", ...stack)
allRows.push([...stack.slice(1)])
if (debug) console.log("<++ row")
stack.pop()
return
}
// Go down one level, and check
const newLevel = lev - 1
if ( typeof current == "string") {
traverseBacklinks(stack, newLevel, "none", 0, allRows)
if (debug) console.log("<-- empty")
stack.pop()
return
}
const page = dv.page(current.path)
if (debug > 2) console.log(" Page: ", page)
let colorIndex = -1
if ( page.file.inlinks.length == 0) {
traverseBacklinks(stack, newLevel, "none", 0, allRows)
} else {
for (const back of page.file.inlinks.values) {
colorIndex += 1
traverseBacklinks(stack, newLevel, back, colorIndex, allRows)
}
}
if (debug) console.log("<--")
stack.pop()
}
backlinktable.js.zip (1.2 KB)
I’ve provided some comments within the script, but it’s not meant for educational purposes, so you do need to know some coding in order to understand what’s happening in the script.