Query for all direct and indirect links and group by tag

What I’m trying to do

My vault is designed with a particular hierarchy of note types where tasks are linked to projects, projects are linked to programs, programs are linked to strategies, and strategies are linked to objectives. The note type are identified via tags (#task, #project, etc.).

I would like to have a DV query (will like need to be DVjavascript) that shows all the notes that are part of the chain - a query for all direct and indirect links.

Things I have tried

The example in the Dataview documentation works, except I would like to group by note type (Task, Project, etc.). Sorry, I don’t have much experience - any help is very appreciated.

After a back-and-forth session with ChatGPT, I was able to get the following script to work for me.

// Define note types and their associated tags
let noteTypes = {
“Objective”: “objective”,
“Goal”: “goal”,
“Strategy”: “strategy”,
“Focus Area”: “focusArea”,
“Plan”: “plan”,
“Program”: “program”,
“Project”: “project”,
“Activity”: “activity”,
“Event”: “event”,
“Task Note”: “taskNote”,

};

function gatherAllLinks(dv, pagePath, visited = new Set()) {
if (visited.has(pagePath)) return ;
visited.add(pagePath);

const page = dv.page(pagePath);
if (!page || !page.file) return [];

let links = [pagePath]; // Include the current page itself
const allLinks = [...page.file.inlinks, ...page.file.outlinks];
allLinks.forEach(linkPath => links = [...links, ...gatherAllLinks(dv, linkPath, visited)]);
return links;

}

// Function to process links and format for the table
function processLinks(dv, links) {
let results = new Map(Object.keys(noteTypes).map(key => [key, ]));

links.forEach(path => {
    const pageInfo = dv.page(path);
    if (pageInfo && pageInfo.file && pageInfo.tags) {
        Object.entries(noteTypes).forEach(([type, tag]) => {
            if (pageInfo.tags.includes(tag)) {
                let formattedLink = `[[${pageInfo.file.name}]]`;
                let modifiedDate = pageInfo.file.mtime ? dv.date(pageInfo.file.mtime).toFormat("yyyy-MM-dd") : "Unknown";
                let createdDate = pageInfo.file.ctime ? dv.date(pageInfo.file.ctime).toFormat("yyyy-MM-dd") : "Unknown";
                
                // Preventing duplicates
                if (!results.get(type).some(entry => entry.link === formattedLink)) {
                    results.get(type).push({ link: formattedLink, modified: modifiedDate, created: createdDate });
                }
            }
        });
    }
});

return results;

}

const currentPagePath = dv.current().file.path;
let allLinks = gatherAllLinks(dv, currentPagePath, new Set());
let processedLinks = processLinks(dv, allLinks);

// Prepare and display the data in the table
let tableData = ;
processedLinks.forEach((value, key) => {
// Aggregate note details for each type
let notes = value.map(n => n.link).join(“
”);
let modified = value.map(n => n.modified).join(“
”);
let created = value.map(n => n.created).join(“
”);
tableData.push([key, notes, modified, created]);
});

dv.table([“Note Type”, “Note”, “Modified”, “Created”], tableData);

With some dummy notes the the table looks like this.