Merge notes based on metadata (with Templater and Dataview api)

I keep playing with Templater and test what I can do to automate my workflow. Recently I found out I can use the Dataview api inside Templater to pick some notes by given metadata and then I can merge them together in one file. For example I wrote this template for merging my daily notes when I want to archive them. It finds all notes in folder “Daily Notes”, filters them by metadata “type: daily”, sorts them by metadata “date”, takes from every note the section “#Log” and puts all that sections in one file.

<%*
let dv = DataviewAPI
let pages = dv.pages('"Daily Notes"').filter(p => p.type == "daily").sort(p => p.date, "asc")

let mergeContent = async (pages) => {
let merged = ""
  for (let p of pages) {
    let name = p.file.name
    let section = "Log"
    let link = "[[" + name + "#" + section + "]]"
    let noteContent = await tp.file.include(link)
    merged = merged + "### " + name + "\n" + noteContent + "\n"
  }
return merged
}
let resultContent = await mergeContent(pages)


%>---
---
***
<%* tR += resultContent %>

I also wrote some additional code to delete old files and replace backlinks, but I am a bit wary about it. It is not tested properly, and I am terrible with regexp, so please, be careful, if you want to use it, some data can be lost.

<%*

//Get notes names

let pageNames = pages.map(p => p.file.name)


// Replace backlinks

const replaceLink = async (filePath, oldLink, newLink) => {
  let file = app.vault.getAbstractFileByPath(filePath)
  let fileContent = await app.vault.read(file)
  let regexp = new RegExp("\\[\\[[^\\]]*" + oldLink + "\\|?[^\\]]*\\]\\]", "g")
  console.log(regex)
  let newContent = fileContent.replaceAll(regexp, "[[" + newLink + "]]")
  await app.vault.modify(file, newContent)
}

const replaceBacklinks = async (oldFileName, newFileName) => {
  let file = app.vault.getMarkdownFiles().find(f => f.basename == oldFileName)
  let backlinks = app.metadataCache.getBacklinksForFile(file).data
  let backlinkPaths = Object.keys(backlinks)
  for (let b of backlinkPaths){
    await replaceLink(b, oldFileName, newFileName)
  }
}

for (let p of pageNames) {
await replaceBacklinks (p, tp.file.title)
}

// Delete old notes

let files = app.vault.getMarkdownFiles()
for (p of pageNames) {
let note = files.find(f => f.basename == p)
await app.vault.trash(note)
}

%>

I’m still very much not a programmer, and honestly, I’m not entirely sure what I’m doing here, ha-ha. But it seems to work, so I thought it may be useful for someone else too.

4 Likes

Thank you for sharing this! This seems like a really nice and lightweight way to combine content! It could be generalized to a JavaScript function that takes a list of paths or links and then the template could be limited to just the function call :slight_smile: . Do you think this will work for sections or block-level links? That is, instead of the whole file, just parts?

I actually used section in my template, so the merged file include only the logs part of my daily notes. I’m not sure about blocks, but probably it should work too. The only problem is that you need the block-ids to exist and to be the same in all notes (so you supposed to write it manually I guess). Then you can use blog-id instead of the section name.

Or, if you already have the list of the links, you can use even easier code to extract content, it was discussed here: Extract note A content from a link in note B and pastit down the link. Is that Possible.

1 Like

Thanks! I think this is going to be very useful :slight_smile: