Dataview JS Getting The Tree Root

I think I’ve mentioned it once (or twice) before, but one of the things which drives me when solving stuff, is personal interest. And whilst doing it the “normal” way is interesting enough, doing it using a mermaid graph, just peaked my interest. A lot.

So no matter what you feel :grinning: , the first solution to this request is the mermaid variant. It works for me, and I’m hoping it’ll work for you, as well. I built the data set matching my example in the previous reply, and lo and behold, here is the script actually doing this!

Script to build the mermaid graph
function toLetters(num) {
 let mod = num % 26,
     pow = num / 26 | 0,
     out = mod ? String.fromCharCode(64 + mod) : (--pow, 'Z')
  return pow ? toLetters(pow) + out : out;
}


const result = await dv.query(`
  LIST directive
  WHERE type AND type = [[Directive]]
  SORT file.name
`)
console.log(result)

if (result.successful) {
  let linkCounter = 0
  let linkSet = {}
  const nodes = result.value.values
  const mFront = "```mermaid\ngraph LR\n"
  let mNodes = [] 
  for (let node of nodes) {
    let leftIdx, leftLink, rightIdx, rightLink

    if ( !node.value?.length)
      node.value = [node.value]
      
    for (let connection of node.value) {
      if ( !connection ) 
        continue; // Bail out if it doesn't link anywhere
        
      leftLink = node.key.path.match(/\/([^\/]+)\.md/)[1]
      rightLink = connection.path.match(/\/([^\/]+)\.md/)[1]
      
      if ( leftLink in linkSet )
        leftIdx = linkSet[leftLink]
      else {
        linkCounter += 1
        leftIdx = toLetters( linkCounter )
        linkSet[leftLink] = leftIdx
      }
    
	  if ( rightLink && rightLink in linkSet )
        rightIdx = linkSet[rightLink]
      else {
        linkCounter += 1
        rightIdx = toLetters( linkCounter )
        linkSet[rightLink] = rightIdx
      }
      
      mNodes.push(` ${ leftIdx }[${ leftLink }] --> ${ rightIdx }[${ rightLink }]`)
    } 
  }
  let classList = []
  for (let i = 1; i<= linkCounter; i++) {
    classList.push( toLetters(i) )
  }
  const mEnd = "\nclass " + classList.join(",") + " internal-link\n```\n"
  
  dv.span(mFront + mNodes.join("\n") + mEnd)
} else 
  dv.paragraph("~~~~\n" + result.error + "\n~~~~")
Explanation of script

This is going to be a cursory explanation. It’s getting late, and it’s kind of complex script, so if you don’t know javascript at all, it’s hard to explain it…

  • The first function toLetters is a helper function, which converts number to a letter combination, in the sequence: A, B, C, … Y, Z, AA, AB, … These are later on used to name the nodes internally in the graph, and to build a classList ... internal-link at the end
  • The query is pretty basic, but it finds all files having the type of [[Directive]], and lists the corresponding directive field of that file. If this field is not a link, things will go astray… :smiley:
  • A random console.log(result) just to check the result of the query. This line could be removed, but I forgot
  • Given a successful result, lets do the magic
    • Reset linkCounter and linkSet. The linkSet will hold all links found in the query, and return the corresponding name when using the link as key into the set

    • Fetch the actual nodes from the query into nodes

    • Define mFront which is the front of the mermaid block

    • Define mNode as an empty array to hold all of the nodes to come

    • Loop through each of the files (or nodes) of the result, and do:

      • Define some helper variables, and if the node.value (aka the directive being singular) is not a list, then make it into a list of one element, just to ease the logic later on
      • Now loop through each of the connections (aka the directive fields) of a given file to create the mermaid link:
        • First of all, if there is no connection (aka directive field), then bail out, as there is nothing to link to
        • Pull out the unique(!) name from the file name and the connection into leftLink and rightLink
        • For each of these, check if it exists in the linkSet. If it does exist use the letter from the linkSet, and if it doesn’t exists get a new letter, and insert the new link into the linkSet. Store the letter into either leftIdx or rightIdx
        • Push the new link connection into mNodes, using the A[note] syntax for both sides. The note is used for display, whilst the A is used by mermaid to keep track of the nodes internally
    • Back out after the for loop, we need to retrack how many nodes we’ve created and add them into a classList, and we also need to end the mermaid script. All this is stored into mEnd. Part of what makes this tick is the concept of doing the class A,B,C,...,K internal-link line. This enables the mermaid engine to actually turn our nodes into the links.

    • Final action is simply to output the mermaid script using dv.span() and let the magic unfold

  • If however the result of the query failed, print a suitable error message

Below is an attached zip file to a folder containing all the needed files to showcase this script:
f54538.zip (3.4 KB)

image

There is one caveat with the script as of now, and that is if your files don’t have a unique name, it’s kind of random which file you’re getting. I haven’t found a way to add aliases to mermaid links.

Update: Credit to Hassle free linking in mermaid graphs - #9 by WhiteNoise for giving me the idea to use mermaid for this

1 Like