Use case or problem
As far as I could find out currently in bases it is only possible to list existing notes and there is no way to use other “sources” as files. I would like to create a Base which contains a list of dangling links (links to non existing files) which are sourced from filtered files.
filters:
and:
- formula["uncreated links"]
formulas:
uncreated links: file.links.filter(!value.asFile().isTruthy())
views:
- type: table
name: Table
order:
- file.name
- formula.uncreated links
from: How do I get a list of broken links in my vault using the base syntax?
Which gives me a list of files which have dangling links in a separate column. But I want to get a list of all the dangling links as a separate row in the base table.
Proposed solution
Allow to filter the files as it is currently but then give the ability to use a column with entries of all files returned from the filter to create row entries and possibly filter them again.
This would give the ability to create rows from a different “source” than from files.
Current workaround (optional)
Nothing found
Related feature requests (optional)
None
1 Like
I found this topic while researching the same problem.
Current best solution seems to be the plugin ipshing/obsidian-broken-links (wasn’t updated for 2 years at the time of writing this comment). To get the desirable output choose “Group by link“.
I doubt this is realistically solvable with Bases. Maybe with grouping feature expanded in few ways:
- when grouping by list - group by individual values from that list
- allow the same file to appear in multiple groups
- allow sorting of groups
But the feature request is valid for the goal, not necessarily for the means. Seeing what uncreated links are most numerable is important for the vault growth and organization. It needs to be a standard Obsidian feature in one way or another.
Maybe there is also a way to solve this with Datacore? I’ll get back with a solution if I find it.
Ok, here is a Datacore solution:
function groupPagesByLinkedPath(
pages,
linkPredicate = () => true,
minPages = 0
) {
const map = new Map();
for (const page of pages) {
for (const link of page.$links) {
let entry = map.get(link.path);
if (!entry) {
entry = { path: link.path, pages: [] };
map.set(link.path, entry);
}
entry.pages.push(page.$path);
}
}
return [...map.values()]
.filter(entry =>
linkPredicate(entry.path, entry)
&& entry.pages.length >= minPages
)
.sort((a, b) => b.pages.length - a.pages.length);
}
function fileExists(filePath, dc) {
const file = dc.useFile(filePath);
return file != null;
}
return function View() {
const pages = dc.useQuery("@page and length(filter($links, (l) => !l.$file)) > 0");
const paths = groupPagesByLinkedPath(pages, (p) => !fileExists(p, dc), 2);
const COLUMNS = [
{ id: "Uncreated link", value: link => dc.fileLink(link.path) },
{ id: "N", value: link => link.pages.length },
{ id: "Pages", value: link => link.pages.map(p => dc.fileLink(p)) }
];
return <dc.Table columns={COLUMNS} rows={paths} />;
//const linkifiedPaths = paths.map( (p) => ({ link: dc.fileLink(p.path), pages: p.pages.map((pp) => ({link: dc.fileLink(pp)})) }) );
//return <dc.List rows={linkifiedPaths} renderer={(p) => p.link} childSource={"pages"} maxChildDepth={1} />;
}
Not very elegant but does the job.