Filter by outgoing links and status and filter results further by status

What I’m trying to do

i have files which i want to filter by status “A” AND 1 or more outlinks, and the results of which (the files relating to the outlinks) i want to filter again by status “B” OR “C”

  • so files that have at least one outlink to files having either one of these statuses i want pushed to the table

i am interested in dataview or dataviewjs (which i tried) solutions – my files have frontmatter fields status: A, B, etc.

Things I have tried

chat robot help and lost hours with it

thanks

Try this code:

~~~dataview
list 
where status = "A" and (contains(map(file.outlinks, (x) => x.status), "B") or contains(map(file.outlinks, (x) => x.status), "C"))
~~~

(Replace the tidles ~ with backticks `)

1 Like

is there a way to push the file(s) with either B or C status to a table as well?

also, i tried to remap the query with await dv.query to be able to perform further (dv.io.load) operations on it, but no dice…the robot cannot help

what’s the dataviewjs way of doing the table i wonder — having a really bad day on this

I think I’m misunderstanding you. Do you want to push all files with B or C status or just those that outlink properly?

Anyways, here’s DJS code that outputs a table with the files and their content (assuming the first option)

~~~dataviewjs
const statusFile = app.vault.getMarkdownFiles().filter((file) => {
	const dvFile = dv.page(file.path)
    const status = app.metadataCache.getFileCache(file)?.frontmatter?.["status"];
    if (!status) {
	    return false;
    } else if (status === "A") {
	    const outlinks = Array.from(dvFile.file.outlinks)
	    const outlinkStatus = Array.from(outlinks?.map(x => app.metadataCache.getFileCache(x)?.frontmatter?.["status"]));
	    return outlinkStatus.includes("B") || outlinkStatus.includes("C"); 
    } else if (status === "B" || status === "C") {
	    return true;
    }
}).sort((a,b) => a.name.localeCompare(b.name));
const fileArray = []
for (let file of statusFile) {
	let dvFile = dv.page(file.path)
	let fileContent = await dv.io.load(file.path)
	fileArray.push([dvFile.file.link, fileContent])
}
console.log(fileArray)
dv.table(["File", "Content"], fileArray)
~~~

Also, if possible, could you unmark this topic as solved?

1 Like

yes, this dvjs script didn’t give me the same results as the plain dv query, but with some effort i managed to make it work

```dataviewjs
// Get all markdown files in the vault
const allFiles = app.vault.getMarkdownFiles();

const filteredFiles = [];

for (const file of allFiles) {
    const dvFile = dv.page(file.path);
    const status = app.metadataCache.getFileCache(file)?.frontmatter?.["status"];

    if (status === "A" && dvFile?.file?.outlinks) {
        const outlinks = dvFile.file.outlinks;

        for (const link of outlinks) {
            const linkedFile = dv.page(link.path);
            const linkedStatus = linkedFile?.file?.frontmatter?.["status"];

            if (linkedStatus === "B" || linkedStatus === "C") {
                const content = await dv.io.load(file.path); // Load file content

                //Regex pattern used to match crawled content
                const regex = /SOMEREGEX/gs;

                if (!regex.test(content)) {
                    filteredFiles.push(file.path); // Use the original file path
                    break; // Exit loop once a match is found for this file
                }
            }
        }
    }
}

const fileLinks = filteredFiles.map(file => [dv.page(file).file.link]);
dv.table(["Outlinked File"], fileLinks);
```

i’d rather leave an effort on your part rewarded with a tick as it worked and i failed to specify anything else

i am really grateful for your input!!

Glad it worked out for you! :slight_smile:

There are many ways to Rome, the saying says, and I just wanted to show some variant queries achieving some of the goals you’ve talked about.

My test file setup

First of all, let me describe my test file setup based upon your hints with a simple table query:

All files follow the name of the status early in the name, and which other files it links to, with “R” being a non-interesting outlink just to show that filtering do work.

My basic LIST query

The query below aims to pick out any files with status “A”, having outlinks of status “B” or “C”, and these files are listed under each of the original files:

```dataview
LIST statusMatches
WHERE status = "A"
FLATTEN list(filter(
  file.outlinks,
  (f) => contains( list("B", "C"), f.status ))) as statusMatches
WHERE length(statusMatches) > 0
```

The trick here is to list(filter( ... )) on the file.outlinks to pick out only those with matching statuses, and using FLATTEN to keep these links in statusMatches. Finally we are only interested in files actually having any in this list.

Output of this query:
image

Two variants of this query from dataviewjs

You said you had issues doing the similar query from within dataviewjs, so here are two queries which would produce just the same output. The first query reuse the DQL query from above, which would allow for further handling in the result.successful branch below:

```dataviewjs

const result = await dv.query(`
  LIST statusMatches
  WHERE status = "A"
  FLATTEN list(filter(
    file.outlinks,
    (f) => contains( list("B", "C"), f.status ))) as statusMatches
  WHERE length(statusMatches) > 0
`)

if ( result.successful ) {
  dv.list(result.value.values)
} else 
  dv.paragraph("~~~~\n" + result.error + "\n~~~~")
```

The second query is a somewhat cleaner script based upon dv.pages(), but I’ve opted for using flatMap() to create the statusMatches which I’m not sure is the optimal way to do this. But the query works, and produces the same output as before:

```dataviewjs

const result = dv.pages()
  .where(p => p.status == "A")

// Extract only status "B" and "C" files
// This could possibly be done in a better way...
result.flatMap( p => {
    p.statusMatches = []
    p.file.outlinks.forEach( out => {
      const outP = dv.page(out.path)
      if ( ["B", "C"].includes(outP.status) ) {
        p.statusMatches.push(outP.file.link)
      }
    })
  })
  
dv.list(result
  .where( r => r.statusMatches.length >= 1 )
  .map( p => new ListPairWidget(p.file.link, p.statusMatches) )
  )
```

Finally, a variant reading the file content

The last query variant uses the DQL query to get the same result as before, but in the result.successful branch we read the file contents of the various files and appends the number of lines of each file. (And we do this slightly ineffective as we re-read the files in the outlinks sections. This could be avoided by keeping track of which files we’ve already read, but I’m leaving that as an exercise for the reader :slight_smile: )

```dataviewjs

const result = await dv.query(`
  LIST statusMatches
  WHERE status = "A"
  FLATTEN list(filter(
    file.outlinks,
    (f) => contains( list("B", "C"), f.status ))) as statusMatches
  WHERE length(statusMatches) > 0
`)

function linesInFile(content) {
  return content.split("\n").length
}

if ( result.successful ) {
  const values = await Promise.all(
    result.value.values.map( async (aFile) => {
      const content = await dv.io.load(aFile.key.path)
      aFile.key += ` (${ linesInFile(content) } lines)`
      aFile.value = await Promise.all(
        aFile.value.map( async (outlink) => {
          const content = await dv.io.load(outlink.path)
          return outlink += ` (${ linesInFile(content) } lines)`
        }))
      return aFile
    }))
  dv.list(values)
} else 
  dv.paragraph("~~~~\n" + result.error + "\n~~~~")
```

Maybe this is a whimsical example, but you never said what you wanted to do with the content anyways, so there you go. It should however showcase how to do file reads based upon a result of links. The output of this variant is:


In summary, it’s no problem to list out files with status matching in both the original or linked files, and if you are so inclined these result can be further processed using dv.io.load() or similar methods. It does get a little hairy depending on which method you chooses, and you need to thread carefully when starting to use multiple await..async stuff in javascript.

This response also showcases that you don’t need to go into the app.metadataCache immediately to lift results, and that using dv.pages or another query is a valid approach as well.

1 Like

believe me, even i don’t know anymore haha…

that’s the thing…I even thought my dataview cache can be at fault…but i don’t know anything anymore… something works once but not the second time…

…threw path error on

list 
where status = "A" and (contains(map(file.outlinks, (x) => x.status), "B") or contains(map(file.outlinks, (x) => x.status), "C"))
  • this is what gives me the proper filtered results

well, not quite so i finally gave up on my original approach to query and instead add some tags for reviewed files – thing is i don’t like to have to remember to tag… i’m lazy

yeah, i know, in my other queries i use dv.pages("")


thanks for both of you to put in great ideas!!

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.